From 19c4f872cc6e2460f31c9f6755ffb23d0f506026 Mon Sep 17 00:00:00 2001 From: Liza Katz Date: Tue, 29 Oct 2019 11:55:24 +0200 Subject: [PATCH 01/25] =?UTF-8?q?Move=20filter=20manager=20=E2=87=92=20NP?= =?UTF-8?q?=20(#48391)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Moved filterManager to NP plugin * Fixed applying filters to dashbaord. * Minor fixes * fixed types * fix jest tests mock of filter * Updated karma mccks * Fixed lens test * fixed import * Removed comment --- .../filter/action/apply_filter_action.ts | 4 +- .../apply_filter_popover_content.tsx | 2 +- .../filter_state_manager.test.ts | 30 ++-- .../filter_manager/filter_state_manager.ts | 2 +- .../public/filter/filter_manager/index.ts | 6 - .../core_plugins/data/public/filter/index.tsx | 2 - src/legacy/core_plugins/data/public/index.ts | 18 +-- src/legacy/core_plugins/data/public/mocks.ts | 2 - src/legacy/core_plugins/data/public/plugin.ts | 13 +- .../components/create_search_bar.tsx | 18 +-- .../data/public/shim/legacy_module.ts | 3 - .../data/public/timefilter/index.ts | 2 + .../lib/change_time_filter.test.ts | 2 +- .../lib/change_time_filter.ts | 2 +- .../lib/extract_time_filter.test.ts | 0 .../lib/extract_time_filter.ts | 0 .../public/control/list_control_factory.js | 4 +- .../control/list_control_factory.test.js | 35 +++-- .../public/control/range_control_factory.js | 4 +- .../control/range_control_factory.test.js | 35 +++-- .../public/vis_controller.js | 4 +- .../discover/embeddable/search_embeddable.ts | 4 +- .../embeddable/visualize_embeddable.ts | 4 +- .../tile_map/public/tile_map_visualization.js | 4 +- .../public/vega_visualization.js | 4 +- .../__tests__/filter_generator.js | 2 +- .../ui/public/filter_manager/query_filter.js | 6 +- .../new_platform/new_platform.karma_mock.js | 21 +++ .../ui/public/vis/vis_filters/vis_filters.js | 2 +- .../loader/embedded_visualize_handler.ts | 5 +- src/plugins/data/public/index.ts | 2 + src/plugins/data/public/mocks.ts | 5 + src/plugins/data/public/plugin.ts | 11 +- .../filter_manager/filter_manager.test.ts | 130 ++++++------------ .../query}/filter_manager/filter_manager.ts | 2 +- .../data/public/query/filter_manager/index.ts | 24 ++++ .../lib/compare_filters.test.ts | 0 .../filter_manager/lib/compare_filters.ts | 0 .../filter_manager/lib/dedup_filters.test.ts | 0 .../filter_manager/lib/dedup_filters.ts | 0 .../lib/generate_mapping_chain.test.ts | 0 .../lib/generate_mapping_chain.ts | 0 .../lib/map_and_flatten_filters.test.ts | 0 .../lib/map_and_flatten_filters.ts | 0 .../filter_manager/lib/map_filter.test.ts | 0 .../query}/filter_manager/lib/map_filter.ts | 20 +-- .../lib/mappers}/map_default.test.ts | 0 .../lib/mappers}/map_default.ts | 0 .../lib/mappers}/map_exists.test.ts | 0 .../filter_manager/lib/mappers}/map_exists.ts | 0 .../lib/mappers}/map_geo_bounding_box.test.ts | 0 .../lib/mappers}/map_geo_bounding_box.ts | 0 .../lib/mappers}/map_geo_polygon.test.ts | 0 .../lib/mappers}/map_geo_polygon.ts | 0 .../lib/mappers}/map_match_all.test.ts | 0 .../lib/mappers}/map_match_all.ts | 0 .../lib/mappers}/map_missing.test.ts | 0 .../lib/mappers}/map_missing.ts | 0 .../lib/mappers}/map_phrase.test.ts | 0 .../filter_manager/lib/mappers}/map_phrase.ts | 0 .../lib/mappers}/map_phrases.ts | 0 .../lib/mappers}/map_query_string.test.ts | 0 .../lib/mappers}/map_query_string.ts | 0 .../lib/mappers}/map_range.test.ts | 0 .../filter_manager/lib/mappers}/map_range.ts | 0 .../filter_manager/lib/only_disabled.test.ts | 0 .../filter_manager/lib/only_disabled.ts | 0 .../filter_manager/lib/uniq_filters.test.ts | 0 .../query}/filter_manager/lib/uniq_filters.ts | 0 .../test_helpers/get_filters_array.ts | 0 .../test_helpers/get_stub_filter.ts | 45 ++++++ .../public/query/filter_manager/types.ts} | 0 .../data/public/query/index.tsx} | 12 +- .../data/public/query/mocks.ts} | 12 +- .../data/public/query/query_service.ts} | 12 +- src/plugins/data/public/types.ts | 3 + .../lens/public/app_plugin/app.test.tsx | 12 +- .../plugins/lens/public/app_plugin/app.tsx | 11 +- .../maps/public/angular/map_controller.js | 11 +- .../maps/public/embeddable/map_embeddable.js | 2 +- .../public/components/search_bar/index.tsx | 4 +- 81 files changed, 306 insertions(+), 252 deletions(-) rename src/legacy/core_plugins/data/public/{filter/filter_manager => timefilter}/lib/change_time_filter.test.ts (96%) rename src/legacy/core_plugins/data/public/{filter/filter_manager => timefilter}/lib/change_time_filter.ts (95%) rename src/legacy/core_plugins/data/public/{filter/filter_manager => timefilter}/lib/extract_time_filter.test.ts (100%) rename src/legacy/core_plugins/data/public/{filter/filter_manager => timefilter}/lib/extract_time_filter.ts (100%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/filter_manager.test.ts (86%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/filter_manager.ts (99%) create mode 100644 src/plugins/data/public/query/filter_manager/index.ts rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/lib/compare_filters.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/lib/compare_filters.ts (100%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/lib/dedup_filters.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/lib/dedup_filters.ts (100%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/lib/generate_mapping_chain.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/lib/generate_mapping_chain.ts (100%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/lib/map_and_flatten_filters.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/lib/map_and_flatten_filters.ts (100%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/lib/map_filter.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/lib/map_filter.ts (84%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_default.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_default.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_exists.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_exists.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_geo_bounding_box.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_geo_bounding_box.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_geo_polygon.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_geo_polygon.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_match_all.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_match_all.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_missing.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_missing.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_phrase.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_phrase.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_phrases.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_query_string.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_query_string.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_range.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/lib => plugins/data/public/query/filter_manager/lib/mappers}/map_range.ts (100%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/lib/only_disabled.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/lib/only_disabled.ts (100%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/lib/uniq_filters.test.ts (100%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/lib/uniq_filters.ts (100%) rename src/{legacy/core_plugins/data/public/filter => plugins/data/public/query}/filter_manager/test_helpers/get_filters_array.ts (100%) create mode 100644 src/plugins/data/public/query/filter_manager/test_helpers/get_stub_filter.ts rename src/{legacy/core_plugins/data/public/filter/filter_manager/partitioned_filters.ts => plugins/data/public/query/filter_manager/types.ts} (100%) rename src/{legacy/core_plugins/data/public/filter/filter_manager/test_helpers/stub_index_pattern.ts => plugins/data/public/query/index.tsx} (84%) rename src/{legacy/core_plugins/data/public/filter/filter_service.mock.ts => plugins/data/public/query/mocks.ts} (80%) rename src/{legacy/core_plugins/data/public/filter/filter_service.ts => plugins/data/public/query/query_service.ts} (82%) diff --git a/src/legacy/core_plugins/data/public/filter/action/apply_filter_action.ts b/src/legacy/core_plugins/data/public/filter/action/apply_filter_action.ts index c436e1d45c6d8..a39175077d9e0 100644 --- a/src/legacy/core_plugins/data/public/filter/action/apply_filter_action.ts +++ b/src/legacy/core_plugins/data/public/filter/action/apply_filter_action.ts @@ -25,8 +25,8 @@ import { createAction, IncompatibleActionError, } from '../../../../../../plugins/ui_actions/public'; -import { changeTimeFilter, extractTimeFilter, FilterManager } from '../filter_manager'; -import { TimefilterContract } from '../../timefilter'; +import { FilterManager } from '../../../../../../plugins/data/public'; +import { TimefilterContract, changeTimeFilter, extractTimeFilter } from '../../timefilter'; import { applyFiltersPopover } from '../apply_filters/apply_filters_popover'; import { IndexPatternsStart } from '../../index_patterns'; export const GLOBAL_APPLY_FILTER_ACTION = 'GLOBAL_APPLY_FILTER_ACTION'; diff --git a/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filter_popover_content.tsx b/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filter_popover_content.tsx index 5f7fbc1996433..8fc6b33f3f68a 100644 --- a/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filter_popover_content.tsx +++ b/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filter_popover_content.tsx @@ -33,7 +33,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component } from 'react'; import { IndexPattern } from '../../index_patterns'; import { getFilterDisplayText } from '../filter_bar/filter_editor/lib/get_filter_display_text'; -import { mapAndFlattenFilters } from '../filter_manager/lib/map_and_flatten_filters'; +import { mapAndFlattenFilters } from '../../../../../../plugins/data/public'; import { getDisplayValueFromFilter } from '../filter_bar/filter_editor/lib/get_display_value'; interface Props { diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.test.ts b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.test.ts index d1cad9a812399..aae9c0754a8d8 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.test.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.test.ts @@ -24,7 +24,7 @@ import { FilterStateManager } from './filter_state_manager'; import { StubState } from './test_helpers/stub_state'; import { getFilter } from './test_helpers/get_stub_filter'; -import { FilterManager } from './filter_manager'; +import { FilterManager } from '../../../../../../plugins/data/public'; import { coreMock } from '../../../../../../core/public/mocks'; const setupMock = coreMock.createSetup(); @@ -101,25 +101,29 @@ describe('filter_state_manager', () => { }); test('should update filter manager global filters', done => { - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); - globalStateStub.filters.push(f1); - - setTimeout(() => { + const updateSubscription = filterManager.getUpdates$().subscribe(() => { expect(filterManager.getGlobalFilters()).toHaveLength(1); + if (updateSubscription) { + updateSubscription.unsubscribe(); + } done(); - }, 100); + }); + + const f1 = getFilter(FilterStateStore.GLOBAL_STATE, true, true, 'age', 34); + globalStateStub.filters.push(f1); }); - test('should update filter manager app filters', done => { - expect(filterManager.getAppFilters()).toHaveLength(0); + test('should update filter manager app filter', done => { + const updateSubscription = filterManager.getUpdates$().subscribe(() => { + expect(filterManager.getAppFilters()).toHaveLength(1); + if (updateSubscription) { + updateSubscription.unsubscribe(); + } + done(); + }); const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34); appStateStub.filters.push(f1); - - setTimeout(() => { - expect(filterManager.getAppFilters()).toHaveLength(1); - done(); - }, 100); }); test('should update URL when filter manager filters are set', () => { diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts index 06f91e35db96e..af8722c37c703 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts @@ -21,7 +21,7 @@ import { FilterStateStore } from '@kbn/es-query'; import _ from 'lodash'; import { State } from 'ui/state_management/state'; -import { FilterManager } from './filter_manager'; +import { FilterManager } from '../../../../../../plugins/data/public'; type GetAppStateFunc = () => State | undefined | null; diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/index.ts b/src/legacy/core_plugins/data/public/filter/filter_manager/index.ts index ac533eaaf89ea..ebb622783c3d1 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/index.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_manager/index.ts @@ -17,10 +17,4 @@ * under the License. */ -export { FilterManager } from './filter_manager'; export { FilterStateManager } from './filter_state_manager'; - -export { uniqFilters } from './lib/uniq_filters'; -export { extractTimeFilter } from './lib/extract_time_filter'; -export { changeTimeFilter } from './lib/change_time_filter'; -export { onlyDisabledFiltersChanged } from './lib/only_disabled'; diff --git a/src/legacy/core_plugins/data/public/filter/index.tsx b/src/legacy/core_plugins/data/public/filter/index.tsx index cda7350ecadef..005c4904a4f39 100644 --- a/src/legacy/core_plugins/data/public/filter/index.tsx +++ b/src/legacy/core_plugins/data/public/filter/index.tsx @@ -17,8 +17,6 @@ * under the License. */ -export * from './filter_service'; - export { FilterBar } from './filter_bar'; export { ApplyFiltersPopover } from './apply_filters'; diff --git a/src/legacy/core_plugins/data/public/index.ts b/src/legacy/core_plugins/data/public/index.ts index cb3869ff57711..502ca206e8e12 100644 --- a/src/legacy/core_plugins/data/public/index.ts +++ b/src/legacy/core_plugins/data/public/index.ts @@ -43,14 +43,7 @@ export { SearchBar, SearchBarProps, SavedQueryAttributes, SavedQuery } from './s /** @public static code */ export * from '../common'; -export { - FilterManager, - FilterStateManager, - uniqFilters, - extractTimeFilter, - changeTimeFilter, - onlyDisabledFiltersChanged, -} from './filter/filter_manager'; +export { FilterStateManager } from './filter/filter_manager'; export { CONTAINS_SPACES, getFromSavedObject, @@ -69,4 +62,11 @@ export { mockIndexPattern, } from './index_patterns'; -export { TimeHistoryContract, TimefilterContract, getTime, InputTimeRange } from './timefilter'; +export { + TimeHistoryContract, + TimefilterContract, + getTime, + InputTimeRange, + extractTimeFilter, + changeTimeFilter, +} from './timefilter'; diff --git a/src/legacy/core_plugins/data/public/mocks.ts b/src/legacy/core_plugins/data/public/mocks.ts index 2a82927bb3ebf..4a7fe8efa4068 100644 --- a/src/legacy/core_plugins/data/public/mocks.ts +++ b/src/legacy/core_plugins/data/public/mocks.ts @@ -17,14 +17,12 @@ * under the License. */ -import { filterServiceMock } from './filter/filter_service.mock'; import { indexPatternsServiceMock } from './index_patterns/index_patterns_service.mock'; import { queryServiceMock } from './query/query_service.mock'; import { timefilterServiceMock } from './timefilter/timefilter_service.mock'; function createDataSetupMock() { return { - filter: filterServiceMock.createSetupContract(), indexPatterns: indexPatternsServiceMock.createSetupContract(), query: queryServiceMock.createSetupContract(), timefilter: timefilterServiceMock.createSetupContract(), diff --git a/src/legacy/core_plugins/data/public/plugin.ts b/src/legacy/core_plugins/data/public/plugin.ts index 2b634e54e5cbf..af7c29bf2f4b9 100644 --- a/src/legacy/core_plugins/data/public/plugin.ts +++ b/src/legacy/core_plugins/data/public/plugin.ts @@ -20,7 +20,6 @@ import { CoreSetup, CoreStart, Plugin } from 'kibana/public'; import { SearchService, SearchStart, createSearchBar, StatetfulSearchBarProps } from './search'; import { QueryService, QuerySetup } from './query'; -import { FilterService, FilterSetup, FilterStart } from './filter'; import { TimefilterService, TimefilterSetup } from './timefilter'; import { IndexPatternsService, IndexPatternsSetup, IndexPatternsStart } from './index_patterns'; import { @@ -60,7 +59,6 @@ export interface DataSetup { query: QuerySetup; timefilter: TimefilterSetup; indexPatterns: IndexPatternsSetup; - filter: FilterSetup; } /** @@ -72,7 +70,6 @@ export interface DataStart { query: QuerySetup; timefilter: TimefilterSetup; indexPatterns: IndexPatternsStart; - filter: FilterStart; search: SearchStart; ui: { SearchBar: React.ComponentType; @@ -93,8 +90,6 @@ export interface DataStart { export class DataPlugin implements Plugin { - // Exposed services, sorted alphabetically - private readonly filter: FilterService = new FilterService(); private readonly indexPatterns: IndexPatternsService = new IndexPatternsService(); private readonly query: QueryService = new QueryService(); private readonly search: SearchService = new SearchService(); @@ -109,14 +104,10 @@ export class DataPlugin uiSettings, store: __LEGACY.storage, }); - const filterService = this.filter.setup({ - uiSettings, - }); this.setupApi = { indexPatterns: this.indexPatterns.setup(), query: this.query.setup(), timefilter: timefilterService, - filter: filterService, }; return this.setupApi; @@ -142,12 +133,11 @@ export class DataPlugin data, store: __LEGACY.storage, timefilter: this.setupApi.timefilter, - filterManager: this.setupApi.filter.filterManager, }); uiActions.registerAction( createFilterAction( - this.setupApi.filter.filterManager, + data.query.filterManager, this.setupApi.timefilter.timefilter, indexPatternsService ) @@ -167,7 +157,6 @@ export class DataPlugin public stop() { this.indexPatterns.stop(); - this.filter.stop(); this.query.stop(); this.search.stop(); this.timefilter.stop(); 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 d801f8a69e2d6..7fdbc996ac8c2 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 @@ -25,7 +25,7 @@ import { DataPublicPluginStart } from 'src/plugins/data/public'; import { Storage } from '../../../types'; import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; import { TimefilterSetup } from '../../../timefilter'; -import { FilterManager, SearchBar } from '../../../'; +import { SearchBar } from '../../../'; import { SearchBarOwnProps } from '.'; interface StatefulSearchBarDeps { @@ -33,16 +33,15 @@ interface StatefulSearchBarDeps { data: DataPublicPluginStart; store: Storage; timefilter: TimefilterSetup; - filterManager: FilterManager; } export type StatetfulSearchBarProps = SearchBarOwnProps & { appName: string; }; -const defaultFiltersUpdated = (filterManager: FilterManager) => { +const defaultFiltersUpdated = (data: DataPublicPluginStart) => { return (filters: Filter[]) => { - filterManager.setFilters(filters); + data.query.filterManager.setFilters(filters); }; }; @@ -55,16 +54,11 @@ const defaultOnRefreshChange = (timefilter: TimefilterSetup) => { }; }; -export function createSearchBar({ - core, - store, - timefilter, - filterManager, - data, -}: StatefulSearchBarDeps) { +export function createSearchBar({ core, store, timefilter, data }: StatefulSearchBarDeps) { // 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 { filterManager } = data.query; const tfRefreshInterval = timefilter.timefilter.getRefreshInterval(); const fmFilters = filterManager.getFilters(); const [refreshInterval, setRefreshInterval] = useState(tfRefreshInterval.value); @@ -124,7 +118,7 @@ export function createSearchBar({ refreshInterval={refreshInterval} isRefreshPaused={refreshPaused} filters={filters} - onFiltersUpdated={defaultFiltersUpdated(filterManager)} + onFiltersUpdated={defaultFiltersUpdated(data)} onRefreshChange={defaultOnRefreshChange(timefilter)} {...props} /> diff --git a/src/legacy/core_plugins/data/public/shim/legacy_module.ts b/src/legacy/core_plugins/data/public/shim/legacy_module.ts index 54f513d07215d..b0ed3d43a4c8c 100644 --- a/src/legacy/core_plugins/data/public/shim/legacy_module.ts +++ b/src/legacy/core_plugins/data/public/shim/legacy_module.ts @@ -25,9 +25,6 @@ import { wrapInI18nContext } from 'ui/i18n'; import { uiModules } from 'ui/modules'; import { npStart } from 'ui/new_platform'; import { FilterBar, ApplyFiltersPopover } from '../filter'; - -// @ts-ignore -import { mapAndFlattenFilters } from '../filter/filter_manager/lib/map_and_flatten_filters'; import { IndexPatterns } from '../index_patterns/index_patterns'; /** @internal */ diff --git a/src/legacy/core_plugins/data/public/timefilter/index.ts b/src/legacy/core_plugins/data/public/timefilter/index.ts index 17564801cf148..a6260e782c12f 100644 --- a/src/legacy/core_plugins/data/public/timefilter/index.ts +++ b/src/legacy/core_plugins/data/public/timefilter/index.ts @@ -23,3 +23,5 @@ export * from './types'; export { Timefilter, TimefilterContract } from './timefilter'; export { TimeHistory, TimeHistoryContract } from './time_history'; export { getTime } from './get_time'; +export { changeTimeFilter } from './lib/change_time_filter'; +export { extractTimeFilter } from './lib/extract_time_filter'; diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/change_time_filter.test.ts b/src/legacy/core_plugins/data/public/timefilter/lib/change_time_filter.test.ts similarity index 96% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/change_time_filter.test.ts rename to src/legacy/core_plugins/data/public/timefilter/lib/change_time_filter.test.ts index 2e397ff931bb6..5e16120f3b3c2 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/change_time_filter.test.ts +++ b/src/legacy/core_plugins/data/public/timefilter/lib/change_time_filter.test.ts @@ -19,7 +19,7 @@ import { RangeFilter } from '@kbn/es-query'; import { changeTimeFilter } from './change_time_filter'; import { TimeRange } from 'src/plugins/data/public'; -import { timefilterServiceMock } from '../../../timefilter/timefilter_service.mock'; +import { timefilterServiceMock } from '../timefilter_service.mock'; const timefilterMock = timefilterServiceMock.createSetupContract(); const timefilter = timefilterMock.timefilter; diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/change_time_filter.ts b/src/legacy/core_plugins/data/public/timefilter/lib/change_time_filter.ts similarity index 95% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/change_time_filter.ts rename to src/legacy/core_plugins/data/public/timefilter/lib/change_time_filter.ts index 8cd1ce5ba6c84..4780ddb6b4b44 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/change_time_filter.ts +++ b/src/legacy/core_plugins/data/public/timefilter/lib/change_time_filter.ts @@ -20,7 +20,7 @@ import moment from 'moment'; import { keys } from 'lodash'; import { RangeFilter } from '@kbn/es-query'; -import { TimefilterContract } from '../../../timefilter'; +import { TimefilterContract } from '../timefilter'; export function convertRangeFilterToTimeRange(filter: RangeFilter) { const key = keys(filter.range)[0]; diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/extract_time_filter.test.ts b/src/legacy/core_plugins/data/public/timefilter/lib/extract_time_filter.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/extract_time_filter.test.ts rename to src/legacy/core_plugins/data/public/timefilter/lib/extract_time_filter.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/extract_time_filter.ts b/src/legacy/core_plugins/data/public/timefilter/lib/extract_time_filter.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/extract_time_filter.ts rename to src/legacy/core_plugins/data/public/timefilter/lib/extract_time_filter.ts diff --git a/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.js b/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.js index 44c68c84579c6..86fe6db9b0778 100644 --- a/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.js +++ b/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.js @@ -26,6 +26,7 @@ import { import { PhraseFilterManager } from './filter_manager/phrase_filter_manager'; import { createSearchSource } from './create_search_source'; import { i18n } from '@kbn/i18n'; +import { npStart } from 'ui/new_platform'; import chrome from 'ui/chrome'; import { start as data } from '../../../../core_plugins/data/public/legacy'; @@ -187,9 +188,10 @@ export async function listControlFactory(controlParams, useTimeFilter, SearchSou // ignore not found error and return control so it can be displayed in disabled state. } + const { filterManager } = npStart.plugins.data.query; return new ListControl( controlParams, - new PhraseFilterManager(controlParams.id, controlParams.fieldName, indexPattern, data.filter.filterManager), + new PhraseFilterManager(controlParams.id, controlParams.fieldName, indexPattern, filterManager), useTimeFilter, SearchSource, ); diff --git a/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.test.js b/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.test.js index d5c23c2c1c855..b40a9f8e6efd4 100644 --- a/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.test.js +++ b/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.test.js @@ -24,6 +24,28 @@ jest.mock('ui/timefilter', () => ({ createFilter: jest.fn(), })); +jest.mock('ui/new_platform', () => ({ + npStart: { + plugins: { + data: { + query: { + filterManager: { + fieldName: 'myNumberField', + getIndexPattern: () => ({ + fields: { getByName: name => { + const fields = { myField: { name: 'myField' } }; + return fields[name]; + } } + }), + getAppFilters: jest.fn().mockImplementation(() => ([])), + getGlobalFilters: jest.fn().mockImplementation(() => ([])), + } + } + } + }, + }, +})); + jest.mock('../../../../core_plugins/data/public/legacy', () => ({ start: { indexPatterns: { @@ -36,19 +58,6 @@ jest.mock('../../../../core_plugins/data/public/legacy', () => ({ }), } }, - filter: { - filterManager: { - fieldName: 'myNumberField', - getIndexPattern: () => ({ - fields: { getByName: name => { - const fields = { myField: { name: 'myField' } }; - return fields[name]; - } } - }), - getAppFilters: jest.fn().mockImplementation(() => ([])), - getGlobalFilters: jest.fn().mockImplementation(() => ([])), - } - } } })); diff --git a/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.js b/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.js index efb208bd80045..2a05a1224aab9 100644 --- a/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.js +++ b/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.js @@ -27,6 +27,7 @@ import { RangeFilterManager } from './filter_manager/range_filter_manager'; import { createSearchSource } from './create_search_source'; import { i18n } from '@kbn/i18n'; import { start as data } from '../../../../core_plugins/data/public/legacy'; +import { npStart } from 'ui/new_platform'; const minMaxAgg = (field) => { const aggBody = {}; @@ -106,9 +107,10 @@ export async function rangeControlFactory(controlParams, useTimeFilter, SearchSo } catch (err) { // ignore not found error and return control so it can be displayed in disabled state. } + const { filterManager } = npStart.plugins.data.query; return new RangeControl( controlParams, - new RangeFilterManager(controlParams.id, controlParams.fieldName, indexPattern, data.filter.filterManager), + new RangeFilterManager(controlParams.id, controlParams.fieldName, indexPattern, filterManager), useTimeFilter, SearchSource, ); diff --git a/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.test.js b/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.test.js index c746d116c70b2..3e6d6a49a1118 100644 --- a/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.test.js +++ b/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.test.js @@ -32,6 +32,28 @@ jest.mock('ui/timefilter', () => ({ createFilter: jest.fn(), })); +jest.mock('ui/new_platform', () => ({ + npStart: { + plugins: { + data: { + query: { + filterManager: { + fieldName: 'myNumberField', + getIndexPattern: () => ({ + fields: { getByName: name => { + const fields = { myNumberField: { name: 'myNumberField' } }; + return fields[name]; + } + } }), + getAppFilters: jest.fn().mockImplementation(() => ([])), + getGlobalFilters: jest.fn().mockImplementation(() => ([])), + } + } + } + }, + }, +})); + jest.mock('../../../../core_plugins/data/public/legacy', () => ({ start: { indexPatterns: { @@ -44,19 +66,6 @@ jest.mock('../../../../core_plugins/data/public/legacy', () => ({ } }), } }, - filter: { - filterManager: { - fieldName: 'myNumberField', - getIndexPattern: () => ({ - fields: { getByName: name => { - const fields = { myNumberField: { name: 'myNumberField' } }; - return fields[name]; - } - } }), - getAppFilters: jest.fn().mockImplementation(() => ([])), - getGlobalFilters: jest.fn().mockImplementation(() => ([])), - } - } } })); diff --git a/src/legacy/core_plugins/input_control_vis/public/vis_controller.js b/src/legacy/core_plugins/input_control_vis/public/vis_controller.js index 1edf5652a76c5..792ff3fe85479 100644 --- a/src/legacy/core_plugins/input_control_vis/public/vis_controller.js +++ b/src/legacy/core_plugins/input_control_vis/public/vis_controller.js @@ -23,7 +23,7 @@ import { I18nContext } from 'ui/i18n'; import { InputControlVis } from './components/vis/input_control_vis'; import { controlFactory } from './control/control_factory'; import { getLineageMap } from './lineage'; -import { start as data } from '../../../core_plugins/data/public/legacy'; +import { npStart } from 'ui/new_platform'; import { SearchSource } from '../../../ui/public/courier/search_source/search_source'; class VisController { @@ -34,7 +34,7 @@ class VisController { this.queryBarUpdateHandler = this.updateControlsFromKbn.bind(this); - this.filterManager = data.filter.filterManager; + this.filterManager = npStart.plugins.data.query.filterManager; this.updateSubsciption = this.filterManager.getUpdates$() .subscribe(this.queryBarUpdateHandler); } diff --git a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts index 5f3ebd6d22e24..e777501d35ca0 100644 --- a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts +++ b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts @@ -22,8 +22,9 @@ import { Subscription } from 'rxjs'; import { Filter, FilterStateStore } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { TExecuteTriggerActions } from 'src/plugins/ui_actions/public'; +import { TimeRange, onlyDisabledFiltersChanged } from '../../../../../../plugins/data/public'; import { setup as data } from '../../../../data/public/legacy'; -import { getTime, onlyDisabledFiltersChanged, Query } from '../../../../data/public'; +import { Query, getTime } from '../../../../data/public'; import { APPLY_FILTER_TRIGGER, Container, @@ -46,7 +47,6 @@ import { RequestAdapter, SearchSource, } from '../kibana_services'; -import { TimeRange } from '../../../../../../plugins/data/public'; import { SEARCH_EMBEDDABLE_TYPE } from './constants'; interface SearchScope extends ng.IScope { diff --git a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts index b9febc3af54ea..ea0b0fe626d7d 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts @@ -30,14 +30,14 @@ import { import { Subscription } from 'rxjs'; import * as Rx from 'rxjs'; import { Filter } from '@kbn/es-query'; -import { TimeRange } from '../../../../../../plugins/data/public'; +import { TimeRange, onlyDisabledFiltersChanged } from '../../../../../../plugins/data/public'; import { EmbeddableInput, EmbeddableOutput, Embeddable, Container, } from '../../../../../../plugins/embeddable/public'; -import { Query, onlyDisabledFiltersChanged } from '../../../../data/public'; +import { Query } from '../../../../data/public'; import { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; const getKeys = (o: T): Array => Object.keys(o) as Array; diff --git a/src/legacy/core_plugins/tile_map/public/tile_map_visualization.js b/src/legacy/core_plugins/tile_map/public/tile_map_visualization.js index 5b8bb07577510..ca798b6bf2470 100644 --- a/src/legacy/core_plugins/tile_map/public/tile_map_visualization.js +++ b/src/legacy/core_plugins/tile_map/public/tile_map_visualization.js @@ -21,9 +21,8 @@ import { get } from 'lodash'; import { GeohashLayer } from './geohash_layer'; import { BaseMapsVisualizationProvider } from './base_maps_visualization'; import { TileMapTooltipFormatterProvider } from './editors/_tooltip_formatter'; +import { npStart } from 'ui/new_platform'; import { getFormat } from '../../../ui/public/visualize/loader/pipeline_helpers/utilities'; -import { start as data } from '../../../core_plugins/data/public/legacy'; -const filterManager = data.filter.filterManager; export const createTileMapVisualization = ({ serviceSettings, $injector }) => { const BaseMapsVisualization = new BaseMapsVisualizationProvider(serviceSettings); @@ -189,6 +188,7 @@ export const createTileMapVisualization = ({ serviceSettings, $injector }) => { filter[filterName] = { ignore_unmapped: true }; filter[filterName][field] = filterData; + const { filterManager } = npStart.plugins.data.query; filterManager.addFilters([filter]); this.vis.updateState(); diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_visualization.js b/src/legacy/core_plugins/vis_type_vega/public/vega_visualization.js index 1dc73d6f9ff20..7aa60bb0cc469 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/vega_visualization.js +++ b/src/legacy/core_plugins/vis_type_vega/public/vega_visualization.js @@ -23,6 +23,7 @@ import { VegaView } from './vega_view/vega_view'; import { VegaMapView } from './vega_view/vega_map_view'; import { timefilter } from 'ui/timefilter'; import { start as data } from '../../../core_plugins/data/public/legacy'; +import { npStart } from 'ui/new_platform'; import { findIndexPatternByTitle } from '../../data/public/index_patterns'; @@ -99,11 +100,12 @@ export const createVegaVisualization = ({ serviceSettings }) => class VegaVisual this._vegaView = null; } + const { filterManager } = npStart.plugins.data.query; const vegaViewParams = { parentEl: this._el, vegaParser, serviceSettings, - queryfilter: data.filter.filterManager, + queryfilter: filterManager, timefilter: timefilter, findIndex: this.findIndex.bind(this), }; diff --git a/src/legacy/ui/public/filter_manager/__tests__/filter_generator.js b/src/legacy/ui/public/filter_manager/__tests__/filter_generator.js index a6bf2e7aa6c4c..ed7e25704d5a5 100644 --- a/src/legacy/ui/public/filter_manager/__tests__/filter_generator.js +++ b/src/legacy/ui/public/filter_manager/__tests__/filter_generator.js @@ -24,7 +24,7 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import { getFilterGenerator } from '..'; import { FilterBarQueryFilterProvider } from '../../filter_manager/query_filter'; -import { uniqFilters } from '../../../../core_plugins/data/public/filter/filter_manager/lib/uniq_filters'; +import { uniqFilters } from '../../../../../plugins/data/public'; import { getPhraseScript } from '@kbn/es-query'; let queryFilter; let filterGen; diff --git a/src/legacy/ui/public/filter_manager/query_filter.js b/src/legacy/ui/public/filter_manager/query_filter.js index 6afe52502df5d..97b3810b7f1c7 100644 --- a/src/legacy/ui/public/filter_manager/query_filter.js +++ b/src/legacy/ui/public/filter_manager/query_filter.js @@ -18,12 +18,10 @@ */ import { FilterStateManager } from 'plugins/data'; +import { npStart } from 'ui/new_platform'; export function FilterBarQueryFilterProvider(getAppState, globalState) { - // TODO: this is imported here to avoid circular imports. - // eslint-disable-next-line @typescript-eslint/no-var-requires - const { start } = require('../../../core_plugins/data/public/legacy'); - const filterManager = start.filter.filterManager; + const { filterManager } = npStart.plugins.data.query; const filterStateManager = new FilterStateManager(globalState, getAppState, filterManager); const queryFilter = {}; diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index 20ea9c9141aca..c74288c98d79c 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -39,6 +39,9 @@ export const npSetup = { }, }, data: { + query: { + filterManager: sinon.fake(), + }, }, inspector: { registerView: () => undefined, @@ -73,6 +76,24 @@ export const npStart = { }, data: { getSuggestions: sinon.fake(), + query: { + filterManager: { + getFetches$: sinon.fake(), + getFilters: sinon.fake(), + getAppFilters: sinon.fake(), + getGlobalFilters: sinon.fake(), + removeFilter: sinon.fake(), + addFilters: sinon.fake(), + setFilters: sinon.fake(), + removeAll: sinon.fake(), + getUpdates$: () => { + return { + subscribe: () => {} + }; + }, + + }, + }, }, inspector: { isAvailable: () => false, diff --git a/src/legacy/ui/public/vis/vis_filters/vis_filters.js b/src/legacy/ui/public/vis/vis_filters/vis_filters.js index f19e2440a21e9..9343585fa9508 100644 --- a/src/legacy/ui/public/vis/vis_filters/vis_filters.js +++ b/src/legacy/ui/public/vis/vis_filters/vis_filters.js @@ -20,7 +20,7 @@ import _ from 'lodash'; import { pushFilterBarFilters } from '../push_filters'; import { onBrushEvent } from './brush_event'; -import { uniqFilters } from '../../../../core_plugins/data/public'; +import { uniqFilters } from '../../../../../plugins/data/public'; import { toggleFilterNegated } from '@kbn/es-query'; /** * For terms aggregations on `__other__` buckets, this assembles a list of applicable filter diff --git a/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.ts b/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.ts index bc2152911d1ec..135260ac01b17 100644 --- a/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.ts +++ b/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.ts @@ -28,7 +28,8 @@ import { toastNotifications } from 'ui/notify'; import { AggConfigs } from 'ui/agg_types/agg_configs'; import { SearchSource } from 'ui/courier'; import { QueryFilter } from 'ui/filter_manager/query_filter'; -import { TimeRange } from 'src/plugins/data/public'; + +import { TimeRange, onlyDisabledFiltersChanged } from '../../../../../plugins/data/public'; import { registries } from '../../../../core_plugins/interpreter/public/registries'; import { Inspector } from '../../inspector'; import { Adapters } from '../../inspector/types'; @@ -42,7 +43,7 @@ import { Vis } from '../../vis'; import { VisFiltersProvider } from '../../vis/vis_filters'; import { PipelineDataLoader } from './pipeline_data_loader'; import { visualizationLoader } from './visualization_loader'; -import { onlyDisabledFiltersChanged, Query } from '../../../../core_plugins/data/public'; +import { Query } from '../../../../core_plugins/data/public'; import { DataAdapter, RequestAdapter } from '../../inspector/adapters'; diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 7e1b3801b62a4..51f26a4bd7f31 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -34,3 +34,5 @@ export * from './types'; export { IRequestTypesMap, IResponseTypesMap } from './search'; export * from './search'; + +export * from './query'; diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index 5e60ca93378d9..2269ba3c55bce 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -18,6 +18,7 @@ */ import { Plugin } from '.'; import { searchSetupMock } from './search/mocks'; +import { queryServiceMock } from './query/mocks'; export type Setup = jest.Mocked>; export type Start = jest.Mocked>; @@ -29,19 +30,23 @@ const autocompleteMock: any = { }; const createSetupContract = (): Setup => { + const querySetupMock = queryServiceMock.createSetupContract(); const setupContract: Setup = { autocomplete: autocompleteMock as Setup['autocomplete'], search: searchSetupMock, + query: querySetupMock, }; return setupContract; }; const createStartContract = (): Start => { + const queryStartMock = queryServiceMock.createStartContract(); const startContract: Start = { autocomplete: autocompleteMock as Start['autocomplete'], getSuggestions: jest.fn(), search: { search: jest.fn() }, + query: queryStartMock, }; return startContract; }; diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index 935a3c5754503..a13e912e77846 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -18,23 +18,29 @@ */ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public'; -import { AutocompleteProviderRegister } from './autocomplete_provider'; import { DataPublicPluginSetup, DataPublicPluginStart } from './types'; -import { SearchService } from './search/search_service'; +import { AutocompleteProviderRegister } from './autocomplete_provider'; import { getSuggestionsProvider } from './suggestions_provider'; +import { SearchService } from './search/search_service'; +import { QueryService } from './query'; export class DataPublicPlugin implements Plugin { private readonly autocomplete = new AutocompleteProviderRegister(); private readonly searchService: SearchService; + private readonly queryService: QueryService; constructor(initializerContext: PluginInitializerContext) { this.searchService = new SearchService(initializerContext); + this.queryService = new QueryService(); } public setup(core: CoreSetup): DataPublicPluginSetup { return { autocomplete: this.autocomplete, search: this.searchService.setup(core), + query: this.queryService.setup({ + uiSettings: core.uiSettings, + }), }; } @@ -43,6 +49,7 @@ export class DataPublicPlugin implements Plugin { @@ -38,9 +34,6 @@ setupMock.uiSettings.get.mockImplementation((key: string) => { }); describe('filter_manager', () => { - let appStateStub: StubState; - let globalStateStub: StubState; - let updateSubscription: Subscription | undefined; let fetchSubscription: Subscription | undefined; let updateListener: sinon.SinonSpy; @@ -50,20 +43,8 @@ describe('filter_manager', () => { beforeEach(() => { updateListener = sinon.stub(); - appStateStub = new StubState(); - globalStateStub = new StubState(); filterManager = new FilterManager(setupMock.uiSettings); readyFilters = getFiltersArray(); - - // FilterStateManager is tested indirectly. - // Therefore, we don't need it's instance. - new FilterStateManager( - globalStateStub, - () => { - return appStateStub; - }, - filterManager - ); }); afterEach(async () => { @@ -84,32 +65,6 @@ describe('filter_manager', () => { expect(updateSubscription).toBeInstanceOf(Subscription); expect(fetchSubscription).toBeInstanceOf(Subscription); }); - - test('should observe global state', done => { - updateSubscription = filterManager.getUpdates$().subscribe(() => { - expect(filterManager.getGlobalFilters()).toHaveLength(1); - if (updateSubscription) { - updateSubscription.unsubscribe(); - } - done(); - }); - - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, true, true, 'age', 34); - globalStateStub.filters.push(f1); - }); - - test('should observe app state', done => { - updateSubscription = filterManager.getUpdates$().subscribe(() => { - expect(filterManager.getAppFilters()).toHaveLength(1); - if (updateSubscription) { - updateSubscription.unsubscribe(); - } - done(); - }); - - const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34); - appStateStub.filters.push(f1); - }); }); describe('get \\ set filters', () => { @@ -222,10 +177,11 @@ describe('filter_manager', () => { updateSubscription = filterManager.getUpdates$().subscribe(updateListener); const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34); filterManager.addFilters(f1); - expect(filterManager.getAppFilters()).toHaveLength(1); + const appFilters = filterManager.getAppFilters(); + expect(appFilters).toHaveLength(1); + expect(appFilters[0]).toEqual(f1); expect(filterManager.getGlobalFilters()).toHaveLength(0); expect(updateListener.callCount).toBe(1); - expect(appStateStub.filters.length).toBe(1); }); test('app state should accept array', async () => { @@ -233,9 +189,10 @@ describe('filter_manager', () => { const f2 = getFilter(FilterStateStore.APP_STATE, false, false, 'gender', 'female'); filterManager.addFilters([f1]); filterManager.addFilters([f2]); - expect(filterManager.getAppFilters()).toHaveLength(2); + const appFilters = filterManager.getAppFilters(); + expect(appFilters).toHaveLength(2); + expect(appFilters).toEqual([f2, f1]); expect(filterManager.getGlobalFilters()).toHaveLength(0); - expect(appStateStub.filters.length).toBe(2); }); test('global state should accept a single filer', async () => { @@ -243,9 +200,10 @@ describe('filter_manager', () => { const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); filterManager.addFilters(f1); expect(filterManager.getAppFilters()).toHaveLength(0); - expect(filterManager.getGlobalFilters()).toHaveLength(1); + const globalFilters = filterManager.getGlobalFilters(); + expect(globalFilters).toHaveLength(1); + expect(globalFilters[0]).toEqual(f1); expect(updateListener.callCount).toBe(1); - expect(globalStateStub.filters.length).toBe(1); }); test('global state should be accept array', async () => { @@ -253,8 +211,9 @@ describe('filter_manager', () => { const f2 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'gender', 'female'); filterManager.addFilters([f1, f2]); expect(filterManager.getAppFilters()).toHaveLength(0); - expect(filterManager.getGlobalFilters()).toHaveLength(2); - expect(globalStateStub.filters.length).toBe(2); + const globalFilters = filterManager.getGlobalFilters(); + expect(globalFilters).toHaveLength(2); + expect(globalFilters).toEqual([f2, f1]); }); test('add multiple filters at once', async () => { @@ -370,7 +329,6 @@ describe('filter_manager', () => { filterManager.addFilters(negatedFilter); // The negated filter should overwrite the positive one - expect(globalStateStub.filters.length).toBe(1); expect(filterManager.getFilters()).toHaveLength(1); expect(filterManager.getFilters()[0]).toEqual(negatedFilter); }); @@ -383,16 +341,16 @@ describe('filter_manager', () => { filterManager.addFilters(negatedFilter); // The negated filter should overwrite the positive one - expect(globalStateStub.filters.length).toBe(1); - expect(globalStateStub.filters[0]).toEqual(negatedFilter); + expect(filterManager.getFilters()).toHaveLength(1); + expect(filterManager.getFilters()[0]).toEqual(negatedFilter); // Add negate: false version of the filter const filter = _.cloneDeep(readyFilters[0]); filter.meta.negate = false; filterManager.addFilters(filter); - expect(globalStateStub.filters.length).toBe(1); - expect(globalStateStub.filters[0]).toEqual(filter); + expect(filterManager.getFilters()).toHaveLength(1); + expect(filterManager.getFilters()[0]).toEqual(filter); }); test('should fire the update and fetch events', async function() { @@ -409,10 +367,6 @@ describe('filter_manager', () => { filterManager.addFilters(readyFilters); - // updates should trigger state saves - expect(appStateStub.save.callCount).toBe(1); - expect(globalStateStub.save.callCount).toBe(1); - // this time, events should be emitted expect(fetchStub).toBeCalledTimes(1); expect(updateStub).toBeCalledTimes(1); @@ -420,25 +374,25 @@ describe('filter_manager', () => { }); describe('filter reconciliation', function() { - test('should de-dupe appStateStub filters being added', async function() { + test('should de-dupe app filters being added', async function() { const newFilter = _.cloneDeep(readyFilters[1]); filterManager.addFilters(readyFilters, false); - expect(appStateStub.filters.length).toBe(3); + expect(filterManager.getFilters()).toHaveLength(3); filterManager.addFilters(newFilter, false); - expect(appStateStub.filters.length).toBe(3); + expect(filterManager.getFilters()).toHaveLength(3); }); - test('should de-dupe globalStateStub filters being added', async function() { + test('should de-dupe global filters being added', async function() { const newFilter = _.cloneDeep(readyFilters[1]); filterManager.addFilters(readyFilters, true); - expect(globalStateStub.filters.length).toBe(3); + expect(filterManager.getFilters()).toHaveLength(3); filterManager.addFilters(newFilter, true); - expect(globalStateStub.filters.length).toBe(3); + expect(filterManager.getFilters()).toHaveLength(3); }); - test('should de-dupe globalStateStub filters being set', async () => { + test('should de-dupe global filters being set', async () => { const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); const f2 = _.cloneDeep(f1); filterManager.setFilters([f1, f2]); @@ -447,7 +401,7 @@ describe('filter_manager', () => { expect(filterManager.getFilters()).toHaveLength(1); }); - test('should de-dupe appStateStub filters being set', async () => { + test('should de-dupe app filters being set', async () => { const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34); const f2 = _.cloneDeep(f1); filterManager.setFilters([f1, f2]); @@ -475,7 +429,7 @@ describe('filter_manager', () => { }); }); - test('should merge conflicting appStateStub filters', async function() { + test('should merge conflicting app filters', async function() { filterManager.addFilters(readyFilters, true); const appFilter = _.cloneDeep(readyFilters[1]); appFilter.meta.negate = true; @@ -580,16 +534,16 @@ describe('filter_manager', () => { test('should remove the filter from appStateStub', async function() { filterManager.addFilters(readyFilters, false); - expect(appStateStub.filters).toHaveLength(3); + expect(filterManager.getAppFilters()).toHaveLength(3); filterManager.removeFilter(readyFilters[0]); - expect(appStateStub.filters).toHaveLength(2); + expect(filterManager.getAppFilters()).toHaveLength(2); }); test('should remove the filter from globalStateStub', async function() { filterManager.addFilters(readyFilters, true); - expect(globalStateStub.filters).toHaveLength(3); + expect(filterManager.getGlobalFilters()).toHaveLength(3); filterManager.removeFilter(readyFilters[0]); - expect(globalStateStub.filters).toHaveLength(2); + expect(filterManager.getGlobalFilters()).toHaveLength(2); }); test('should fire the update and fetch events', async function() { @@ -619,8 +573,8 @@ describe('filter_manager', () => { filterManager.removeFilter(readyFilters[0]); - expect(globalStateStub.filters).toHaveLength(1); - expect(appStateStub.filters).toHaveLength(1); + expect(filterManager.getAppFilters()).toHaveLength(1); + expect(filterManager.getGlobalFilters()).toHaveLength(1); }); test('should remove matching filters by comparison', async function() { @@ -629,12 +583,12 @@ describe('filter_manager', () => { filterManager.removeFilter(_.cloneDeep(readyFilters[0])); - expect(globalStateStub.filters).toHaveLength(1); - expect(appStateStub.filters).toHaveLength(1); + expect(filterManager.getAppFilters()).toHaveLength(1); + expect(filterManager.getGlobalFilters()).toHaveLength(1); filterManager.removeFilter(_.cloneDeep(readyFilters[2])); - expect(globalStateStub.filters).toHaveLength(1); - expect(appStateStub.filters).toHaveLength(0); + expect(filterManager.getAppFilters()).toHaveLength(0); + expect(filterManager.getGlobalFilters()).toHaveLength(1); }); test('should do nothing with a non-matching filter', async function() { @@ -645,19 +599,19 @@ describe('filter_manager', () => { missedFilter.meta.negate = !readyFilters[0].meta.negate; filterManager.removeFilter(missedFilter); - expect(globalStateStub.filters).toHaveLength(2); - expect(appStateStub.filters).toHaveLength(1); + expect(filterManager.getAppFilters()).toHaveLength(1); + expect(filterManager.getGlobalFilters()).toHaveLength(2); }); test('should remove all the filters from both states', async function() { filterManager.addFilters([readyFilters[0], readyFilters[1]], true); filterManager.addFilters([readyFilters[2]], false); - expect(globalStateStub.filters).toHaveLength(2); - expect(appStateStub.filters).toHaveLength(1); + expect(filterManager.getAppFilters()).toHaveLength(1); + expect(filterManager.getGlobalFilters()).toHaveLength(2); filterManager.removeAll(); - expect(globalStateStub.filters).toHaveLength(0); - expect(appStateStub.filters).toHaveLength(0); + expect(filterManager.getAppFilters()).toHaveLength(0); + expect(filterManager.getGlobalFilters()).toHaveLength(0); }); }); diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_manager.ts b/src/plugins/data/public/query/filter_manager/filter_manager.ts similarity index 99% rename from src/legacy/core_plugins/data/public/filter/filter_manager/filter_manager.ts rename to src/plugins/data/public/query/filter_manager/filter_manager.ts index b3d6bd6873f50..66b65a40926cb 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_manager.ts +++ b/src/plugins/data/public/query/filter_manager/filter_manager.ts @@ -28,7 +28,7 @@ import { compareFilters } from './lib/compare_filters'; import { mapAndFlattenFilters } from './lib/map_and_flatten_filters'; import { uniqFilters } from './lib/uniq_filters'; import { onlyDisabledFiltersChanged } from './lib/only_disabled'; -import { PartitionedFilters } from './partitioned_filters'; +import { PartitionedFilters } from './types'; export class FilterManager { private filters: Filter[] = []; diff --git a/src/plugins/data/public/query/filter_manager/index.ts b/src/plugins/data/public/query/filter_manager/index.ts new file mode 100644 index 0000000000000..7955cdd825ee6 --- /dev/null +++ b/src/plugins/data/public/query/filter_manager/index.ts @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { FilterManager } from './filter_manager'; + +export { uniqFilters } from './lib/uniq_filters'; +export { mapAndFlattenFilters } from './lib/map_and_flatten_filters'; +export { onlyDisabledFiltersChanged } from './lib/only_disabled'; diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/compare_filters.test.ts b/src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/compare_filters.test.ts rename to src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/compare_filters.ts b/src/plugins/data/public/query/filter_manager/lib/compare_filters.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/compare_filters.ts rename to src/plugins/data/public/query/filter_manager/lib/compare_filters.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/dedup_filters.test.ts b/src/plugins/data/public/query/filter_manager/lib/dedup_filters.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/dedup_filters.test.ts rename to src/plugins/data/public/query/filter_manager/lib/dedup_filters.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/dedup_filters.ts b/src/plugins/data/public/query/filter_manager/lib/dedup_filters.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/dedup_filters.ts rename to src/plugins/data/public/query/filter_manager/lib/dedup_filters.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/generate_mapping_chain.test.ts b/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/generate_mapping_chain.test.ts rename to src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/generate_mapping_chain.ts b/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/generate_mapping_chain.ts rename to src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_and_flatten_filters.test.ts b/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_and_flatten_filters.test.ts rename to src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_and_flatten_filters.ts b/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_and_flatten_filters.ts rename to src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_filter.test.ts b/src/plugins/data/public/query/filter_manager/lib/map_filter.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_filter.test.ts rename to src/plugins/data/public/query/filter_manager/lib/map_filter.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_filter.ts b/src/plugins/data/public/query/filter_manager/lib/map_filter.ts similarity index 84% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_filter.ts rename to src/plugins/data/public/query/filter_manager/lib/map_filter.ts index c0d251e647fd1..cda9591e40b33 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_filter.ts +++ b/src/plugins/data/public/query/filter_manager/lib/map_filter.ts @@ -20,16 +20,16 @@ import { Filter } from '@kbn/es-query'; import { reduceRight } from 'lodash'; -import { mapMatchAll } from './map_match_all'; -import { mapPhrase } from './map_phrase'; -import { mapPhrases } from './map_phrases'; -import { mapRange } from './map_range'; -import { mapExists } from './map_exists'; -import { mapMissing } from './map_missing'; -import { mapQueryString } from './map_query_string'; -import { mapGeoBoundingBox } from './map_geo_bounding_box'; -import { mapGeoPolygon } from './map_geo_polygon'; -import { mapDefault } from './map_default'; +import { mapMatchAll } from './mappers/map_match_all'; +import { mapPhrase } from './mappers/map_phrase'; +import { mapPhrases } from './mappers/map_phrases'; +import { mapRange } from './mappers/map_range'; +import { mapExists } from './mappers/map_exists'; +import { mapMissing } from './mappers/map_missing'; +import { mapQueryString } from './mappers/map_query_string'; +import { mapGeoBoundingBox } from './mappers/map_geo_bounding_box'; +import { mapGeoPolygon } from './mappers/map_geo_polygon'; +import { mapDefault } from './mappers/map_default'; import { generateMappingChain } from './generate_mapping_chain'; export function mapFilter(filter: Filter) { diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_default.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_default.test.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_default.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_default.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_default.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_default.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_exists.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_exists.test.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_exists.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_exists.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_geo_bounding_box.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_geo_bounding_box.test.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_geo_bounding_box.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_geo_bounding_box.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_geo_polygon.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_geo_polygon.test.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_geo_polygon.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_geo_polygon.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_match_all.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_match_all.test.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_match_all.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_match_all.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_missing.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_missing.test.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_missing.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_missing.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_phrase.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_phrase.test.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_phrase.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_phrase.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_phrases.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_phrases.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_query_string.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_query_string.test.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_query_string.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_query_string.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_range.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_range.test.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_range.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_range.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/map_range.ts rename to src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/only_disabled.test.ts b/src/plugins/data/public/query/filter_manager/lib/only_disabled.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/only_disabled.test.ts rename to src/plugins/data/public/query/filter_manager/lib/only_disabled.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/only_disabled.ts b/src/plugins/data/public/query/filter_manager/lib/only_disabled.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/only_disabled.ts rename to src/plugins/data/public/query/filter_manager/lib/only_disabled.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/uniq_filters.test.ts b/src/plugins/data/public/query/filter_manager/lib/uniq_filters.test.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/uniq_filters.test.ts rename to src/plugins/data/public/query/filter_manager/lib/uniq_filters.test.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/lib/uniq_filters.ts b/src/plugins/data/public/query/filter_manager/lib/uniq_filters.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/lib/uniq_filters.ts rename to src/plugins/data/public/query/filter_manager/lib/uniq_filters.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/test_helpers/get_filters_array.ts b/src/plugins/data/public/query/filter_manager/test_helpers/get_filters_array.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/test_helpers/get_filters_array.ts rename to src/plugins/data/public/query/filter_manager/test_helpers/get_filters_array.ts diff --git a/src/plugins/data/public/query/filter_manager/test_helpers/get_stub_filter.ts b/src/plugins/data/public/query/filter_manager/test_helpers/get_stub_filter.ts new file mode 100644 index 0000000000000..20d9e236f49be --- /dev/null +++ b/src/plugins/data/public/query/filter_manager/test_helpers/get_stub_filter.ts @@ -0,0 +1,45 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Filter, FilterStateStore } from '@kbn/es-query'; + +export function getFilter( + store: FilterStateStore, + disabled: boolean, + negated: boolean, + queryKey: string, + queryValue: any +): Filter { + return { + $state: { + store, + }, + meta: { + index: 'logstash-*', + disabled, + negate: negated, + alias: null, + }, + query: { + match: { + [queryKey]: queryValue, + }, + }, + }; +} diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/partitioned_filters.ts b/src/plugins/data/public/query/filter_manager/types.ts similarity index 100% rename from src/legacy/core_plugins/data/public/filter/filter_manager/partitioned_filters.ts rename to src/plugins/data/public/query/filter_manager/types.ts diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/test_helpers/stub_index_pattern.ts b/src/plugins/data/public/query/index.tsx similarity index 84% rename from src/legacy/core_plugins/data/public/filter/filter_manager/test_helpers/stub_index_pattern.ts rename to src/plugins/data/public/query/index.tsx index d429fc7f70f38..44b371b6adf19 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/test_helpers/stub_index_pattern.ts +++ b/src/plugins/data/public/query/index.tsx @@ -17,12 +17,6 @@ * under the License. */ -export class StubIndexPatterns { - async get(index: string) { - return { - fields: { - getByName: () => undefined, - }, - }; - } -} +export * from './query_service'; + +export * from './filter_manager'; diff --git a/src/legacy/core_plugins/data/public/filter/filter_service.mock.ts b/src/plugins/data/public/query/mocks.ts similarity index 80% rename from src/legacy/core_plugins/data/public/filter/filter_service.mock.ts rename to src/plugins/data/public/query/mocks.ts index 94268ef69c49a..e5030c5765316 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_service.mock.ts +++ b/src/plugins/data/public/query/mocks.ts @@ -17,12 +17,12 @@ * under the License. */ -import { FilterService, FilterStart, FilterSetup } from '.'; +import { QueryService, QueryStart, QuerySetup } from '.'; -type FilterServiceClientContract = PublicMethodsOf; +type QueryServiceClientContract = PublicMethodsOf; const createSetupContractMock = () => { - const setupContract: jest.Mocked = { + const setupContract: jest.Mocked = { filterManager: jest.fn() as any, }; @@ -30,7 +30,7 @@ const createSetupContractMock = () => { }; const createStartContractMock = () => { - const startContract: jest.Mocked = { + const startContract: jest.Mocked = { filterManager: jest.fn() as any, }; @@ -38,7 +38,7 @@ const createStartContractMock = () => { }; const createMock = () => { - const mocked: jest.Mocked = { + const mocked: jest.Mocked = { setup: jest.fn(), start: jest.fn(), stop: jest.fn(), @@ -49,7 +49,7 @@ const createMock = () => { return mocked; }; -export const filterServiceMock = { +export const queryServiceMock = { create: createMock, createSetupContract: createSetupContractMock, createStartContract: createStartContractMock, diff --git a/src/legacy/core_plugins/data/public/filter/filter_service.ts b/src/plugins/data/public/query/query_service.ts similarity index 82% rename from src/legacy/core_plugins/data/public/filter/filter_service.ts rename to src/plugins/data/public/query/query_service.ts index 0c46259ef0e00..d34909a5e03b7 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_service.ts +++ b/src/plugins/data/public/query/query_service.ts @@ -21,18 +21,18 @@ import { UiSettingsClientContract } from 'src/core/public'; import { FilterManager } from './filter_manager'; /** - * Filter Service + * Query Service * @internal */ -export interface FilterServiceDependencies { +export interface QueryServiceDependencies { uiSettings: UiSettingsClientContract; } -export class FilterService { +export class QueryService { filterManager!: FilterManager; - public setup({ uiSettings }: FilterServiceDependencies) { + public setup({ uiSettings }: QueryServiceDependencies) { this.filterManager = new FilterManager(uiSettings); return { @@ -52,5 +52,5 @@ export class FilterService { } /** @public */ -export type FilterSetup = ReturnType; -export type FilterStart = ReturnType; +export type QuerySetup = ReturnType; +export type QueryStart = ReturnType; diff --git a/src/plugins/data/public/types.ts b/src/plugins/data/public/types.ts index 5f94734fef083..9939815c1efd1 100644 --- a/src/plugins/data/public/types.ts +++ b/src/plugins/data/public/types.ts @@ -22,15 +22,18 @@ export * from './autocomplete_provider/types'; import { AutocompletePublicPluginSetup, AutocompletePublicPluginStart } from '.'; import { ISearchSetup, ISearchStart } from './search'; import { IGetSuggestions } from './suggestions_provider/types'; +import { QuerySetup, QueryStart } from './query'; export interface DataPublicPluginSetup { autocomplete: AutocompletePublicPluginSetup; search: ISearchSetup; + query: QuerySetup; } export interface DataPublicPluginStart { autocomplete: AutocompletePublicPluginStart; getSuggestions: IGetSuggestions; search: ISearchStart; + query: QueryStart; } export { IGetSuggestions } from './suggestions_provider/types'; 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 87f0872c52343..85494701749f1 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 @@ -97,6 +97,11 @@ describe('Lens App', () => { }, }, }, + data: { + query: { + filterManager: createMockFilterManager(), + }, + }, dataShim: { indexPatterns: { indexPatterns: { @@ -106,9 +111,6 @@ describe('Lens App', () => { }, }, timefilter: { history: {} }, - filter: { - filterManager: createMockFilterManager(), - }, }, store: { get: jest.fn(), @@ -592,7 +594,7 @@ describe('Lens App', () => { const instance = mount(); - args.dataShim.filter.filterManager.setFilters([ + args.data.query.filterManager.setFilters([ buildExistsFilter({ name: 'myfield' }, { id: 'index1' }), ]); @@ -723,7 +725,7 @@ describe('Lens App', () => { query: { query: 'new', language: 'lucene' }, }); - args.dataShim.filter.filterManager.setFilters([ + args.data.query.filterManager.setFilters([ buildExistsFilter({ name: 'myfield' }, { id: 'index1' }), ]); instance.update(); 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 bd75198714dc3..3f27fe3cbc072 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx @@ -82,10 +82,9 @@ export function App({ const { lastKnownDoc } = state; useEffect(() => { - const subscription = dataShim.filter.filterManager.getUpdates$().subscribe({ + const subscription = data.query.filterManager.getUpdates$().subscribe({ next: () => { - setState(s => ({ ...s, filters: dataShim.filter.filterManager.getFilters() })); - + setState(s => ({ ...s, filters: data.query.filterManager.getFilters() })); trackUiEvent('app_filters_updated'); }, }); @@ -227,9 +226,7 @@ export function App({ setState(s => ({ ...s, savedQuery })); }} onSavedQueryUpdated={savedQuery => { - dataShim.filter.filterManager.setFilters( - savedQuery.attributes.filters || state.filters - ); + data.query.filterManager.setFilters(savedQuery.attributes.filters || state.filters); setState(s => ({ ...s, savedQuery: { ...savedQuery }, // Shallow query for reference issues @@ -242,7 +239,7 @@ export function App({ })); }} onClearSavedQuery={() => { - dataShim.filter.filterManager.removeAll(); + data.query.filterManager.removeAll(); setState(s => ({ ...s, savedQuery: undefined, diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index d8f957bd38199..a3102417a9cb6 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -54,6 +54,7 @@ import { } from '../../common/constants'; import { FilterStateStore } from '@kbn/es-query'; import { start as data } from '../../../../../../src/legacy/core_plugins/data/public/legacy'; +import { npStart } from 'ui/new_platform'; const { savedQueryService } = data.search.services; @@ -62,7 +63,7 @@ const REACT_ANCHOR_DOM_ELEMENT_ID = 'react-maps-root'; const app = uiModules.get(MAP_APP_PATH, []); app.controller('GisMapController', ($scope, $route, kbnUrl, localStorage, AppState, globalState) => { - + const { filterManager } = npStart.plugins.data.query; const savedMap = $route.current.locals.map; let unsubscribe; let initialLayerListConfig; @@ -98,7 +99,7 @@ app.controller('GisMapController', ($scope, $route, kbnUrl, localStorage, AppSta $scope.$evalAsync(() => { // appState $state.query = $scope.query; - $state.filters = data.filter.filterManager.getAppFilters(); + $state.filters = filterManager.getAppFilters(); $state.save(); // globalState @@ -107,7 +108,7 @@ app.controller('GisMapController', ($scope, $route, kbnUrl, localStorage, AppSta pause: $scope.refreshConfig.isPaused, value: $scope.refreshConfig.interval, }; - globalState.filters = data.filter.filterManager.getGlobalFilters(); + globalState.filters = filterManager.getGlobalFilters(); globalState.save(); }); } @@ -198,8 +199,8 @@ app.controller('GisMapController', ($scope, $route, kbnUrl, localStorage, AppSta /* End of Saved Queries */ async function onQueryChange({ filters, query, time }) { if (filters) { - await data.filter.filterManager.setFilters(filters); // Maps and merges filters - $scope.filters = data.filter.filterManager.getFilters(); + filterManager.setFilters(filters); // Maps and merges filters + $scope.filters = filterManager.getFilters(); } if (query) { $scope.query = query; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js index ad190f46f6876..3cff39f684a28 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js @@ -11,7 +11,7 @@ import { render, unmountComponentAtNode } from 'react-dom'; import 'mapbox-gl/dist/mapbox-gl.css'; import { Embeddable, APPLY_FILTER_TRIGGER } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { onlyDisabledFiltersChanged } from '../../../../../../src/legacy/core_plugins/data/public'; +import { onlyDisabledFiltersChanged } from '../../../../../../src/plugins/data/public'; import { I18nContext } from 'ui/i18n'; 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 index 2ee93471b62d0..e024a4e68492b 100644 --- a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx @@ -17,6 +17,7 @@ import { TimeRange, Query } from 'src/plugins/data/common/types'; import { SavedQuery } from 'src/legacy/core_plugins/data/public'; import { OnTimeChangeProps } from '@elastic/eui'; +import { npStart } from 'ui/new_platform'; import { start as data } from '../../../../../../../src/legacy/core_plugins/data/public/legacy'; import { inputsActions } from '../../store/inputs'; @@ -39,12 +40,11 @@ import { timelineActions, hostsActions, networkActions } from '../../store/actio const { ui: { SearchBar }, - filter, search, timefilter, } = data; -export const siemFilterManager = filter.filterManager; +export const siemFilterManager = npStart.plugins.data.query.filterManager; export const savedQueryService = search.services.savedQueryService; interface SiemSearchBarRedux { From 51df8afd7ec233ae90fbfafb0c52bbbd1a22ec54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 29 Oct 2019 11:51:54 +0100 Subject: [PATCH 02/25] =?UTF-8?q?[Logs=20UI]=20Fix=20chart=20and=20table?= =?UTF-8?q?=20state=20loss=20due=20to=20loading=20indica=E2=80=A6=20(#4935?= =?UTF-8?q?6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the result loading indicators of the log rate analysis page such that they are rendered as an overlay to the existing panels. This has the advantage that the page layout doesn't jump during the loading process and the charts and table maintain their filter and expansion states. --- .../components/loading_overlay_wrapper.tsx | 45 +++++ .../logs/analysis/page_results_content.tsx | 148 +++++++-------- .../analysis/sections/anomalies/index.tsx | 179 +++++++++--------- .../logs/analysis/sections/log_rate/index.tsx | 120 ++++++------ .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 6 files changed, 257 insertions(+), 237 deletions(-) create mode 100644 x-pack/legacy/plugins/infra/public/components/loading_overlay_wrapper.tsx diff --git a/x-pack/legacy/plugins/infra/public/components/loading_overlay_wrapper.tsx b/x-pack/legacy/plugins/infra/public/components/loading_overlay_wrapper.tsx new file mode 100644 index 0000000000000..0f70c40059c93 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/loading_overlay_wrapper.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiLoadingSpinner } from '@elastic/eui'; +import { transparentize } from 'polished'; +import React from 'react'; + +import { euiStyled } from '../../../../common/eui_styled_components'; + +export const LoadingOverlayWrapper: React.FC< + React.HTMLAttributes & { + isLoading: boolean; + loadingChildren?: React.ReactNode; + } +> = ({ children, isLoading, loadingChildren, ...rest }) => { + return ( + + {children} + {isLoading ? {loadingChildren} : null} + + ); +}; + +const Overlay: React.FC = ({ children }) => ( + {children ? children : } +); + +const RelativeDiv = euiStyled.div` + position: relative; +`; + +const OverlayDiv = euiStyled.div` + align-items: center; + background-color: ${props => transparentize(0.3, props.theme.eui.euiColorEmptyShade)}; + display: flex; + height: 100%; + justify-content: center; + left: 0; + position: absolute; + top: 0; + width: 100%; +`; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index ffc48a0af9de9..e740689da50f6 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -6,23 +6,23 @@ import datemath from '@elastic/datemath'; import { + EuiBadge, EuiFlexGroup, EuiFlexItem, EuiPage, EuiPanel, EuiSuperDatePicker, - EuiBadge, EuiText, } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; import numeral from '@elastic/numeral'; import { FormattedMessage } from '@kbn/i18n/react'; import moment from 'moment'; import React, { useCallback, useContext, useMemo, useState } from 'react'; + import euiStyled from '../../../../../../common/eui_styled_components'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; import { bucketSpan } from '../../../../common/log_analysis'; -import { LoadingPage } from '../../../components/loading_page'; +import { LoadingOverlayWrapper } from '../../../components/loading_overlay_wrapper'; import { LogAnalysisJobs, StringTimeRange, @@ -162,89 +162,77 @@ export const AnalysisResultsContent = ({ ); return ( - <> - {isLoading && !logEntryRate ? ( - - ) : ( - <> - - + + + + + - - - - {!isLoading && logEntryRate ? ( - - - - {numeral(logEntryRate.totalNumberOfLogEntries).format('0.00a')} - - - ), - startTime: ( - {moment(queryTimeRange.value.startTime).format(dateFormat)} - ), - endTime: ( - {moment(queryTimeRange.value.endTime).format(dateFormat)} - ), - }} - /> - - ) : null} - - - + + + + {numeral(logEntryRate.totalNumberOfLogEntries).format('0.00a')} + + + ), + startTime: ( + {moment(queryTimeRange.value.startTime).format(dateFormat)} + ), + endTime: {moment(queryTimeRange.value.endTime).format(dateFormat)}, + }} /> - - - - - - - {isFirstUse && !hasResults ? : null} - - + + + ) : null} - - - + - - - )} - + + + + + {isFirstUse && !hasResults ? : null} + + + + + + + + + + ); }; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index 5aa5891d7981d..745db984afbd5 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -8,10 +8,10 @@ import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, - EuiLoadingChart, EuiSpacer, EuiStat, EuiTitle, + EuiLoadingSpinner, } from '@elastic/eui'; import numeral from '@elastic/numeral'; import { i18n } from '@kbn/i18n'; @@ -31,6 +31,7 @@ import { AnomaliesChart } from './chart'; import { AnomaliesTable } from './table'; import { LogAnalysisJobProblemIndicator } from '../../../../../components/logging/log_analysis_job_status'; import { AnalyzeInMlButton } from '../analyze_in_ml_button'; +import { LoadingOverlayWrapper } from '../../../../../components/loading_overlay_wrapper'; export const AnomaliesResults: React.FunctionComponent<{ isLoading: boolean; @@ -53,15 +54,6 @@ export const AnomaliesResults: React.FunctionComponent<{ viewSetupForUpdate, jobId, }) => { - const title = i18n.translate('xpack.infra.logs.analysis.anomaliesSectionTitle', { - defaultMessage: 'Anomalies', - }); - - const loadingAriaLabel = i18n.translate( - 'xpack.infra.logs.analysis.anomaliesSectionLoadingAriaLabel', - { defaultMessage: 'Loading anomalies' } - ); - const hasAnomalies = useMemo(() => { return results && results.histogramBuckets ? results.histogramBuckets.some(bucket => { @@ -117,90 +109,91 @@ export const AnomaliesResults: React.FunctionComponent<{ onRecreateMlJobForUpdate={viewSetupForUpdate} /> - {isLoading ? ( - - - - - - ) : !results || (results && results.histogramBuckets && !results.histogramBuckets.length) ? ( - - {i18n.translate('xpack.infra.logs.analysis.anomalySectionNoDataTitle', { - defaultMessage: 'There is no data to display.', - })} - - } - titleSize="m" - body={ -

- {i18n.translate('xpack.infra.logs.analysis.anomalySectionNoDataBody', { - defaultMessage: 'You may want to adjust your time range.', - })} -

- } - /> - ) : !hasAnomalies ? ( - - {i18n.translate('xpack.infra.logs.analysis.anomalySectionNoAnomaliesTitle', { - defaultMessage: 'No anomalies were detected.', - })} - - } - titleSize="m" - /> - ) : ( - <> - - - - - - - - - - - }> + {!results || (results && results.histogramBuckets && !results.histogramBuckets.length) ? ( + + {i18n.translate('xpack.infra.logs.analysis.anomalySectionNoDataTitle', { + defaultMessage: 'There is no data to display.', + })} + + } + titleSize="m" + body={ +

+ {i18n.translate('xpack.infra.logs.analysis.anomalySectionNoDataBody', { + defaultMessage: 'You may want to adjust your time range.', + })} +

+ } + /> + ) : !hasAnomalies ? ( + + {i18n.translate('xpack.infra.logs.analysis.anomalySectionNoAnomaliesTitle', { + defaultMessage: 'No anomalies were detected.', + })} + + } + titleSize="m" /> - - )} + ) : ( + <> + + + + + + + + + + + + + )} + ); }; +const title = i18n.translate('xpack.infra.logs.analysis.anomaliesSectionTitle', { + defaultMessage: 'Anomalies', +}); + interface ParsedAnnotationDetails { anomalyScoresByPartition: Array<{ partitionId: string; maximumAnomalyScore: number }>; } @@ -211,6 +204,7 @@ const overallAnomalyScoreLabel = i18n.translate( defaultMessage: 'Max anomaly scores:', } ); + const AnnotationTooltip: React.FunctionComponent<{ details: string }> = ({ details }) => { const parsedDetails: ParsedAnnotationDetails = JSON.parse(details); return ( @@ -245,3 +239,10 @@ const renderAnnotationTooltip = (details?: string) => { const TooltipWrapper = euiStyled('div')` white-space: nowrap; `; + +const loadingAriaLabel = i18n.translate( + 'xpack.infra.logs.analysis.anomaliesSectionLoadingAriaLabel', + { defaultMessage: 'Loading anomalies' } +); + +const LoadingOverlayContent = () => ; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx index d4ddd14bfaa28..682eb23fa4774 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx @@ -4,15 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - EuiEmptyPrompt, - EuiFlexGroup, - EuiFlexItem, - EuiLoadingChart, - EuiSpacer, - EuiTitle, - EuiText, -} from '@elastic/eui'; +import { EuiEmptyPrompt, EuiLoadingSpinner, EuiSpacer, EuiTitle, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; @@ -20,6 +12,7 @@ import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/ import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { LogEntryRateBarChart } from './bar_chart'; import { getLogEntryRatePartitionedSeries } from '../helpers/data_formatters'; +import { LoadingOverlayWrapper } from '../../../../../components/loading_overlay_wrapper'; export const LogRateResults = ({ isLoading, @@ -32,15 +25,6 @@ export const LogRateResults = ({ setTimeRange: (timeRange: TimeRange) => void; timeRange: TimeRange; }) => { - const title = i18n.translate('xpack.infra.logs.analysis.logRateSectionTitle', { - defaultMessage: 'Log entries', - }); - - const loadingAriaLabel = i18n.translate( - 'xpack.infra.logs.analysis.logRateSectionLoadingAriaLabel', - { defaultMessage: 'Loading log rate results' } - ); - const logEntryRateSeries = useMemo( () => (results && results.histogramBuckets ? getLogEntryRatePartitionedSeries(results) : []), [results] @@ -51,57 +35,61 @@ export const LogRateResults = ({

{title}

- {isLoading ? ( - <> - - - - - - - - ) : !results || (results && results.histogramBuckets && !results.histogramBuckets.length) ? ( - <> - - - {i18n.translate('xpack.infra.logs.analysis.logRateSectionNoDataTitle', { - defaultMessage: 'There is no data to display.', - })} - - } - titleSize="m" - body={ + }> + {!results || (results && results.histogramBuckets && !results.histogramBuckets.length) ? ( + <> + + + {i18n.translate('xpack.infra.logs.analysis.logRateSectionNoDataTitle', { + defaultMessage: 'There is no data to display.', + })} + + } + titleSize="m" + body={ +

+ {i18n.translate('xpack.infra.logs.analysis.logRateSectionNoDataBody', { + defaultMessage: 'You may want to adjust your time range.', + })} +

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

- {i18n.translate('xpack.infra.logs.analysis.logRateSectionNoDataBody', { - defaultMessage: 'You may want to adjust your time range.', + + {i18n.translate('xpack.infra.logs.analysis.logRateSectionBucketSpanLabel', { + defaultMessage: 'Bucket span: ', + })} + + {i18n.translate('xpack.infra.logs.analysis.logRateSectionBucketSpanValue', { + defaultMessage: '15 minutes', })}

- } - /> - - ) : ( - <> - -

- - {i18n.translate('xpack.infra.logs.analysis.logRateSectionBucketSpanLabel', { - defaultMessage: 'Bucket span: ', - })} - - {i18n.translate('xpack.infra.logs.analysis.logRateSectionBucketSpanValue', { - defaultMessage: '15 minutes', - })} -

-
- - - )} +
+ + + )} +
); }; + +const title = i18n.translate('xpack.infra.logs.analysis.logRateSectionTitle', { + defaultMessage: 'Log entries', +}); + +const loadingAriaLabel = i18n.translate( + 'xpack.infra.logs.analysis.logRateSectionLoadingAriaLabel', + { defaultMessage: 'Loading log rate results' } +); + +const LoadingOverlayContent = () => ; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 78c128daafff5..39db6ff3791df 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -5074,7 +5074,6 @@ "xpack.infra.logs.index.documentTitle": "ログ", "xpack.infra.logs.index.settingsTabTitle": "設定", "xpack.infra.logs.index.streamTabTitle": "ストリーム", - "xpack.infra.logs.logsAnalysisResults.loadingMessage": "結果を読み込み中...", "xpack.infra.logs.logsAnalysisResults.onboardingSuccessContent": "機械学習ロボットがデータの収集を開始するまでしばらくお待ちください。", "xpack.infra.logs.logsAnalysisResults.onboardingSuccessTitle": "成功!", "xpack.infra.logs.streamPage.documentTitle": "{previousTitle} | ストリーム", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index dc72e6cf4becd..10ffb2b7d731d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -5075,7 +5075,6 @@ "xpack.infra.logs.index.documentTitle": "Logs", "xpack.infra.logs.index.settingsTabTitle": "设置", "xpack.infra.logs.index.streamTabTitle": "流式传输", - "xpack.infra.logs.logsAnalysisResults.loadingMessage": "正在加载结果......", "xpack.infra.logs.logsAnalysisResults.onboardingSuccessContent": "请注意,我们的 Machine Learning 机器人若干分钟后才会开始收集数据。", "xpack.infra.logs.logsAnalysisResults.onboardingSuccessTitle": "成功!", "xpack.infra.logs.streamPage.documentTitle": "{previousTitle} | 流式传输", From c09e1cd477b5d2f95f878ba71e519fe1fbbe00ee Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Tue, 29 Oct 2019 12:00:35 +0100 Subject: [PATCH 03/25] use embeddables in visualize editor (#48744) --- .../public/visualize/editor/editor.html | 20 +++--- .../kibana/public/visualize/editor/editor.js | 41 +++++------ .../public/visualize/editor/visualization.js | 68 ++++++++++++++++++ .../visualize/editor/visualization_editor.js | 7 +- .../embeddable/visualize_embeddable.ts | 27 +++++-- .../visualize_embeddable_factory.tsx | 33 +++++++-- .../components/vis_editor_visualization.js | 28 ++++---- .../vis_editor_visualization.test.js | 71 ------------------- .../ui/public/vis/editors/default/default.js | 35 +++++---- 9 files changed, 178 insertions(+), 152 deletions(-) create mode 100644 src/legacy/core_plugins/kibana/public/visualize/editor/visualization.js delete mode 100644 src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor_visualization.test.js diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html index 50e921e14973b..0ef3cce832bc7 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html @@ -72,14 +72,6 @@ index-patterns="[indexPattern]" > - -
-
+

diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js index b24cf447d21d6..50e787b634e18 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js @@ -22,6 +22,7 @@ import { Subscription } from 'rxjs'; import { i18n } from '@kbn/i18n'; import '../saved_visualizations/saved_visualizations'; import './visualization_editor'; +import './visualization'; import 'ui/vis/editors/default/sidebar'; import 'ui/visualize'; import 'ui/collapsible_sidebar'; @@ -46,7 +47,6 @@ import { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; import { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query'; import { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; import { timefilter } from 'ui/timefilter'; -import { getVisualizeLoader } from '../../../../../ui/public/visualize/loader'; import { showShareContextMenu, ShareContextMenuExtensionsRegistryProvider } from 'ui/share'; import { getUnhashableStatesProvider } from 'ui/state_management/state_hashing'; import { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal'; @@ -142,6 +142,7 @@ function VisEditor( AppState, $window, $injector, + $timeout, indexPatterns, kbnUrl, redirectWhenMissing, @@ -403,22 +404,21 @@ function VisEditor( $appStatus.dirty = status.dirty || !savedVis.id; }); - $scope.$watch('state.query', (newQuery) => { - const query = migrateLegacyQuery(newQuery); - $scope.updateQueryAndFetch({ query }); + $scope.$watch('state.query', (newQuery, oldQuery) => { + if (!_.isEqual(newQuery, oldQuery)) { + const query = migrateLegacyQuery(newQuery); + if (!_.isEqual(query, newQuery)) { + $state.query = query; + } + $scope.fetch(); + } }); $state.replace(); const updateTimeRange = () => { $scope.timeRange = timefilter.getTime(); - // In case we are running in embedded mode (i.e. we used the visualize loader to embed) - // the visualization, we need to update the timeRange on the visualize handler. - if ($scope._handler) { - $scope._handler.update({ - timeRange: $scope.timeRange, - }); - } + $scope.$broadcast('render'); }; const subscriptions = new Subscription(); @@ -435,9 +435,10 @@ function VisEditor( // update the searchSource when query updates $scope.fetch = function () { $state.save(); + $scope.query = $state.query; savedVis.searchSource.setField('query', $state.query); savedVis.searchSource.setField('filter', $state.filters); - $scope.vis.forceReload(); + $scope.$broadcast('render'); }; // update the searchSource when filters update @@ -460,16 +461,8 @@ function VisEditor( subscriptions.unsubscribe(); }); - if (!$scope.chrome.getVisible()) { - getVisualizeLoader().then(loader => { - $scope._handler = loader.embedVisualizationWithSavedObject($element.find('.visualize')[0], savedVis, { - timeRange: $scope.timeRange, - uiState: $scope.uiState, - appState: $state, - listenOnChange: false - }); - }); - } + + $timeout(() => { $scope.$broadcast('render'); }); } $scope.updateQueryAndFetch = function ({ query, dateRange }) { @@ -482,7 +475,9 @@ function VisEditor( timefilter.setTime(dateRange); // If nothing has changed, trigger the fetch manually, otherwise it will happen as a result of the changes - if (!isUpdate) $scope.fetch(); + if (!isUpdate) { + $scope.vis.forceReload(); + } }; $scope.onRefreshChange = function ({ isPaused, refreshInterval }) { diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/visualization.js b/src/legacy/core_plugins/kibana/public/visualize/editor/visualization.js new file mode 100644 index 0000000000000..ade806bc2fc31 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/visualization.js @@ -0,0 +1,68 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { uiModules } from 'ui/modules'; +import 'angular-sanitize'; +import { start as embeddables } from '../../../../../core_plugins/embeddable_api/public/np_ready/public/legacy'; + +uiModules + .get('kibana/directive', ['ngSanitize']) + .directive('visualizationEmbedded', function (Private, $timeout, getAppState) { + + return { + restrict: 'E', + scope: { + savedObj: '=', + uiState: '=?', + timeRange: '=', + filters: '=', + query: '=', + }, + link: function ($scope, element) { + $scope.renderFunction = async () => { + if (!$scope._handler) { + $scope._handler = await embeddables.getEmbeddableFactory('visualization').createFromObject($scope.savedObj, { + timeRange: $scope.timeRange, + filters: $scope.filters || [], + query: $scope.query, + appState: getAppState(), + uiState: $scope.uiState, + }); + $scope._handler.render(element[0]); + + } else { + $scope._handler.updateInput({ + timeRange: $scope.timeRange, + filters: $scope.filters || [], + query: $scope.query, + }); + } + }; + + $scope.$on('render', (event) => { + event.preventDefault(); + $timeout(() => { $scope.renderFunction(); }); + }); + + $scope.$on('$destroy', () => { + $scope._handler.destroy(); + }); + } + }; + }); diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/visualization_editor.js b/src/legacy/core_plugins/kibana/public/visualize/editor/visualization_editor.js index a2ed44df2f5b0..2cf6e8e7cf86e 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/visualization_editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/visualization_editor.js @@ -17,7 +17,6 @@ * under the License. */ -import { debounce } from 'lodash'; import { uiModules } from 'ui/modules'; import 'angular-sanitize'; import { VisEditorTypesRegistryProvider } from 'ui/registry/vis_editor_types'; @@ -34,6 +33,7 @@ uiModules uiState: '=?', timeRange: '=', filters: '=', + query: '=', }, link: function ($scope, element) { const editorType = $scope.savedObj.vis.type.editor; @@ -46,6 +46,7 @@ uiModules uiState: $scope.uiState, timeRange: $scope.timeRange, filters: $scope.filters, + query: $scope.query, appState: getAppState(), }); }; @@ -58,10 +59,6 @@ uiModules $scope.$on('$destroy', () => { editor.destroy(); }); - - $scope.$watchGroup(['timeRange', 'filters'], debounce(() => { - $scope.renderFunction(); - }, 100)); } }; }); diff --git a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts index ea0b0fe626d7d..56aaea1c240f1 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts @@ -22,6 +22,7 @@ import { StaticIndexPattern } from 'ui/index_patterns'; import { PersistedState } from 'ui/persisted_state'; import { VisualizeLoader } from 'ui/visualize/loader'; import { EmbeddedVisualizeHandler } from 'ui/visualize/loader/embedded_visualize_handler'; +import { AppState } from 'ui/state_management/app_state'; import { VisSavedObject, VisualizeLoaderParams, @@ -48,6 +49,8 @@ export interface VisualizeEmbeddableConfiguration { editUrl: string; loader: VisualizeLoader; editable: boolean; + appState?: AppState; + uiState?: PersistedState; } export interface VisualizeInput extends EmbeddableInput { @@ -57,6 +60,8 @@ export interface VisualizeInput extends EmbeddableInput { vis?: { colors?: { [key: string]: string }; }; + appState?: AppState; + uiState?: PersistedState; } export interface VisualizeOutput extends EmbeddableOutput { @@ -69,6 +74,7 @@ export interface VisualizeOutput extends EmbeddableOutput { export class VisualizeEmbeddable extends Embeddable { private savedVisualization: VisSavedObject; private loader: VisualizeLoader; + private appState: AppState | undefined; private uiState: PersistedState; private handler?: EmbeddedVisualizeHandler; private timeRange?: TimeRange; @@ -86,6 +92,8 @@ export class VisualizeEmbeddable extends Embeddable { this.handleChanges(); @@ -149,7 +163,7 @@ export class VisualizeEmbeddable extends Embeddable & { id: string }, parent?: Container ): Promise { @@ -140,11 +141,12 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< const savedVisualizations = $injector.get('savedVisualizations'); try { - const visId = savedObjectId; + const visId = savedObject.id as string; - const editUrl = chrome.addBasePath(`/app/kibana${savedVisualizations.urlFor(visId)}`); + const editUrl = visId + ? chrome.addBasePath(`/app/kibana${savedVisualizations.urlFor(visId)}`) + : ''; const loader = await getVisualizeLoader(); - const savedObject = await savedVisualizations.get(visId); const isLabsEnabled = config.get('visualize:enableLabs'); if (!isLabsEnabled && savedObject.vis.type.stage === 'experimental') { @@ -160,6 +162,8 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< indexPatterns, editUrl, editable: this.isEditable(), + appState: input.appState, + uiState: input.uiState, }, input, parent @@ -170,6 +174,25 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< } } + public async createFromSavedObject( + savedObjectId: string, + input: Partial & { id: string }, + parent?: Container + ): Promise { + const $injector = await chrome.dangerouslyGetActiveInjector(); + const savedVisualizations = $injector.get('savedVisualizations'); + + try { + const visId = savedObjectId; + + const savedObject = await savedVisualizations.get(visId); + return this.createFromObject(savedObject, input, parent); + } catch (e) { + console.error(e); // eslint-disable-line no-console + return new ErrorEmbeddable(e, input, parent); + } + } + public async create() { // TODO: This is a bit of a hack to preserve the original functionality. Ideally we will clean this up // to allow for in place creation of visualizations without having to navigate away to a new URL. diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor_visualization.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor_visualization.js index 122ede2d2a658..f873cf9c178f8 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor_visualization.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor_visualization.js @@ -18,9 +18,8 @@ */ import PropTypes from 'prop-types'; import React, { Component } from 'react'; -import { get, isEqual } from 'lodash'; +import { get } from 'lodash'; import { keyCodes, EuiFlexGroup, EuiFlexItem, EuiButton, EuiText, EuiSwitch } from '@elastic/eui'; -import { getVisualizeLoader } from 'ui/visualize/loader/visualize_loader'; import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; import { getInterval, @@ -30,6 +29,7 @@ import { AUTO_INTERVAL, } from './lib/get_interval'; import { PANEL_TYPES } from '../../common/panel_types'; +import { start as embeddables } from '../../../embeddable_api/public/np_ready/public/legacy'; const MIN_CHART_HEIGHT = 300; @@ -65,23 +65,21 @@ class VisEditorVisualizationUI extends Component { }; async _loadVisualization() { - const loader = await getVisualizeLoader(); - if (!this._visEl.current) { // In case the visualize loader isn't done before the component is unmounted. return; } - const { uiState, timeRange, appState, savedObj, onDataChange } = this.props; + const { timeRange, appState, savedObj, onDataChange } = this.props; - this._handler = loader.embedVisualizationWithSavedObject(this._visEl.current, savedObj, { - listenOnChange: false, - uiState, - timeRange, - appState, + this._handler = await embeddables.getEmbeddableFactory('visualization').createFromObject(savedObj, { + vis: {}, + timeRange: timeRange, + filters: appState.filters || [], }); + this._handler.render(this._visEl.current); - this._subscription = this._handler.data$.subscribe(data => { + this._subscription = this._handler.handler.data$.subscribe(data => { this.setPanelInterval(data.visData); onDataChange(data); }); @@ -152,10 +150,12 @@ class VisEditorVisualizationUI extends Component { this._loadVisualization(); } - componentDidUpdate(prevProps) { - if (this._handler && !isEqual(this.props.timeRange, prevProps.timeRange)) { - this._handler.update({ + componentDidUpdate() { + if (this._handler) { + this._handler.updateInput({ timeRange: this.props.timeRange, + filters: this.props.filters || [], + query: this.props.query, }); } } diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor_visualization.test.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor_visualization.test.js deleted file mode 100644 index e60626f8fbf0a..0000000000000 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor_visualization.test.js +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -jest.mock('ui/visualize/loader/visualize_loader', () => ({})); - -jest.mock('ui/new_platform'); - -import React from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { VisEditorVisualization } from './vis_editor_visualization'; - -describe('getVisualizeLoader', () => { - let updateStub; - - beforeEach(() => { - updateStub = jest.fn(); - const handlerMock = { - update: updateStub, - data$: { - subscribe: () => {}, - }, - }; - const loaderMock = { - embedVisualizationWithSavedObject: () => handlerMock, - }; - require('ui/visualize/loader/visualize_loader').getVisualizeLoader = async () => loaderMock; - }); - - it('should not call _handler.update until getVisualizeLoader returns _handler', async () => { - const wrapper = mountWithIntl(); - - // Set prop to force DOM change and componentDidUpdate to be triggered - wrapper.setProps({ - timeRange: { - from: '2019-03-20T20:35:37.637Z', - to: '2019-03-23T18:40:16.486Z', - }, - }); - - expect(updateStub).not.toHaveBeenCalled(); - - // Ensure all promises resolve - await new Promise(resolve => process.nextTick(resolve)); - - // Set prop to force DOM change and componentDidUpdate to be triggered - wrapper.setProps({ - timeRange: { - from: 'now/d', - to: 'now/d', - }, - }); - - expect(updateStub).toHaveBeenCalled(); - }); -}); diff --git a/src/legacy/ui/public/vis/editors/default/default.js b/src/legacy/ui/public/vis/editors/default/default.js index af0ebc65680c3..43d2962df0a1e 100644 --- a/src/legacy/ui/public/vis/editors/default/default.js +++ b/src/legacy/ui/public/vis/editors/default/default.js @@ -34,10 +34,11 @@ import { parentPipelineAggHelper } from 'ui/agg_types/metrics/lib/parent_pipelin import { DefaultEditorSize } from '../../editor_size'; import { VisEditorTypesRegistryProvider } from '../../../registry/vis_editor_types'; -import { getVisualizeLoader } from '../../../visualize/loader/visualize_loader'; import { AggGroupNames } from './agg_groups'; -const defaultEditor = function ($rootScope, $compile) { +import { start as embeddables } from '../../../../../core_plugins/embeddable_api/public/np_ready/public/legacy'; + +const defaultEditor = function ($rootScope, $compile, getAppState) { return class DefaultEditor { static key = 'default'; @@ -57,7 +58,7 @@ const defaultEditor = function ($rootScope, $compile) { } } - render({ uiState, timeRange, filters, appState }) { + render({ uiState, timeRange, filters, query }) { let $scope; const updateScope = () => { @@ -66,7 +67,7 @@ const defaultEditor = function ($rootScope, $compile) { //$scope.$apply(); }; - return new Promise(resolve => { + return new Promise(async (resolve) => { if (!this.$scope) { this.$scope = $scope = $rootScope.$new(); @@ -157,23 +158,21 @@ const defaultEditor = function ($rootScope, $compile) { if (!this._handler) { const visualizationEl = this.el.find('.visEditor__canvas')[0]; - getVisualizeLoader().then(loader => { - if (!visualizationEl) { - return; - } - this._loader = loader; - this._handler = this._loader.embedVisualizationWithSavedObject(visualizationEl, this.savedObj, { - uiState: uiState, - listenOnChange: false, - timeRange: timeRange, - filters: filters, - appState: appState, - }); + + this._handler = await embeddables.getEmbeddableFactory('visualization').createFromObject(this.savedObj, { + uiState: uiState, + appState: getAppState(), + timeRange: timeRange, + filters: filters || [], + query: query, }); + this._handler.render(visualizationEl); + } else { - this._handler.update({ + this._handler.updateInput({ timeRange: timeRange, - filters: filters, + filters: filters || [], + query: query, }); } From 5e05bbd05fa8f4bee67afeb42bccb44b38910534 Mon Sep 17 00:00:00 2001 From: Marjorie Jones Date: Tue, 29 Oct 2019 11:29:21 +0000 Subject: [PATCH 04/25] [DOCs] Rename some instances of Infrastructure to Metrics (#49195) * Renaming Infrastructure to Metrics whenever the context is the metrics app. * More renames, and picking up a few stray references to Infrastructure/Metrics/Logs UI too. * Fixing typo --- docs/infrastructure/getting-started.asciidoc | 4 ++-- docs/infrastructure/index.asciidoc | 4 ++-- docs/infrastructure/infra-ui.asciidoc | 4 ++-- docs/infrastructure/metrics-explorer.asciidoc | 2 +- docs/logs/using.asciidoc | 2 +- docs/settings/general-infra-logs-ui-settings.asciidoc | 4 ++-- docs/settings/infrastructure-ui-settings.asciidoc | 8 ++++---- docs/settings/logs-ui-settings.asciidoc | 8 ++++---- docs/uptime-guide/overview.asciidoc | 4 ++-- docs/uptime/overview.asciidoc | 4 ++-- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/infrastructure/getting-started.asciidoc b/docs/infrastructure/getting-started.asciidoc index 1c5645f5a6e4e..7122ad5c19f75 100644 --- a/docs/infrastructure/getting-started.asciidoc +++ b/docs/infrastructure/getting-started.asciidoc @@ -1,6 +1,6 @@ [role="xpack"] [[xpack-metrics-getting-started]] -== Getting started with infrastructure monitoring +== Getting started with metrics To get started with the Metrics app in Kibana, you need to start collecting metrics data for your infrastructure. @@ -8,4 +8,4 @@ Kibana provides step-by-step instructions to help you add metrics data. The {metrics-guide}[Metrics Monitoring Guide] is a good source for more detailed information and instructions. [role="screenshot"] -image::infrastructure/images/metrics-add-data.png[Screenshot showing Add metric data to Kibana UI] +image::infrastructure/images/metrics-add-data.png[Screenshot showing Add metric data to Kibana] diff --git a/docs/infrastructure/index.asciidoc b/docs/infrastructure/index.asciidoc index 17361ef6a6080..5e2d0f3e757b0 100644 --- a/docs/infrastructure/index.asciidoc +++ b/docs/infrastructure/index.asciidoc @@ -4,13 +4,13 @@ [partintro] -- -The Metrics app enables you to monitor your infrastructure and identify problems in real time. +The Metrics app enables you to monitor your infrastructure metrics and identify problems in real time. You start with a visual summary of your infrastructure where you can view basic metrics for common servers, containers, and services. Then you can drill down to view more detailed metrics or other information for that component. You can: -* View an inventory of your infrastructure by hosts, Kubernetes pod or Docker containers. +* View your infrastructure metrics by hosts, Kubernetes pods or Docker containers. You can group and filter the data in various ways to help you identify the items that interest you. * View current and historic values for metrics such as CPU usage, memory usage, and network traffic for each component. diff --git a/docs/infrastructure/infra-ui.asciidoc b/docs/infrastructure/infra-ui.asciidoc index 5c8c50a978d63..120a22541717c 100644 --- a/docs/infrastructure/infra-ui.asciidoc +++ b/docs/infrastructure/infra-ui.asciidoc @@ -2,12 +2,12 @@ [[infra-ui]] == Using the Metrics app -Use the Metrics app in {kib} to monitor your infrastructure and identify problems in real time. +Use the Metrics app in {kib} to monitor your infrastructure metrics and identify problems in real time. You can explore metrics for hosts, containers, and services. You can also drill down to view more detailed metrics, or seamlessly switch to view the corresponding logs, application traces, and uptime information. Initially, the *Inventory* tab shows an overview of the hosts in of your infrastructure and the current CPU usage for each host. -From here, you can drill down into areas of interest. +From here, you can view other metrics or drill down into areas of interest. [role="screenshot"] image::infrastructure/images/infra-sysmon.png[Infrastructure Overview in Kibana] diff --git a/docs/infrastructure/metrics-explorer.asciidoc b/docs/infrastructure/metrics-explorer.asciidoc index 2919eaa976d6a..c20718dac1c7a 100644 --- a/docs/infrastructure/metrics-explorer.asciidoc +++ b/docs/infrastructure/metrics-explorer.asciidoc @@ -15,7 +15,7 @@ image::infrastructure/images/metrics-explorer-screen.png[Metrics Explorer in Kib * Metrics Explorer uses data collected from {metricbeat-ref}/metricbeat-overview.html[Metricbeat]. * You need read permissions on `metricbeat-*` or the metric index specified in the Metrics configuration. -* Metrics Explorer uses the timestamp field set in the Infrastructure configuration. +* Metrics Explorer uses the timestamp field from the *Settings* tab. By default that is set to `@timestamp`. * The interval for the X Axis is set to `auto`. The bucket size is determined by the time range. diff --git a/docs/logs/using.asciidoc b/docs/logs/using.asciidoc index 65693f4399e53..916ad42a6d221 100644 --- a/docs/logs/using.asciidoc +++ b/docs/logs/using.asciidoc @@ -17,7 +17,7 @@ image::logs/images/logs-console.png[Logs Console in Kibana] Use the search bar to perform ad hoc searches for specific text. You can also create structured queries using {kibana-ref}/kuery-query.html[Kibana Query Language]. For example, enter `host.hostname : "host1"` to see only the information for `host1`. -// ++ this isn't quite the same as the corresponding infrastructure description now. +// ++ this isn't quite the same as the corresponding metrics description now. [float] [[logs-configure-source]] diff --git a/docs/settings/general-infra-logs-ui-settings.asciidoc b/docs/settings/general-infra-logs-ui-settings.asciidoc index 31a12c6e2e905..7b32372a1f59a 100644 --- a/docs/settings/general-infra-logs-ui-settings.asciidoc +++ b/docs/settings/general-infra-logs-ui-settings.asciidoc @@ -1,4 +1,4 @@ -`xpack.infra.enabled`:: Set to `false` to disable the Logs and Metrics UI plugin {kib}. Defaults to `true`. +`xpack.infra.enabled`:: Set to `false` to disable the Logs and Metrics app plugin {kib}. Defaults to `true`. `xpack.infra.sources.default.logAlias`:: Index pattern for matching indices that contain log data. Defaults to `filebeat-*,kibana_sample_data_logs*`. To match multiple wildcard patterns, use a comma to separate the names, with no space after the comma. For example, `logstash-app1-*,default-logs-*`. @@ -6,7 +6,7 @@ `xpack.infra.sources.default.fields.timestamp`:: Timestamp used to sort log entries. Defaults to `@timestamp`. -`xpack.infra.sources.default.fields.message`:: Fields used to display messages in the Logs UI. Defaults to `['message', '@message']`. +`xpack.infra.sources.default.fields.message`:: Fields used to display messages in the Logs app. Defaults to `['message', '@message']`. `xpack.infra.sources.default.fields.tiebreaker`:: Field used to break ties between two entries with the same timestamp. Defaults to `_doc`. diff --git a/docs/settings/infrastructure-ui-settings.asciidoc b/docs/settings/infrastructure-ui-settings.asciidoc index 1617f2847c50e..ed69c27feab72 100644 --- a/docs/settings/infrastructure-ui-settings.asciidoc +++ b/docs/settings/infrastructure-ui-settings.asciidoc @@ -1,14 +1,14 @@ [role="xpack"] [[infrastructure-ui-settings-kb]] -=== Metrics UI settings in Kibana +=== Metrics settings in Kibana ++++ -Metrics UI settings +Metrics settings ++++ -You do not need to configure any settings to use the Metrics UI. It is enabled by default. +You do not need to configure any settings to use the Metrics app in {kib}. It is enabled by default. [float] [[general-infra-ui-settings-kb]] -==== General Metrics UI settings +==== General Metrics settings include::general-infra-logs-ui-settings.asciidoc[] \ No newline at end of file diff --git a/docs/settings/logs-ui-settings.asciidoc b/docs/settings/logs-ui-settings.asciidoc index a2c9e12e22fb0..5b6dd902091ae 100644 --- a/docs/settings/logs-ui-settings.asciidoc +++ b/docs/settings/logs-ui-settings.asciidoc @@ -1,14 +1,14 @@ [role="xpack"] [[logs-ui-settings-kb]] -=== Logs UI settings in Kibana +=== Logs app settings in Kibana ++++ -Logs UI settings +Logs settings ++++ -You do not need to configure any settings to use the Logs UI. It is enabled by default. +You do not need to configure any settings to use the Logs app in {kib}. It is enabled by default. [float] [[general-logs-ui-settings-kb]] -==== General Logs UI settings +==== General Logs settings include::general-infra-logs-ui-settings.asciidoc[] diff --git a/docs/uptime-guide/overview.asciidoc b/docs/uptime-guide/overview.asciidoc index 09867e7b05f2a..c6bd71b1f5574 100644 --- a/docs/uptime-guide/overview.asciidoc +++ b/docs/uptime-guide/overview.asciidoc @@ -33,8 +33,8 @@ The {kibana-ref}/xpack-uptime.html[Elasticsearch Uptime app] in Kibana provides [float] === Example deployments -// ++ I like the Infra/logging diagram which shows Infrastructure and Logging apps as separate components inside Kibana -// ++ In diagram, should be Uptime app, not Uptime UI, possibly even Elastic Uptime? Also applies to Infra/logging/APM. +// ++ I like the Infra/logging diagram which shows Metrics and Logging apps as separate components inside Kibana +// ++ In diagram, should be Uptime app, not Uptime UI, possibly even Elastic Uptime? Also applies to Metrics/Logging/APM. // ++ Need more whitespace around components. image::images/uptime-simple-deployment.png[Uptime simple deployment] diff --git a/docs/uptime/overview.asciidoc b/docs/uptime/overview.asciidoc index ea7047ae940a8..098ce12a56991 100644 --- a/docs/uptime/overview.asciidoc +++ b/docs/uptime/overview.asciidoc @@ -34,7 +34,7 @@ in an `up` or `down` state are displayed, based on the last check reported by He for each monitor. Next to the counts, there is a histogram displaying the change over time throughout the -selected date range. +selected date range. [float] === Monitor list @@ -56,7 +56,7 @@ ID and URL, its IP address, and a dedicated sparkline showing its check status o image::uptime/images/observability_integrations.png[Observability integrations] The Monitor list also contains a menu of possible integrations. If Uptime detects Kubernetes or -Docker related host information, it will provide links to open the Metrics UI or Logs UI pre-filtered +Docker related host information, it will provide links to open the Metrics app or Logs app pre-filtered for this host. Additionally, this feature supplies links to simply filter the other views on the host's IP address, to help you quickly determine if these other solutions contain data relevant to your current interest. From a6d0b6018bcd9e1c7688465e91c0934d5f9cd422 Mon Sep 17 00:00:00 2001 From: Liza Katz Date: Tue, 29 Oct 2019 14:16:08 +0200 Subject: [PATCH 05/25] =?UTF-8?q?Move=20Storage=20=E2=87=92=20NP=20kibana?= =?UTF-8?q?=5Futils=20(#49448)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move storage to kibana_utils * Updated all references to Storage and replace places where it was referenced to as "store" to avoid confusion. * fixed tests * Delete data legacy dependencies plugin * Imports fix * update snapshots --- src/legacy/core_plugins/data/public/legacy.ts | 7 +-- src/legacy/core_plugins/data/public/plugin.ts | 34 +++++--------- .../query/persisted_log/persisted_log.ts | 6 +-- .../query_bar_input.test.tsx.snap | 24 +++++----- .../components/query_bar_input.test.tsx | 6 +-- .../query_bar/components/query_bar_input.tsx | 6 +-- .../components/query_bar_top_row.test.tsx | 4 +- .../components/query_bar_top_row.tsx | 12 ++--- .../query/query_bar/lib/get_query_log.ts | 6 +-- .../components/create_search_bar.tsx | 8 ++-- .../search_bar/components/search_bar.test.tsx | 4 +- .../public/shim/legacy_dependencies_plugin.ts | 44 ------------------- .../data/public/timefilter/time_history.ts | 6 +-- .../public/timefilter/timefilter_service.ts | 8 ++-- src/legacy/core_plugins/data/public/types.ts | 10 +---- .../kibana/public/discover/kibana_services.ts | 1 + .../kibana/public/visualize/editor/editor.js | 1 + .../public/components/vis_editor.js | 9 ++-- .../contexts/query_input_bar_context.ts | 4 +- .../ui/public/agg_types/buckets/filters.ts | 2 +- src/legacy/ui/public/autoload/modules.js | 1 - src/legacy/ui/public/chrome/chrome.js | 2 +- .../storage/index.js} | 4 +- .../vis/editors/default/controls/filter.tsx | 4 +- src/plugins/kibana_utils/public/index.ts | 1 + .../public/storage/__tests__/storage.js | 0 .../kibana_utils}/public/storage/index.ts | 3 +- .../kibana_utils}/public/storage/storage.ts | 15 +++---- .../kibana_utils/public/storage/types.ts} | 14 +++++- .../plugins/canvas/public/lib/clipboard.js | 2 +- .../graph/public/angular/templates/index.html | 2 +- x-pack/legacy/plugins/graph/public/app.js | 4 +- .../plugins/graph/public/components/app.tsx | 8 ++-- .../public/components/search_bar.test.tsx | 2 +- x-pack/legacy/plugins/graph/public/index.ts | 2 +- .../lens/public/app_plugin/app.test.tsx | 8 ++-- .../plugins/lens/public/app_plugin/app.tsx | 12 ++--- .../plugins/lens/public/app_plugin/plugin.tsx | 4 +- .../dimension_panel/dimension_panel.test.tsx | 4 +- .../dimension_panel/dimension_panel.tsx | 4 +- .../indexpattern_plugin/indexpattern.test.ts | 4 +- .../indexpattern_plugin/indexpattern.tsx | 6 +-- .../definitions/date_histogram.test.tsx | 14 +++--- .../operations/definitions/index.ts | 4 +- .../operations/definitions/terms.test.tsx | 14 +++--- .../public/indexpattern_plugin/plugin.tsx | 2 +- .../public/lens_ui_telemetry/factory.test.ts | 4 +- .../lens/public/lens_ui_telemetry/factory.ts | 6 +-- .../maps/public/angular/map_controller.js | 1 + .../__snapshots__/view.test.js.snap | 2 +- .../connected_components/layer_panel/view.js | 4 +- .../metricbeat_migration/flyout/flyout.js | 2 +- .../plugins/monitoring/public/monitoring.js | 1 + 53 files changed, 152 insertions(+), 210 deletions(-) delete mode 100644 src/legacy/core_plugins/data/public/shim/legacy_dependencies_plugin.ts rename src/legacy/ui/public/{storage/directive.js => directives/storage/index.js} (90%) rename src/{legacy/ui => plugins/kibana_utils}/public/storage/__tests__/storage.js (100%) rename src/{legacy/ui => plugins/kibana_utils}/public/storage/index.ts (94%) rename src/{legacy/ui => plugins/kibana_utils}/public/storage/storage.ts (73%) rename src/{legacy/ui/public/storage/web_storage.ts => plugins/kibana_utils/public/storage/types.ts} (70%) diff --git a/src/legacy/core_plugins/data/public/legacy.ts b/src/legacy/core_plugins/data/public/legacy.ts index e151726a6d702..b1d838aed992d 100644 --- a/src/legacy/core_plugins/data/public/legacy.ts +++ b/src/legacy/core_plugins/data/public/legacy.ts @@ -35,18 +35,13 @@ */ import { npSetup, npStart } from 'ui/new_platform'; -import { LegacyDependenciesPlugin } from './shim/legacy_dependencies_plugin'; import { plugin } from '.'; const dataPlugin = plugin(); -const legacyPlugin = new LegacyDependenciesPlugin(); -export const setup = dataPlugin.setup(npSetup.core, { - __LEGACY: legacyPlugin.setup(), -}); +export const setup = dataPlugin.setup(npSetup.core); export const start = dataPlugin.start(npStart.core, { data: npStart.plugins.data, uiActions: npSetup.plugins.uiActions, - __LEGACY: legacyPlugin.start(), }); diff --git a/src/legacy/core_plugins/data/public/plugin.ts b/src/legacy/core_plugins/data/public/plugin.ts index af7c29bf2f4b9..597ad86d39d85 100644 --- a/src/legacy/core_plugins/data/public/plugin.ts +++ b/src/legacy/core_plugins/data/public/plugin.ts @@ -22,10 +22,7 @@ import { SearchService, SearchStart, createSearchBar, StatetfulSearchBarProps } import { QueryService, QuerySetup } from './query'; import { TimefilterService, TimefilterSetup } from './timefilter'; import { IndexPatternsService, IndexPatternsSetup, IndexPatternsStart } from './index_patterns'; -import { - LegacyDependenciesPluginSetup, - LegacyDependenciesPluginStart, -} from './shim/legacy_dependencies_plugin'; +import { Storage, IStorageWrapper } from '../../../../../src/plugins/kibana_utils/public'; import { DataPublicPluginStart } from '../../../../plugins/data/public'; import { initLegacyModule } from './shim/legacy_module'; import { IUiActionsSetup } from '../../../../plugins/ui_actions/public'; @@ -35,19 +32,9 @@ import { } from './filter/action/apply_filter_action'; import { APPLY_FILTER_TRIGGER } from '../../../../plugins/embeddable/public'; -/** - * Interface for any dependencies on other plugins' `setup` contracts. - * - * @internal - */ -export interface DataPluginSetupDependencies { - __LEGACY: LegacyDependenciesPluginSetup; -} - export interface DataPluginStartDependencies { data: DataPublicPluginStart; uiActions: IUiActionsSetup; - __LEGACY: LegacyDependenciesPluginStart; } /** @@ -87,22 +74,24 @@ export interface DataStart { * in the setup/start interfaces. The remaining items exported here are either types, * or static code. */ -export class DataPlugin - implements - Plugin { + +export class DataPlugin implements Plugin { private readonly indexPatterns: IndexPatternsService = new IndexPatternsService(); private readonly query: QueryService = new QueryService(); private readonly search: SearchService = new SearchService(); private readonly timefilter: TimefilterService = new TimefilterService(); private setupApi!: DataSetup; + private storage!: IStorageWrapper; - public setup(core: CoreSetup, { __LEGACY }: DataPluginSetupDependencies): DataSetup { + public setup(core: CoreSetup): DataSetup { const { uiSettings } = core; + this.storage = new Storage(window.localStorage); + const timefilterService = this.timefilter.setup({ uiSettings, - store: __LEGACY.storage, + storage: this.storage, }); this.setupApi = { indexPatterns: this.indexPatterns.setup(), @@ -113,10 +102,7 @@ export class DataPlugin return this.setupApi; } - public start( - core: CoreStart, - { __LEGACY, data, uiActions }: DataPluginStartDependencies - ): DataStart { + public start(core: CoreStart, { data, uiActions }: DataPluginStartDependencies): DataStart { const { uiSettings, http, notifications, savedObjects } = core; const indexPatternsService = this.indexPatterns.start({ @@ -131,7 +117,7 @@ export class DataPlugin const SearchBar = createSearchBar({ core, data, - store: __LEGACY.storage, + storage: this.storage, timefilter: this.setupApi.timefilter, }); diff --git a/src/legacy/core_plugins/data/public/query/persisted_log/persisted_log.ts b/src/legacy/core_plugins/data/public/query/persisted_log/persisted_log.ts index e0e6a0d0c44e4..553b0bf5ef7e0 100644 --- a/src/legacy/core_plugins/data/public/query/persisted_log/persisted_log.ts +++ b/src/legacy/core_plugins/data/public/query/persisted_log/persisted_log.ts @@ -20,7 +20,7 @@ import _ from 'lodash'; import * as Rx from 'rxjs'; import { map } from 'rxjs/operators'; -import { Storage } from '../../types'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; const defaultIsDuplicate = (oldItem: any, newItem: any) => { return _.isEqual(oldItem, newItem); @@ -37,12 +37,12 @@ export class PersistedLog { public maxLength?: number; public filterDuplicates?: boolean; public isDuplicate: (oldItem: T, newItem: T) => boolean; - public storage: Storage; + public storage: IStorageWrapper; public items: T[]; private update$ = new Rx.BehaviorSubject(undefined); - constructor(name: string, options: PersistedLogOptions = {}, storage: Storage) { + constructor(name: string, options: PersistedLogOptions = {}, storage: IStorageWrapper) { this.name = name; this.maxLength = typeof options.maxLength === 'string' diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap index 286e60cca9712..37dc0d14730c0 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap @@ -304,12 +304,12 @@ exports[`QueryBarInput Should disable autoFocus on EuiFieldText when disableAuto "update": [MockFunction], }, }, - "store": Object { + "storage": Object { "clear": [MockFunction], "get": [MockFunction], "remove": [MockFunction], "set": [MockFunction], - "store": Object { + "storage": Object { "clear": [MockFunction], "getItem": [MockFunction], "key": [MockFunction], @@ -867,12 +867,12 @@ exports[`QueryBarInput Should disable autoFocus on EuiFieldText when disableAuto "update": [MockFunction], }, }, - "store": Object { + "storage": Object { "clear": [MockFunction], "get": [MockFunction], "remove": [MockFunction], "set": [MockFunction], - "store": Object { + "storage": Object { "clear": [MockFunction], "getItem": [MockFunction], "key": [MockFunction], @@ -1418,12 +1418,12 @@ exports[`QueryBarInput Should pass the query language to the language switcher 1 "update": [MockFunction], }, }, - "store": Object { + "storage": Object { "clear": [MockFunction], "get": [MockFunction], "remove": [MockFunction], "set": [MockFunction], - "store": Object { + "storage": Object { "clear": [MockFunction], "getItem": [MockFunction], "key": [MockFunction], @@ -1978,12 +1978,12 @@ exports[`QueryBarInput Should pass the query language to the language switcher 1 "update": [MockFunction], }, }, - "store": Object { + "storage": Object { "clear": [MockFunction], "get": [MockFunction], "remove": [MockFunction], "set": [MockFunction], - "store": Object { + "storage": Object { "clear": [MockFunction], "getItem": [MockFunction], "key": [MockFunction], @@ -2529,12 +2529,12 @@ exports[`QueryBarInput Should render the given query 1`] = ` "update": [MockFunction], }, }, - "store": Object { + "storage": Object { "clear": [MockFunction], "get": [MockFunction], "remove": [MockFunction], "set": [MockFunction], - "store": Object { + "storage": Object { "clear": [MockFunction], "getItem": [MockFunction], "key": [MockFunction], @@ -3089,12 +3089,12 @@ exports[`QueryBarInput Should render the given query 1`] = ` "update": [MockFunction], }, }, - "store": Object { + "storage": Object { "clear": [MockFunction], "get": [MockFunction], "remove": [MockFunction], "set": [MockFunction], - "store": Object { + "storage": Object { "clear": [MockFunction], "getItem": [MockFunction], "key": [MockFunction], diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.tsx index f1249da997dfe..3edb689ca2bfe 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.tsx @@ -58,7 +58,7 @@ const createMockWebStorage = () => ({ }); const createMockStorage = () => ({ - store: createMockWebStorage(), + storage: createMockWebStorage(), get: jest.fn(), set: jest.fn(), remove: jest.fn(), @@ -80,7 +80,7 @@ const mockIndexPattern = { ], } as IndexPattern; -function wrapQueryBarInputInContext(testProps: any, store?: any) { +function wrapQueryBarInputInContext(testProps: any, storage?: any) { const defaultOptions = { screenTitle: 'Another Screen', intl: null as any, @@ -89,7 +89,7 @@ function wrapQueryBarInputInContext(testProps: any, store?: any) { const services = { ...startMock, appName: testProps.appName || 'test', - store: store || createMockStorage(), + storage: storage || createMockStorage(), }; return ( diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index a57018b118185..d73e741b6d5cb 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -365,7 +365,7 @@ export class QueryBarInputUI extends Component { body: JSON.stringify({ opt_in: language === 'kuery' }), }); - this.services.store.set('kibana.userQueryLanguage', language); + this.services.storage.set('kibana.userQueryLanguage', language); const newQuery = { query: '', language }; this.onChange(newQuery); @@ -387,10 +387,10 @@ export class QueryBarInputUI extends Component { }; private initPersistedLog = () => { - const { uiSettings, store, appName } = this.services; + const { uiSettings, storage, appName } = this.services; this.persistedLog = this.props.persistedLog ? this.props.persistedLog - : getQueryLog(uiSettings, store, appName, this.props.query.language); + : getQueryLog(uiSettings, storage, appName, this.props.query.language); }; public onMouseEnterSuggestion = (index: number) => { diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.test.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.test.tsx index 7ab191062e32d..7281eea956fbf 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.test.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.test.tsx @@ -79,7 +79,7 @@ const createMockWebStorage = () => ({ }); const createMockStorage = () => ({ - store: createMockWebStorage(), + storage: createMockWebStorage(), get: jest.fn(), set: jest.fn(), remove: jest.fn(), @@ -112,7 +112,7 @@ function wrapQueryBarTopRowInContext(testProps: any) { const services = { ...startMock, appName: 'discover', - store: createMockStorage(), + storage: createMockStorage(), }; return ( 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 9a846ab82f47c..26ee2d80ebf65 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 @@ -73,7 +73,7 @@ function QueryBarTopRowUI(props: Props) { const [isDateRangeInvalid, setIsDateRangeInvalid] = useState(false); const kibana = useKibana(); - const { uiSettings, notifications, store, appName, docLinks } = kibana.services; + const { uiSettings, notifications, storage, appName, docLinks } = kibana.services; const kueryQuerySyntaxLink: string = docLinks!.links.query.kueryQuerySyntax; @@ -82,7 +82,7 @@ function QueryBarTopRowUI(props: Props) { useEffect(() => { if (!props.query) return; - persistedLog = getQueryLog(uiSettings!, store, appName, props.query.language); + persistedLog = getQueryLog(uiSettings!, storage, appName, props.query.language); }, [queryLanguage]); function onClickSubmitButton(event: React.MouseEvent) { @@ -211,7 +211,7 @@ function QueryBarTopRowUI(props: Props) { } function shouldRenderQueryInput(): boolean { - return Boolean(props.showQueryInput && props.indexPatterns && props.query && store); + return Boolean(props.showQueryInput && props.indexPatterns && props.query && storage); } function renderUpdateButton() { @@ -293,7 +293,7 @@ function QueryBarTopRowUI(props: Props) { if ( language === 'kuery' && typeof query === 'string' && - (!store || !store.get('kibana.luceneSyntaxWarningOptOut')) && + (!storage || !storage.get('kibana.luceneSyntaxWarningOptOut')) && doesKueryExpressionHaveLuceneSyntaxError(query) ) { const toast = notifications!.toasts.addWarning({ @@ -337,8 +337,8 @@ function QueryBarTopRowUI(props: Props) { } function onLuceneSyntaxWarningOptOut(toast: Toast) { - if (!store) return; - store.set('kibana.luceneSyntaxWarningOptOut', true); + if (!storage) return; + storage.set('kibana.luceneSyntaxWarningOptOut', true); notifications!.toasts.remove(toast); } diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/get_query_log.ts b/src/legacy/core_plugins/data/public/query/query_bar/lib/get_query_log.ts index 8b26e14c6ed7b..f78eb5e07f189 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/get_query_log.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/lib/get_query_log.ts @@ -18,12 +18,12 @@ */ import { UiSettingsClientContract } from 'src/core/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { PersistedLog } from '../../persisted_log'; -import { Storage } from '../../../types'; export function getQueryLog( uiSettings: UiSettingsClientContract, - store: Storage, + storage: IStorageWrapper, appName: string, language: string ) { @@ -33,6 +33,6 @@ export function getQueryLog( maxLength: uiSettings.get('history:limit'), filterDuplicates: true, }, - store + storage ); } 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 7fdbc996ac8c2..c186edf9a3ac9 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 @@ -22,7 +22,7 @@ import { Subscription } from 'rxjs'; import { Filter } from '@kbn/es-query'; import { CoreStart } from 'src/core/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { Storage } from '../../../types'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; import { TimefilterSetup } from '../../../timefilter'; import { SearchBar } from '../../../'; @@ -31,7 +31,7 @@ import { SearchBarOwnProps } from '.'; interface StatefulSearchBarDeps { core: CoreStart; data: DataPublicPluginStart; - store: Storage; + storage: IStorageWrapper; timefilter: TimefilterSetup; } @@ -54,7 +54,7 @@ const defaultOnRefreshChange = (timefilter: TimefilterSetup) => { }; }; -export function createSearchBar({ core, store, timefilter, data }: StatefulSearchBarDeps) { +export function createSearchBar({ core, storage, timefilter, data }: StatefulSearchBarDeps) { // 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) => { @@ -107,7 +107,7 @@ export function createSearchBar({ core, store, timefilter, data }: StatefulSearc services={{ appName: props.appName, data, - store, + storage, ...core, }} > diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx index 73e81a38572c3..62b60fa262cb8 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx @@ -56,7 +56,7 @@ const createMockWebStorage = () => ({ }); const createMockStorage = () => ({ - store: createMockWebStorage(), + storage: createMockWebStorage(), get: jest.fn(), set: jest.fn(), remove: jest.fn(), @@ -95,7 +95,7 @@ function wrapSearchBarInContext(testProps: any) { savedObjects: startMock.savedObjects, notifications: startMock.notifications, http: startMock.http, - store: createMockStorage(), + storage: createMockStorage(), }; return ( diff --git a/src/legacy/core_plugins/data/public/shim/legacy_dependencies_plugin.ts b/src/legacy/core_plugins/data/public/shim/legacy_dependencies_plugin.ts deleted file mode 100644 index 83d276fe9ba74..0000000000000 --- a/src/legacy/core_plugins/data/public/shim/legacy_dependencies_plugin.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { Storage } from 'ui/storage'; -import { Plugin } from '../../../../../../src/core/public'; - -/** @internal */ -export interface LegacyDependenciesPluginSetup { - storage: Storage; -} - -export interface LegacyDependenciesPluginStart { - storage: Storage; -} - -export class LegacyDependenciesPlugin implements Plugin { - public setup() { - return { - storage: new Storage(window.localStorage), - } as LegacyDependenciesPluginSetup; - } - - public start() { - return { - storage: new Storage(window.localStorage), - } as LegacyDependenciesPluginStart; - } -} diff --git a/src/legacy/core_plugins/data/public/timefilter/time_history.ts b/src/legacy/core_plugins/data/public/timefilter/time_history.ts index 22778d1adea3c..36ad1a4427a47 100644 --- a/src/legacy/core_plugins/data/public/timefilter/time_history.ts +++ b/src/legacy/core_plugins/data/public/timefilter/time_history.ts @@ -19,13 +19,13 @@ import moment from 'moment'; import { TimeRange } from 'src/plugins/data/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { PersistedLog } from '../query/persisted_log'; -import { Storage } from '../types'; export class TimeHistory { private history: PersistedLog; - constructor(store: Storage) { + constructor(storage: IStorageWrapper) { const historyOptions = { maxLength: 10, filterDuplicates: true, @@ -33,7 +33,7 @@ export class TimeHistory { return oldItem.from === newItem.from && oldItem.to === newItem.to; }, }; - this.history = new PersistedLog('kibana.timepicker.timeHistory', historyOptions, store); + this.history = new PersistedLog('kibana.timepicker.timeHistory', historyOptions, storage); } add(time: TimeRange) { diff --git a/src/legacy/core_plugins/data/public/timefilter/timefilter_service.ts b/src/legacy/core_plugins/data/public/timefilter/timefilter_service.ts index cda9b93ef08aa..831ccebedc9cc 100644 --- a/src/legacy/core_plugins/data/public/timefilter/timefilter_service.ts +++ b/src/legacy/core_plugins/data/public/timefilter/timefilter_service.ts @@ -18,8 +18,8 @@ */ import { UiSettingsClientContract } from 'src/core/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { TimeHistory, Timefilter, TimeHistoryContract, TimefilterContract } from './index'; -import { Storage } from '../types'; /** * Filter Service @@ -28,16 +28,16 @@ import { Storage } from '../types'; export interface TimeFilterServiceDependencies { uiSettings: UiSettingsClientContract; - store: Storage; + storage: IStorageWrapper; } export class TimefilterService { - public setup({ uiSettings, store }: TimeFilterServiceDependencies): TimefilterSetup { + public setup({ uiSettings, storage }: TimeFilterServiceDependencies): TimefilterSetup { const timefilterConfig = { timeDefaults: uiSettings.get('timepicker:timeDefaults'), refreshIntervalDefaults: uiSettings.get('timepicker:refreshIntervalDefaults'), }; - const history = new TimeHistory(store); + const history = new TimeHistory(storage); const timefilter = new Timefilter(timefilterConfig, history); return { diff --git a/src/legacy/core_plugins/data/public/types.ts b/src/legacy/core_plugins/data/public/types.ts index 2c02a9b764755..b6c9c47cc0ae6 100644 --- a/src/legacy/core_plugins/data/public/types.ts +++ b/src/legacy/core_plugins/data/public/types.ts @@ -19,13 +19,7 @@ import { UiSettingsClientContract, CoreStart } from 'src/core/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; - -export interface Storage { - get: (key: string) => any; - set: (key: string, value: any) => void; - remove: (key: string) => any; - clear: () => void; -} +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; export interface IDataPluginServices extends Partial { appName: string; @@ -33,6 +27,6 @@ export interface IDataPluginServices extends Partial { savedObjects: CoreStart['savedObjects']; notifications: CoreStart['notifications']; http: CoreStart['http']; - store: Storage; + storage: IStorageWrapper; data: DataPublicPluginStart; } diff --git a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts index dd0674073f442..b78d05e68acad 100644 --- a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts @@ -18,6 +18,7 @@ */ import 'ui/collapsible_sidebar'; import 'ui/directives/listen'; +import 'ui/directives/storage'; import 'ui/fixed_scroll'; import 'ui/directives/css_truncate'; diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js index 50e787b634e18..f8cdfa956aa60 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js @@ -26,6 +26,7 @@ import './visualization'; import 'ui/vis/editors/default/sidebar'; import 'ui/visualize'; import 'ui/collapsible_sidebar'; +import 'ui/directives/storage'; import { capabilities } from 'ui/capabilities'; import chrome from 'ui/chrome'; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js index 3497a35f5c99d..842d3aa6c4ad7 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js @@ -32,10 +32,10 @@ import { fetchFields } from '../lib/fetch_fields'; import { extractIndexPatterns } from '../../common/extract_index_patterns'; import { npStart } from 'ui/new_platform'; -import { Storage } from 'ui/storage'; + import { CoreStartContextProvider } from '../contexts/query_input_bar_context'; import { KibanaContextProvider } from '../../../../../plugins/kibana_react/public'; -const localStorage = new Storage(window.localStorage); +import { Storage } from '../../../../../plugins/kibana_utils/public'; import { timefilter } from 'ui/timefilter'; const VIS_STATE_DEBOUNCE_DELAY = 200; @@ -46,6 +46,7 @@ export class VisEditor extends Component { super(props); const { vis } = props; this.appState = vis.API.getAppState(); + this.localStorage = new Storage(window.localStorage); this.state = { model: props.visParams, dirty: false, @@ -63,7 +64,7 @@ export class VisEditor extends Component { appName: APP_NAME, uiSettings: npStart.core.uiSettings, savedObjectsClient: npStart.core.savedObjects.client, - store: localStorage, + store: this.localStorage, }; } @@ -169,7 +170,7 @@ export class VisEditor extends Component { (null); diff --git a/src/legacy/ui/public/agg_types/buckets/filters.ts b/src/legacy/ui/public/agg_types/buckets/filters.ts index f0450f220f610..44a97abb7a1d7 100644 --- a/src/legacy/ui/public/agg_types/buckets/filters.ts +++ b/src/legacy/ui/public/agg_types/buckets/filters.ts @@ -21,7 +21,6 @@ import _ from 'lodash'; import angular from 'angular'; import { i18n } from '@kbn/i18n'; -import { Storage } from 'ui/storage'; import chrome from 'ui/chrome'; import { buildEsQuery } from '@kbn/es-query'; @@ -29,6 +28,7 @@ import { FiltersParamEditor, FilterValue } from '../../vis/editors/default/contr import { createFilterFilters } from './create_filter/filters'; import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; import { setup as data } from '../../../../core_plugins/data/public/legacy'; +import { Storage } from '../../../../../plugins/kibana_utils/public'; const { getQueryLog } = data.query.helpers; const config = chrome.getUiSettingsClient(); diff --git a/src/legacy/ui/public/autoload/modules.js b/src/legacy/ui/public/autoload/modules.js index d662d479fc86b..e1d897236297e 100644 --- a/src/legacy/ui/public/autoload/modules.js +++ b/src/legacy/ui/public/autoload/modules.js @@ -27,7 +27,6 @@ import '../promises'; import '../modals'; import '../state_management/app_state'; import '../state_management/global_state'; -import '../storage'; import '../style_compile'; import '../url'; import '../directives/watch_multi'; diff --git a/src/legacy/ui/public/chrome/chrome.js b/src/legacy/ui/public/chrome/chrome.js index a5a0521013a6e..d644965e09225 100644 --- a/src/legacy/ui/public/chrome/chrome.js +++ b/src/legacy/ui/public/chrome/chrome.js @@ -26,7 +26,7 @@ import '../config'; import '../notify'; import '../private'; import '../promises'; -import '../storage'; +import '../directives/storage'; import '../directives/watch_multi'; import './services'; import '../react_components'; diff --git a/src/legacy/ui/public/storage/directive.js b/src/legacy/ui/public/directives/storage/index.js similarity index 90% rename from src/legacy/ui/public/storage/directive.js rename to src/legacy/ui/public/directives/storage/index.js index a5bb2ee3b6b0b..8c18012672c1b 100644 --- a/src/legacy/ui/public/storage/directive.js +++ b/src/legacy/ui/public/directives/storage/index.js @@ -18,8 +18,8 @@ */ -import { uiModules } from '../modules'; -import { Storage } from './storage'; +import { uiModules } from '../../modules'; +import { Storage } from '../../../../../plugins/kibana_utils/public'; const createService = function (type) { return function ($window) { diff --git a/src/legacy/ui/public/vis/editors/default/controls/filter.tsx b/src/legacy/ui/public/vis/editors/default/controls/filter.tsx index 2c0a2b6be37f8..4ebe7b0d835d7 100644 --- a/src/legacy/ui/public/vis/editors/default/controls/filter.tsx +++ b/src/legacy/ui/public/vis/editors/default/controls/filter.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { Query, QueryBarInput } from 'plugins/data'; import { AggConfig } from '../../..'; import { npStart } from '../../../../new_platform'; -import { Storage } from '../../../../storage'; +import { Storage } from '../../../../../../../plugins/kibana_utils/public'; import { KibanaContextProvider } from '../../../../../../../plugins/kibana_react/public'; const localStorage = new Storage(window.localStorage); @@ -94,7 +94,7 @@ function FilterRow({ { try { - return this.store.setItem(key, angular.toJson(value)); + return this.store.setItem(key, JSON.stringify(value)); } catch (e) { return false; } diff --git a/src/legacy/ui/public/storage/web_storage.ts b/src/plugins/kibana_utils/public/storage/types.ts similarity index 70% rename from src/legacy/ui/public/storage/web_storage.ts rename to src/plugins/kibana_utils/public/storage/types.ts index d5f775431143d..875bb44bcad17 100644 --- a/src/legacy/ui/public/storage/web_storage.ts +++ b/src/plugins/kibana_utils/public/storage/types.ts @@ -17,4 +17,16 @@ * under the License. */ -export type WebStorage = Storage; +export interface IStorageWrapper { + get: (key: string) => any; + set: (key: string, value: any) => void; + remove: (key: string) => any; + clear: () => void; +} + +export interface IStorage { + getItem: (key: string) => any; + setItem: (key: string, value: any) => void; + removeItem: (key: string) => any; + clear: () => void; +} diff --git a/x-pack/legacy/plugins/canvas/public/lib/clipboard.js b/x-pack/legacy/plugins/canvas/public/lib/clipboard.js index 2cebf2a5bad96..f9d68769c9c3a 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/clipboard.js +++ b/x-pack/legacy/plugins/canvas/public/lib/clipboard.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Storage } from 'ui/storage'; +import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; import { LOCALSTORAGE_CLIPBOARD } from '../../common/lib/constants'; import { getWindow } from './get_window'; diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/index.html b/x-pack/legacy/plugins/graph/public/angular/templates/index.html index 686f0f590a19c..b3f5bce7ea6ec 100644 --- a/x-pack/legacy/plugins/graph/public/angular/templates/index.html +++ b/x-pack/legacy/plugins/graph/public/angular/templates/index.html @@ -61,7 +61,7 @@ initial-query="initialQuery" plugin-data-start="pluginDataStart" core-start="coreStart" - store="store" + storage="storage" no-index-patterns="noIndexPatterns" >

diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index 41e5819bcbf37..ab83815457981 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -100,7 +100,7 @@ export function initGraphApp(angularModule, deps) { app.directive('graphApp', function (reactDirective) { return reactDirective(GraphApp, [ - ['store', { watchDepth: 'reference' }], + ['storage', { watchDepth: 'reference' }], ['isInitialized', { watchDepth: 'reference' }], ['currentIndexPattern', { watchDepth: 'reference' }], ['indexPatternProvider', { watchDepth: 'reference' }], @@ -310,7 +310,7 @@ export function initGraphApp(angularModule, deps) { // register things on scope passed down to react components $scope.pluginDataStart = npData; - $scope.store = new Storage(window.localStorage); + $scope.storage = new Storage(window.localStorage); $scope.coreStart = coreStart; $scope.loading = false; $scope.reduxStore = store; diff --git a/x-pack/legacy/plugins/graph/public/components/app.tsx b/x-pack/legacy/plugins/graph/public/components/app.tsx index aa2221441793f..5ff7fc2e5da93 100644 --- a/x-pack/legacy/plugins/graph/public/components/app.tsx +++ b/x-pack/legacy/plugins/graph/public/components/app.tsx @@ -10,8 +10,8 @@ import { DataPublicPluginStart } from 'src/plugins/data/public'; import { Provider } from 'react-redux'; import React, { useState } from 'react'; import { I18nProvider } from '@kbn/i18n/react'; -import { Storage } from 'ui/storage'; import { CoreStart } from 'kibana/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { FieldManager } from './field_manager'; import { SearchBarProps, SearchBar } from './search_bar'; import { GraphStore } from '../state_management'; @@ -23,7 +23,7 @@ export interface GraphAppProps extends SearchBarProps { coreStart: CoreStart; // This is not named dataStart because of Angular treating data- prefix differently pluginDataStart: DataPublicPluginStart; - store: Storage; + storage: IStorageWrapper; reduxStore: GraphStore; isInitialized: boolean; noIndexPatterns: boolean; @@ -34,7 +34,7 @@ export function GraphApp(props: GraphAppProps) { const { coreStart, pluginDataStart, - store, + storage, reduxStore, noIndexPatterns, ...searchBarProps @@ -45,7 +45,7 @@ export function GraphApp(props: GraphAppProps) { {}, }, }; diff --git a/x-pack/legacy/plugins/graph/public/index.ts b/x-pack/legacy/plugins/graph/public/index.ts index 0249ca74035d6..5e500367ccdc5 100644 --- a/x-pack/legacy/plugins/graph/public/index.ts +++ b/x-pack/legacy/plugins/graph/public/index.ts @@ -14,11 +14,11 @@ import chrome from 'ui/chrome'; import { IPrivate } from 'ui/private'; // @ts-ignore import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; -import { Storage } from 'ui/storage'; // @ts-ignore import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry'; import { npSetup, npStart } from 'ui/new_platform'; +import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { start as data } from '../../../../../src/legacy/core_plugins/data/public/legacy'; import { GraphPlugin } from './plugin'; 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 85494701749f1..a1710d67b31db 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 @@ -10,7 +10,7 @@ import { act } from 'react-dom/test-utils'; import { buildExistsFilter } from '@kbn/es-query'; import { App } from './app'; import { EditorFrameInstance } from '../types'; -import { Storage } from 'ui/storage'; +import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; import { Document, SavedObjectStore } from '../persistence'; import { mount } from 'enzyme'; @@ -80,7 +80,7 @@ describe('Lens App', () => { data: typeof dataStartMock; core: typeof core; dataShim: DataStart; - store: Storage; + storage: Storage; docId?: string; docStorage: SavedObjectStore; redirectTo: (id?: string) => void; @@ -112,7 +112,7 @@ describe('Lens App', () => { }, timefilter: { history: {} }, }, - store: { + storage: { get: jest.fn(), }, docStorage: { @@ -125,7 +125,7 @@ describe('Lens App', () => { data: typeof dataStartMock; core: typeof core; dataShim: DataStart; - store: Storage; + storage: Storage; docId?: string; docStorage: SavedObjectStore; redirectTo: (id?: string) => void; 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 3f27fe3cbc072..a95e0450f614c 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx @@ -8,7 +8,6 @@ import _ from 'lodash'; 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'; @@ -20,6 +19,7 @@ import { Query, } from 'src/legacy/core_plugins/data/public'; import { Filter } from '@kbn/es-query'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { start as navigation } from '../../../../../../src/legacy/core_plugins/navigation/public/legacy'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import { Document, SavedObjectStore } from '../persistence'; @@ -49,7 +49,7 @@ export function App({ data, dataShim, core, - store, + storage, docId, docStorage, redirectTo, @@ -58,14 +58,14 @@ export function App({ data: DataPublicPluginStart; core: CoreStart; dataShim: DataStart; - store: Storage; + storage: IStorageWrapper; docId?: string; docStorage: SavedObjectStore; redirectTo: (id?: string) => void; }) { const timeDefaults = core.uiSettings.get('timepicker:timeDefaults'); const language = - store.get('kibana.userQueryLanguage') || core.uiSettings.get('search:queryLanguage'); + storage.get('kibana.userQueryLanguage') || core.uiSettings.get('search:queryLanguage'); const [state, setState] = useState({ isLoading: !!docId, @@ -170,7 +170,7 @@ export function App({ services={{ appName: 'lens', data, - store, + storage, ...core, }} > @@ -247,7 +247,7 @@ export function App({ query: { query: '', language: - store.get('kibana.userQueryLanguage') || + storage.get('kibana.userQueryLanguage') || core.uiSettings.get('search:queryLanguage'), }, })); diff --git a/x-pack/legacy/plugins/lens/public/app_plugin/plugin.tsx b/x-pack/legacy/plugins/lens/public/app_plugin/plugin.tsx index b7960b23651c6..56c19ea2bb9f2 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/plugin.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/plugin.tsx @@ -8,12 +8,12 @@ import React from 'react'; import { I18nProvider, FormattedMessage } from '@kbn/i18n/react'; import { HashRouter, Switch, Route, RouteComponentProps } from 'react-router-dom'; import chrome from 'ui/chrome'; -import { Storage } from 'ui/storage'; import { CoreSetup, CoreStart } from 'src/core/public'; import { npSetup, npStart } from 'ui/new_platform'; import { DataPublicPluginStart } from 'src/plugins/data/public'; import { DataStart } from '../../../../../../src/legacy/core_plugins/data/public'; import { start as dataShimStart } from '../../../../../../src/legacy/core_plugins/data/public/legacy'; +import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; import { editorFrameSetup, editorFrameStart, editorFrameStop } from '../editor_frame_plugin'; import { indexPatternDatasourceSetup, indexPatternDatasourceStop } from '../indexpattern_plugin'; import { SavedObjectIndexStore } from '../persistence'; @@ -84,7 +84,7 @@ export class AppPlugin { data={data} dataShim={dataShim} editorFrame={this.instance!} - store={new Storage(localStorage)} + storage={new Storage(localStorage)} docId={routeProps.match.params.id} docStorage={store} redirectTo={id => { diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.test.tsx index 49a04e59b7cc9..a6ccc6361c3d0 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.test.tsx @@ -18,7 +18,7 @@ import { SavedObjectsClientContract, HttpServiceBase, } from 'src/core/public'; -import { Storage } from 'ui/storage'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { IndexPatternPrivateState } from '../types'; jest.mock('ui/new_platform'); @@ -133,7 +133,7 @@ describe('IndexPatternDimensionPanel', () => { layerId: 'first', uniqueLabel: 'stuff', filterOperations: () => true, - storage: {} as Storage, + storage: {} as IStorageWrapper, uiSettings: {} as UiSettingsClientContract, savedObjectsClient: {} as SavedObjectsClientContract, http: {} as HttpServiceBase, diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.tsx index c0c774a225642..a1242947a87d3 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.tsx @@ -7,13 +7,13 @@ import _ from 'lodash'; import React, { memo, useMemo } from 'react'; import { EuiButtonIcon } from '@elastic/eui'; -import { Storage } from 'ui/storage'; import { i18n } from '@kbn/i18n'; import { UiSettingsClientContract, SavedObjectsClientContract, HttpServiceBase, } from 'src/core/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { DatasourceDimensionPanelProps, StateSetter } from '../../types'; import { IndexPatternColumn, OperationType } from '../indexpattern'; import { getAvailableOperationsByMetadata, buildColumn, changeField } from '../operations'; @@ -29,7 +29,7 @@ export type IndexPatternDimensionPanelProps = DatasourceDimensionPanelProps & { setState: StateSetter; dragDropContext: DragContextState; uiSettings: UiSettingsClientContract; - storage: Storage; + storage: IStorageWrapper; savedObjectsClient: SavedObjectsClientContract; layerId: string; http: HttpServiceBase; diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.test.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.test.ts index b4f01078f1f78..2edf9dd0b5acb 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.test.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.test.ts @@ -5,7 +5,7 @@ */ import chromeMock from 'ui/chrome'; -import { Storage } from 'ui/storage'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { SavedObjectsClientContract } from 'kibana/public'; import { getIndexPatternDatasource, IndexPatternColumn, uniqueLabels } from './indexpattern'; import { DatasourcePublicAPI, Operation, Datasource } from '../types'; @@ -143,7 +143,7 @@ describe('IndexPattern Data Source', () => { beforeEach(() => { indexPatternDatasource = getIndexPatternDatasource({ chrome: chromeMock, - storage: {} as Storage, + storage: {} as IStorageWrapper, core: coreMock.createStart(), savedObjectsClient: {} as SavedObjectsClientContract, data: pluginsMock.createStart().data, diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx index 359eb687b5741..1ab6ab6307e8d 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx @@ -9,8 +9,8 @@ import React from 'react'; import { render } from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; import { CoreStart, SavedObjectsClientContract } from 'src/core/public'; -import { Storage } from 'ui/storage'; import { i18n } from '@kbn/i18n'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { DatasourceDimensionPanelProps, DatasourceDataPanelProps, @@ -104,7 +104,7 @@ export function getIndexPatternDatasource({ // Core start is being required here because it contains the savedObject client // In the new platform, this plugin wouldn't be initialized until after setup core: CoreStart; - storage: Storage; + storage: IStorageWrapper; savedObjectsClient: SavedObjectsClientContract; data: ReturnType; }) { @@ -221,7 +221,7 @@ export function getIndexPatternDatasource({ { columnId="col1" layerId="first" currentColumn={state.layers.first.columns.col1 as DateHistogramIndexPatternColumn} - storage={{} as Storage} + storage={{} as IStorageWrapper} uiSettings={{} as UiSettingsClientContract} savedObjectsClient={{} as SavedObjectsClientContract} http={{} as HttpServiceBase} @@ -346,7 +346,7 @@ describe('date_histogram', () => { columnId="col2" currentColumn={state.layers.second.columns.col2 as DateHistogramIndexPatternColumn} layerId="second" - storage={{} as Storage} + storage={{} as IStorageWrapper} uiSettings={{} as UiSettingsClientContract} savedObjectsClient={{} as SavedObjectsClientContract} http={{} as HttpServiceBase} @@ -364,7 +364,7 @@ describe('date_histogram', () => { columnId="col1" currentColumn={state.layers.third.columns.col1 as DateHistogramIndexPatternColumn} layerId="third" - storage={{} as Storage} + storage={{} as IStorageWrapper} uiSettings={{} as UiSettingsClientContract} savedObjectsClient={{} as SavedObjectsClientContract} http={{} as HttpServiceBase} @@ -383,7 +383,7 @@ describe('date_histogram', () => { columnId="col1" layerId="third" currentColumn={state.layers.third.columns.col1 as DateHistogramIndexPatternColumn} - storage={{} as Storage} + storage={{} as IStorageWrapper} uiSettings={{} as UiSettingsClientContract} savedObjectsClient={{} as SavedObjectsClientContract} http={{} as HttpServiceBase} @@ -406,7 +406,7 @@ describe('date_histogram', () => { columnId="col1" layerId="first" currentColumn={state.layers.first.columns.col1 as DateHistogramIndexPatternColumn} - storage={{} as Storage} + storage={{} as IStorageWrapper} uiSettings={{} as UiSettingsClientContract} savedObjectsClient={{} as SavedObjectsClientContract} http={{} as HttpServiceBase} @@ -469,7 +469,7 @@ describe('date_histogram', () => { columnId="col1" layerId="first" currentColumn={state.layers.first.columns.col1 as DateHistogramIndexPatternColumn} - storage={{} as Storage} + storage={{} as IStorageWrapper} uiSettings={{} as UiSettingsClientContract} savedObjectsClient={{} as SavedObjectsClientContract} http={{} as HttpServiceBase} diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/index.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/index.ts index fdb5419588e7e..ea6a54f5fa17f 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/index.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/index.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Storage } from 'ui/storage'; import { UiSettingsClientContract, SavedObjectsClientContract, HttpServiceBase, } from 'src/core/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { termsOperation } from './terms'; import { cardinalityOperation } from './cardinality'; import { minOperation, averageOperation, sumOperation, maxOperation } from './metrics'; @@ -48,7 +48,7 @@ export interface ParamEditorProps { columnId: string; layerId: string; uiSettings: UiSettingsClientContract; - storage: Storage; + storage: IStorageWrapper; savedObjectsClient: SavedObjectsClientContract; http: HttpServiceBase; } diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/terms.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/terms.test.tsx index df5bfac6d03a4..9630f850dc247 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/terms.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/terms.test.tsx @@ -12,7 +12,7 @@ import { SavedObjectsClientContract, HttpServiceBase, } from 'src/core/public'; -import { Storage } from 'ui/storage'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { createMockedIndexPattern } from '../../mocks'; import { TermsIndexPatternColumn } from './terms'; import { termsOperation } from '.'; @@ -329,7 +329,7 @@ describe('terms', () => { columnId="col1" currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn} layerId="first" - storage={{} as Storage} + storage={{} as IStorageWrapper} uiSettings={{} as UiSettingsClientContract} savedObjectsClient={{} as SavedObjectsClientContract} http={{} as HttpServiceBase} @@ -355,7 +355,7 @@ describe('terms', () => { columnId="col1" currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn} layerId="first" - storage={{} as Storage} + storage={{} as IStorageWrapper} uiSettings={{} as UiSettingsClientContract} savedObjectsClient={{} as SavedObjectsClientContract} http={{} as HttpServiceBase} @@ -403,7 +403,7 @@ describe('terms', () => { columnId="col1" layerId="first" currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn} - storage={{} as Storage} + storage={{} as IStorageWrapper} uiSettings={{} as UiSettingsClientContract} savedObjectsClient={{} as SavedObjectsClientContract} http={{} as HttpServiceBase} @@ -427,7 +427,7 @@ describe('terms', () => { columnId="col1" layerId="first" currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn} - storage={{} as Storage} + storage={{} as IStorageWrapper} uiSettings={{} as UiSettingsClientContract} savedObjectsClient={{} as SavedObjectsClientContract} http={{} as HttpServiceBase} @@ -472,7 +472,7 @@ describe('terms', () => { columnId="col1" layerId="first" currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn} - storage={{} as Storage} + storage={{} as IStorageWrapper} uiSettings={{} as UiSettingsClientContract} savedObjectsClient={{} as SavedObjectsClientContract} http={{} as HttpServiceBase} @@ -491,7 +491,7 @@ describe('terms', () => { columnId="col1" layerId="first" currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn} - storage={{} as Storage} + storage={{} as IStorageWrapper} uiSettings={{} as UiSettingsClientContract} savedObjectsClient={{} as SavedObjectsClientContract} http={{} as HttpServiceBase} diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/plugin.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/plugin.tsx index 9c4835437546e..4dde289259c41 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/plugin.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/plugin.tsx @@ -8,8 +8,8 @@ import { Registry } from '@kbn/interpreter/target/common'; import { CoreSetup } from 'src/core/public'; // The following dependencies on ui/* and src/legacy/core_plugins must be mocked when testing import chrome, { Chrome } from 'ui/chrome'; -import { Storage } from 'ui/storage'; import { npSetup, npStart } from 'ui/new_platform'; +import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; import { ExpressionFunction } from '../../../../../../src/legacy/core_plugins/interpreter/public'; import { functionsRegistry } from '../../../../../../src/legacy/core_plugins/interpreter/public/registries'; import { getIndexPatternDatasource } from './indexpattern'; diff --git a/x-pack/legacy/plugins/lens/public/lens_ui_telemetry/factory.test.ts b/x-pack/legacy/plugins/lens/public/lens_ui_telemetry/factory.test.ts index 6e860c594f4a5..15d36a7c31169 100644 --- a/x-pack/legacy/plugins/lens/public/lens_ui_telemetry/factory.test.ts +++ b/x-pack/legacy/plugins/lens/public/lens_ui_telemetry/factory.test.ts @@ -11,9 +11,9 @@ import { trackUiEvent, trackSuggestionEvent, } from './factory'; -import { Storage } from 'src/legacy/core_plugins/data/public/types'; import { coreMock } from 'src/core/public/mocks'; import { HttpServiceBase } from 'kibana/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; jest.useFakeTimers(); @@ -30,7 +30,7 @@ const createMockStorage = () => { }; describe('Lens UI telemetry', () => { - let storage: jest.Mocked; + let storage: jest.Mocked; let http: jest.Mocked; let dateSpy: jest.SpyInstance; diff --git a/x-pack/legacy/plugins/lens/public/lens_ui_telemetry/factory.ts b/x-pack/legacy/plugins/lens/public/lens_ui_telemetry/factory.ts index 673910deae339..1a8ec604eda54 100644 --- a/x-pack/legacy/plugins/lens/public/lens_ui_telemetry/factory.ts +++ b/x-pack/legacy/plugins/lens/public/lens_ui_telemetry/factory.ts @@ -7,7 +7,7 @@ import moment from 'moment'; import { HttpServiceBase } from 'src/core/public'; -import { Storage } from 'src/legacy/core_plugins/data/public/types'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { BASE_API_URL } from '../../common'; const STORAGE_KEY = 'lens-ui-telemetry'; @@ -43,11 +43,11 @@ export class LensReportManager { private events: Record> = {}; private suggestionEvents: Record> = {}; - private storage: Storage; + private storage: IStorageWrapper; private http: HttpServiceBase; private timer: ReturnType; - constructor({ storage, http }: { storage: Storage; http: HttpServiceBase }) { + constructor({ storage, http }: { storage: IStorageWrapper; http: HttpServiceBase }) { this.storage = storage; this.http = http; diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index a3102417a9cb6..cef2a70a6d03d 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -7,6 +7,7 @@ import _ from 'lodash'; import chrome from 'ui/chrome'; import 'ui/directives/listen'; +import 'ui/directives/storage'; import React from 'react'; import { I18nProvider } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/__snapshots__/view.test.js.snap b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/__snapshots__/view.test.js.snap index c3d4ff673e3d5..4377fa4725483 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/__snapshots__/view.test.js.snap +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/__snapshots__/view.test.js.snap @@ -6,7 +6,7 @@ exports[`LayerPanel is rendered 1`] = ` Object { "appName": "maps", "data": undefined, - "store": Storage { + "storage": Storage { "clear": [Function], "get": [Function], "remove": [Function], diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.js index 28afabc40bd75..78cb8aa827e35 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.js @@ -30,8 +30,8 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public'; +import { Storage } from '../../../../../../../src/plugins/kibana_utils/public'; -import { Storage } from 'ui/storage'; const localStorage = new Storage(window.localStorage); // This import will eventually become a dependency injected by the fully deangularized NP plugin. @@ -154,7 +154,7 @@ export class LayerPanel extends React.Component { Date: Tue, 29 Oct 2019 15:28:54 +0300 Subject: [PATCH 06/25] Convert agg_types/__tests__ to JEST (Buckets folder) (#49333) --- .../agg_types/__tests__/agg_param_writer.js | 103 ------ .../__tests__/buckets/_date_range.js | 80 ----- .../agg_types/__tests__/buckets/_geo_hash.js | 257 --------------- .../agg_types/__tests__/buckets/_histogram.js | 210 ------------- .../agg_types/__tests__/buckets/_range.js | 71 ----- .../buckets/create_filter/date_histogram.js | 121 -------- .../buckets/create_filter/date_range.js | 67 ---- .../buckets/create_filter/filters.js | 61 ---- .../buckets/create_filter/histogram.js | 61 ---- .../buckets/create_filter/ip_range.js | 97 ------ .../__tests__/buckets/create_filter/range.js | 66 ---- .../__tests__/buckets/create_filter/terms.js | 104 ------- .../buckets/date_histogram/_editor.js | 124 -------- .../buckets/date_histogram/_params.js | 215 ------------- .../__tests__/buckets/significant_terms.js | 95 ------ .../agg_types/__tests__/buckets/terms.js | 193 ------------ .../__tests__/utils/_stub_agg_params.js | 67 ---- src/legacy/ui/public/agg_types/agg_config.ts | 2 +- .../create_filter/date_histogram.test.ts | 122 ++++++++ .../buckets/create_filter/date_histogram.ts | 5 +- .../buckets/create_filter/date_range.test.ts | 77 +++++ .../buckets/create_filter/filters.test.ts | 66 ++++ .../buckets/create_filter/histogram.test.ts | 73 +++++ .../buckets/create_filter/ip_range.test.ts | 104 +++++++ .../buckets/create_filter/range.test.ts | 74 +++++ .../buckets/create_filter/terms.test.ts | 113 +++++++ .../agg_types/buckets/date_range.test.ts | 112 +++++++ .../public/agg_types/buckets/geo_hash.test.ts | 216 +++++++++++++ .../agg_types/buckets/histogram.test.ts | 292 ++++++++++++++++++ .../ui/public/agg_types/buckets/histogram.ts | 18 +- .../ui/public/agg_types/buckets/range.test.ts | 95 ++++++ .../buckets/significant_terms.test.ts | 110 +++++++ .../ui/public/agg_types/buckets/terms.test.ts | 78 +++++ .../agg_types/filter/prop_filter.test.ts | 5 +- .../metrics/lib/make_nested_label.test.ts | 2 +- .../agg_types/metrics/parent_pipeline.test.ts | 4 +- .../metrics/sibling_pipeline.test.ts | 4 +- .../agg_types/metrics/std_deviation.test.ts | 4 +- .../public/agg_types/metrics/top_hit.test.ts | 4 +- .../public/agg_types/param_types/json.test.ts | 7 +- .../agg_types/param_types/string.test.ts | 7 +- 41 files changed, 1567 insertions(+), 2019 deletions(-) delete mode 100644 src/legacy/ui/public/agg_types/__tests__/agg_param_writer.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/buckets/_date_range.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/buckets/_geo_hash.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/buckets/_histogram.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/buckets/_range.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/date_histogram.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/date_range.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/filters.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/histogram.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/ip_range.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/range.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/terms.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/buckets/date_histogram/_editor.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/buckets/date_histogram/_params.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/buckets/significant_terms.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/buckets/terms.js delete mode 100644 src/legacy/ui/public/agg_types/__tests__/utils/_stub_agg_params.js create mode 100644 src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.test.ts create mode 100644 src/legacy/ui/public/agg_types/buckets/create_filter/date_range.test.ts create mode 100644 src/legacy/ui/public/agg_types/buckets/create_filter/filters.test.ts create mode 100644 src/legacy/ui/public/agg_types/buckets/create_filter/histogram.test.ts create mode 100644 src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.test.ts create mode 100644 src/legacy/ui/public/agg_types/buckets/create_filter/range.test.ts create mode 100644 src/legacy/ui/public/agg_types/buckets/create_filter/terms.test.ts create mode 100644 src/legacy/ui/public/agg_types/buckets/date_range.test.ts create mode 100644 src/legacy/ui/public/agg_types/buckets/geo_hash.test.ts create mode 100644 src/legacy/ui/public/agg_types/buckets/histogram.test.ts create mode 100644 src/legacy/ui/public/agg_types/buckets/range.test.ts create mode 100644 src/legacy/ui/public/agg_types/buckets/significant_terms.test.ts create mode 100644 src/legacy/ui/public/agg_types/buckets/terms.test.ts diff --git a/src/legacy/ui/public/agg_types/__tests__/agg_param_writer.js b/src/legacy/ui/public/agg_types/__tests__/agg_param_writer.js deleted file mode 100644 index c85899ca5704e..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/agg_param_writer.js +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import { VisProvider } from '../../vis'; -import { aggTypes } from '..'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { AggGroupNames } from '../../vis/editors/default/agg_groups'; - -// eslint-disable-next-line import/no-default-export -export default function AggParamWriterHelper(Private) { - const Vis = Private(VisProvider); - const stubbedLogstashIndexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - - /** - * Helper object for writing aggParams. Specify an aggType and it will find a vis & schema, and - * wire up the supporting objects required to feed in parameters, and get #write() output. - * - * Use cases: - * - Verify that the interval parameter of the histogram visualization casts its input to a number - * ```js - * it('casts to a number', function () { - * let writer = new AggParamWriter({ aggType: 'histogram' }); - * let output = writer.write({ interval : '100/10' }); - * expect(output.params.interval).to.be.a('number'); - * expect(output.params.interval).to.be(100); - * }); - * ``` - * - * @class AggParamWriter - * @param {object} opts - describe the properties of this paramWriter - * @param {string} opts.aggType - the name of the aggType we want to test. ('histogram', 'filter', etc.) - */ - class AggParamWriter { - - constructor(opts) { - this.aggType = opts.aggType; - if (_.isString(this.aggType)) { - this.aggType = aggTypes.buckets.find(agg => agg.name === this.aggType) || aggTypes.metrics.find(agg => agg.name === this.aggType); - } - - // not configurable right now, but totally required - this.indexPattern = stubbedLogstashIndexPattern; - - // the schema that the aggType satisfies - this.visAggSchema = null; - - this.vis = new Vis(this.indexPattern, { - type: 'histogram', - aggs: [{ - id: 1, - type: this.aggType.name, - params: {} - }] - }); - } - - write(paramValues, modifyAggConfig = null) { - paramValues = _.clone(paramValues); - - if (this.aggType.paramByName('field') && !paramValues.field) { - // pick a field rather than force a field to be specified everywhere - if (this.aggType.type === AggGroupNames.Metrics) { - paramValues.field = _.sample(this.indexPattern.fields.getByType('number')); - } else { - const type = this.aggType.paramByName('field').filterFieldTypes || 'string'; - let field; - do { - field = _.sample(this.indexPattern.fields.getByType(type)); - } while (!field.aggregatable); - paramValues.field = field.name; - } - } - - const aggConfig = this.vis.aggs.aggs[0]; - aggConfig.setParams(paramValues); - - if (modifyAggConfig) { - modifyAggConfig(aggConfig); - } - - return aggConfig.write(this.vis.aggs); - } - } - - return AggParamWriter; -} diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/_date_range.js b/src/legacy/ui/public/agg_types/__tests__/buckets/_date_range.js deleted file mode 100644 index 94603dfa69a66..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/_date_range.js +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { set } from 'lodash'; -import expect from '@kbn/expect'; -import sinon from 'sinon'; -import ngMock from 'ng_mock'; -import { aggTypes } from '../..'; -import AggParamWriterProvider from '../agg_param_writer'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import chrome from '../../../chrome'; - -const config = chrome.getUiSettingsClient(); - -describe('date_range params', function () { - let paramWriter; - let timeField; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - const AggParamWriter = Private(AggParamWriterProvider); - const indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - - timeField = indexPattern.timeFieldName; - paramWriter = new AggParamWriter({ aggType: 'date_range' }); - })); - - describe('getKey', () => { - const dateRange = aggTypes.buckets.find(agg => agg.name === 'date_range'); - it('should return object', () => { - const bucket = { from: 'from-date', to: 'to-date', key: 'from-dateto-date' }; - expect(dateRange.getKey(bucket)).to.equal({ from: 'from-date', to: 'to-date' }); - }); - }); - - describe('time_zone', () => { - beforeEach(() => { - sinon.stub(config, 'get'); - sinon.stub(config, 'isDefault'); - }); - - it('should use the specified time_zone', () => { - const output = paramWriter.write({ time_zone: 'Europe/Kiev' }); - expect(output.params).to.have.property('time_zone', 'Europe/Kiev'); - }); - - it('should use the Kibana time_zone if no parameter specified', () => { - config.isDefault.withArgs('dateFormat:tz').returns(false); - config.get.withArgs('dateFormat:tz').returns('Europe/Riga'); - const output = paramWriter.write({}); - expect(output.params).to.have.property('time_zone', 'Europe/Riga'); - }); - - it('should use the fixed time_zone from the index pattern typeMeta', () => { - set(paramWriter.indexPattern, ['typeMeta', 'aggs', 'date_range', timeField, 'time_zone'], 'Europe/Rome'); - const output = paramWriter.write({ field: timeField }); - expect(output.params).to.have.property('time_zone', 'Europe/Rome'); - }); - - afterEach(() => { - config.get.restore(); - config.isDefault.restore(); - }); - }); -}); diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/_geo_hash.js b/src/legacy/ui/public/agg_types/__tests__/buckets/_geo_hash.js deleted file mode 100644 index 7172d1f40936e..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/_geo_hash.js +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import sinon from 'sinon'; -import { geoHashBucketAgg } from '../../buckets/geo_hash'; -import * as AggConfigModule from '../../agg_config'; -import * as BucketAggTypeModule from '../../buckets/_bucket_agg_type'; - -describe('Geohash Agg', () => { - - const initialZoom = 10; - const initialMapBounds = { - top_left: { lat: 1.0, lon: -1.0 }, - bottom_right: { lat: -1.0, lon: 1.0 } - }; - - const BucketAggTypeMock = (aggOptions) => { - return aggOptions; - }; - const AggConfigMock = (parent, aggOptions) => { - return aggOptions; - }; - const createAggregationMock = (aggOptions) => { - return new AggConfigMock(null, aggOptions); - }; - - const aggMock = { - getField: () => { - return { - name: 'location' - }; - }, - params: { - isFilteredByCollar: true, - useGeocentroid: true, - mapZoom: initialZoom - }, - aggConfigs: {}, - type: 'geohash_grid', - }; - aggMock.aggConfigs.createAggConfig = createAggregationMock; - - - before(function () { - sinon.stub(AggConfigModule, 'AggConfig').callsFake(AggConfigMock); - sinon.stub(BucketAggTypeModule, 'BucketAggType').callsFake(BucketAggTypeMock); - }); - - after(function () { - AggConfigModule.AggConfig.restore(); - BucketAggTypeModule.BucketAggType.restore(); - }); - - function initAggParams() { - aggMock.params.isFilteredByCollar = true; - aggMock.params.useGeocentroid = true; - aggMock.params.mapBounds = initialMapBounds; - } - - function zoomMap(zoomChange) { - aggMock.params.mapZoom += zoomChange; - } - - function moveMap(newBounds) { - aggMock.params.mapBounds = newBounds; - } - - function resetMap() { - aggMock.params.mapZoom = initialZoom; - aggMock.params.mapBounds = initialMapBounds; - aggMock.params.mapCollar = { - top_left: { lat: 1.5, lon: -1.5 }, - bottom_right: { lat: -1.5, lon: 1.5 }, - zoom: initialZoom - }; - } - - describe('precision parameter', () => { - - const PRECISION_PARAM_INDEX = 2; - let precisionParam; - beforeEach(() => { - precisionParam = geoHashBucketAgg.params[PRECISION_PARAM_INDEX]; - }); - - it('should select precision parameter', () => { - expect(precisionParam.name).to.equal('precision'); - }); - - describe('precision parameter write', () => { - - const zoomToGeoHashPrecision = { - 0: 1, - 1: 2, - 2: 2, - 3: 2, - 4: 3, - 5: 3, - 6: 4, - 7: 4, - 8: 4, - 9: 5, - 10: 5, - 11: 6, - 12: 6, - 13: 6, - 14: 7, - 15: 7, - 16: 7, - 17: 7, - 18: 7, - 19: 7, - 20: 7, - 21: 7 - }; - - Object.keys(zoomToGeoHashPrecision).forEach((zoomLevel) => { - it(`zoom level ${zoomLevel} should correspond to correct geohash-precision`, () => { - const output = { params: {} }; - precisionParam.write({ - params: { - autoPrecision: true, - mapZoom: zoomLevel - } - }, output); - expect(output.params.precision).to.equal(zoomToGeoHashPrecision[zoomLevel]); - }); - }); - }); - - }); - - describe('getRequestAggs', () => { - - describe('initial aggregation creation', () => { - let requestAggs; - beforeEach(() => { - initAggParams(); - requestAggs = geoHashBucketAgg.getRequestAggs(aggMock); - }); - - it('should create filter, geohash_grid, and geo_centroid aggregations', () => { - expect(requestAggs.length).to.equal(3); - expect(requestAggs[0].type).to.equal('filter'); - expect(requestAggs[1].type).to.equal('geohash_grid'); - expect(requestAggs[2].type).to.equal('geo_centroid'); - }); - - it('should set mapCollar in vis session state', () => { - expect(aggMock).to.have.property('lastMapCollar'); - expect(aggMock.lastMapCollar).to.have.property('top_left'); - expect(aggMock.lastMapCollar).to.have.property('bottom_right'); - expect(aggMock.lastMapCollar).to.have.property('zoom'); - }); - - // there was a bug because of an "&& mapZoom" check which excluded 0 as a valid mapZoom, but it is. - it('should create filter, geohash_grid, and geo_centroid aggregations when zoom level 0', () => { - aggMock.params.mapZoom = 0; - requestAggs = geoHashBucketAgg.getRequestAggs(aggMock); - expect(requestAggs.length).to.equal(3); - expect(requestAggs[0].type).to.equal('filter'); - expect(requestAggs[1].type).to.equal('geohash_grid'); - expect(requestAggs[2].type).to.equal('geo_centroid'); - }); - }); - - describe('aggregation options', () => { - - beforeEach(() => { - initAggParams(); - }); - - it('should only create geohash_grid and geo_centroid aggregations when isFilteredByCollar is false', () => { - aggMock.params.isFilteredByCollar = false; - const requestAggs = geoHashBucketAgg.getRequestAggs(aggMock); - expect(requestAggs.length).to.equal(2); - expect(requestAggs[0].type).to.equal('geohash_grid'); - expect(requestAggs[1].type).to.equal('geo_centroid'); - }); - - it('should only create filter and geohash_grid aggregations when useGeocentroid is false', () => { - aggMock.params.useGeocentroid = false; - const requestAggs = geoHashBucketAgg.getRequestAggs(aggMock); - expect(requestAggs.length).to.equal(2); - expect(requestAggs[0].type).to.equal('filter'); - expect(requestAggs[1].type).to.equal('geohash_grid'); - - }); - }); - - describe('aggregation creation after map interaction', () => { - - let origRequestAggs; - let origMapCollar; - beforeEach(() => { - resetMap(); - initAggParams(); - origRequestAggs = geoHashBucketAgg.getRequestAggs(aggMock); - origMapCollar = JSON.stringify(aggMock.lastMapCollar, null, ''); - }); - - it('should not change geo_bounding_box filter aggregation and vis session state when map movement is within map collar', () => { - moveMap({ - top_left: { lat: 1.1, lon: -1.1 }, - bottom_right: { lat: -0.9, lon: 0.9 } - }); - - const newRequestAggs = geoHashBucketAgg.getRequestAggs(aggMock); - expect(JSON.stringify(origRequestAggs[0].params, null, '')).to.equal(JSON.stringify(newRequestAggs[0].params, null, '')); - - const newMapCollar = JSON.stringify(aggMock.lastMapCollar, null, ''); - expect(origMapCollar).to.equal(newMapCollar); - }); - - it('should change geo_bounding_box filter aggregation and vis session state when map movement is outside map collar', () => { - moveMap({ - top_left: { lat: 10.0, lon: -10.0 }, - bottom_right: { lat: 9.0, lon: -9.0 } - }); - - const newRequestAggs = geoHashBucketAgg.getRequestAggs(aggMock); - expect(JSON.stringify(origRequestAggs[0].params, null, '')).not.to.equal(JSON.stringify(newRequestAggs[0].params, null, '')); - - const newMapCollar = JSON.stringify(aggMock.lastMapCollar, null, ''); - expect(origMapCollar).not.to.equal(newMapCollar); - }); - - it('should change geo_bounding_box filter aggregation and vis session state when map zoom level changes', () => { - zoomMap(-1); - - geoHashBucketAgg.getRequestAggs(aggMock); - - const newMapCollar = JSON.stringify(aggMock.lastMapCollar, null, ''); - expect(origMapCollar).not.to.equal(newMapCollar); - }); - - }); - - }); -}); diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/_histogram.js b/src/legacy/ui/public/agg_types/__tests__/buckets/_histogram.js deleted file mode 100644 index 26ad80e28ae9b..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/_histogram.js +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import sinon from 'sinon'; -import ngMock from 'ng_mock'; -import { aggTypes } from '../..'; -import chrome from '../../../chrome'; -import AggParamWriterProvider from '../agg_param_writer'; - -const config = chrome.getUiSettingsClient(); -const histogram = aggTypes.buckets.find(agg => agg.name === 'histogram'); -describe('Histogram Agg', function () { - - describe('ordered', function () { - - it('is ordered', function () { - expect(histogram.ordered).to.be.ok(); - }); - - it('is not ordered by date', function () { - expect(histogram.ordered).to.not.have.property('date'); - }); - }); - - - describe('params', function () { - let paramWriter; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - const AggParamWriter = Private(AggParamWriterProvider); - paramWriter = new AggParamWriter({ aggType: 'histogram' }); - })); - - describe('intervalBase', () => { - it('should not be written to the DSL', () => { - const output = paramWriter.write({ intervalBase: 100 }); - expect(output.params).not.to.have.property('intervalBase'); - }); - }); - - describe('interval', function () { - // reads aggConfig.params.interval, writes to dsl.interval - - it('accepts a whole number', function () { - const output = paramWriter.write({ interval: 100 }); - expect(output.params).to.have.property('interval', 100); - }); - - it('accepts a decimal number', function () { - const output = paramWriter.write({ interval: 0.1 }); - expect(output.params).to.have.property('interval', 0.1); - }); - - it('accepts a decimal number string', function () { - const output = paramWriter.write({ interval: '0.1' }); - expect(output.params).to.have.property('interval', 0.1); - }); - - it('accepts a whole number string', function () { - const output = paramWriter.write({ interval: '10' }); - expect(output.params).to.have.property('interval', 10); - }); - - it('fails on non-numeric values', function () { - // template validation prevents this from users, not devs - const output = paramWriter.write({ interval: [] }); - expect(isNaN(output.params.interval)).to.be.ok(); - }); - - describe('interval scaling', () => { - - beforeEach(() => { - sinon.stub(config, 'get'); - }); - - it('will respect the histogram:maxBars setting', () => { - config.get.withArgs('histogram:maxBars').returns(5); - const output = paramWriter.write({ interval: 5 }, - aggConfig => aggConfig.setAutoBounds({ min: 0, max: 10000 })); - expect(output.params).to.have.property('interval', 2000); - }); - - it('will return specified interval, if bars are below histogram:maxBars config', () => { - config.get.withArgs('histogram:maxBars').returns(10000); - const output = paramWriter.write({ interval: 5 }, - aggConfig => aggConfig.setAutoBounds({ min: 0, max: 10000 })); - expect(output.params).to.have.property('interval', 5); - }); - - it('will set to intervalBase if interval is below base', () => { - const output = paramWriter.write({ interval: 3, intervalBase: 8 }); - expect(output.params).to.have.property('interval', 8); - }); - - it('will round to nearest intervalBase multiple if interval is above base', () => { - const roundUp = paramWriter.write({ interval: 46, intervalBase: 10 }); - expect(roundUp.params).to.have.property('interval', 50); - const roundDown = paramWriter.write({ interval: 43, intervalBase: 10 }); - expect(roundDown.params).to.have.property('interval', 40); - }); - - it('will not change interval if it is a multiple of base', () => { - const output = paramWriter.write({ interval: 35, intervalBase: 5 }); - expect(output.params).to.have.property('interval', 35); - }); - - it('will round to intervalBase after scaling histogram:maxBars', () => { - config.get.withArgs('histogram:maxBars').returns(100); - const output = paramWriter.write({ interval: 5, intervalBase: 6 }, - aggConfig => aggConfig.setAutoBounds({ min: 0, max: 1000 })); - // 100 buckets in 0 to 1000 would result in an interval of 10, so we should - // round to the next multiple of 6 -> 12 - expect(output.params).to.have.property('interval', 12); - }); - - afterEach(() => { - config.get.restore(); - }); - }); - }); - - describe('min_doc_count', function () { - it('casts true values to 0', function () { - let output = paramWriter.write({ min_doc_count: true }); - expect(output.params).to.have.property('min_doc_count', 0); - - output = paramWriter.write({ min_doc_count: 'yes' }); - expect(output.params).to.have.property('min_doc_count', 0); - - output = paramWriter.write({ min_doc_count: 1 }); - expect(output.params).to.have.property('min_doc_count', 0); - - output = paramWriter.write({ min_doc_count: {} }); - expect(output.params).to.have.property('min_doc_count', 0); - }); - - it('writes 1 for falsy values', function () { - let output = paramWriter.write({ min_doc_count: '' }); - expect(output.params).to.have.property('min_doc_count', 1); - - output = paramWriter.write({ min_doc_count: null }); - expect(output.params).to.have.property('min_doc_count', 1); - - output = paramWriter.write({ min_doc_count: undefined }); - expect(output.params).to.have.property('min_doc_count', 1); - }); - }); - - describe('extended_bounds', function () { - it('does not write when only eb.min is set', function () { - const output = paramWriter.write({ - has_extended_bounds: true, - extended_bounds: { min: 0 } - }); - expect(output.params).not.to.have.property('extended_bounds'); - }); - - it('does not write when only eb.max is set', function () { - const output = paramWriter.write({ - has_extended_bounds: true, - extended_bounds: { max: 0 } - }); - expect(output.params).not.to.have.property('extended_bounds'); - }); - - it('writes when both eb.min and eb.max are set', function () { - const output = paramWriter.write({ - has_extended_bounds: true, - extended_bounds: { min: 99, max: 100 } - }); - expect(output.params.extended_bounds).to.have.property('min', 99); - expect(output.params.extended_bounds).to.have.property('max', 100); - }); - - it('does not write when nothing is set', function () { - const output = paramWriter.write({ - has_extended_bounds: true, - extended_bounds: {} - }); - expect(output.params).to.not.have.property('extended_bounds'); - }); - - it('does not write when has_extended_bounds is false', function () { - const output = paramWriter.write({ - has_extended_bounds: false, - extended_bounds: { min: 99, max: 100 } - }); - expect(output.params).to.not.have.property('extended_bounds'); - }); - }); - }); -}); diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/_range.js b/src/legacy/ui/public/agg_types/__tests__/buckets/_range.js deleted file mode 100644 index e47802aa6f4bf..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/_range.js +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { values } from 'lodash'; -import ngMock from 'ng_mock'; -import expect from '@kbn/expect'; -import resp from 'fixtures/agg_resp/range'; -import { VisProvider } from '../../../vis'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; - -describe('Range Agg', function () { - const buckets = values(resp.aggregations[1].buckets); - - let Vis; - let indexPattern; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - Vis = Private(VisProvider); - indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - indexPattern.stubSetFieldFormat('bytes', 'bytes', { - pattern: '0,0.[000] b' - }); - })); - - describe('formating', function () { - it('formats bucket keys properly', function () { - const vis = new Vis(indexPattern, { - type: 'histogram', - aggs: [ - { - type: 'range', - schema: 'segment', - params: { - field: 'bytes', - ranges: [ - { from: 0, to: 1000 }, - { from: 1000, to: 2000 } - ] - } - } - ] - }); - - const agg = vis.aggs.byName('range')[0]; - const format = function (val) { - return agg.fieldFormatter()(agg.getKey(val)); - }; - expect(format(buckets[0])).to.be('≥ -∞ and < 1 KB'); - expect(format(buckets[1])).to.be('≥ 1 KB and < 2.5 KB'); - expect(format(buckets[2])).to.be('≥ 2.5 KB and < +∞'); - - }); - }); -}); diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/date_histogram.js b/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/date_histogram.js deleted file mode 100644 index 11e410c43b592..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/date_histogram.js +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import moment from 'moment'; -import aggResp from 'fixtures/agg_resp/date_histogram'; -import ngMock from 'ng_mock'; -import expect from '@kbn/expect'; -import { VisProvider } from '../../../../vis'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { createFilterDateHistogram } from '../../../buckets/create_filter/date_histogram'; -import { intervalOptions } from '../../../buckets/_interval_options'; - -describe('AggConfig Filters', function () { - describe('date_histogram', function () { - let vis; - let agg; - let field; - let filter; - let bucketKey; - let bucketStart; - - let init; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - const Vis = Private(VisProvider); - const indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - - init = function (interval, duration) { - interval = interval || 'auto'; - if (interval === 'custom') interval = agg.params.customInterval; - duration = duration || moment.duration(15, 'minutes'); - field = _.sample(_.reject(indexPattern.fields.getByType('date'), 'scripted')); - vis = new Vis(indexPattern, { - type: 'histogram', - aggs: [ - { - type: 'date_histogram', - schema: 'segment', - params: { field: field.name, interval: interval, customInterval: '5d' } - } - ] - }); - - agg = vis.aggs.aggs[0]; - bucketKey = _.sample(aggResp.aggregations['1'].buckets).key; - bucketStart = moment(bucketKey); - - const timePad = moment.duration(duration / 2); - agg.buckets.setBounds({ - min: bucketStart.clone().subtract(timePad), - max: bucketStart.clone().add(timePad), - }); - agg.buckets.setInterval(interval); - - filter = createFilterDateHistogram(agg, bucketKey); - }; - })); - - it('creates a valid range filter', function () { - init(); - - expect(filter).to.have.property('range'); - expect(filter.range).to.have.property(field.name); - - const fieldParams = filter.range[field.name]; - expect(fieldParams).to.have.property('gte'); - expect(fieldParams.gte).to.be.a('string'); - - expect(fieldParams).to.have.property('lt'); - expect(fieldParams.lt).to.be.a('string'); - - expect(fieldParams).to.have.property('format'); - expect(fieldParams.format).to.be('strict_date_optional_time'); - - expect(fieldParams.gte).to.be.lessThan(fieldParams.lt); - - expect(filter).to.have.property('meta'); - expect(filter.meta).to.have.property('index', vis.indexPattern.id); - }); - - - it('extends the filter edge to 1ms before the next bucket for all interval options', function () { - intervalOptions.forEach(function (option) { - let duration; - if (option.val !== 'custom' && moment(1, option.val).isValid()) { - duration = moment.duration(10, option.val); - - if (+duration < 10) { - throw new Error('unable to create interval for ' + option.val); - } - } - - init(option.val, duration); - - const interval = agg.buckets.getInterval(); - const params = filter.range[field.name]; - - expect(params.gte).to.be(bucketStart.toISOString()); - expect(params.lt).to.be(bucketStart.clone().add(interval).toISOString()); - }); - }); - }); -}); diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/date_range.js b/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/date_range.js deleted file mode 100644 index 3ba03f232428f..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/date_range.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - - -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import moment from 'moment'; -import { VisProvider } from '../../../../vis'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { createFilterDateRange } from '../../../buckets/create_filter/date_range'; - -describe('AggConfig Filters', function () { - describe('Date range', function () { - let indexPattern; - let Vis; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - Vis = Private(VisProvider); - indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - })); - - it('should return a range filter for date_range agg', function () { - const vis = new Vis(indexPattern, { - type: 'histogram', - aggs: [ - { - type: 'date_range', - params: { - field: '@timestamp', - ranges: [ - { from: '2014-01-01', to: '2014-12-31' } - ] - } - } - ] - }); - - const aggConfig = vis.aggs.byName('date_range')[0]; - const from = new Date('1 Feb 2015'); - const to = new Date('7 Feb 2015'); - const filter = createFilterDateRange(aggConfig, { from: from.valueOf(), to: to.valueOf() }); - expect(filter).to.have.property('range'); - expect(filter).to.have.property('meta'); - expect(filter.meta).to.have.property('index', indexPattern.id); - expect(filter.range).to.have.property('@timestamp'); - expect(filter.range['@timestamp']).to.have.property('gte', moment(from).toISOString()); - expect(filter.range['@timestamp']).to.have.property('lt', moment(to).toISOString()); - }); - }); -}); diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/filters.js b/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/filters.js deleted file mode 100644 index 409c9a40b19c4..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/filters.js +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import { VisProvider } from '../../../../vis'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { createFilterFilters } from '../../../buckets/create_filter/filters'; - -describe('AggConfig Filters', function () { - describe('filters', function () { - let indexPattern; - let Vis; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - Vis = Private(VisProvider); - indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - })); - - it('should return a filters filter', function () { - const vis = new Vis(indexPattern, { - type: 'histogram', - aggs: [ - { - type: 'filters', - schema: 'segment', - params: { - filters: [ - { input: { query: 'type:apache', language: 'lucene' } }, - { input: { query: 'type:nginx', language: 'lucene' } } - ] - } - } - ] - }); - - const aggConfig = vis.aggs.byName('filters')[0]; - const filter = createFilterFilters(aggConfig, 'type:nginx'); - expect(filter.query.bool.must[0].query_string.query).to.be('type:nginx'); - expect(filter.meta).to.have.property('index', indexPattern.id); - expect(filter.meta).to.have.property('alias', 'type:nginx'); - }); - }); -}); diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/histogram.js b/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/histogram.js deleted file mode 100644 index 6d4534bba4dd1..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/histogram.js +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - - -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import { VisProvider } from '../../../../vis'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { createFilterHistogram } from '../../../buckets/create_filter/histogram'; - -describe('AggConfig Filters', function () { - describe('histogram', function () { - let indexPattern; - let Vis; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - Vis = Private(VisProvider); - indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - })); - - it('should return an range filter for histogram', function () { - const vis = new Vis(indexPattern, { - type: 'histogram', - aggs: [ - { - type: 'histogram', - schema: 'segment', - params: { field: 'bytes', interval: 1024 } - } - ] - }); - - const aggConfig = vis.aggs.byName('histogram')[0]; - const filter = createFilterHistogram(aggConfig, 2048); - expect(filter).to.have.property('meta'); - expect(filter.meta).to.have.property('index', indexPattern.id); - expect(filter).to.have.property('range'); - expect(filter.range).to.have.property('bytes'); - expect(filter.range.bytes).to.have.property('gte', 2048); - expect(filter.range.bytes).to.have.property('lt', 3072); - expect(filter.meta).to.have.property('formattedValue', '2,048'); - }); - }); -}); diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/ip_range.js b/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/ip_range.js deleted file mode 100644 index e29ebd689db20..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/ip_range.js +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import { VisProvider } from '../../../../vis'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { createFilterIpRange } from '../../../buckets/create_filter/ip_range'; -describe('AggConfig Filters', function () { - - describe('IP range', function () { - let indexPattern; - let Vis; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - Vis = Private(VisProvider); - indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - })); - - it('should return a range filter for ip_range agg', function () { - const vis = new Vis(indexPattern, { - type: 'histogram', - aggs: [ - { - type: 'ip_range', - schema: 'segment', - params: { - field: 'ip', - ipRangeType: 'fromTo', - ranges: { - fromTo: [ - { from: '0.0.0.0', to: '1.1.1.1' } - ] - } - } - } - ] - }); - - const aggConfig = vis.aggs.byName('ip_range')[0]; - const filter = createFilterIpRange(aggConfig, { type: 'fromTo', from: '0.0.0.0', to: '1.1.1.1' }); - expect(filter).to.have.property('range'); - expect(filter).to.have.property('meta'); - expect(filter.meta).to.have.property('index', indexPattern.id); - expect(filter.range).to.have.property('ip'); - expect(filter.range.ip).to.have.property('gte', '0.0.0.0'); - expect(filter.range.ip).to.have.property('lte', '1.1.1.1'); - }); - - it('should return a range filter for ip_range agg using a CIDR mask', function () { - const vis = new Vis(indexPattern, { - type: 'histogram', - aggs: [ - { - type: 'ip_range', - schema: 'segment', - params: { - field: 'ip', - ipRangeType: 'mask', - ranges: { - mask: [ - { mask: '67.129.65.201/27' } - ] - } - } - } - ] - }); - - const aggConfig = vis.aggs.byName('ip_range')[0]; - const filter = createFilterIpRange(aggConfig, { type: 'mask', mask: '67.129.65.201/27' }); - expect(filter).to.have.property('range'); - expect(filter).to.have.property('meta'); - expect(filter.meta).to.have.property('index', indexPattern.id); - expect(filter.range).to.have.property('ip'); - expect(filter.range.ip).to.have.property('gte', '67.129.65.192'); - expect(filter.range.ip).to.have.property('lte', '67.129.65.223'); - }); - }); -}); diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/range.js b/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/range.js deleted file mode 100644 index 228fd6bce5cfb..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/range.js +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import { VisProvider } from '../../../../vis'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { createFilterRange } from '../../../buckets/create_filter/range'; - -describe('AggConfig Filters', function () { - - describe('range', function () { - let indexPattern; - let Vis; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - Vis = Private(VisProvider); - indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - })); - - it('should return a range filter for range agg', function () { - const vis = new Vis(indexPattern, { - type: 'histogram', - aggs: [ - { - type: 'range', - schema: 'segment', - params: { - field: 'bytes', - ranges: [ - { from: 1024, to: 2048 } - ] - } - } - ] - }); - - const aggConfig = vis.aggs.byName('range')[0]; - const filter = createFilterRange(aggConfig, { gte: 1024, lt: 2048.0 }); - expect(filter).to.have.property('range'); - expect(filter).to.have.property('meta'); - expect(filter.meta).to.have.property('index', indexPattern.id); - expect(filter.range).to.have.property('bytes'); - expect(filter.range.bytes).to.have.property('gte', 1024.0); - expect(filter.range.bytes).to.have.property('lt', 2048.0); - expect(filter.meta).to.have.property('formattedValue', '≥ 1,024 and < 2,048'); - }); - }); -}); diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/terms.js b/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/terms.js deleted file mode 100644 index a2812ffb97965..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/create_filter/terms.js +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import { VisProvider } from '../../../../vis'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { createFilterTerms } from '../../../buckets/create_filter/terms'; - -describe('AggConfig Filters', function () { - - describe('terms', function () { - let indexPattern; - let Vis; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - Vis = Private(VisProvider); - indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - })); - - it('should return a match_phrase filter for terms', function () { - const vis = new Vis(indexPattern, { - type: 'histogram', - aggs: [ { type: 'terms', schema: 'segment', params: { field: '_type' } } ] - }); - const aggConfig = vis.aggs.byName('terms')[0]; - const filter = createFilterTerms(aggConfig, 'apache'); - expect(filter).to.have.property('query'); - expect(filter.query).to.have.property('match_phrase'); - expect(filter.query.match_phrase).to.have.property('_type'); - expect(filter.query.match_phrase._type).to.be('apache'); - expect(filter).to.have.property('meta'); - expect(filter.meta).to.have.property('index', indexPattern.id); - - }); - - it('should set query to true or false for boolean filter', () => { - const vis = new Vis(indexPattern, { - type: 'histogram', - aggs: [ { type: 'terms', schema: 'segment', params: { field: 'ssl' } } ] - }); - const aggConfig = vis.aggs.byName('terms')[0]; - const filterFalse = createFilterTerms(aggConfig, 0); - expect(filterFalse).to.have.property('query'); - expect(filterFalse.query).to.have.property('match_phrase'); - expect(filterFalse.query.match_phrase).to.have.property('ssl'); - expect(filterFalse.query.match_phrase.ssl).to.be(false); - - const filterTrue = createFilterTerms(aggConfig, 1); - expect(filterTrue).to.have.property('query'); - expect(filterTrue.query).to.have.property('match_phrase'); - expect(filterTrue.query.match_phrase).to.have.property('ssl'); - expect(filterTrue.query.match_phrase.ssl).to.be(true); - }); - - it('should generate correct __missing__ filter', () => { - const vis = new Vis(indexPattern, { - type: 'histogram', - aggs: [ { type: 'terms', schema: 'segment', params: { field: '_type' } } ] - }); - const aggConfig = vis.aggs.byName('terms')[0]; - const filter = createFilterTerms(aggConfig, '__missing__'); - expect(filter).to.have.property('exists'); - expect(filter.exists).to.have.property('field', '_type'); - expect(filter).to.have.property('meta'); - expect(filter.meta).to.have.property('index', indexPattern.id); - expect(filter.meta).to.have.property('negate', true); - }); - - it('should generate correct __other__ filter', () => { - const vis = new Vis(indexPattern, { - type: 'histogram', - aggs: [ { type: 'terms', schema: 'segment', params: { field: '_type' } } ] - }); - const aggConfig = vis.aggs.byName('terms')[0]; - const filter = createFilterTerms(aggConfig, '__other__', { terms: ['apache'] })[0]; - expect(filter).to.have.property('query'); - expect(filter.query).to.have.property('bool'); - expect(filter.query.bool).to.have.property('should'); - expect(filter.query.bool.should[0]).to.have.property('match_phrase'); - expect(filter.query.bool.should[0].match_phrase).to.have.property('_type', 'apache'); - expect(filter).to.have.property('meta'); - expect(filter.meta).to.have.property('index', indexPattern.id); - expect(filter.meta).to.have.property('negate', true); - }); - }); -}); diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/date_histogram/_editor.js b/src/legacy/ui/public/agg_types/__tests__/buckets/date_histogram/_editor.js deleted file mode 100644 index 7b8f099a1f9b4..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/date_histogram/_editor.js +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import $ from 'jquery'; -import ngMock from 'ng_mock'; -import expect from '@kbn/expect'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { VisProvider } from '../../../../vis'; -import { intervalOptions } from '../../../buckets/_interval_options'; - -describe.skip('editor', function () { - - let indexPattern; - let vis; - let agg; - let render; - let $scope; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private, $injector, $compile) { - indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - - const Vis = Private(VisProvider); - - /** - * Render the AggParams editor for the date histogram aggregation - * - * @param {object} params - the agg params to give to the date_histogram - * by default - * @return {object} - object pointing to the different inputs, keys - * are the aggParam name and the value is an object - * with $el, $scope, and a few helpers for getting - * data from them. - */ - render = function (params) { - vis = new Vis(indexPattern, { - type: 'histogram', - aggs: [ - { schema: 'metric', type: 'avg', params: { field: 'bytes' } }, - { schema: 'segment', type: 'date_histogram', params: params || {} } - ] - }); - - const $el = $('' + - ''); - const $parentScope = $injector.get('$rootScope').$new(); - - agg = $parentScope.agg = vis.aggs.bySchemaName('segment')[0]; - $parentScope.groupName = 'buckets'; - $parentScope.vis = vis; - - $compile($el)($parentScope); - $scope = $el.scope(); - $scope.$digest(); - - const $inputs = $('vis-agg-param-editor', $el); - return _.transform($inputs.toArray(), function (inputs, e) { - const $el = $(e); - const $scope = $el.scope(); - - inputs[$scope.aggParam.name] = { - $el: $el, - $scope: $scope, - $input: function () { - return $el.find('[ng-model]').first(); - }, - modelValue: function () { - return this.$input().controller('ngModel').$modelValue; - } - }; - }, {}); - }; - - })); - - describe('random field/interval', function () { - let params; - let field; - let interval; - - beforeEach(ngMock.inject(function () { - field = _.sample(indexPattern.fields); - interval = _.sample(intervalOptions); - params = render({ field: field, interval: interval.val }); - })); - - it('renders the field editor', function () { - expect(agg.params.field).to.be(field); - - expect(params).to.have.property('field'); - expect(params.field).to.have.property('$el'); - expect($scope.agg.params.field).to.be(field); - }); - - it('renders the interval editor', function () { - expect(agg.params.interval).to.be(interval.val); - - expect(params).to.have.property('interval'); - expect(params.interval).to.have.property('$el'); - expect($scope.agg.params.interval).to.be(interval.val); - }); - }); - - -}); diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/date_histogram/_params.js b/src/legacy/ui/public/agg_types/__tests__/buckets/date_histogram/_params.js deleted file mode 100644 index 88646bd36ee80..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/date_histogram/_params.js +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import moment from 'moment'; -import expect from '@kbn/expect'; -import sinon from 'sinon'; -import ngMock from 'ng_mock'; -import AggParamWriterProvider from '../../agg_param_writer'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import chrome from '../../../../chrome'; -import { aggTypes } from '../../..'; -import { AggConfig } from '../../../agg_config'; -import { timefilter } from 'ui/timefilter'; - -const config = chrome.getUiSettingsClient(); - -describe('date_histogram params', function () { - - let paramWriter; - let writeInterval; - let write; - - let getTimeBounds; - let timeField; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - const AggParamWriter = Private(AggParamWriterProvider); - const indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - - timeField = indexPattern.timeFieldName; - - paramWriter = new AggParamWriter({ aggType: 'date_histogram' }); - writeInterval = function (interval, timeRange, params = {}) { - return paramWriter.write({ ...params, interval: interval, field: timeField, timeRange: timeRange }); - }; - write = (params) => { - return paramWriter.write({ interval: '10s', ...params }); - }; - - const now = moment(); - getTimeBounds = function (n, units) { - timefilter.enableAutoRefreshSelector(); - timefilter.enableTimeRangeSelector(); - return { - from: now.clone().subtract(n, units), - to: now.clone() - }; - }; - })); - - describe('interval', function () { - it('accepts a valid calendar interval', function () { - const output = writeInterval('d'); - expect(output.params).to.have.property('calendar_interval', '1d'); - }); - - it('accepts a valid fixed interval', () => { - const output = writeInterval('100s'); - expect(output.params).to.have.property('fixed_interval', '100s'); - }); - - it('throws error when interval is invalid', function () { - expect(() => writeInterval('foo')).to.throw('TypeError: "foo" is not a valid interval.'); - }); - - it('automatically picks an interval', function () { - const timeBounds = getTimeBounds(15, 'm'); - const output = writeInterval('auto', timeBounds); - expect(output.params).to.have.property('fixed_interval', '30s'); - }); - - it('does not scale down the interval', () => { - const timeBounds = getTimeBounds(1, 'm'); - const output = writeInterval('h', timeBounds); - expect(output.params).to.have.property('calendar_interval', '1h'); - expect(output).not.to.have.property('metricScaleText'); - expect(output).not.to.have.property('metricScale'); - }); - - describe('scaling behavior', () => { - - it('should not scale without scaleMetricValues: true', function () { - const timeBounds = getTimeBounds(30, 'm'); - const output = writeInterval('s', timeBounds); - expect(output.params).to.have.property('fixed_interval', '10s'); - expect(output).not.to.have.property('metricScaleText'); - expect(output).not.to.property('metricScale'); - }); - - describe('only scales when all metrics are sum or count', function () { - const tests = [ - [ false, 'avg', 'count', 'sum' ], - [ true, 'count', 'sum' ], - [ false, 'count', 'cardinality' ] - ]; - - tests.forEach(function (test) { - const should = test.shift(); - const typeNames = test.slice(); - - it(typeNames.join(', ') + ' should ' + (should ? '' : 'not') + ' scale', function () { - const timeBounds = getTimeBounds(1, 'y'); - - const vis = paramWriter.vis; - vis.aggs.aggs.splice(0); - - const histoConfig = new AggConfig(vis.aggs, { - type: aggTypes.buckets.find(agg => agg.name === 'date_histogram'), - schema: 'segment', - params: { interval: 's', field: timeField, timeRange: timeBounds, scaleMetricValues: true } - }); - - vis.aggs.aggs.push(histoConfig); - - typeNames.forEach(function (type) { - vis.aggs.aggs.push(new AggConfig(vis.aggs, { - type: aggTypes.metrics.find(agg => agg.name === type), - schema: 'metric' - })); - }); - - const output = histoConfig.write(vis.aggs); - expect(_.has(output, 'metricScale')).to.be(should); - }); - }); - }); - }); - }); - - describe('time_zone', () => { - beforeEach(() => { - sinon.stub(config, 'get'); - sinon.stub(config, 'isDefault'); - }); - - it('should use the specified time_zone', () => { - const output = write({ time_zone: 'Europe/Kiev' }); - expect(output.params).to.have.property('time_zone', 'Europe/Kiev'); - }); - - it('should use the Kibana time_zone if no parameter specified', () => { - config.isDefault.withArgs('dateFormat:tz').returns(false); - config.get.withArgs('dateFormat:tz').returns('Europe/Riga'); - const output = write({}); - expect(output.params).to.have.property('time_zone', 'Europe/Riga'); - }); - - it('should use the fixed time_zone from the index pattern typeMeta', () => { - _.set(paramWriter.indexPattern, ['typeMeta', 'aggs', 'date_histogram', timeField, 'time_zone'], 'Europe/Rome'); - const output = write({ field: timeField }); - expect(output.params).to.have.property('time_zone', 'Europe/Rome'); - }); - - afterEach(() => { - config.get.restore(); - config.isDefault.restore(); - }); - }); - - describe('extended_bounds', function () { - it('should write a long value if a moment passed in', function () { - const then = moment(0); - const now = moment(500); - const output = write({ - extended_bounds: { - min: then, - max: now - } - }); - - expect(typeof output.params.extended_bounds.min).to.be('number'); - expect(typeof output.params.extended_bounds.max).to.be('number'); - expect(output.params.extended_bounds.min).to.be(then.valueOf()); - expect(output.params.extended_bounds.max).to.be(now.valueOf()); - - - }); - - it('should write a long if a long is passed', function () { - const then = 0; - const now = 500; - const output = write({ - extended_bounds: { - min: then, - max: now - } - }); - - expect(typeof output.params.extended_bounds.min).to.be('number'); - expect(typeof output.params.extended_bounds.max).to.be('number'); - expect(output.params.extended_bounds.min).to.be(then.valueOf()); - expect(output.params.extended_bounds.max).to.be(now.valueOf()); - - - }); - }); -}); diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/significant_terms.js b/src/legacy/ui/public/agg_types/__tests__/buckets/significant_terms.js deleted file mode 100644 index ae52fb476c120..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/significant_terms.js +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import { aggTypes } from '../..'; - -describe('Significant Terms Agg', function () { - - describe('order agg editor UI', function () { - - describe('convert include/exclude from old format', function () { - - let $rootScope; - - function init({ aggParams = {} }) { - ngMock.module('kibana'); - ngMock.inject(function (_$rootScope_) { - const significantTerms = aggTypes.buckets.find(agg => agg.name === 'significant_terms'); - - $rootScope = _$rootScope_; - $rootScope.agg = { - id: 'test', - params: aggParams, - type: significantTerms, - getParam: key => aggParams[key], - }; - }); - } - - function testSerializeAndWrite(aggConfig) { - const includeArg = $rootScope.agg.type.paramByName('include'); - const excludeArg = $rootScope.agg.type.paramByName('exclude'); - - expect(includeArg.serialize(aggConfig.params.include, aggConfig)).to.equal('404'); - expect(excludeArg.serialize(aggConfig.params.exclude, aggConfig)).to.equal('400'); - - const output = { params: {} }; - - includeArg.write(aggConfig, output); - excludeArg.write(aggConfig, output); - - expect(output.params.include).to.equal('404'); - expect(output.params.exclude).to.equal('400'); - } - - it('it doesnt do anything with string type', function () { - init({ - aggParams: { - include: '404', - exclude: '400', - field: { - type: 'string' - }, - } - }); - - testSerializeAndWrite($rootScope.agg); - }); - - it('converts object to string type', function () { - init({ - aggParams: { - include: { - pattern: '404' - }, exclude: { - pattern: '400' - }, - field: { - type: 'string' - }, - } - }); - - testSerializeAndWrite($rootScope.agg); - }); - }); - }); -}); diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/terms.js b/src/legacy/ui/public/agg_types/__tests__/buckets/terms.js deleted file mode 100644 index 600323d32d38c..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/terms.js +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import { aggTypes } from '../..'; - -describe('Terms Agg', function () { - describe('order agg editor UI', function () { - - let $rootScope; - - function init({ metricAggs = [], aggParams = {} }) { - ngMock.module('kibana'); - ngMock.inject(function ($controller, _$rootScope_) { - const terms = aggTypes.buckets.find(agg => agg.name === 'terms'); - const orderAggController = terms.paramByName('orderAgg').controller; - - $rootScope = _$rootScope_; - $rootScope.agg = { - id: 'test', - params: aggParams, - type: terms, - vis: { - aggs: [] - }, - getParam: key => aggParams[key], - }; - $rootScope.metricAggs = metricAggs; - $controller(orderAggController, { $scope: $rootScope }); - $rootScope.$digest(); - }); - } - - // should be rewritten after EUIficate order_agg.html - it.skip('selects _key if the selected metric becomes incompatible', function () { - init({ - metricAggs: [ - { - id: 'agg1', - type: { - name: 'count' - } - } - ] - }); - - expect($rootScope.agg.params.orderBy).to.be('agg1'); - $rootScope.metricAggs = [ - { - id: 'agg1', - type: { - name: 'top_hits' - } - } - ]; - $rootScope.$digest(); - expect($rootScope.agg.params.orderBy).to.be('_key'); - }); - - // should be rewritten after EUIficate order_agg.html - it.skip('selects _key if the selected metric is removed', function () { - init({ - metricAggs: [ - { - id: 'agg1', - type: { - name: 'count' - } - } - ] - }); - expect($rootScope.agg.params.orderBy).to.be('agg1'); - $rootScope.metricAggs = []; - $rootScope.$digest(); - expect($rootScope.agg.params.orderBy).to.be('_key'); - }); - - describe.skip('custom field formatter', () => { - beforeEach(() => { - init({ - metricAggs: [ - { - id: 'agg1', - type: { - name: 'count' - } - } - ], - aggParams: { - otherBucketLabel: 'Other', - missingBucketLabel: 'Missing' - } - }); - $rootScope.$digest(); - }); - - it ('converts __other__ key', () => { - const formatter = $rootScope.agg.type.getFormat($rootScope.agg).getConverterFor('text'); - expect(formatter('__other__')).to.be('Other'); - }); - - it ('converts __missing__ key', () => { - const formatter = $rootScope.agg.type.getFormat($rootScope.agg).getConverterFor('text'); - expect(formatter('__missing__')).to.be('Missing'); - }); - }); - - it('adds "custom metric" option'); - it('lists all metric agg responses'); - it('lists individual values of a multi-value metric'); - it('displays a metric editor if "custom metric" is selected'); - it('saves the "custom metric" to state and refreshes from it'); - it('invalidates the form if the metric agg form is not complete'); - - describe.skip('convert include/exclude from old format', function () { - - it('it doesnt do anything with string type', function () { - init({ - aggParams: { - include: '404', - exclude: '400', - field: { - type: 'string' - }, - } - }); - - const aggConfig = $rootScope.agg; - const includeArg = $rootScope.agg.type.params.byName.include; - const excludeArg = $rootScope.agg.type.params.byName.exclude; - - expect(includeArg.serialize(aggConfig.params.include, aggConfig)).to.equal('404'); - expect(excludeArg.serialize(aggConfig.params.exclude, aggConfig)).to.equal('400'); - - const output = { params: {} }; - - includeArg.write(aggConfig, output); - excludeArg.write(aggConfig, output); - - expect(output.params.include).to.equal('404'); - expect(output.params.exclude).to.equal('400'); - }); - - it('converts object to string type', function () { - init({ - aggParams: { - include: { - pattern: '404' - }, exclude: { - pattern: '400' - }, - field: { - type: 'string' - }, - } - }); - - const aggConfig = $rootScope.agg; - const includeArg = $rootScope.agg.type.params.byName.include; - const excludeArg = $rootScope.agg.type.params.byName.exclude; - - expect(includeArg.serialize(aggConfig.params.include, aggConfig)).to.equal('404'); - expect(excludeArg.serialize(aggConfig.params.exclude, aggConfig)).to.equal('400'); - - const output = { params: {} }; - - includeArg.write(aggConfig, output); - excludeArg.write(aggConfig, output); - - expect(output.params.include).to.equal('404'); - expect(output.params.exclude).to.equal('400'); - }); - - }); - }); -}); diff --git a/src/legacy/ui/public/agg_types/__tests__/utils/_stub_agg_params.js b/src/legacy/ui/public/agg_types/__tests__/utils/_stub_agg_params.js deleted file mode 100644 index f30572bcc0ed5..0000000000000 --- a/src/legacy/ui/public/agg_types/__tests__/utils/_stub_agg_params.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import sinon from 'sinon'; -import { BaseParamType } from '../../param_types/base'; -import { FieldParamType } from '../../param_types/field'; -import { OptionedParamType } from '../../param_types/optioned'; -import { createLegacyClass } from '../../../utils/legacy_class'; - -function ParamClassStub(parent, body) { - const stub = sinon.spy(body || function () { - stub.Super && stub.Super.call(this); - }); - if (parent) createLegacyClass(stub).inherits(parent); - return stub; -} - -/** - * stub all of the param classes, but ensure that they still inherit properly. - * This method should be passed directly to ngMock.inject(); - * - * ```js - * let stubParamClasses = require('./utils/_stub_agg_params'); - * describe('something', function () { - * beforeEach(ngMock.inject(stubParamClasses)); - * }) - * ``` - * - * @param {PrivateLoader} Private - The private module loader, inject by passing this function to ngMock.inject() - * @return {undefined} - */ -// eslint-disable-next-line import/no-default-export -export default function stubParamClasses(Private) { - const BaseAggParam = Private.stub( - BaseParamType, - new ParamClassStub(null, function (config) { - _.assign(this, config); - }) - ); - - Private.stub( - FieldParamType, - new ParamClassStub(BaseAggParam) - ); - - Private.stub( - OptionedParamType, - new ParamClassStub(BaseAggParam) - ); -} diff --git a/src/legacy/ui/public/agg_types/agg_config.ts b/src/legacy/ui/public/agg_types/agg_config.ts index a5b1aa7cf9c0b..eedfc1cc05a84 100644 --- a/src/legacy/ui/public/agg_types/agg_config.ts +++ b/src/legacy/ui/public/agg_types/agg_config.ts @@ -332,7 +332,7 @@ export class AggConfig { return this.type.getValue(this, bucket); } - getKey(bucket: any, key: string) { + getKey(bucket: any, key?: string) { if (this.type.getKey) { return this.type.getKey(bucket, key, this); } else { diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.test.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.test.ts new file mode 100644 index 0000000000000..f67fa55b27859 --- /dev/null +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.test.ts @@ -0,0 +1,122 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import moment from 'moment'; +import { RangeFilter } from '@kbn/es-query'; +import { createFilterDateHistogram } from './date_histogram'; +import { intervalOptions } from '../_interval_options'; +import { AggConfigs } from '../../agg_configs'; +import { IBucketDateHistogramAggConfig } from '../date_histogram'; +import { BUCKET_TYPES } from '../bucket_agg_types'; + +jest.mock('ui/new_platform'); + +describe('AggConfig Filters', () => { + describe('date_histogram', () => { + let agg: IBucketDateHistogramAggConfig; + let filter: RangeFilter; + let bucketStart: any; + let field: any; + + const init = (interval: string = 'auto', duration: any = moment.duration(15, 'minutes')) => { + field = { + name: 'date', + }; + + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: () => field, + filter: () => [field], + }, + } as any; + const aggConfigs = new AggConfigs( + indexPattern, + [ + { + type: BUCKET_TYPES.DATE_HISTOGRAM, + schema: 'segment', + params: { field: field.name, interval, customInterval: '5d' }, + }, + ], + null + ); + const bucketKey = 1422579600000; + + agg = aggConfigs.aggs[0] as IBucketDateHistogramAggConfig; + bucketStart = moment(bucketKey); + + const timePad = moment.duration(duration / 2); + + agg.buckets.setBounds({ + min: bucketStart.clone().subtract(timePad), + max: bucketStart.clone().add(timePad), + }); + agg.buckets.setInterval(interval); + filter = createFilterDateHistogram(agg, bucketKey); + }; + + it('creates a valid range filter', () => { + init(); + + expect(filter).toHaveProperty('range'); + expect(filter.range).toHaveProperty(field.name); + + const fieldParams = filter.range[field.name]; + expect(fieldParams).toHaveProperty('gte'); + expect(typeof fieldParams.gte).toBe('string'); + + expect(fieldParams).toHaveProperty('lt'); + expect(typeof fieldParams.lt).toBe('string'); + + expect(fieldParams).toHaveProperty('format'); + expect(fieldParams.format).toBe('strict_date_optional_time'); + + expect(filter).toHaveProperty('meta'); + expect(filter.meta).toHaveProperty('index', '1234'); + }); + + it('extends the filter edge to 1ms before the next bucket for all interval options', () => { + intervalOptions.forEach(option => { + let duration; + if (option.val !== 'custom' && moment(1, option.val).isValid()) { + // @ts-ignore + duration = moment.duration(10, option.val); + + if (+duration < 10) { + throw new Error('unable to create interval for ' + option.val); + } + } + init(option.val, duration); + + const interval = agg.buckets.getInterval(); + const params = filter.range[field.name]; + + expect(params.gte).toBe(bucketStart.toISOString()); + expect(params.lt).toBe( + bucketStart + .clone() + .add(interval) + .toISOString() + ); + }); + }); + }); +}); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.ts index 6bda085335309..8c6140cc4b37a 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.ts @@ -21,7 +21,10 @@ import moment from 'moment'; import { buildRangeFilter } from '@kbn/es-query'; import { IBucketDateHistogramAggConfig } from '../date_histogram'; -export const createFilterDateHistogram = (agg: IBucketDateHistogramAggConfig, key: string) => { +export const createFilterDateHistogram = ( + agg: IBucketDateHistogramAggConfig, + key: string | number +) => { const start = moment(key); const interval = agg.buckets.getInterval(); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/date_range.test.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/date_range.test.ts new file mode 100644 index 0000000000000..35b6c38bad799 --- /dev/null +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/date_range.test.ts @@ -0,0 +1,77 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import moment from 'moment'; +import { createFilterDateRange } from './date_range'; +import { DateFormat } from '../../../../../../plugins/data/common'; +import { AggConfigs } from '../../agg_configs'; +import { BUCKET_TYPES } from '../bucket_agg_types'; + +jest.mock('ui/new_platform'); + +describe('AggConfig Filters', () => { + describe('Date range', () => { + const getAggConfigs = () => { + const field = { + name: '@timestamp', + format: new DateFormat({}, () => {}), + }; + + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: () => field, + filter: () => [field], + }, + } as any; + + return new AggConfigs( + indexPattern, + [ + { + type: BUCKET_TYPES.DATE_RANGE, + params: { + field: '@timestamp', + ranges: [{ from: '2014-01-01', to: '2014-12-31' }], + }, + }, + ], + null + ); + }; + + it('should return a range filter for date_range agg', () => { + const aggConfigs = getAggConfigs(); + const from = new Date('1 Feb 2015'); + const to = new Date('7 Feb 2015'); + const filter = createFilterDateRange(aggConfigs.aggs[0], { + from: from.valueOf(), + to: to.valueOf(), + }); + + expect(filter).toHaveProperty('range'); + expect(filter).toHaveProperty('meta'); + expect(filter.meta).toHaveProperty('index', '1234'); + expect(filter.range).toHaveProperty('@timestamp'); + expect(filter.range['@timestamp']).toHaveProperty('gte', moment(from).toISOString()); + expect(filter.range['@timestamp']).toHaveProperty('lt', moment(to).toISOString()); + }); + }); +}); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/filters.test.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/filters.test.ts new file mode 100644 index 0000000000000..125532fe070ba --- /dev/null +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/filters.test.ts @@ -0,0 +1,66 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { createFilterFilters } from './filters'; +import { AggConfigs } from '../../agg_configs'; + +jest.mock('ui/new_platform'); + +describe('AggConfig Filters', () => { + describe('filters', () => { + const getAggConfigs = () => { + const field = { + name: 'bytes', + }; + + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: () => field, + filter: () => [field], + }, + } as any; + + return new AggConfigs( + indexPattern, + [ + { + type: 'filters', + schema: 'segment', + params: { + filters: [ + { input: { query: 'type:apache', language: 'lucene' } }, + { input: { query: 'type:nginx', language: 'lucene' } }, + ], + }, + }, + ], + null + ); + }; + it('should return a filters filter', () => { + const aggConfigs = getAggConfigs(); + const filter = createFilterFilters(aggConfigs.aggs[0], 'type:nginx'); + + expect(filter!.query.bool.must[0].query_string.query).toBe('type:nginx'); + expect(filter!.meta).toHaveProperty('index', '1234'); + expect(filter!.meta).toHaveProperty('alias', 'type:nginx'); + }); + }); +}); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.test.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.test.ts new file mode 100644 index 0000000000000..0095df75b8914 --- /dev/null +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.test.ts @@ -0,0 +1,73 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { createFilterHistogram } from './histogram'; +import { AggConfigs } from '../../agg_configs'; +import { BUCKET_TYPES } from '../bucket_agg_types'; +import { BytesFormat } from '../../../../../../plugins/data/common'; + +jest.mock('ui/new_platform'); + +describe('AggConfig Filters', () => { + describe('histogram', () => { + const getAggConfigs = () => { + const field = { + name: 'bytes', + format: new BytesFormat({}, () => {}), + }; + + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: () => field, + filter: () => [field], + }, + } as any; + + return new AggConfigs( + indexPattern, + [ + { + id: BUCKET_TYPES.HISTOGRAM, + type: BUCKET_TYPES.HISTOGRAM, + schema: 'buckets', + params: { + field: 'bytes', + interval: 1024, + }, + }, + ], + null + ); + }; + + it('should return an range filter for histogram', () => { + const aggConfigs = getAggConfigs(); + const filter = createFilterHistogram(aggConfigs.aggs[0], '2048'); + + expect(filter).toHaveProperty('meta'); + expect(filter.meta).toHaveProperty('index', '1234'); + expect(filter).toHaveProperty('range'); + expect(filter.range).toHaveProperty('bytes'); + expect(filter.range.bytes).toHaveProperty('gte', 2048); + expect(filter.range.bytes).toHaveProperty('lt', 3072); + expect(filter.meta).toHaveProperty('formattedValue', '2,048'); + }); + }); +}); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.test.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.test.ts new file mode 100644 index 0000000000000..2e030d820b396 --- /dev/null +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.test.ts @@ -0,0 +1,104 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { createFilterIpRange } from './ip_range'; +import { AggConfigs } from '../../agg_configs'; +import { IpFormat } from '../../../../../../plugins/data/common'; +import { BUCKET_TYPES } from '../bucket_agg_types'; + +jest.mock('ui/new_platform'); + +describe('AggConfig Filters', () => { + describe('IP range', () => { + const getAggConfigs = (aggs: Array>) => { + const field = { + name: 'ip', + format: IpFormat, + }; + + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: () => field, + filter: () => [field], + }, + } as any; + + return new AggConfigs(indexPattern, aggs, null); + }; + + it('should return a range filter for ip_range agg', () => { + const aggConfigs = getAggConfigs([ + { + type: BUCKET_TYPES.IP_RANGE, + schema: 'segment', + params: { + field: 'ip', + ipRangeType: 'range', + ranges: { + fromTo: [{ from: '0.0.0.0', to: '1.1.1.1' }], + }, + }, + }, + ]); + + const filter = createFilterIpRange(aggConfigs.aggs[0], { + type: 'range', + from: '0.0.0.0', + to: '1.1.1.1', + }); + + expect(filter).toHaveProperty('range'); + expect(filter).toHaveProperty('meta'); + expect(filter.meta).toHaveProperty('index', '1234'); + expect(filter.range).toHaveProperty('ip'); + expect(filter.range.ip).toHaveProperty('gte', '0.0.0.0'); + expect(filter.range.ip).toHaveProperty('lte', '1.1.1.1'); + }); + + it('should return a range filter for ip_range agg using a CIDR mask', () => { + const aggConfigs = getAggConfigs([ + { + type: BUCKET_TYPES.IP_RANGE, + schema: 'segment', + params: { + field: 'ip', + ipRangeType: 'mask', + ranges: { + mask: [{ mask: '67.129.65.201/27' }], + }, + }, + }, + ]); + + const filter = createFilterIpRange(aggConfigs.aggs[0], { + type: 'mask', + mask: '67.129.65.201/27', + }); + + expect(filter).toHaveProperty('range'); + expect(filter).toHaveProperty('meta'); + expect(filter.meta).toHaveProperty('index', '1234'); + expect(filter.range).toHaveProperty('ip'); + expect(filter.range.ip).toHaveProperty('gte', '67.129.65.192'); + expect(filter.range.ip).toHaveProperty('lte', '67.129.65.223'); + }); + }); +}); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/range.test.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/range.test.ts new file mode 100644 index 0000000000000..04476ba62ccd5 --- /dev/null +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/range.test.ts @@ -0,0 +1,74 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { createFilterRange } from './range'; +import { BytesFormat } from '../../../../../../plugins/data/common'; +import { AggConfigs } from '../../agg_configs'; +import { BUCKET_TYPES } from '../bucket_agg_types'; + +jest.mock('ui/new_platform'); + +describe('AggConfig Filters', () => { + describe('range', () => { + const getAggConfigs = () => { + const field = { + name: 'bytes', + format: new BytesFormat({}, () => {}), + }; + + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: () => field, + filter: () => [field], + }, + } as any; + + return new AggConfigs( + indexPattern, + [ + { + id: BUCKET_TYPES.RANGE, + type: BUCKET_TYPES.RANGE, + schema: 'buckets', + params: { + field: 'bytes', + ranges: [{ from: 1024, to: 2048 }], + }, + }, + ], + null + ); + }; + + it('should return a range filter for range agg', () => { + const aggConfigs = getAggConfigs(); + const filter = createFilterRange(aggConfigs.aggs[0], { gte: 1024, lt: 2048.0 }); + + expect(filter).toHaveProperty('range'); + expect(filter).toHaveProperty('meta'); + expect(filter.meta).toHaveProperty('index', '1234'); + expect(filter.range).toHaveProperty('bytes'); + expect(filter.range.bytes).toHaveProperty('gte', 1024.0); + expect(filter.range.bytes).toHaveProperty('lt', 2048.0); + expect(filter.meta).toHaveProperty('formattedValue', '≥ 1,024 and < 2,048'); + }); + }); +}); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/terms.test.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/terms.test.ts new file mode 100644 index 0000000000000..b00e939eac8d8 --- /dev/null +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/terms.test.ts @@ -0,0 +1,113 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { ExistsFilter, Filter } from '@kbn/es-query'; +import { createFilterTerms } from './terms'; +import { AggConfigs } from '../../agg_configs'; +import { BUCKET_TYPES } from '../bucket_agg_types'; + +jest.mock('ui/new_platform'); + +describe('AggConfig Filters', () => { + describe('terms', () => { + const getAggConfigs = (aggs: Array>) => { + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: () => field, + filter: () => [field], + }, + } as any; + + const field = { + name: 'field', + indexPattern, + }; + + return new AggConfigs(indexPattern, aggs, null); + }; + + it('should return a match_phrase filter for terms', () => { + const aggConfigs = getAggConfigs([ + { type: BUCKET_TYPES.TERMS, schema: 'segment', params: { field: 'field' } }, + ]); + + const filter = createFilterTerms(aggConfigs.aggs[0], 'apache', {}) as Filter; + + expect(filter).toHaveProperty('query'); + expect(filter.query).toHaveProperty('match_phrase'); + expect(filter.query.match_phrase).toHaveProperty('field'); + expect(filter.query.match_phrase.field).toBe('apache'); + expect(filter).toHaveProperty('meta'); + expect(filter.meta).toHaveProperty('index', '1234'); + }); + + it('should set query to true or false for boolean filter', () => { + const aggConfigs = getAggConfigs([ + { type: BUCKET_TYPES.TERMS, schema: 'segment', params: { field: 'field' } }, + ]); + + const filterFalse = createFilterTerms(aggConfigs.aggs[0], '', {}) as Filter; + + expect(filterFalse).toHaveProperty('query'); + expect(filterFalse.query).toHaveProperty('match_phrase'); + expect(filterFalse.query.match_phrase).toHaveProperty('field'); + expect(filterFalse.query.match_phrase.field).toBeFalsy(); + + const filterTrue = createFilterTerms(aggConfigs.aggs[0], '1', {}) as Filter; + + expect(filterTrue).toHaveProperty('query'); + expect(filterTrue.query).toHaveProperty('match_phrase'); + expect(filterTrue.query.match_phrase).toHaveProperty('field'); + expect(filterTrue.query.match_phrase.field).toBeTruthy(); + }); + // + it('should generate correct __missing__ filter', () => { + const aggConfigs = getAggConfigs([ + { type: BUCKET_TYPES.TERMS, schema: 'segment', params: { field: 'field' } }, + ]); + const filter = createFilterTerms(aggConfigs.aggs[0], '__missing__', {}) as ExistsFilter; + + expect(filter).toHaveProperty('exists'); + expect(filter.exists).toHaveProperty('field', 'field'); + expect(filter).toHaveProperty('meta'); + expect(filter.meta).toHaveProperty('index', '1234'); + expect(filter.meta).toHaveProperty('negate', true); + }); + // + it('should generate correct __other__ filter', () => { + const aggConfigs = getAggConfigs([ + { type: BUCKET_TYPES.TERMS, schema: 'segment', params: { field: 'field' } }, + ]); + + const [filter] = createFilterTerms(aggConfigs.aggs[0], '__other__', { + terms: ['apache'], + }) as Filter[]; + + expect(filter).toHaveProperty('query'); + expect(filter.query).toHaveProperty('bool'); + expect(filter.query.bool).toHaveProperty('should'); + expect(filter.query.bool.should[0]).toHaveProperty('match_phrase'); + expect(filter.query.bool.should[0].match_phrase).toHaveProperty('field', 'apache'); + expect(filter).toHaveProperty('meta'); + expect(filter.meta).toHaveProperty('index', '1234'); + expect(filter.meta).toHaveProperty('negate', true); + }); + }); +}); diff --git a/src/legacy/ui/public/agg_types/buckets/date_range.test.ts b/src/legacy/ui/public/agg_types/buckets/date_range.test.ts new file mode 100644 index 0000000000000..7d9fe002636a2 --- /dev/null +++ b/src/legacy/ui/public/agg_types/buckets/date_range.test.ts @@ -0,0 +1,112 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { AggConfigs } from '../agg_configs'; +import { BUCKET_TYPES } from './bucket_agg_types'; +import { npStart } from 'ui/new_platform'; + +jest.mock('ui/new_platform'); + +describe('date_range params', () => { + const getAggConfigs = (params: Record = {}, hasIncludeTypeMeta: boolean = true) => { + const field = { + name: 'bytes', + }; + + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: () => field, + filter: () => [field], + }, + typeMeta: hasIncludeTypeMeta + ? { + aggs: { + date_range: { + bytes: { + time_zone: 'defaultTimeZone', + }, + }, + }, + } + : undefined, + } as any; + + return new AggConfigs( + indexPattern, + [ + { + id: BUCKET_TYPES.DATE_RANGE, + type: BUCKET_TYPES.DATE_RANGE, + schema: 'buckets', + params, + }, + ], + null + ); + }; + + describe('getKey', () => { + it('should return object', () => { + const aggConfigs = getAggConfigs(); + const dateRange = aggConfigs.aggs[0]; + const bucket = { from: 'from-date', to: 'to-date', key: 'from-dateto-date' }; + + expect(dateRange.getKey(bucket)).toEqual({ from: 'from-date', to: 'to-date' }); + }); + }); + + describe('time_zone', () => { + it('should use the specified time_zone', () => { + const aggConfigs = getAggConfigs({ + time_zone: 'Europe/Minsk', + field: 'bytes', + }); + const dateRange = aggConfigs.aggs[0]; + const params = dateRange.toDsl()[BUCKET_TYPES.DATE_RANGE]; + + expect(params.time_zone).toBe('Europe/Minsk'); + }); + + it('should use the fixed time_zone from the index pattern typeMeta', () => { + const aggConfigs = getAggConfigs({ + field: 'bytes', + }); + const dateRange = aggConfigs.aggs[0]; + const params = dateRange.toDsl()[BUCKET_TYPES.DATE_RANGE]; + + expect(params.time_zone).toBe('defaultTimeZone'); + }); + + it('should use the Kibana time_zone if no parameter specified', () => { + npStart.core.uiSettings.get = jest.fn(() => 'kibanaTimeZone'); + + const aggConfigs = getAggConfigs( + { + field: 'bytes', + }, + false + ); + const dateRange = aggConfigs.aggs[0]; + const params = dateRange.toDsl()[BUCKET_TYPES.DATE_RANGE]; + + expect(params.time_zone).toBe('kibanaTimeZone'); + }); + }); +}); diff --git a/src/legacy/ui/public/agg_types/buckets/geo_hash.test.ts b/src/legacy/ui/public/agg_types/buckets/geo_hash.test.ts new file mode 100644 index 0000000000000..5c599f16e09c2 --- /dev/null +++ b/src/legacy/ui/public/agg_types/buckets/geo_hash.test.ts @@ -0,0 +1,216 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { geoHashBucketAgg, IBucketGeoHashGridAggConfig } from './geo_hash'; +import { AggConfigs } from '../agg_configs'; +import { BUCKET_TYPES } from './bucket_agg_types'; + +jest.mock('ui/new_platform'); + +describe('Geohash Agg', () => { + const getAggConfigs = (params?: Record) => { + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: () => field, + filter: () => [field], + }, + } as any; + + const field = { + name: 'location', + indexPattern, + }; + + return new AggConfigs( + indexPattern, + [ + { + id: BUCKET_TYPES.GEOHASH_GRID, + type: BUCKET_TYPES.GEOHASH_GRID, + schema: 'segment', + params: { + field: { + name: 'location', + }, + isFilteredByCollar: true, + useGeocentroid: true, + mapZoom: 10, + mapBounds: { + top_left: { lat: 1.0, lon: -1.0 }, + bottom_right: { lat: -1.0, lon: 1.0 }, + }, + ...params, + }, + }, + ], + null + ); + }; + + describe('precision parameter', () => { + const PRECISION_PARAM_INDEX = 2; + + let precisionParam: any; + + beforeEach(() => { + precisionParam = geoHashBucketAgg.params[PRECISION_PARAM_INDEX]; + }); + + it('should select precision parameter', () => { + expect(precisionParam.name).toEqual('precision'); + }); + + describe('precision parameter write', () => { + const zoomToGeoHashPrecision: Record = { + 0: 1, + 1: 2, + 2: 2, + 3: 2, + 4: 3, + 5: 3, + 6: 4, + 7: 4, + 8: 4, + 9: 5, + 10: 5, + 11: 6, + 12: 6, + 13: 6, + 14: 7, + 15: 7, + 16: 8, + 17: 8, + 18: 8, + 19: 9, + 20: 9, + 21: 10, + }; + + Object.keys(zoomToGeoHashPrecision).forEach((zoomLevel: string) => { + it(`zoom level ${zoomLevel} should correspond to correct geohash-precision`, () => { + const aggConfigs = getAggConfigs({ + autoPrecision: true, + mapZoom: zoomLevel, + }); + + const { [BUCKET_TYPES.GEOHASH_GRID]: params } = aggConfigs.aggs[0].toDsl(); + + expect(params.precision).toEqual(zoomToGeoHashPrecision[zoomLevel]); + }); + }); + }); + }); + + describe('getRequestAggs', () => { + describe('initial aggregation creation', () => { + let aggConfigs: AggConfigs; + let geoHashGridAgg: IBucketGeoHashGridAggConfig; + + beforeEach(() => { + aggConfigs = getAggConfigs(); + geoHashGridAgg = aggConfigs.aggs[0] as IBucketGeoHashGridAggConfig; + }); + + it('should create filter, geohash_grid, and geo_centroid aggregations', () => { + const requestAggs = geoHashBucketAgg.getRequestAggs( + geoHashGridAgg + ) as IBucketGeoHashGridAggConfig[]; + + expect(requestAggs.length).toEqual(3); + expect(requestAggs[0].type.name).toEqual('filter'); + expect(requestAggs[1].type.name).toEqual('geohash_grid'); + expect(requestAggs[2].type.name).toEqual('geo_centroid'); + }); + + it('should set mapCollar in vis session state', () => { + const [, geoHashAgg] = geoHashBucketAgg.getRequestAggs( + geoHashGridAgg + ) as IBucketGeoHashGridAggConfig[]; + + expect(geoHashAgg).toHaveProperty('lastMapCollar'); + expect(geoHashAgg.lastMapCollar).toHaveProperty('top_left'); + expect(geoHashAgg.lastMapCollar).toHaveProperty('bottom_right'); + expect(geoHashAgg.lastMapCollar).toHaveProperty('zoom'); + }); + }); + }); + + describe('aggregation options', () => { + it('should only create geohash_grid and geo_centroid aggregations when isFilteredByCollar is false', () => { + const aggConfigs = getAggConfigs({ isFilteredByCollar: false }); + const requestAggs = geoHashBucketAgg.getRequestAggs(aggConfigs + .aggs[0] as IBucketGeoHashGridAggConfig) as IBucketGeoHashGridAggConfig[]; + + expect(requestAggs.length).toEqual(2); + expect(requestAggs[0].type.name).toEqual('geohash_grid'); + expect(requestAggs[1].type.name).toEqual('geo_centroid'); + }); + + it('should only create filter and geohash_grid aggregations when useGeocentroid is false', () => { + const aggConfigs = getAggConfigs({ useGeocentroid: false }); + const requestAggs = geoHashBucketAgg.getRequestAggs(aggConfigs + .aggs[0] as IBucketGeoHashGridAggConfig) as IBucketGeoHashGridAggConfig[]; + + expect(requestAggs.length).toEqual(2); + expect(requestAggs[0].type.name).toEqual('filter'); + expect(requestAggs[1].type.name).toEqual('geohash_grid'); + }); + }); + + describe('aggregation creation after map interaction', () => { + let originalRequestAggs: IBucketGeoHashGridAggConfig[]; + + beforeEach(() => { + originalRequestAggs = geoHashBucketAgg.getRequestAggs(getAggConfigs() + .aggs[0] as IBucketGeoHashGridAggConfig) as IBucketGeoHashGridAggConfig[]; + }); + + it('should change geo_bounding_box filter aggregation and vis session state when map movement is outside map collar', () => { + const [, geoBoxingBox] = geoHashBucketAgg.getRequestAggs(getAggConfigs({ + mapBounds: { + top_left: { lat: 10.0, lon: -10.0 }, + bottom_right: { lat: 9.0, lon: -9.0 }, + }, + }).aggs[0] as IBucketGeoHashGridAggConfig) as IBucketGeoHashGridAggConfig[]; + + expect(originalRequestAggs[1].params).not.toEqual(geoBoxingBox.params); + }); + + it('should not change geo_bounding_box filter aggregation and vis session state when map movement is within map collar', () => { + const [, geoBoxingBox] = geoHashBucketAgg.getRequestAggs(getAggConfigs({ + mapBounds: { + top_left: { lat: 1, lon: -1 }, + bottom_right: { lat: -1, lon: 1 }, + }, + }).aggs[0] as IBucketGeoHashGridAggConfig) as IBucketGeoHashGridAggConfig[]; + + expect(originalRequestAggs[1].params).toEqual(geoBoxingBox.params); + }); + + it('should change geo_bounding_box filter aggregation and vis session state when map zoom level changes', () => { + const [, geoBoxingBox] = geoHashBucketAgg.getRequestAggs(getAggConfigs({ + mapZoom: -1, + }).aggs[0] as IBucketGeoHashGridAggConfig) as IBucketGeoHashGridAggConfig[]; + + expect(originalRequestAggs[1].lastMapCollar).not.toEqual(geoBoxingBox.lastMapCollar); + }); + }); +}); diff --git a/src/legacy/ui/public/agg_types/buckets/histogram.test.ts b/src/legacy/ui/public/agg_types/buckets/histogram.test.ts new file mode 100644 index 0000000000000..338af2e41cb88 --- /dev/null +++ b/src/legacy/ui/public/agg_types/buckets/histogram.test.ts @@ -0,0 +1,292 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { npStart } from 'ui/new_platform'; +import { AggConfigs } from '../index'; +import { BUCKET_TYPES } from './bucket_agg_types'; +import { IBucketHistogramAggConfig, histogramBucketAgg, AutoBounds } from './histogram'; +import { BucketAggType } from './_bucket_agg_type'; + +jest.mock('ui/new_platform'); + +describe('Histogram Agg', () => { + const getAggConfigs = (params: Record = {}) => { + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: () => field, + filter: () => [field], + }, + } as any; + + const field = { + name: 'field', + indexPattern, + }; + + return new AggConfigs( + indexPattern, + [ + { + field: { + name: 'field', + }, + id: 'test', + type: BUCKET_TYPES.HISTOGRAM, + schema: 'segment', + params, + }, + ], + null + ); + }; + + const getParams = (options: Record) => { + const aggConfigs = getAggConfigs({ + ...options, + field: { + name: 'field', + }, + }); + return aggConfigs.aggs[0].toDsl()[BUCKET_TYPES.HISTOGRAM]; + }; + + describe('ordered', () => { + let histogramType: BucketAggType; + + beforeEach(() => { + histogramType = histogramBucketAgg; + }); + + it('is ordered', () => { + expect(histogramType.ordered).toBeDefined(); + }); + + it('is not ordered by date', () => { + expect(histogramType.ordered).not.toHaveProperty('date'); + }); + }); + + describe('params', () => { + describe('intervalBase', () => { + it('should not be written to the DSL', () => { + const aggConfigs = getAggConfigs({ + intervalBase: 100, + field: { + name: 'field', + }, + }); + const { [BUCKET_TYPES.HISTOGRAM]: params } = aggConfigs.aggs[0].toDsl(); + + expect(params).not.toHaveProperty('intervalBase'); + }); + }); + + describe('interval', () => { + it('accepts a whole number', () => { + const params = getParams({ + interval: 100, + }); + + expect(params).toHaveProperty('interval', 100); + }); + + it('accepts a decimal number', function() { + const params = getParams({ + interval: 0.1, + }); + + expect(params).toHaveProperty('interval', 0.1); + }); + + it('accepts a decimal number string', function() { + const params = getParams({ + interval: '0.1', + }); + + expect(params).toHaveProperty('interval', 0.1); + }); + + it('accepts a whole number string', function() { + const params = getParams({ + interval: '10', + }); + + expect(params).toHaveProperty('interval', 10); + }); + + it('fails on non-numeric values', function() { + const params = getParams({ + interval: [], + }); + + expect(params.interval).toBeNaN(); + }); + + describe('interval scaling', () => { + const getInterval = ( + maxBars: number, + params?: Record, + autoBounds?: AutoBounds + ) => { + const aggConfigs = getAggConfigs({ + ...params, + field: { + name: 'field', + }, + }); + const aggConfig = aggConfigs.aggs[0] as IBucketHistogramAggConfig; + + if (autoBounds) { + aggConfig.setAutoBounds(autoBounds); + } + + // mock histogram:maxBars value; + npStart.core.uiSettings.get = jest.fn(() => maxBars); + + return aggConfig.write(aggConfigs).params; + }; + + it('will respect the histogram:maxBars setting', () => { + const params = getInterval( + 5, + { interval: 5 }, + { + min: 0, + max: 10000, + } + ); + + expect(params).toHaveProperty('interval', 2000); + }); + + it('will return specified interval, if bars are below histogram:maxBars config', () => { + const params = getInterval(100, { interval: 5 }); + + expect(params).toHaveProperty('interval', 5); + }); + + it('will set to intervalBase if interval is below base', () => { + const params = getInterval(1000, { interval: 3, intervalBase: 8 }); + + expect(params).toHaveProperty('interval', 8); + }); + + it('will round to nearest intervalBase multiple if interval is above base', () => { + const roundUp = getInterval(1000, { interval: 46, intervalBase: 10 }); + expect(roundUp).toHaveProperty('interval', 50); + + const roundDown = getInterval(1000, { interval: 43, intervalBase: 10 }); + expect(roundDown).toHaveProperty('interval', 40); + }); + + it('will not change interval if it is a multiple of base', () => { + const output = getInterval(1000, { interval: 35, intervalBase: 5 }); + + expect(output).toHaveProperty('interval', 35); + }); + + it('will round to intervalBase after scaling histogram:maxBars', () => { + const output = getInterval(100, { interval: 5, intervalBase: 6 }, { min: 0, max: 1000 }); + + // 100 buckets in 0 to 1000 would result in an interval of 10, so we should + // round to the next multiple of 6 -> 12 + expect(output).toHaveProperty('interval', 12); + }); + }); + + describe('min_doc_count', () => { + let output: Record; + + it('casts true values to 0', () => { + output = getParams({ min_doc_count: true }); + expect(output).toHaveProperty('min_doc_count', 0); + + output = getParams({ min_doc_count: 'yes' }); + expect(output).toHaveProperty('min_doc_count', 0); + + output = getParams({ min_doc_count: 1 }); + expect(output).toHaveProperty('min_doc_count', 0); + + output = getParams({ min_doc_count: {} }); + expect(output).toHaveProperty('min_doc_count', 0); + }); + + it('writes 1 for falsy values', () => { + output = getParams({ min_doc_count: '' }); + expect(output).toHaveProperty('min_doc_count', 1); + + output = getParams({ min_doc_count: null }); + expect(output).toHaveProperty('min_doc_count', 1); + + output = getParams({ min_doc_count: undefined }); + expect(output).toHaveProperty('min_doc_count', 1); + }); + }); + + describe('extended_bounds', function() { + it('does not write when only eb.min is set', function() { + const output = getParams({ + has_extended_bounds: true, + extended_bounds: { min: 0 }, + }); + expect(output).not.toHaveProperty('extended_bounds'); + }); + + it('does not write when only eb.max is set', function() { + const output = getParams({ + has_extended_bounds: true, + extended_bounds: { max: 0 }, + }); + + expect(output).not.toHaveProperty('extended_bounds'); + }); + + it('writes when both eb.min and eb.max are set', function() { + const output = getParams({ + has_extended_bounds: true, + extended_bounds: { min: 99, max: 100 }, + }); + + expect(output.extended_bounds).toHaveProperty('min', 99); + expect(output.extended_bounds).toHaveProperty('max', 100); + }); + + it('does not write when nothing is set', function() { + const output = getParams({ + has_extended_bounds: true, + extended_bounds: {}, + }); + + expect(output).not.toHaveProperty('extended_bounds'); + }); + + it('does not write when has_extended_bounds is false', function() { + const output = getParams({ + has_extended_bounds: false, + extended_bounds: { min: 99, max: 100 }, + }); + + expect(output).not.toHaveProperty('extended_bounds'); + }); + }); + }); + }); +}); diff --git a/src/legacy/ui/public/agg_types/buckets/histogram.ts b/src/legacy/ui/public/agg_types/buckets/histogram.ts index 23edefc67d506..74a2da4a0eb67 100644 --- a/src/legacy/ui/public/agg_types/buckets/histogram.ts +++ b/src/legacy/ui/public/agg_types/buckets/histogram.ts @@ -21,7 +21,7 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import { toastNotifications } from 'ui/notify'; -import chrome from '../../chrome'; +import { npStart } from 'ui/new_platform'; import { BucketAggType, IBucketAggConfig, BucketAggParam } from './_bucket_agg_type'; import { createFilterHistogram } from './create_filter/histogram'; import { NumberIntervalParamEditor } from '../../vis/editors/default/controls/number_interval'; @@ -32,7 +32,7 @@ import { AggConfig } from '../agg_config'; import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; import { BUCKET_TYPES } from './bucket_agg_types'; -interface AutoBounds { +export interface AutoBounds { min: number; max: number; } @@ -42,7 +42,8 @@ export interface IBucketHistogramAggConfig extends IBucketAggConfig { getAutoBounds: () => AutoBounds; } -const config = chrome.getUiSettingsClient(); +const getUIConfig = () => npStart.core.uiSettings; + export const histogramBucketAgg = new BucketAggType({ name: BUCKET_TYPES.HISTOGRAM, title: i18n.translate('common.ui.aggTypes.buckets.histogramTitle', { @@ -135,25 +136,30 @@ export const histogramBucketAgg = new BucketAggType({ if (interval <= 0) { interval = 1; } + const autoBounds = aggConfig.getAutoBounds(); // ensure interval does not create too many buckets and crash browser - if (aggConfig.getAutoBounds()) { - const range = aggConfig.getAutoBounds().max - aggConfig.getAutoBounds().min; + if (autoBounds) { + const range = autoBounds.max - autoBounds.min; const bars = range / interval; + + const config = getUIConfig(); if (bars > config.get('histogram:maxBars')) { const minInterval = range / config.get('histogram:maxBars'); + // Round interval by order of magnitude to provide clean intervals // Always round interval up so there will always be less buckets than histogram:maxBars const orderOfMagnitude = Math.pow(10, Math.floor(Math.log10(minInterval))); let roundInterval = orderOfMagnitude; + while (roundInterval < minInterval) { roundInterval += orderOfMagnitude; } interval = roundInterval; } } - const base = aggConfig.params.intervalBase; + if (base) { if (interval < base) { // In case the specified interval is below the base, just increase it to it's base diff --git a/src/legacy/ui/public/agg_types/buckets/range.test.ts b/src/legacy/ui/public/agg_types/buckets/range.test.ts new file mode 100644 index 0000000000000..f7cae60cce773 --- /dev/null +++ b/src/legacy/ui/public/agg_types/buckets/range.test.ts @@ -0,0 +1,95 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { AggConfigs } from '../agg_configs'; +import { BUCKET_TYPES } from './bucket_agg_types'; +import { NumberFormat } from '../../../../../plugins/data/common/'; + +jest.mock('ui/new_platform'); + +const buckets = [ + { + to: 1024, + to_as_string: '1024.0', + doc_count: 20904, + }, + { + from: 1024, + from_as_string: '1024.0', + to: 2560, + to_as_string: '2560.0', + doc_count: 23358, + }, + { + from: 2560, + from_as_string: '2560.0', + doc_count: 174250, + }, +]; + +describe('Range Agg', () => { + const getAggConfigs = () => { + const field = { + name: 'bytes', + format: new NumberFormat( + { + pattern: '0,0.[000] b', + }, + () => {} + ), + }; + + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: () => field, + filter: () => [field], + }, + } as any; + + return new AggConfigs( + indexPattern, + [ + { + type: BUCKET_TYPES.RANGE, + schema: 'segment', + params: { + field: 'bytes', + ranges: [{ from: 0, to: 1000 }, { from: 1000, to: 2000 }], + }, + }, + ], + null + ); + }; + + describe('formating', () => { + it('formats bucket keys properly', () => { + const aggConfigs = getAggConfigs(); + const agg = aggConfigs.aggs[0]; + + const format = (val: any) => agg.fieldFormatter()(agg.getKey(val)); + + expect(format(buckets[0])).toBe('≥ -∞ and < 1 KB'); + expect(format(buckets[1])).toBe('≥ 1 KB and < 2.5 KB'); + expect(format(buckets[2])).toBe('≥ 2.5 KB and < +∞'); + }); + }); +}); diff --git a/src/legacy/ui/public/agg_types/buckets/significant_terms.test.ts b/src/legacy/ui/public/agg_types/buckets/significant_terms.test.ts new file mode 100644 index 0000000000000..454f1bf70a790 --- /dev/null +++ b/src/legacy/ui/public/agg_types/buckets/significant_terms.test.ts @@ -0,0 +1,110 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { AggConfigs } from '../index'; +import { BUCKET_TYPES } from './bucket_agg_types'; +import { significantTermsBucketAgg } from './significant_terms'; + +jest.mock('ui/new_platform'); + +describe('Significant Terms Agg', () => { + describe('order agg editor UI', () => { + describe('convert include/exclude from old format', () => { + const getAggConfigs = (params: Record = {}) => { + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: () => field, + filter: () => [field], + }, + } as any; + + const field = { + name: 'field', + indexPattern, + }; + + return new AggConfigs( + indexPattern, + [ + { + id: 'test', + type: BUCKET_TYPES.SIGNIFICANT_TERMS, + schema: 'segment', + params, + }, + ], + null + ); + }; + + const testSerializeAndWrite = (aggs: AggConfigs) => { + const agg = aggs.aggs[0]; + const { [BUCKET_TYPES.SIGNIFICANT_TERMS]: params } = agg.toDsl(); + + expect(params.field).toBe('field'); + expect(params.include).toBe('404'); + expect(params.exclude).toBe('400'); + }; + + it('should generate correct label', () => { + const aggConfigs = getAggConfigs({ + size: 'SIZE', + field: { + name: 'FIELD', + }, + }); + const label = significantTermsBucketAgg.makeLabel(aggConfigs.aggs[0]); + + expect(label).toBe('Top SIZE unusual terms in FIELD'); + }); + + it('should doesnt do anything with string type', () => { + const aggConfigs = getAggConfigs({ + include: '404', + exclude: '400', + field: { + name: 'field', + type: 'string', + }, + }); + + testSerializeAndWrite(aggConfigs); + }); + + it('should converts object to string type', () => { + const aggConfigs = getAggConfigs({ + include: { + pattern: '404', + }, + exclude: { + pattern: '400', + }, + field: { + name: 'field', + type: 'string', + }, + }); + + testSerializeAndWrite(aggConfigs); + }); + }); + }); +}); diff --git a/src/legacy/ui/public/agg_types/buckets/terms.test.ts b/src/legacy/ui/public/agg_types/buckets/terms.test.ts new file mode 100644 index 0000000000000..24ac332ae4d55 --- /dev/null +++ b/src/legacy/ui/public/agg_types/buckets/terms.test.ts @@ -0,0 +1,78 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { AggConfigs } from '../index'; +import { BUCKET_TYPES } from './bucket_agg_types'; + +jest.mock('ui/new_platform'); + +describe('Terms Agg', () => { + describe('order agg editor UI', () => { + const getAggConfigs = (params: Record = {}) => { + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: () => field, + filter: () => [field], + }, + } as any; + + const field = { + name: 'field', + indexPattern, + }; + + return new AggConfigs( + indexPattern, + [ + { + id: 'test', + params, + type: BUCKET_TYPES.TERMS, + }, + ], + null + ); + }; + + it('converts object to string type', function() { + const aggConfigs = getAggConfigs({ + include: { + pattern: '404', + }, + exclude: { + pattern: '400', + }, + field: { + name: 'field', + }, + orderAgg: { + type: 'count', + }, + }); + + const { [BUCKET_TYPES.TERMS]: params } = aggConfigs.aggs[0].toDsl(); + + expect(params.field).toBe('field'); + expect(params.include).toBe('404'); + expect(params.exclude).toBe('400'); + }); + }); +}); diff --git a/src/legacy/ui/public/agg_types/filter/prop_filter.test.ts b/src/legacy/ui/public/agg_types/filter/prop_filter.test.ts index 0d32c9dc769da..431e1161e0dbd 100644 --- a/src/legacy/ui/public/agg_types/filter/prop_filter.test.ts +++ b/src/legacy/ui/public/agg_types/filter/prop_filter.test.ts @@ -27,7 +27,7 @@ describe('prop filter', () => { nameFilter = propFilter('name'); }); - function getObjects(...names: string[]) { + const getObjects = (...names: string[]) => { const count = new Map(); const objects = []; @@ -41,8 +41,9 @@ describe('prop filter', () => { }); count.set(name, count.get(name) + 1); } + return objects; - } + }; it('returns list when no filters are provided', () => { const objects = getObjects('table', 'table', 'pie'); diff --git a/src/legacy/ui/public/agg_types/metrics/lib/make_nested_label.test.ts b/src/legacy/ui/public/agg_types/metrics/lib/make_nested_label.test.ts index aed5bd630d3d2..479ff40b7c0ae 100644 --- a/src/legacy/ui/public/agg_types/metrics/lib/make_nested_label.test.ts +++ b/src/legacy/ui/public/agg_types/metrics/lib/make_nested_label.test.ts @@ -18,7 +18,7 @@ */ import { makeNestedLabel } from './make_nested_label'; -import { IMetricAggConfig } from 'ui/agg_types/metrics/metric_agg_type'; +import { IMetricAggConfig } from '../metric_agg_type'; describe('metric agg make_nested_label', () => { const generateAggConfig = (metricLabel: string): IMetricAggConfig => { diff --git a/src/legacy/ui/public/agg_types/metrics/parent_pipeline.test.ts b/src/legacy/ui/public/agg_types/metrics/parent_pipeline.test.ts index bf88adcee92b7..7c7a2a68cd7c5 100644 --- a/src/legacy/ui/public/agg_types/metrics/parent_pipeline.test.ts +++ b/src/legacy/ui/public/agg_types/metrics/parent_pipeline.test.ts @@ -22,8 +22,8 @@ import { derivativeMetricAgg } from './derivative'; import { cumulativeSumMetricAgg } from './cumulative_sum'; import { movingAvgMetricAgg } from './moving_avg'; import { serialDiffMetricAgg } from './serial_diff'; -import { AggConfigs } from 'ui/agg_types'; -import { IMetricAggConfig, MetricAggType } from 'ui/agg_types/metrics/metric_agg_type'; +import { AggConfigs } from '../agg_configs'; +import { IMetricAggConfig, MetricAggType } from './metric_agg_type'; jest.mock('../../vis/editors/default/schemas', () => { class MockedSchemas { diff --git a/src/legacy/ui/public/agg_types/metrics/sibling_pipeline.test.ts b/src/legacy/ui/public/agg_types/metrics/sibling_pipeline.test.ts index a3381aca6f9e7..e038936de07d2 100644 --- a/src/legacy/ui/public/agg_types/metrics/sibling_pipeline.test.ts +++ b/src/legacy/ui/public/agg_types/metrics/sibling_pipeline.test.ts @@ -23,8 +23,8 @@ import { bucketAvgMetricAgg } from './bucket_avg'; import { bucketMinMetricAgg } from './bucket_min'; import { bucketMaxMetricAgg } from './bucket_max'; -import { AggConfigs } from 'ui/agg_types'; -import { IMetricAggConfig, MetricAggType } from 'ui/agg_types/metrics/metric_agg_type'; +import { AggConfigs } from '../agg_configs'; +import { IMetricAggConfig, MetricAggType } from './metric_agg_type'; jest.mock('../../vis/editors/default/schemas', () => { class MockedSchemas { diff --git a/src/legacy/ui/public/agg_types/metrics/std_deviation.test.ts b/src/legacy/ui/public/agg_types/metrics/std_deviation.test.ts index ca81e8daee449..ae09b5cd78977 100644 --- a/src/legacy/ui/public/agg_types/metrics/std_deviation.test.ts +++ b/src/legacy/ui/public/agg_types/metrics/std_deviation.test.ts @@ -18,8 +18,8 @@ */ import { IStdDevAggConfig, stdDeviationMetricAgg } from './std_deviation'; -import { AggConfigs } from 'ui/agg_types'; -import { METRIC_TYPES } from 'ui/agg_types/metrics/metric_agg_types'; +import { AggConfigs } from '../agg_configs'; +import { METRIC_TYPES } from './metric_agg_types'; jest.mock('ui/new_platform'); diff --git a/src/legacy/ui/public/agg_types/metrics/top_hit.test.ts b/src/legacy/ui/public/agg_types/metrics/top_hit.test.ts index 4ed6fcdcf641b..e9d1ebb93d3ba 100644 --- a/src/legacy/ui/public/agg_types/metrics/top_hit.test.ts +++ b/src/legacy/ui/public/agg_types/metrics/top_hit.test.ts @@ -19,8 +19,8 @@ import { dropRight, last } from 'lodash'; import { topHitMetricAgg } from './top_hit'; -import { AggConfigs } from 'ui/agg_types'; -import { IMetricAggConfig } from 'ui/agg_types/metrics/metric_agg_type'; +import { AggConfigs } from '../agg_configs'; +import { IMetricAggConfig } from './metric_agg_type'; import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; jest.mock('ui/new_platform'); diff --git a/src/legacy/ui/public/agg_types/param_types/json.test.ts b/src/legacy/ui/public/agg_types/param_types/json.test.ts index fb31385505a76..827299814c62a 100644 --- a/src/legacy/ui/public/agg_types/param_types/json.test.ts +++ b/src/legacy/ui/public/agg_types/param_types/json.test.ts @@ -19,7 +19,7 @@ import { BaseParamType } from './base'; import { JsonParamType } from './json'; -import { AggConfig } from 'ui/agg_types'; +import { AggConfig } from '../agg_config'; jest.mock('ui/new_platform'); @@ -28,13 +28,12 @@ describe('JSON', function() { let aggConfig: AggConfig; let output: Record; - function initAggParam(config: Record = {}) { - return new JsonParamType({ + const initAggParam = (config: Record = {}) => + new JsonParamType({ ...config, type: 'json', name: paramName, }); - } beforeEach(function() { aggConfig = { params: {} } as AggConfig; diff --git a/src/legacy/ui/public/agg_types/param_types/string.test.ts b/src/legacy/ui/public/agg_types/param_types/string.test.ts index 3d496ecf898e4..fd5ccebde993e 100644 --- a/src/legacy/ui/public/agg_types/param_types/string.test.ts +++ b/src/legacy/ui/public/agg_types/param_types/string.test.ts @@ -19,7 +19,7 @@ import { BaseParamType } from './base'; import { StringParamType } from './string'; -import { AggConfig } from 'ui/agg_types'; +import { AggConfig } from '../agg_config'; jest.mock('ui/new_platform'); @@ -28,13 +28,12 @@ describe('String', function() { let aggConfig: AggConfig; let output: Record; - function initAggParam(config: Record = {}) { - return new StringParamType({ + const initAggParam = (config: Record = {}) => + new StringParamType({ ...config, type: 'string', name: paramName, }); - } beforeEach(() => { aggConfig = { params: {} } as AggConfig; From 75384e1bcd96edbb338c1f604154ee03cd9e4831 Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Tue, 29 Oct 2019 13:36:45 +0100 Subject: [PATCH 07/25] [ML] Advanced wizard functional UI tests (#49480) This PR adds tests for the ML advanced wizard. --- .../start_datafeed_modal.js | 3 + .../components/datafeed_step/datafeed.tsx | 2 +- .../advanced_detector_modal.tsx | 13 +- .../advanced_detector_modal/modal_wrapper.tsx | 15 +- .../advanced_view/detector_list.tsx | 18 +- .../advanced_view/metric_selector.tsx | 2 +- .../anomaly_detection/advanced_job.ts | 805 ++++++++++++++++++ .../anomaly_detection/index.ts | 1 + .../anomaly_detection/multi_metric_job.ts | 14 +- .../anomaly_detection/population_job.ts | 18 +- .../anomaly_detection/saved_search_job.ts | 6 + .../anomaly_detection/single_metric_job.ts | 8 + .../es_archives/ml/farequote/mappings.json | 20 +- .../services/machine_learning/api.ts | 107 +++ .../services/machine_learning/index.ts | 1 + .../machine_learning/job_management.ts | 27 +- .../services/machine_learning/job_table.ts | 7 +- .../machine_learning/job_type_selection.ts | 9 + .../machine_learning/job_wizard_advanced.ts | 315 +++++++ .../machine_learning/job_wizard_common.ts | 165 ++-- .../job_wizard_multi_metric.ts | 7 +- .../machine_learning/job_wizard_population.ts | 17 +- x-pack/test/functional/services/ml.ts | 5 +- 23 files changed, 1483 insertions(+), 102 deletions(-) create mode 100644 x-pack/test/functional/apps/machine_learning/anomaly_detection/advanced_job.ts create mode 100644 x-pack/test/functional/services/machine_learning/job_wizard_advanced.ts diff --git a/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js b/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js index 133adea7cff6e..0c356959cd2af 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js +++ b/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js @@ -136,6 +136,7 @@ export class StartDatafeedModal extends Component { onClose={this.closeModal} style={{ width: '850px' }} maxWidth={false} + data-test-subj="mlStartDatafeedModal" > @@ -178,6 +179,7 @@ export class StartDatafeedModal extends Component { = ({ setCurrentStep, isCurrentStep }) = {isCurrentStep && ( - + diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_detector_modal/advanced_detector_modal.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_detector_modal/advanced_detector_modal.tsx index b9e9df77d35e3..5f93361982ea0 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_detector_modal/advanced_detector_modal.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_detector_modal/advanced_detector_modal.tsx @@ -213,7 +213,7 @@ export const AdvancedDetectorModal: FC = ({ - + = ({ /> - + = ({ - + = ({ /> - + = ({ /> - + = ({ /> - + = ({ placeholder={descriptionPlaceholder} value={descriptionOption} onChange={e => setDescriptionOption(e.target.value)} + data-test-subj="mlAdvancedDetectorDescriptionInput" /> diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_detector_modal/modal_wrapper.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_detector_modal/modal_wrapper.tsx index 8b85c658fcadf..d1ee8a6be557a 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_detector_modal/modal_wrapper.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_detector_modal/modal_wrapper.tsx @@ -28,7 +28,11 @@ interface Props { export const ModalWrapper: FC = ({ onCreateClick, closeModal, saveEnabled, children }) => { return ( - + = ({ onCreateClick, closeModal, saveEnabled {children} - + - + = ({ isActive, onEditJob, onDeleteJob }) => defaultMessage: 'Edit', } )} + data-test-subj="mlAdvancedDetectorEditButton" /> @@ -75,6 +77,7 @@ export const DetectorList: FC = ({ isActive, onEditJob, onDeleteJob }) => defaultMessage: 'Delete', } )} + data-test-subj="mlAdvancedDetectorDeleteButton" /> @@ -98,14 +101,16 @@ export const DetectorList: FC = ({ isActive, onEditJob, onDeleteJob }) => {detectors.map((d, i) => ( - + {d.detector_description !== undefined ? ( -
{d.detector_description}
+
+ {d.detector_description} +
) : ( - detectorToString(d) + )}
{isActive && ( @@ -117,7 +122,7 @@ export const DetectorList: FC = ({ isActive, onEditJob, onDeleteJob }) => {d.detector_description !== undefined && ( - {detectorToString(d)} + )}
@@ -142,6 +147,7 @@ const NoDetectorsWarning: FC<{ show: boolean }> = ({ show }) => { defaultMessage: 'No detectors', })} iconType="alert" + data-test-subj="mlAdvancedNoDetectorsMessage" > = ({ validation
); }; + +const StringifiedDetector: FC<{ detector: Detector }> = ({ detector }) => { + return
{detectorToString(detector)}
; +}; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_view/metric_selector.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_view/metric_selector.tsx index fd597875f2716..5bc38ca934165 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_view/metric_selector.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_view/metric_selector.tsx @@ -35,7 +35,7 @@ export const MetricSelector: FC = ({ - + > => { + return arg.hasOwnProperty('field'); +}; +const isDetectorWithByField = (arg: any): arg is Required> => { + return arg.hasOwnProperty('byField'); +}; +const isDetectorWithOverField = (arg: any): arg is Required> => { + return arg.hasOwnProperty('overField'); +}; +const isDetectorWithPartitionField = ( + arg: any +): arg is Required> => { + return arg.hasOwnProperty('partitionField'); +}; +const isDetectorWithExcludeFrequent = ( + arg: any +): arg is Required> => { + return arg.hasOwnProperty('excludeFrequent'); +}; +const isDetectorWithDescription = (arg: any): arg is Required> => { + return arg.hasOwnProperty('description'); +}; + +// DatafeedConfig +const isDatafeedConfigWithQueryDelay = ( + arg: any +): arg is Required> => { + return arg.hasOwnProperty('queryDelay'); +}; +const isDatafeedConfigWithFrequency = ( + arg: any +): arg is Required> => { + return arg.hasOwnProperty('frequency'); +}; +const isDatafeedConfigWithScrollSize = ( + arg: any +): arg is Required> => { + return arg.hasOwnProperty('scrollSize'); +}; + +// PickFieldsConfig +const isPickFieldsConfigWithCategorizationField = ( + arg: any +): arg is Required> => { + return arg.hasOwnProperty('categorizationField'); +}; +const isPickFieldsConfigWithSummaryCountField = ( + arg: any +): arg is Required> => { + return arg.hasOwnProperty('summaryCountField'); +}; + +// eslint-disable-next-line import/no-default-export +export default function({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + + const defaultValues = { + datafeedQuery: `{ + "bool": { + "must": [ + { + "match_all": {} + } + ] + } +}`, + queryDelay: '60s', + frequency: '450s', + scrollSize: '1000', + }; + + const testDataList = [ + { + suiteTitle: 'with multiple metric detectors and custom datafeed settings', + jobSource: 'ecommerce', + jobId: `ec_advanced_1_${Date.now()}`, + get jobIdClone(): string { + return `${this.jobId}_clone`; + }, + jobDescription: + 'Create advanced job from ecommerce dataset with multiple metric detectors and custom datafeed settings', + jobGroups: ['automated', 'ecommerce', 'advanced'], + get jobGroupsClone(): string[] { + return [...this.jobGroups, 'clone']; + }, + pickFieldsConfig: { + detectors: [ + { + identifier: 'high_count', + function: 'high_count', + description: 'high_count detector without split', + } as Detector, + { + identifier: 'mean("products.base_price") by "category.keyword"', + function: 'mean', + field: 'products.base_price', + byField: 'category.keyword', + } as Detector, + { + identifier: 'sum("products.discount_amount") over customer_id', + function: 'sum', + field: 'products.discount_amount', + overField: 'customer_id', + } as Detector, + { + identifier: 'median(total_quantity) partition_field_name=customer_gender', + function: 'median', + field: 'total_quantity', + partitionField: 'customer_gender', + } as Detector, + { + identifier: + 'max(total_quantity) by "geoip.continent_name" over customer_id partition_field_name=customer_gender', + function: 'max', + field: 'total_quantity', + byField: 'geoip.continent_name', + overField: 'customer_id', + partitionField: 'customer_gender', + } as Detector, + ], + influencers: ['customer_id', 'category.keyword', 'geoip.continent_name', 'customer_gender'], + bucketSpan: '1h', + memoryLimit: '10mb', + } as PickFieldsConfig, + datafeedConfig: { + queryDelay: '55s', + frequency: '350s', + scrollSize: '999', + } as DatafeedConfig, + expected: { + wizard: { + timeField: 'order_date', + }, + row: { + recordCount: '4,675', + memoryStatus: 'ok', + jobState: 'closed', + datafeedState: 'stopped', + latestTimestamp: '2019-07-12 23:45:36', + }, + counts: { + processed_record_count: '4,675', + processed_field_count: '32,725', + input_bytes: '1.1 MB', + input_field_count: '32,725', + invalid_date_count: '0', + missing_field_count: '0', + out_of_order_timestamp_count: '0', + empty_bucket_count: '0', + sparse_bucket_count: '0', + bucket_count: '743', + earliest_record_timestamp: '2019-06-12 00:04:19', + latest_record_timestamp: '2019-07-12 23:45:36', + input_record_count: '4,675', + latest_bucket_timestamp: '2019-07-12 23:00:00', + }, + modelSizeStats: { + result_type: 'model_size_stats', + model_bytes_exceeded: '0', + model_bytes_memory_limit: '10485760', + total_by_field_count: '37', + total_over_field_count: '92', + total_partition_field_count: '8', + bucket_allocation_failures_count: '0', + memory_status: 'ok', + timestamp: '2019-07-12 22:00:00', + }, + }, + }, + { + suiteTitle: 'with categorization detector and default datafeed settings', + jobSource: 'ecommerce', + jobId: `ec_advanced_2_${Date.now()}`, + get jobIdClone(): string { + return `${this.jobId}_clone`; + }, + jobDescription: + 'Create advanced job from ecommerce dataset with a categorization detector and default datafeed settings', + jobGroups: ['automated', 'ecommerce', 'advanced'], + get jobGroupsClone(): string[] { + return [...this.jobGroups, 'clone']; + }, + pickFieldsConfig: { + categorizationField: 'products.product_name', + detectors: [ + { + identifier: 'count by mlcategory', + function: 'count', + byField: 'mlcategory', + } as Detector, + ], + influencers: ['mlcategory'], + bucketSpan: '12h', + memoryLimit: '100mb', + } as PickFieldsConfig, + datafeedConfig: {} as DatafeedConfig, + expected: { + wizard: { + timeField: 'order_date', + }, + row: { + recordCount: '4,675', + memoryStatus: 'ok', + jobState: 'closed', + datafeedState: 'stopped', + latestTimestamp: '2019-07-12 23:45:36', + }, + counts: { + processed_record_count: '4,675', + processed_field_count: '4,588', + input_bytes: '4.4 MB', + input_field_count: '6,154', + invalid_date_count: '0', + missing_field_count: '87', + out_of_order_timestamp_count: '0', + empty_bucket_count: '0', + sparse_bucket_count: '0', + bucket_count: '61', + earliest_record_timestamp: '2019-06-12 00:04:19', + latest_record_timestamp: '2019-07-12 23:45:36', + input_record_count: '4,675', + latest_bucket_timestamp: '2019-07-12 12:00:00', + }, + modelSizeStats: { + result_type: 'model_size_stats', + model_bytes_exceeded: '0', + model_bytes_memory_limit: '104857600', + total_by_field_count: '3,787', + total_over_field_count: '0', + total_partition_field_count: '2', + bucket_allocation_failures_count: '0', + memory_status: 'ok', + timestamp: '2019-07-12 00:00:00', + }, + }, + }, + ]; + + describe('advanced job', function() { + this.tags(['smoke', 'mlqa']); + before(async () => { + await esArchiver.load('ml/ecommerce'); + }); + + after(async () => { + await esArchiver.unload('ml/ecommerce'); + await ml.api.cleanMlIndices(); + }); + + for (const testData of testDataList) { + describe(`${testData.suiteTitle}`, function() { + it('job creation loads the job management page', async () => { + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToJobManagement(); + }); + + it('job creation loads the new job source selection page', async () => { + await ml.jobManagement.navigateToNewJobSourceSelection(); + }); + + it('job creation loads the job type selection page', async () => { + await ml.jobSourceSelection.selectSource(testData.jobSource); + }); + + it('job creation loads the advanced job wizard page', async () => { + await ml.jobTypeSelection.selectAdvancedJob(); + }); + + it('job creation displays the configure datafeed step', async () => { + await ml.jobWizardCommon.assertConfigureDatafeedSectionExists(); + }); + + it('job creation pre-fills the datafeed query editor', async () => { + await ml.jobWizardAdvanced.assertDatafeedQueryEditorExists(); + await ml.jobWizardAdvanced.assertDatafeedQueryEditorValue(defaultValues.datafeedQuery); + }); + + it('job creation inputs the query delay', async () => { + await ml.jobWizardAdvanced.assertQueryDelayInputExists(); + await ml.jobWizardAdvanced.assertQueryDelayValue(defaultValues.queryDelay); + if (isDatafeedConfigWithQueryDelay(testData.datafeedConfig)) { + await ml.jobWizardAdvanced.setQueryDelay(testData.datafeedConfig.queryDelay); + } + }); + + it('job creation inputs the frequency', async () => { + await ml.jobWizardAdvanced.assertFrequencyInputExists(); + await ml.jobWizardAdvanced.assertFrequencyValue(defaultValues.frequency); + if (isDatafeedConfigWithFrequency(testData.datafeedConfig)) { + await ml.jobWizardAdvanced.setFrequency(testData.datafeedConfig.frequency); + } + }); + + it('job creation inputs the scroll size', async () => { + await ml.jobWizardAdvanced.assertScrollSizeInputExists(); + await ml.jobWizardAdvanced.assertScrollSizeValue(defaultValues.scrollSize); + if (isDatafeedConfigWithScrollSize(testData.datafeedConfig)) { + await ml.jobWizardAdvanced.setScrollSize(testData.datafeedConfig.scrollSize); + } + }); + + it('job creation pre-fills the time field', async () => { + await ml.jobWizardAdvanced.assertTimeFieldInputExists(); + await ml.jobWizardAdvanced.assertTimeFieldSelection([testData.expected.wizard.timeField]); + }); + + it('job creation displays the pick fields step', async () => { + await ml.jobWizardCommon.advanceToPickFieldsSection(); + }); + + it('job creation selects the categorization field', async () => { + await ml.jobWizardAdvanced.assertCategorizationFieldInputExists(); + if (isPickFieldsConfigWithCategorizationField(testData.pickFieldsConfig)) { + await ml.jobWizardAdvanced.selectCategorizationField( + testData.pickFieldsConfig.categorizationField + ); + } else { + await ml.jobWizardAdvanced.assertCategorizationFieldSelection([]); + } + }); + + it('job creation selects the summary count field', async () => { + await ml.jobWizardAdvanced.assertSummaryCountFieldInputExists(); + if (isPickFieldsConfigWithSummaryCountField(testData.pickFieldsConfig)) { + await ml.jobWizardAdvanced.selectSummaryCountField( + testData.pickFieldsConfig.summaryCountField + ); + } else { + await ml.jobWizardAdvanced.assertSummaryCountFieldSelection([]); + } + }); + + it('job creation adds detectors', async () => { + for (const detector of testData.pickFieldsConfig.detectors) { + await ml.jobWizardAdvanced.openCreateDetectorModal(); + await ml.jobWizardAdvanced.assertDetectorFunctionInputExists(); + await ml.jobWizardAdvanced.assertDetectorFunctionSelection([]); + await ml.jobWizardAdvanced.assertDetectorFieldInputExists(); + await ml.jobWizardAdvanced.assertDetectorFieldSelection([]); + await ml.jobWizardAdvanced.assertDetectorByFieldInputExists(); + await ml.jobWizardAdvanced.assertDetectorByFieldSelection([]); + await ml.jobWizardAdvanced.assertDetectorOverFieldInputExists(); + await ml.jobWizardAdvanced.assertDetectorOverFieldSelection([]); + await ml.jobWizardAdvanced.assertDetectorPartitionFieldInputExists(); + await ml.jobWizardAdvanced.assertDetectorPartitionFieldSelection([]); + await ml.jobWizardAdvanced.assertDetectorExcludeFrequentInputExists(); + await ml.jobWizardAdvanced.assertDetectorExcludeFrequentSelection([]); + await ml.jobWizardAdvanced.assertDetectorDescriptionInputExists(); + await ml.jobWizardAdvanced.assertDetectorDescriptionValue(''); + + await ml.jobWizardAdvanced.selectDetectorFunction(detector.function); + if (isDetectorWithField(detector)) { + await ml.jobWizardAdvanced.selectDetectorField(detector.field); + } + if (isDetectorWithByField(detector)) { + await ml.jobWizardAdvanced.selectDetectorByField(detector.byField); + } + if (isDetectorWithOverField(detector)) { + await ml.jobWizardAdvanced.selectDetectorOverField(detector.overField); + } + if (isDetectorWithPartitionField(detector)) { + await ml.jobWizardAdvanced.selectDetectorPartitionField(detector.partitionField); + } + if (isDetectorWithExcludeFrequent(detector)) { + await ml.jobWizardAdvanced.selectDetectorExcludeFrequent(detector.excludeFrequent); + } + if (isDetectorWithDescription(detector)) { + await ml.jobWizardAdvanced.setDetectorDescription(detector.description); + } + + await ml.jobWizardAdvanced.confirmAddDetectorModal(); + } + }); + + it('job creation displays detector entries', async () => { + for (const [index, detector] of testData.pickFieldsConfig.detectors.entries()) { + await ml.jobWizardAdvanced.assertDetectorEntryExists( + index, + detector.identifier, + isDetectorWithDescription(detector) ? detector.description : undefined + ); + } + }); + + it('job creation inputs the bucket span', async () => { + await ml.jobWizardCommon.assertBucketSpanInputExists(); + await ml.jobWizardCommon.setBucketSpan(testData.pickFieldsConfig.bucketSpan); + }); + + it('job creation inputs influencers', async () => { + await ml.jobWizardCommon.assertInfluencerInputExists(); + await ml.jobWizardCommon.assertInfluencerSelection([]); + for (const influencer of testData.pickFieldsConfig.influencers) { + await ml.jobWizardCommon.addInfluencer(influencer); + } + }); + + it('job creation inputs the model memory limit', async () => { + await ml.jobWizardCommon.assertModelMemoryLimitInputExists({ + withAdvancedSection: false, + }); + await ml.jobWizardCommon.setModelMemoryLimit(testData.pickFieldsConfig.memoryLimit, { + withAdvancedSection: false, + }); + }); + + it('job creation displays the job details step', async () => { + await ml.jobWizardCommon.advanceToJobDetailsSection(); + }); + + it('job creation inputs the job id', async () => { + await ml.jobWizardCommon.assertJobIdInputExists(); + await ml.jobWizardCommon.setJobId(testData.jobId); + }); + + it('job creation inputs the job description', async () => { + await ml.jobWizardCommon.assertJobDescriptionInputExists(); + await ml.jobWizardCommon.setJobDescription(testData.jobDescription); + }); + + it('job creation inputs job groups', async () => { + await ml.jobWizardCommon.assertJobGroupInputExists(); + for (const jobGroup of testData.jobGroups) { + await ml.jobWizardCommon.addJobGroup(jobGroup); + } + await ml.jobWizardCommon.assertJobGroupSelection(testData.jobGroups); + }); + + it('job creation displays the model plot switch', async () => { + await ml.jobWizardCommon.assertModelPlotSwitchExists({ withAdvancedSection: false }); + }); + + it('job creation enables the dedicated index switch', async () => { + await ml.jobWizardCommon.assertDedicatedIndexSwitchExists({ withAdvancedSection: false }); + await ml.jobWizardCommon.activateDedicatedIndexSwitch({ withAdvancedSection: false }); + }); + + it('job creation displays the validation step', async () => { + await ml.jobWizardCommon.advanceToValidationSection(); + }); + + it('job creation displays the summary step', async () => { + await ml.jobWizardCommon.advanceToSummarySection(); + }); + + it('job creation creates the job and finishes processing', async () => { + await ml.jobWizardCommon.assertCreateJobButtonExists(); + await ml.jobWizardAdvanced.createJob(); + await ml.jobManagement.assertStartDatafeedModalExists(); + await ml.jobManagement.confirmStartDatafeedModal(); + await ml.jobManagement.waitForJobCompletion(testData.jobId); + }); + + it('job creation displays the created job in the job list', async () => { + await ml.jobTable.refreshJobList(); + await ml.jobTable.filterWithSearchString(testData.jobId); + const rows = await ml.jobTable.parseJobTable(); + expect(rows.filter(row => row.id === testData.jobId)).to.have.length(1); + }); + + it('job creation displays details for the created job in the job list', async () => { + await ml.jobTable.assertJobRowFields(testData.jobId, { + id: testData.jobId, + description: testData.jobDescription, + jobGroups: [...new Set(testData.jobGroups)].sort(), + recordCount: testData.expected.row.recordCount, + memoryStatus: testData.expected.row.memoryStatus, + jobState: testData.expected.row.jobState, + datafeedState: testData.expected.row.datafeedState, + latestTimestamp: testData.expected.row.latestTimestamp, + }); + + await ml.jobTable.assertJobRowDetailsCounts( + testData.jobId, + { + job_id: testData.jobId, + processed_record_count: testData.expected.counts.processed_record_count, + processed_field_count: testData.expected.counts.processed_field_count, + input_bytes: testData.expected.counts.input_bytes, + input_field_count: testData.expected.counts.input_field_count, + invalid_date_count: testData.expected.counts.invalid_date_count, + missing_field_count: testData.expected.counts.missing_field_count, + out_of_order_timestamp_count: testData.expected.counts.out_of_order_timestamp_count, + empty_bucket_count: testData.expected.counts.empty_bucket_count, + sparse_bucket_count: testData.expected.counts.sparse_bucket_count, + bucket_count: testData.expected.counts.bucket_count, + earliest_record_timestamp: testData.expected.counts.earliest_record_timestamp, + latest_record_timestamp: testData.expected.counts.latest_record_timestamp, + input_record_count: testData.expected.counts.input_record_count, + latest_bucket_timestamp: testData.expected.counts.latest_bucket_timestamp, + }, + { + job_id: testData.jobId, + result_type: testData.expected.modelSizeStats.result_type, + model_bytes_exceeded: testData.expected.modelSizeStats.model_bytes_exceeded, + model_bytes_memory_limit: testData.expected.modelSizeStats.model_bytes_memory_limit, + total_by_field_count: testData.expected.modelSizeStats.total_by_field_count, + total_over_field_count: testData.expected.modelSizeStats.total_over_field_count, + total_partition_field_count: + testData.expected.modelSizeStats.total_partition_field_count, + bucket_allocation_failures_count: + testData.expected.modelSizeStats.bucket_allocation_failures_count, + memory_status: testData.expected.modelSizeStats.memory_status, + timestamp: testData.expected.modelSizeStats.timestamp, + } + ); + }); + + it('job creation has detector results', async () => { + for (let i = 0; i < testData.pickFieldsConfig.detectors.length; i++) { + await ml.api.assertDetectorResultsExist(testData.jobId, i); + } + }); + + it('job cloning clicks the clone action and loads the advanced wizard', async () => { + await ml.jobTable.clickCloneJobAction(testData.jobId); + await ml.jobTypeSelection.assertAdvancedJobWizardOpen(); + }); + + it('job cloning displays the configure datafeed step', async () => { + await ml.jobWizardCommon.assertConfigureDatafeedSectionExists(); + }); + + it('job cloning pre-fills the datafeed query editor', async () => { + await ml.jobWizardAdvanced.assertDatafeedQueryEditorExists(); + await ml.jobWizardAdvanced.assertDatafeedQueryEditorValue(defaultValues.datafeedQuery); + }); + + it('job cloning pre-fills the query delay', async () => { + await ml.jobWizardAdvanced.assertQueryDelayInputExists(); + if (isDatafeedConfigWithQueryDelay(testData.datafeedConfig)) { + await ml.jobWizardAdvanced.assertQueryDelayValue(testData.datafeedConfig.queryDelay); + } + }); + + it('job cloning pre-fills the frequency', async () => { + await ml.jobWizardAdvanced.assertFrequencyInputExists(); + if (isDatafeedConfigWithFrequency(testData.datafeedConfig)) { + await ml.jobWizardAdvanced.assertFrequencyValue(testData.datafeedConfig.frequency); + } + }); + + it('job cloning pre-fills the scroll size', async () => { + await ml.jobWizardAdvanced.assertScrollSizeInputExists(); + await ml.jobWizardAdvanced.assertScrollSizeValue( + isDatafeedConfigWithScrollSize(testData.datafeedConfig) + ? testData.datafeedConfig.scrollSize + : defaultValues.scrollSize + ); + }); + + it('job creation pre-fills the time field', async () => { + await ml.jobWizardAdvanced.assertTimeFieldInputExists(); + await ml.jobWizardAdvanced.assertTimeFieldSelection([testData.expected.wizard.timeField]); + }); + + it('job cloning displays the pick fields step', async () => { + await ml.jobWizardCommon.advanceToPickFieldsSection(); + }); + + it('job cloning pre-fills the categorization field', async () => { + await ml.jobWizardAdvanced.assertCategorizationFieldInputExists(); + await ml.jobWizardAdvanced.assertCategorizationFieldSelection( + isPickFieldsConfigWithCategorizationField(testData.pickFieldsConfig) + ? [testData.pickFieldsConfig.categorizationField] + : [] + ); + }); + + it('job cloning pre-fills the summary count field', async () => { + await ml.jobWizardAdvanced.assertSummaryCountFieldInputExists(); + await ml.jobWizardAdvanced.assertSummaryCountFieldSelection( + isPickFieldsConfigWithSummaryCountField(testData.pickFieldsConfig) + ? [testData.pickFieldsConfig.summaryCountField] + : [] + ); + }); + + it('job cloning pre-fills detectors', async () => { + for (const [index, detector] of testData.pickFieldsConfig.detectors.entries()) { + await ml.jobWizardAdvanced.assertDetectorEntryExists( + index, + detector.identifier, + isDetectorWithDescription(detector) ? detector.description : undefined + ); + await ml.jobWizardAdvanced.clickEditDetector(index); + + await ml.jobWizardAdvanced.assertDetectorFunctionInputExists(); + await ml.jobWizardAdvanced.assertDetectorFieldInputExists(); + await ml.jobWizardAdvanced.assertDetectorByFieldInputExists(); + await ml.jobWizardAdvanced.assertDetectorOverFieldInputExists(); + await ml.jobWizardAdvanced.assertDetectorPartitionFieldInputExists(); + await ml.jobWizardAdvanced.assertDetectorExcludeFrequentInputExists(); + await ml.jobWizardAdvanced.assertDetectorDescriptionInputExists(); + + await ml.jobWizardAdvanced.assertDetectorFunctionSelection([detector.function]); + await ml.jobWizardAdvanced.assertDetectorFieldSelection( + isDetectorWithField(detector) ? [detector.field] : [] + ); + await ml.jobWizardAdvanced.assertDetectorByFieldSelection( + isDetectorWithByField(detector) ? [detector.byField] : [] + ); + await ml.jobWizardAdvanced.assertDetectorOverFieldSelection( + isDetectorWithOverField(detector) ? [detector.overField] : [] + ); + await ml.jobWizardAdvanced.assertDetectorPartitionFieldSelection( + isDetectorWithPartitionField(detector) ? [detector.partitionField] : [] + ); + await ml.jobWizardAdvanced.assertDetectorExcludeFrequentSelection( + isDetectorWithExcludeFrequent(detector) ? [detector.excludeFrequent] : [] + ); + // Currently, a description different form the identifier is generated for detectors with partition field + await ml.jobWizardAdvanced.assertDetectorDescriptionValue( + isDetectorWithDescription(detector) + ? detector.description + : detector.identifier.replace('partition_field_name', 'partitionfield') + ); + + await ml.jobWizardAdvanced.cancelAddDetectorModal(); + } + }); + + it('job cloning pre-fills the bucket span', async () => { + await ml.jobWizardCommon.assertBucketSpanInputExists(); + await ml.jobWizardCommon.assertBucketSpanValue(testData.pickFieldsConfig.bucketSpan); + }); + + it('job cloning pre-fills influencers', async () => { + await ml.jobWizardCommon.assertInfluencerInputExists(); + await ml.jobWizardCommon.assertInfluencerSelection(testData.pickFieldsConfig.influencers); + }); + + it('job cloning pre-fills the model memory limit', async () => { + await ml.jobWizardCommon.assertModelMemoryLimitInputExists({ + withAdvancedSection: false, + }); + await ml.jobWizardCommon.assertModelMemoryLimitValue( + testData.pickFieldsConfig.memoryLimit, + { + withAdvancedSection: false, + } + ); + }); + + it('job cloning displays the job details step', async () => { + await ml.jobWizardCommon.advanceToJobDetailsSection(); + }); + + it('job cloning does not pre-fill the job id', async () => { + await ml.jobWizardCommon.assertJobIdInputExists(); + await ml.jobWizardCommon.assertJobIdValue(''); + }); + + it('job cloning inputs the clone job id', async () => { + await ml.jobWizardCommon.setJobId(testData.jobIdClone); + }); + + it('job cloning pre-fills the job description', async () => { + await ml.jobWizardCommon.assertJobDescriptionInputExists(); + await ml.jobWizardCommon.assertJobDescriptionValue(testData.jobDescription); + }); + + it('job cloning pre-fills job groups', async () => { + await ml.jobWizardCommon.assertJobGroupInputExists(); + await ml.jobWizardCommon.assertJobGroupSelection(testData.jobGroups); + }); + + it('job cloning inputs the clone job group', async () => { + await ml.jobWizardCommon.assertJobGroupInputExists(); + await ml.jobWizardCommon.addJobGroup('clone'); + await ml.jobWizardCommon.assertJobGroupSelection(testData.jobGroupsClone); + }); + + it('job cloning pre-fills the model plot switch', async () => { + await ml.jobWizardCommon.assertModelPlotSwitchExists({ withAdvancedSection: false }); + await ml.jobWizardCommon.assertModelPlotSwitchCheckedState(false, { + withAdvancedSection: false, + }); + }); + + it('job cloning pre-fills the dedicated index switch', async () => { + await ml.jobWizardCommon.assertDedicatedIndexSwitchExists({ withAdvancedSection: false }); + await ml.jobWizardCommon.assertDedicatedIndexSwitchCheckedState(true, { + withAdvancedSection: false, + }); + }); + + it('job cloning displays the validation step', async () => { + await ml.jobWizardCommon.advanceToValidationSection(); + }); + + it('job cloning displays the summary step', async () => { + await ml.jobWizardCommon.advanceToSummarySection(); + }); + + it('job cloning creates the job and finishes processing', async () => { + await ml.jobWizardCommon.assertCreateJobButtonExists(); + await ml.jobWizardAdvanced.createJob(); + await ml.jobManagement.assertStartDatafeedModalExists(); + await ml.jobManagement.confirmStartDatafeedModal(); + await ml.jobManagement.waitForJobCompletion(testData.jobIdClone); + }); + + it('job cloning displays the created job in the job list', async () => { + await ml.jobTable.refreshJobList(); + await ml.jobTable.filterWithSearchString(testData.jobIdClone); + const rows = await ml.jobTable.parseJobTable(); + expect(rows.filter(row => row.id === testData.jobIdClone)).to.have.length(1); + }); + + it('job creation displays details for the created job in the job list', async () => { + await ml.jobTable.assertJobRowFields(testData.jobIdClone, { + id: testData.jobIdClone, + description: testData.jobDescription, + jobGroups: [...new Set(testData.jobGroupsClone)].sort(), + recordCount: testData.expected.row.recordCount, + memoryStatus: testData.expected.row.memoryStatus, + jobState: testData.expected.row.jobState, + datafeedState: testData.expected.row.datafeedState, + latestTimestamp: testData.expected.row.latestTimestamp, + }); + + await ml.jobTable.assertJobRowDetailsCounts( + testData.jobIdClone, + { + job_id: testData.jobIdClone, + processed_record_count: testData.expected.counts.processed_record_count, + processed_field_count: testData.expected.counts.processed_field_count, + input_bytes: testData.expected.counts.input_bytes, + input_field_count: testData.expected.counts.input_field_count, + invalid_date_count: testData.expected.counts.invalid_date_count, + missing_field_count: testData.expected.counts.missing_field_count, + out_of_order_timestamp_count: testData.expected.counts.out_of_order_timestamp_count, + empty_bucket_count: testData.expected.counts.empty_bucket_count, + sparse_bucket_count: testData.expected.counts.sparse_bucket_count, + bucket_count: testData.expected.counts.bucket_count, + earliest_record_timestamp: testData.expected.counts.earliest_record_timestamp, + latest_record_timestamp: testData.expected.counts.latest_record_timestamp, + input_record_count: testData.expected.counts.input_record_count, + latest_bucket_timestamp: testData.expected.counts.latest_bucket_timestamp, + }, + { + job_id: testData.jobIdClone, + result_type: testData.expected.modelSizeStats.result_type, + model_bytes_exceeded: testData.expected.modelSizeStats.model_bytes_exceeded, + model_bytes_memory_limit: testData.expected.modelSizeStats.model_bytes_memory_limit, + total_by_field_count: testData.expected.modelSizeStats.total_by_field_count, + total_over_field_count: testData.expected.modelSizeStats.total_over_field_count, + total_partition_field_count: + testData.expected.modelSizeStats.total_partition_field_count, + bucket_allocation_failures_count: + testData.expected.modelSizeStats.bucket_allocation_failures_count, + memory_status: testData.expected.modelSizeStats.memory_status, + timestamp: testData.expected.modelSizeStats.timestamp, + } + ); + }); + + it('job creation has detector results', async () => { + for (let i = 0; i < testData.pickFieldsConfig.detectors.length; i++) { + await ml.api.assertDetectorResultsExist(testData.jobIdClone, i); + } + }); + }); + } + }); +} diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/index.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/index.ts index 8d2e58bb0614d..ba307a24cd739 100644 --- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/index.ts +++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/index.ts @@ -11,5 +11,6 @@ export default function({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./multi_metric_job')); loadTestFile(require.resolve('./population_job')); loadTestFile(require.resolve('./saved_search_job')); + loadTestFile(require.resolve('./advanced_job')); }); } diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/multi_metric_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/multi_metric_job.ts index 6163e99b5eaa4..11cb48de260f1 100644 --- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/multi_metric_job.ts +++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/multi_metric_job.ts @@ -221,6 +221,12 @@ export default function({ getService }: FtrProviderContext) { ); }); + it('job creation has detector results', async () => { + for (let i = 0; i < aggAndFieldIdentifiers.length; i++) { + await ml.api.assertDetectorResultsExist(jobId, i); + } + }); + it('job cloning clicks the clone action and loads the multi metric wizard', async () => { await ml.jobTable.clickCloneJobAction(jobId); await ml.jobTypeSelection.assertMultiMetricJobWizardOpen(); @@ -258,7 +264,7 @@ export default function({ getService }: FtrProviderContext) { it('job cloning pre-fills the split field', async () => { await ml.jobWizardMultiMetric.assertSplitFieldInputExists(); - await ml.jobWizardMultiMetric.assertSplitFieldSelection(splitField); + await ml.jobWizardMultiMetric.assertSplitFieldSelection([splitField]); }); it('job cloning pre-fills influencers', async () => { @@ -351,5 +357,11 @@ export default function({ getService }: FtrProviderContext) { getExpectedModelSizeStats(jobIdClone) ); }); + + it('job cloning has detector results', async () => { + for (let i = 0; i < aggAndFieldIdentifiers.length; i++) { + await ml.api.assertDetectorResultsExist(jobId, i); + } + }); }); } diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/population_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/population_job.ts index 7ccd9214591f2..71e66cc569f4e 100644 --- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/population_job.ts +++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/population_job.ts @@ -248,6 +248,12 @@ export default function({ getService }: FtrProviderContext) { ); }); + it('job creation has detector results', async () => { + for (let i = 0; i < detectors.length; i++) { + await ml.api.assertDetectorResultsExist(jobId, i); + } + }); + it('job cloning clicks the clone action and loads the population wizard', async () => { await ml.jobTable.clickCloneJobAction(jobId); await ml.jobTypeSelection.assertPopulationJobWizardOpen(); @@ -275,14 +281,16 @@ export default function({ getService }: FtrProviderContext) { it('job cloning pre-fills the population field', async () => { await ml.jobWizardPopulation.assertPopulationFieldInputExists(); - await ml.jobWizardPopulation.assertPopulationFieldSelection(populationField); + await ml.jobWizardPopulation.assertPopulationFieldSelection([populationField]); }); it('job cloning pre-fills detectors and shows preview with split cards', async () => { for (const [index, detector] of detectors.entries()) { await ml.jobWizardCommon.assertDetectorPreviewExists(detector.identifier, index, 'SCATTER'); - await ml.jobWizardPopulation.assertDetectorSplitFieldSelection(index, detector.splitField); + await ml.jobWizardPopulation.assertDetectorSplitFieldSelection(index, [ + detector.splitField, + ]); await ml.jobWizardPopulation.assertDetectorSplitExists(index); await ml.jobWizardPopulation.assertDetectorSplitFrontCardTitle( index, @@ -387,5 +395,11 @@ export default function({ getService }: FtrProviderContext) { getExpectedModelSizeStats(jobIdClone) ); }); + + it('job cloning has detector results', async () => { + for (let i = 0; i < detectors.length; i++) { + await ml.api.assertDetectorResultsExist(jobId, i); + } + }); }); } diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/saved_search_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/saved_search_job.ts index 5645bc7277d19..0330e141b0890 100644 --- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/saved_search_job.ts +++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/saved_search_job.ts @@ -468,6 +468,12 @@ export default function({ getService }: FtrProviderContext) { } ); }); + + it('has detector results', async () => { + for (let i = 0; i < testData.aggAndFieldIdentifiers.length; i++) { + await ml.api.assertDetectorResultsExist(testData.jobId, i); + } + }); }); } }); diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_job.ts index 06ec840b36aae..b5a544b7af9f6 100644 --- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_job.ts +++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_job.ts @@ -202,6 +202,10 @@ export default function({ getService }: FtrProviderContext) { ); }); + it('job creation has detector results', async () => { + await ml.api.assertDetectorResultsExist(jobId, 0); + }); + it('job cloning clicks the clone action and loads the single metric wizard', async () => { await ml.jobTable.clickCloneJobAction(jobId); await ml.jobTypeSelection.assertSingleMetricJobWizardOpen(); @@ -319,6 +323,10 @@ export default function({ getService }: FtrProviderContext) { ); }); + it('job cloning has detector results', async () => { + await ml.api.assertDetectorResultsExist(jobId, 0); + }); + it('job deletion has results for the job before deletion', async () => { await ml.api.assertJobResultsExist(jobIdClone); }); diff --git a/x-pack/test/functional/es_archives/ml/farequote/mappings.json b/x-pack/test/functional/es_archives/ml/farequote/mappings.json index b4c6be7655805..4fe559cc85fe1 100644 --- a/x-pack/test/functional/es_archives/ml/farequote/mappings.json +++ b/x-pack/test/functional/es_archives/ml/farequote/mappings.json @@ -21,24 +21,6 @@ "airline": { "type": "keyword" }, - "host": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "path": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, "responsetime": { "type": "float" }, @@ -1102,4 +1084,4 @@ } } } -} \ No newline at end of file +} diff --git a/x-pack/test/functional/services/machine_learning/api.ts b/x-pack/test/functional/services/machine_learning/api.ts index 148e276dc4a14..270722a97d6b6 100644 --- a/x-pack/test/functional/services/machine_learning/api.ts +++ b/x-pack/test/functional/services/machine_learning/api.ts @@ -8,10 +8,13 @@ import expect from '@kbn/expect'; import { isEmpty } from 'lodash'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { JOB_STATE, DATAFEED_STATE } from '../../../../legacy/plugins/ml/common/constants/states'; + export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { const es = getService('es'); const log = getService('log'); const retry = getService('retry'); + const esSupertest = getService('esSupertest'); return { async hasJobResults(jobId: string): Promise { @@ -54,6 +57,54 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { ); }, + async hasDetectorResults(jobId: string, detectorIndex: number): Promise { + const response = await es.search({ + index: '.ml-anomalies-*', + body: { + size: 1, + query: { + bool: { + must: [ + { + match: { + job_id: jobId, + }, + }, + { + match: { + result_type: 'record', + }, + }, + { + match: { + detector_index: detectorIndex, + }, + }, + ], + }, + }, + }, + }); + + return response.hits.hits.length > 0; + }, + + async assertDetectorResultsExist(jobId: string, detectorIndex: number) { + await retry.waitForWithTimeout( + `results for detector ${detectorIndex} on job ${jobId} to exist`, + 30 * 1000, + async () => { + if ((await this.hasDetectorResults(jobId, detectorIndex)) === true) { + return true; + } else { + throw new Error( + `expected results for detector ${detectorIndex} on job '${jobId}' to exist` + ); + } + } + ); + }, + async deleteIndices(indices: string) { log.debug(`Deleting indices: '${indices}'...`); const deleteResponse = await es.indices.delete({ @@ -79,5 +130,61 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { async cleanMlIndices() { await this.deleteIndices('.ml-*'); }, + + async getJobState(jobId: string): Promise { + log.debug(`Fetching job state for job ${jobId}`); + const jobStats = await esSupertest + .get(`/_ml/anomaly_detectors/${jobId}/_stats`) + .expect(200) + .then((res: any) => res.body); + + expect(jobStats.jobs).to.have.length(1); + const state: JOB_STATE = jobStats.jobs[0].state; + + return state; + }, + + async waitForJobState(jobId: string, expectedJobState: JOB_STATE) { + await retry.waitForWithTimeout( + `job state to be ${expectedJobState}`, + 2 * 60 * 1000, + async () => { + const state = await this.getJobState(jobId); + if (state === expectedJobState) { + return true; + } else { + throw new Error(`expected job state to be ${expectedJobState} but got ${state}`); + } + } + ); + }, + + async getDatafeedState(datafeedId: string): Promise { + log.debug(`Fetching datafeed state for datafeed ${datafeedId}`); + const datafeedStats = await esSupertest + .get(`/_ml/datafeeds/${datafeedId}/_stats`) + .expect(200) + .then((res: any) => res.body); + + expect(datafeedStats.datafeeds).to.have.length(1); + const state: DATAFEED_STATE = datafeedStats.datafeeds[0].state; + + return state; + }, + + async waitForDatafeedState(datafeedId: string, expectedDatafeedState: DATAFEED_STATE) { + await retry.waitForWithTimeout( + `datafeed state to be ${expectedDatafeedState}`, + 2 * 60 * 1000, + async () => { + const state = await this.getDatafeedState(datafeedId); + if (state === expectedDatafeedState) { + return true; + } else { + throw new Error(`expected job state to be ${expectedDatafeedState} but got ${state}`); + } + } + ); + }, }; } diff --git a/x-pack/test/functional/services/machine_learning/index.ts b/x-pack/test/functional/services/machine_learning/index.ts index 0dc588ffcc9e5..c5ebe3e9cc156 100644 --- a/x-pack/test/functional/services/machine_learning/index.ts +++ b/x-pack/test/functional/services/machine_learning/index.ts @@ -12,6 +12,7 @@ export { MachineLearningJobManagementProvider } from './job_management'; export { MachineLearningJobSourceSelectionProvider } from './job_source_selection'; export { MachineLearningJobTableProvider } from './job_table'; export { MachineLearningJobTypeSelectionProvider } from './job_type_selection'; +export { MachineLearningJobWizardAdvancedProvider } from './job_wizard_advanced'; export { MachineLearningJobWizardCommonProvider } from './job_wizard_common'; export { MachineLearningJobWizardMultiMetricProvider } from './job_wizard_multi_metric'; export { MachineLearningJobWizardPopulationProvider } from './job_wizard_population'; diff --git a/x-pack/test/functional/services/machine_learning/job_management.ts b/x-pack/test/functional/services/machine_learning/job_management.ts index 44cf06ab44735..ddab5fd68f13c 100644 --- a/x-pack/test/functional/services/machine_learning/job_management.ts +++ b/x-pack/test/functional/services/machine_learning/job_management.ts @@ -3,11 +3,19 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { ProvidedType } from '@kbn/test/types/ftr'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { MachineLearningAPIProvider } from './api'; -export function MachineLearningJobManagementProvider({ getService }: FtrProviderContext) { +import { JOB_STATE, DATAFEED_STATE } from '../../../../legacy/plugins/ml/common/constants/states'; + +export function MachineLearningJobManagementProvider( + { getService }: FtrProviderContext, + mlApi: ProvidedType +) { const testSubjects = getService('testSubjects'); + const retry = getService('retry'); return { async navigateToNewJobSourceSelection() { @@ -26,5 +34,22 @@ export function MachineLearningJobManagementProvider({ getService }: FtrProvider async assertJobStatsBarExists() { await testSubjects.existOrFail('~mlJobStatsBar'); }, + + async assertStartDatafeedModalExists() { + // this retry can be removed as soon as #48734 is merged + await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail('mlStartDatafeedModal'); + }); + }, + + async confirmStartDatafeedModal() { + await testSubjects.click('mlStartDatafeedModalStartButton'); + await testSubjects.missingOrFail('mlStartDatafeedModal'); + }, + + async waitForJobCompletion(jobId: string) { + await mlApi.waitForDatafeedState(`datafeed-${jobId}`, DATAFEED_STATE.STOPPED); + await mlApi.waitForJobState(jobId, JOB_STATE.CLOSED); + }, }; } diff --git a/x-pack/test/functional/services/machine_learning/job_table.ts b/x-pack/test/functional/services/machine_learning/job_table.ts index 17c5d0f54eaa3..7eded43d1f058 100644 --- a/x-pack/test/functional/services/machine_learning/job_table.ts +++ b/x-pack/test/functional/services/machine_learning/job_table.ts @@ -157,6 +157,11 @@ export function MachineLearningJobTableProvider({ getService }: FtrProviderConte }); } + public async refreshJobList() { + await testSubjects.click('mlRefreshJobListButton'); + await this.waitForJobsToLoad(); + } + public async waitForJobsToLoad() { await testSubjects.existOrFail('~mlJobListTable', { timeout: 60 * 1000 }); await testSubjects.existOrFail('mlJobListTable loaded', { timeout: 30 * 1000 }); @@ -206,7 +211,7 @@ export function MachineLearningJobTableProvider({ getService }: FtrProviderConte } public async clickActionsMenu(jobId: string) { - retry.tryForTime(30 * 1000, async () => { + await retry.tryForTime(30 * 1000, async () => { if (!(await testSubjects.exists('mlActionButtonDeleteJob'))) { await testSubjects.click(this.rowSelector(jobId, 'euiCollapsedItemActionsButton')); await testSubjects.existOrFail('mlActionButtonDeleteJob', { timeout: 5000 }); diff --git a/x-pack/test/functional/services/machine_learning/job_type_selection.ts b/x-pack/test/functional/services/machine_learning/job_type_selection.ts index 0957558f62165..6686b5b28f200 100644 --- a/x-pack/test/functional/services/machine_learning/job_type_selection.ts +++ b/x-pack/test/functional/services/machine_learning/job_type_selection.ts @@ -36,5 +36,14 @@ export function MachineLearningJobTypeSelectionProvider({ getService }: FtrProvi async assertPopulationJobWizardOpen() { await testSubjects.existOrFail('mlPageJobWizard population'); }, + + async selectAdvancedJob() { + await testSubjects.clickWhenNotDisabled('mlJobTypeLinkAdvancedJob'); + await this.assertAdvancedJobWizardOpen(); + }, + + async assertAdvancedJobWizardOpen() { + await testSubjects.existOrFail('mlPageJobWizard advanced'); + }, }; } diff --git a/x-pack/test/functional/services/machine_learning/job_wizard_advanced.ts b/x-pack/test/functional/services/machine_learning/job_wizard_advanced.ts new file mode 100644 index 0000000000000..71b76a6885592 --- /dev/null +++ b/x-pack/test/functional/services/machine_learning/job_wizard_advanced.ts @@ -0,0 +1,315 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function MachineLearningJobWizardAdvancedProvider({ + getService, + getPageObjects, +}: FtrProviderContext) { + const comboBox = getService('comboBox'); + const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + const aceEditor = getService('aceEditor'); + + return { + async getValueOrPlaceholder(inputLocator: string): Promise { + const value = await testSubjects.getAttribute(inputLocator, 'value'); + if (value !== '') { + return value; + } else { + return await testSubjects.getAttribute(inputLocator, 'placeholder'); + } + }, + + async assertDatafeedQueryEditorExists() { + await testSubjects.existOrFail('mlAdvancedDatafeedQueryEditor > codeEditorContainer'); + }, + + async assertDatafeedQueryEditorValue(expectedValue: string) { + const actualValue = await aceEditor.getValue( + 'mlAdvancedDatafeedQueryEditor > codeEditorContainer' + ); + expect(actualValue).to.eql(expectedValue); + }, + + async assertQueryDelayInputExists() { + await testSubjects.existOrFail('mlJobWizardInputQueryDelay'); + }, + + async assertQueryDelayValue(expectedValue: string) { + const actualQueryDelay = await this.getValueOrPlaceholder('mlJobWizardInputQueryDelay'); + expect(actualQueryDelay).to.eql(expectedValue); + }, + + async setQueryDelay(queryDelay: string) { + await testSubjects.setValue('mlJobWizardInputQueryDelay', queryDelay, { + clearWithKeyboard: true, + typeCharByChar: true, + }); + await this.assertQueryDelayValue(queryDelay); + }, + + async assertFrequencyInputExists() { + await testSubjects.existOrFail('mlJobWizardInputFrequency'); + }, + + async assertFrequencyValue(expectedValue: string) { + const actualFrequency = await this.getValueOrPlaceholder('mlJobWizardInputFrequency'); + expect(actualFrequency).to.eql(expectedValue); + }, + + async setFrequency(frequency: string) { + await testSubjects.setValue('mlJobWizardInputFrequency', frequency, { + clearWithKeyboard: true, + typeCharByChar: true, + }); + await this.assertFrequencyValue(frequency); + }, + + async assertScrollSizeInputExists() { + await testSubjects.existOrFail('mlJobWizardInputScrollSize'); + }, + + async assertScrollSizeValue(expectedValue: string) { + const actualScrollSize = await this.getValueOrPlaceholder('mlJobWizardInputScrollSize'); + expect(actualScrollSize).to.eql(expectedValue); + }, + + async setScrollSize(scrollSize: string) { + await testSubjects.setValue('mlJobWizardInputScrollSize', scrollSize, { + clearWithKeyboard: true, + typeCharByChar: true, + }); + await this.assertScrollSizeValue(scrollSize); + }, + + async assertTimeFieldInputExists() { + await testSubjects.existOrFail('mlTimeFieldNameSelect > comboBoxInput'); + }, + + async assertTimeFieldSelection(expectedIdentifier: string[]) { + const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( + 'mlTimeFieldNameSelect > comboBoxInput' + ); + expect(comboBoxSelectedOptions).to.eql(expectedIdentifier); + }, + + async selectTimeField(identifier: string) { + await comboBox.set('mlTimeFieldNameSelect > comboBoxInput', identifier); + await this.assertTimeFieldSelection([identifier]); + }, + + async assertCategorizationFieldInputExists() { + await testSubjects.existOrFail('mlCategorizationFieldNameSelect > comboBoxInput'); + }, + + async assertCategorizationFieldSelection(expectedIdentifier: string[]) { + const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( + 'mlCategorizationFieldNameSelect > comboBoxInput' + ); + expect(comboBoxSelectedOptions).to.eql(expectedIdentifier); + }, + + async selectCategorizationField(identifier: string) { + await comboBox.set('mlCategorizationFieldNameSelect > comboBoxInput', identifier); + await this.assertCategorizationFieldSelection([identifier]); + }, + + async assertSummaryCountFieldInputExists() { + await testSubjects.existOrFail('mlSummaryCountFieldNameSelect > comboBoxInput'); + }, + + async assertSummaryCountFieldSelection(expectedIdentifier: string[]) { + const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( + 'mlSummaryCountFieldNameSelect > comboBoxInput' + ); + expect(comboBoxSelectedOptions).to.eql(expectedIdentifier); + }, + + async selectSummaryCountField(identifier: string) { + await comboBox.set('mlSummaryCountFieldNameSelect > comboBoxInput', identifier); + await this.assertSummaryCountFieldSelection([identifier]); + }, + + async assertAddDetectorButtonExists() { + await testSubjects.existOrFail('mlAddDetectorButton'); + }, + + async openCreateDetectorModal() { + await testSubjects.click('mlAddDetectorButton'); + await this.assertCreateDetectorModalExists(); + }, + + async assertCreateDetectorModalExists() { + // this retry can be removed as soon as #48734 is merged + await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail('mlCreateDetectorModal'); + }); + }, + + async assertDetectorFunctionInputExists() { + await testSubjects.existOrFail('mlAdvancedFunctionSelect > comboBoxInput'); + }, + + async assertDetectorFunctionSelection(expectedIdentifier: string[]) { + const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( + 'mlAdvancedFunctionSelect > comboBoxInput' + ); + expect(comboBoxSelectedOptions).to.eql(expectedIdentifier); + }, + + async selectDetectorFunction(identifier: string) { + await comboBox.set('mlAdvancedFunctionSelect > comboBoxInput', identifier); + await this.assertDetectorFunctionSelection([identifier]); + }, + + async assertDetectorFieldInputExists() { + await testSubjects.existOrFail('mlAdvancedFieldSelect > comboBoxInput'); + }, + + async assertDetectorFieldSelection(expectedIdentifier: string[]) { + const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( + 'mlAdvancedFieldSelect > comboBoxInput' + ); + expect(comboBoxSelectedOptions).to.eql(expectedIdentifier); + }, + + async selectDetectorField(identifier: string) { + await comboBox.set('mlAdvancedFieldSelect > comboBoxInput', identifier); + await this.assertDetectorFieldSelection([identifier]); + }, + + async assertDetectorByFieldInputExists() { + await testSubjects.existOrFail('mlAdvancedByFieldSelect > comboBoxInput'); + }, + + async assertDetectorByFieldSelection(expectedIdentifier: string[]) { + const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( + 'mlAdvancedByFieldSelect > comboBoxInput' + ); + expect(comboBoxSelectedOptions).to.eql(expectedIdentifier); + }, + + async selectDetectorByField(identifier: string) { + await comboBox.set('mlAdvancedByFieldSelect > comboBoxInput', identifier); + await this.assertDetectorByFieldSelection([identifier]); + }, + + async assertDetectorOverFieldInputExists() { + await testSubjects.existOrFail('mlAdvancedOverFieldSelect > comboBoxInput'); + }, + + async assertDetectorOverFieldSelection(expectedIdentifier: string[]) { + const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( + 'mlAdvancedOverFieldSelect > comboBoxInput' + ); + expect(comboBoxSelectedOptions).to.eql(expectedIdentifier); + }, + + async selectDetectorOverField(identifier: string) { + await comboBox.set('mlAdvancedOverFieldSelect > comboBoxInput', identifier); + await this.assertDetectorOverFieldSelection([identifier]); + }, + + async assertDetectorPartitionFieldInputExists() { + await testSubjects.existOrFail('mlAdvancedPartitionFieldSelect > comboBoxInput'); + }, + + async assertDetectorPartitionFieldSelection(expectedIdentifier: string[]) { + const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( + 'mlAdvancedPartitionFieldSelect > comboBoxInput' + ); + expect(comboBoxSelectedOptions).to.eql(expectedIdentifier); + }, + + async selectDetectorPartitionField(identifier: string) { + await comboBox.set('mlAdvancedPartitionFieldSelect > comboBoxInput', identifier); + await this.assertDetectorPartitionFieldSelection([identifier]); + }, + + async assertDetectorExcludeFrequentInputExists() { + await testSubjects.existOrFail('mlAdvancedExcludeFrequentSelect > comboBoxInput'); + }, + + async assertDetectorExcludeFrequentSelection(expectedIdentifier: string[]) { + const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( + 'mlAdvancedExcludeFrequentSelect > comboBoxInput' + ); + expect(comboBoxSelectedOptions).to.eql(expectedIdentifier); + }, + + async selectDetectorExcludeFrequent(identifier: string) { + await comboBox.set('mlAdvancedExcludeFrequentSelect > comboBoxInput', identifier); + await this.assertDetectorExcludeFrequentSelection([identifier]); + }, + + async assertDetectorDescriptionInputExists() { + await testSubjects.existOrFail('mlAdvancedDetectorDescriptionInput'); + }, + + async assertDetectorDescriptionValue(expectedValue: string) { + const actualDetectorDescription = await testSubjects.getAttribute( + 'mlAdvancedDetectorDescriptionInput', + 'value' + ); + expect(actualDetectorDescription).to.eql(expectedValue); + }, + + async setDetectorDescription(description: string) { + await testSubjects.setValue('mlAdvancedDetectorDescriptionInput', description, { + clearWithKeyboard: true, + }); + await this.assertDetectorDescriptionValue(description); + }, + + async confirmAddDetectorModal() { + await testSubjects.clickWhenNotDisabled('mlCreateDetectorModalSaveButton'); + await testSubjects.missingOrFail('mlCreateDetectorModal'); + }, + + async cancelAddDetectorModal() { + await testSubjects.clickWhenNotDisabled('mlCreateDetectorModalCancelButton'); + await testSubjects.missingOrFail('mlCreateDetectorModal'); + }, + + async assertDetectorEntryExists( + detectorIndex: number, + expectedDetectorName: string, + expectedDetectorDescription?: string + ) { + await testSubjects.existOrFail(`mlAdvancedDetector ${detectorIndex}`); + + const actualDetectorIdentifier = await testSubjects.getVisibleText( + `mlAdvancedDetector ${detectorIndex} > mlDetectorIdentifier` + ); + expect(actualDetectorIdentifier).to.eql(expectedDetectorName); + + if (expectedDetectorDescription !== undefined) { + const actualDetectorDescription = await testSubjects.getVisibleText( + `mlAdvancedDetector ${detectorIndex} > mlDetectorDescription` + ); + expect(actualDetectorDescription).to.eql(expectedDetectorDescription); + } + }, + + async clickEditDetector(detectorIndex: number) { + await testSubjects.click( + `mlAdvancedDetector ${detectorIndex} > mlAdvancedDetectorEditButton` + ); + await this.assertCreateDetectorModalExists(); + }, + + async createJob() { + await testSubjects.clickWhenNotDisabled('mlJobWizardButtonCreateJob'); + // this retry can be removed as soon as #48734 is merged + await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail('mlStartDatafeedModal'); + }); + }, + }; +} diff --git a/x-pack/test/functional/services/machine_learning/job_wizard_common.ts b/x-pack/test/functional/services/machine_learning/job_wizard_common.ts index 73764e8f36518..3a71f96fa3fbd 100644 --- a/x-pack/test/functional/services/machine_learning/job_wizard_common.ts +++ b/x-pack/test/functional/services/machine_learning/job_wizard_common.ts @@ -12,6 +12,15 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid const retry = getService('retry'); const testSubjects = getService('testSubjects'); + interface SectionOptions { + withAdvancedSection: boolean; + } + + function advancedSectionSelector(subSelector?: string) { + const subj = 'mlJobWizardAdvancedSection'; + return !subSelector ? subj : `${subj} > ${subSelector}`; + } + return { async clickNextButton() { await testSubjects.existOrFail('mlJobWizardNavButtonNext'); @@ -38,6 +47,10 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid await testSubjects.existOrFail('mlJobWizardStepTitleSummary'); }, + async assertConfigureDatafeedSectionExists() { + await testSubjects.existOrFail('mlJobWizardStepTitleConfigureDatafeed'); + }, + async advanceToPickFieldsSection() { await this.clickNextButton(); await this.assertPickFieldsSectionExists(); @@ -153,75 +166,127 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid expect(await this.getSelectedJobGroups()).to.contain(jobGroup); }, - async assertModelPlotSwitchExists() { - await this.ensureAdvancedSectionOpen(); - await testSubjects.existOrFail('mlJobWizardAdvancedSection > mlJobWizardSwitchModelPlot', { - allowHidden: true, - }); + async assertModelPlotSwitchExists( + sectionOptions: SectionOptions = { withAdvancedSection: true } + ) { + let subj = 'mlJobWizardSwitchModelPlot'; + if (sectionOptions.withAdvancedSection === true) { + await this.ensureAdvancedSectionOpen(); + subj = advancedSectionSelector(subj); + } + await testSubjects.existOrFail(subj, { allowHidden: true }); }, - async getModelPlotSwitchCheckedState(): Promise { - await this.ensureAdvancedSectionOpen(); - return await testSubjects.isSelected( - 'mlJobWizardAdvancedSection > mlJobWizardSwitchModelPlot' - ); + async getModelPlotSwitchCheckedState( + sectionOptions: SectionOptions = { withAdvancedSection: true } + ): Promise { + let subj = 'mlJobWizardSwitchModelPlot'; + if (sectionOptions.withAdvancedSection === true) { + await this.ensureAdvancedSectionOpen(); + subj = advancedSectionSelector(subj); + } + return await testSubjects.isSelected(subj); }, - async assertModelPlotSwitchCheckedState(expectedValue: boolean) { - await this.ensureAdvancedSectionOpen(); - const actualCheckedState = await this.getModelPlotSwitchCheckedState(); + async assertModelPlotSwitchCheckedState( + expectedValue: boolean, + sectionOptions: SectionOptions = { withAdvancedSection: true } + ) { + const actualCheckedState = await this.getModelPlotSwitchCheckedState({ + withAdvancedSection: sectionOptions.withAdvancedSection, + }); expect(actualCheckedState).to.eql(expectedValue); }, - async assertDedicatedIndexSwitchExists() { - await this.ensureAdvancedSectionOpen(); - await testSubjects.existOrFail( - 'mlJobWizardAdvancedSection > mlJobWizardSwitchUseDedicatedIndex', - { allowHidden: true } - ); + async assertDedicatedIndexSwitchExists( + sectionOptions: SectionOptions = { withAdvancedSection: true } + ) { + let subj = 'mlJobWizardSwitchUseDedicatedIndex'; + if (sectionOptions.withAdvancedSection === true) { + await this.ensureAdvancedSectionOpen(); + subj = advancedSectionSelector(subj); + } + await testSubjects.existOrFail(subj, { allowHidden: true }); }, - async getDedicatedIndexSwitchCheckedState(): Promise { - await this.ensureAdvancedSectionOpen(); - return await testSubjects.isSelected( - 'mlJobWizardAdvancedSection > mlJobWizardSwitchUseDedicatedIndex' - ); + async getDedicatedIndexSwitchCheckedState( + sectionOptions: SectionOptions = { withAdvancedSection: true } + ): Promise { + let subj = 'mlJobWizardSwitchUseDedicatedIndex'; + if (sectionOptions.withAdvancedSection === true) { + await this.ensureAdvancedSectionOpen(); + subj = advancedSectionSelector(subj); + } + return await testSubjects.isSelected(subj); }, - async assertDedicatedIndexSwitchCheckedState(expectedValue: boolean) { - await this.ensureAdvancedSectionOpen(); - const actualCheckedState = await this.getDedicatedIndexSwitchCheckedState(); + async assertDedicatedIndexSwitchCheckedState( + expectedValue: boolean, + sectionOptions: SectionOptions = { withAdvancedSection: true } + ) { + const actualCheckedState = await this.getDedicatedIndexSwitchCheckedState({ + withAdvancedSection: sectionOptions.withAdvancedSection, + }); expect(actualCheckedState).to.eql(expectedValue); }, - async activateDedicatedIndexSwitch() { - if ((await this.getDedicatedIndexSwitchCheckedState()) === false) { - await testSubjects.clickWhenNotDisabled('mlJobWizardSwitchUseDedicatedIndex'); + async activateDedicatedIndexSwitch( + sectionOptions: SectionOptions = { withAdvancedSection: true } + ) { + let subj = 'mlJobWizardSwitchUseDedicatedIndex'; + if (sectionOptions.withAdvancedSection === true) { + await this.ensureAdvancedSectionOpen(); + subj = advancedSectionSelector(subj); + } + if ( + (await this.getDedicatedIndexSwitchCheckedState({ + withAdvancedSection: sectionOptions.withAdvancedSection, + })) === false + ) { + await testSubjects.clickWhenNotDisabled(subj); } - await this.assertDedicatedIndexSwitchCheckedState(true); + await this.assertDedicatedIndexSwitchCheckedState(true, { + withAdvancedSection: sectionOptions.withAdvancedSection, + }); }, - async assertModelMemoryLimitInputExists() { - await this.ensureAdvancedSectionOpen(); - await testSubjects.existOrFail( - 'mlJobWizardAdvancedSection > mlJobWizardInputModelMemoryLimit' - ); + async assertModelMemoryLimitInputExists( + sectionOptions: SectionOptions = { withAdvancedSection: true } + ) { + let subj = 'mlJobWizardInputModelMemoryLimit'; + if (sectionOptions.withAdvancedSection === true) { + await this.ensureAdvancedSectionOpen(); + subj = advancedSectionSelector(subj); + } + await testSubjects.existOrFail(subj); }, - async assertModelMemoryLimitValue(expectedValue: string) { - await this.ensureAdvancedSectionOpen(); - const actualModelMemoryLimit = await testSubjects.getAttribute( - 'mlJobWizardAdvancedSection > mlJobWizardInputModelMemoryLimit', - 'value' - ); + async assertModelMemoryLimitValue( + expectedValue: string, + sectionOptions: SectionOptions = { withAdvancedSection: true } + ) { + let subj = 'mlJobWizardInputModelMemoryLimit'; + if (sectionOptions.withAdvancedSection === true) { + await this.ensureAdvancedSectionOpen(); + subj = advancedSectionSelector(subj); + } + const actualModelMemoryLimit = await testSubjects.getAttribute(subj, 'value'); expect(actualModelMemoryLimit).to.eql(expectedValue); }, - async setModelMemoryLimit(modelMemoryLimit: string) { - await testSubjects.setValue('mlJobWizardInputModelMemoryLimit', modelMemoryLimit, { - clearWithKeyboard: true, + async setModelMemoryLimit( + modelMemoryLimit: string, + sectionOptions: SectionOptions = { withAdvancedSection: true } + ) { + let subj = 'mlJobWizardInputModelMemoryLimit'; + if (sectionOptions.withAdvancedSection === true) { + await this.ensureAdvancedSectionOpen(); + subj = advancedSectionSelector(subj); + } + await testSubjects.setValue(subj, modelMemoryLimit, { clearWithKeyboard: true }); + await this.assertModelMemoryLimitValue(modelMemoryLimit, { + withAdvancedSection: sectionOptions.withAdvancedSection, }); - await this.assertModelMemoryLimitValue(modelMemoryLimit); }, async assertInfluencerInputExists() { @@ -237,7 +302,7 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid }, async addInfluencer(influencer: string) { - await comboBox.setCustom('mlInfluencerSelect > comboBoxInput', influencer); + await comboBox.set('mlInfluencerSelect > comboBoxInput', influencer); expect(await this.getSelectedInfluencers()).to.contain(influencer); }, @@ -293,16 +358,16 @@ export function MachineLearningJobWizardCommonProvider({ getService }: FtrProvid async ensureAdvancedSectionOpen() { await retry.tryForTime(5000, async () => { - if ((await testSubjects.exists('mlJobWizardAdvancedSection')) === false) { + if ((await testSubjects.exists(advancedSectionSelector())) === false) { await testSubjects.click('mlJobWizardToggleAdvancedSection'); - await testSubjects.existOrFail('mlJobWizardAdvancedSection', { timeout: 1000 }); + await testSubjects.existOrFail(advancedSectionSelector(), { timeout: 1000 }); } }); }, async createJobAndWaitForCompletion() { await testSubjects.clickWhenNotDisabled('mlJobWizardButtonCreateJob'); - await testSubjects.existOrFail('mlJobWizardButtonRunInRealTime', { timeout: 5 * 60 * 1000 }); + await testSubjects.existOrFail('mlJobWizardButtonRunInRealTime', { timeout: 2 * 60 * 1000 }); }, }; } diff --git a/x-pack/test/functional/services/machine_learning/job_wizard_multi_metric.ts b/x-pack/test/functional/services/machine_learning/job_wizard_multi_metric.ts index d9df6a9d682a7..2fb768d924cff 100644 --- a/x-pack/test/functional/services/machine_learning/job_wizard_multi_metric.ts +++ b/x-pack/test/functional/services/machine_learning/job_wizard_multi_metric.ts @@ -16,17 +16,16 @@ export function MachineLearningJobWizardMultiMetricProvider({ getService }: FtrP await testSubjects.existOrFail('mlMultiMetricSplitFieldSelect > comboBoxInput'); }, - async assertSplitFieldSelection(identifier: string) { + async assertSplitFieldSelection(expectedIdentifier: string[]) { const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( 'mlMultiMetricSplitFieldSelect > comboBoxInput' ); - expect(comboBoxSelectedOptions.length).to.eql(1); - expect(comboBoxSelectedOptions[0]).to.eql(identifier); + expect(comboBoxSelectedOptions).to.eql(expectedIdentifier); }, async selectSplitField(identifier: string) { await comboBox.set('mlMultiMetricSplitFieldSelect > comboBoxInput', identifier); - await this.assertSplitFieldSelection(identifier); + await this.assertSplitFieldSelection([identifier]); }, async assertDetectorSplitExists(splitField: string) { diff --git a/x-pack/test/functional/services/machine_learning/job_wizard_population.ts b/x-pack/test/functional/services/machine_learning/job_wizard_population.ts index 892bdaf394936..8ff9d5c12a642 100644 --- a/x-pack/test/functional/services/machine_learning/job_wizard_population.ts +++ b/x-pack/test/functional/services/machine_learning/job_wizard_population.ts @@ -16,17 +16,16 @@ export function MachineLearningJobWizardPopulationProvider({ getService }: FtrPr await testSubjects.existOrFail('mlPopulationSplitFieldSelect > comboBoxInput'); }, - async assertPopulationFieldSelection(identifier: string) { + async assertPopulationFieldSelection(expectedIdentifier: string[]) { const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( 'mlPopulationSplitFieldSelect > comboBoxInput' ); - expect(comboBoxSelectedOptions.length).to.eql(1); - expect(comboBoxSelectedOptions[0]).to.eql(identifier); + expect(comboBoxSelectedOptions).to.eql(expectedIdentifier); }, async selectPopulationField(identifier: string) { await comboBox.set('mlPopulationSplitFieldSelect > comboBoxInput', identifier); - await this.assertPopulationFieldSelection(identifier); + await this.assertPopulationFieldSelection([identifier]); }, async assertDetectorSplitFieldInputExists(detectorPosition: number) { @@ -35,12 +34,14 @@ export function MachineLearningJobWizardPopulationProvider({ getService }: FtrPr ); }, - async assertDetectorSplitFieldSelection(detectorPosition: number, identifier: string) { + async assertDetectorSplitFieldSelection( + detectorPosition: number, + expectedIdentifier: string[] + ) { const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( `mlDetector ${detectorPosition} > mlByFieldSelect > comboBoxInput` ); - expect(comboBoxSelectedOptions.length).to.eql(1); - expect(comboBoxSelectedOptions[0]).to.eql(identifier); + expect(comboBoxSelectedOptions).to.eql(expectedIdentifier); }, async selectDetectorSplitField(detectorPosition: number, identifier: string) { @@ -48,7 +49,7 @@ export function MachineLearningJobWizardPopulationProvider({ getService }: FtrPr `mlDetector ${detectorPosition} > mlByFieldSelect > comboBoxInput`, identifier ); - await this.assertDetectorSplitFieldSelection(detectorPosition, identifier); + await this.assertDetectorSplitFieldSelection(detectorPosition, [identifier]); }, async assertDetectorSplitExists(detectorPosition: number) { diff --git a/x-pack/test/functional/services/ml.ts b/x-pack/test/functional/services/ml.ts index 3feb45ae23bbc..8609552b5dc55 100644 --- a/x-pack/test/functional/services/ml.ts +++ b/x-pack/test/functional/services/ml.ts @@ -15,6 +15,7 @@ import { MachineLearningJobSourceSelectionProvider, MachineLearningJobTableProvider, MachineLearningJobTypeSelectionProvider, + MachineLearningJobWizardAdvancedProvider, MachineLearningJobWizardCommonProvider, MachineLearningJobWizardMultiMetricProvider, MachineLearningJobWizardPopulationProvider, @@ -28,10 +29,11 @@ export function MachineLearningProvider(context: FtrProviderContext) { const api = MachineLearningAPIProvider(context); const dataFrameAnalytics = MachineLearningDataFrameAnalyticsProvider(context); const dataVisualizer = MachineLearningDataVisualizerProvider(context); - const jobManagement = MachineLearningJobManagementProvider(context); + const jobManagement = MachineLearningJobManagementProvider(context, api); const jobSourceSelection = MachineLearningJobSourceSelectionProvider(context); const jobTable = MachineLearningJobTableProvider(context); const jobTypeSelection = MachineLearningJobTypeSelectionProvider(context); + const jobWizardAdvanced = MachineLearningJobWizardAdvancedProvider(context); const jobWizardCommon = MachineLearningJobWizardCommonProvider(context); const jobWizardMultiMetric = MachineLearningJobWizardMultiMetricProvider(context); const jobWizardPopulation = MachineLearningJobWizardPopulationProvider(context); @@ -48,6 +50,7 @@ export function MachineLearningProvider(context: FtrProviderContext) { jobSourceSelection, jobTable, jobTypeSelection, + jobWizardAdvanced, jobWizardCommon, jobWizardMultiMetric, jobWizardPopulation, From 19ea92abb7fba244a6cc0981ca67e0b82b1a3d1d Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 29 Oct 2019 06:15:33 -0700 Subject: [PATCH 08/25] move react/jsx-a11y rules into shared react preset (#49244) * move react/jsx-a11y rules into shared react preset * autofix react/jsx-closing-tag-location * autofix react/no-unknown-property * manually fix react/no-unescaped-entities * maually fix react/jsx-pascal-case * manually fix react/prefer-stateless-function * disable known violations in specific plugins/areas * remove code override --- .eslintrc.js | 201 ++++++++++++++++++ packages/eslint-config-kibana/.eslintrc.js | 1 + packages/eslint-config-kibana/javascript.js | 67 ------ packages/eslint-config-kibana/react.js | 84 ++++++++ packages/eslint-config-kibana/typescript.js | 28 --- .../rendering/rendering_service.test.tsx | 2 +- .../search_bar/components/search_bar.test.tsx | 6 +- .../search_bar/components/search_bar.tsx | 2 +- .../public/views/table/table_row.tsx | 1 + .../pager/tool_bar_pager_buttons.tsx | 4 +- .../public/top_nav_menu/top_nav_menu.test.tsx | 2 +- .../react/inject_ui_capabilities.test.tsx | 1 + .../legacy/inject_ui_capabilities.test.tsx | 1 + .../react/ui_capabilities_provider.tsx | 22 +- .../editors/default/components/agg.test.tsx | 2 +- .../ui_settings/use_ui_setting.test.tsx | 10 +- .../core_plugin_a/public/application.tsx | 2 +- .../plugins/kbn_tp_top_nav/public/top_nav.tsx | 39 ++-- .../search_explorer/public/demo_strategy.tsx | 2 +- .../search_explorer/public/es_strategy.tsx | 2 +- .../search_explorer/public/guide_section.tsx | 2 +- .../search_explorer/public/search_api.tsx | 2 +- .../app/ServiceNodeMetrics/index.tsx | 6 +- .../components/app/TraceOverview/index.tsx | 2 +- .../LocalUIFilters/Filter/FilterBadgeList.tsx | 2 +- .../shared/LocalUIFilters/Filter/index.tsx | 2 +- .../shared/Stacktrace/Variables.tsx | 48 ++--- .../shared/Summary/TransactionSummary.tsx | 2 +- .../autocomplete_field/suggestion_item.tsx | 36 ++-- .../pages/walkthrough/initial/index.tsx | 153 +++++++------ .../workpad_export/flyout/snippets_step.tsx | 2 +- .../api/__tests__/shareable.test.tsx | 18 +- .../components/settings/blacklist_form.tsx | 2 +- .../template_details/tabs/tab_aliases.tsx | 2 +- .../template_details/tabs/tab_mappings.tsx | 2 +- .../template_details/tabs/tab_settings.tsx | 2 +- .../autocomplete_field/suggestion_item.tsx | 38 ++-- .../log_text_stream/loading_item_view.tsx | 43 ++-- .../infra/public/pages/link_to/link_to.tsx | 42 ++-- .../analysis/sections/anomalies/index.tsx | 2 +- .../dimension_panel/popover_editor.tsx | 2 +- .../severity_cell/severity_cell.tsx | 2 +- .../actions_panel/actions_panel.tsx | 2 +- .../additional_section/additional_section.tsx | 2 +- .../components/advanced_view/settings.tsx | 2 +- .../components/validation_step/validation.tsx | 2 +- .../authentication_state_page.tsx | 40 ++-- .../components/account_management_page.tsx | 38 ++-- .../disabled_login_form.tsx | 26 +-- .../field_renderers/field_renderers.tsx | 2 +- .../matrix_over_time/index.test.tsx | 8 +- .../public/components/skeleton_row/index.tsx | 2 +- .../components/stat_items/index.test.tsx | 4 +- .../body/renderers/file_draggable.test.tsx | 4 +- .../confirm_alter_active_space_modal.tsx | 66 +++--- .../components/spaces_header_nav_button.tsx | 30 ++- .../views/nav_control/nav_control_popover.tsx | 4 +- .../reindex/flyout/warnings_step.tsx | 4 +- .../public/components/types.ts | 1 + .../__tests__/empty_state.test.tsx | 4 +- .../adapters/framework/kibana_global_help.tsx | 2 +- .../public/components/general_error.tsx | 2 +- 62 files changed, 632 insertions(+), 504 deletions(-) create mode 100644 packages/eslint-config-kibana/react.js diff --git a/.eslintrc.js b/.eslintrc.js index 22d8e67ea702d..9c81ba6efbd2b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -55,6 +55,207 @@ module.exports = { extends: ['@elastic/eslint-config-kibana', 'plugin:@elastic/eui/recommended'], overrides: [ + /** + * Temporarily disable some react rules for specific plugins, remove in separate PRs + */ + { + files: ['packages/kbn-ui-framework/**/*.{js,ts,tsx}'], + rules: { + 'jsx-a11y/no-onchange': 'off', + }, + }, + { + files: ['src/core/public/application/**/*.{js,ts,tsx}'], + rules: { + 'react/no-danger': 'off', + }, + }, + { + files: ['src/legacy/core_plugins/console/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['src/legacy/core_plugins/data/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['src/legacy/core_plugins/expressions/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['src/legacy/core_plugins/kbn_vislib_vis_types/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['src/legacy/core_plugins/kibana/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/rules-of-hooks': 'off', + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['src/legacy/core_plugins/tile_map/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['src/legacy/core_plugins/vis_type_markdown/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['src/legacy/core_plugins/vis_type_metric/**/*.{js,ts,tsx}'], + rules: { + 'jsx-a11y/click-events-have-key-events': 'off', + }, + }, + { + files: ['src/legacy/core_plugins/vis_type_table/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['src/legacy/core_plugins/vis_type_vega/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['src/legacy/ui/public/vis/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['src/plugins/es_ui_shared/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['src/plugins/eui_utils/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['src/plugins/kibana_react/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/rules-of-hooks': 'off', + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['src/plugins/kibana_utils/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['x-pack/legacy/plugins/canvas/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + 'jsx-a11y/click-events-have-key-events': 'off', + }, + }, + { + files: ['x-pack/legacy/plugins/cross_cluster_replication/**/*.{js,ts,tsx}'], + rules: { + 'jsx-a11y/click-events-have-key-events': 'off', + }, + }, + { + files: ['x-pack/legacy/plugins/graph/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['x-pack/legacy/plugins/index_management/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + 'react-hooks/rules-of-hooks': 'off', + }, + }, + { + files: ['x-pack/legacy/plugins/infra/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + 'react-hooks/rules-of-hooks': 'off', + }, + }, + { + files: ['x-pack/legacy/plugins/lens/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + 'react-hooks/rules-of-hooks': 'off', + }, + }, + { + files: ['x-pack/legacy/plugins/ml/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + 'react-hooks/rules-of-hooks': 'off', + 'jsx-a11y/click-events-have-key-events': 'off', + }, + }, + { + files: ['x-pack/legacy/plugins/monitoring/**/*.{js,ts,tsx}'], + rules: { + 'jsx-a11y/click-events-have-key-events': 'off', + }, + }, + { + files: ['x-pack/legacy/plugins/siem/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + 'react-hooks/rules-of-hooks': 'off', + }, + }, + { + files: ['x-pack/legacy/plugins/snapshot_restore/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['x-pack/legacy/plugins/spaces/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['x-pack/legacy/plugins/transform/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + files: ['x-pack/legacy/plugins/uptime/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/exhaustive-deps': 'off', + 'react-hooks/rules-of-hooks': 'off', + }, + }, + { + files: ['x-pack/legacy/plugins/watcher/**/*.{js,ts,tsx}'], + rules: { + 'react-hooks/rules-of-hooks': 'off', + 'react-hooks/exhaustive-deps': 'off', + }, + }, + /** * Prettier */ diff --git a/packages/eslint-config-kibana/.eslintrc.js b/packages/eslint-config-kibana/.eslintrc.js index 4412ce81368a1..36f0b95c8e69b 100644 --- a/packages/eslint-config-kibana/.eslintrc.js +++ b/packages/eslint-config-kibana/.eslintrc.js @@ -3,6 +3,7 @@ module.exports = { './javascript.js', './typescript.js', './jest.js', + './react.js', ], plugins: ['@kbn/eslint-plugin-eslint'], diff --git a/packages/eslint-config-kibana/javascript.js b/packages/eslint-config-kibana/javascript.js index 8462da5cac801..0c79669c15e73 100644 --- a/packages/eslint-config-kibana/javascript.js +++ b/packages/eslint-config-kibana/javascript.js @@ -1,6 +1,3 @@ -const semver = require('semver'); -const { readdirSync } = require('fs'); -const PKG = require('../../package.json'); const RESTRICTED_GLOBALS = require('./restricted_globals'); const RESTRICTED_MODULES = { paths: ['gulp-util'] }; @@ -16,18 +13,12 @@ module.exports = { plugins: [ 'mocha', 'babel', - 'react', - 'react-hooks', 'import', 'no-unsanitized', 'prefer-object-spread', - 'jsx-a11y', ], settings: { - react: { - version: semver.valid(semver.coerce(PKG.dependencies.react)), - }, 'import/resolver': { '@kbn/eslint-import-resolver-kibana': { forceNode: true, @@ -120,64 +111,6 @@ module.exports = { 'object-curly-spacing': 'off', // overridden with babel/object-curly-spacing 'babel/object-curly-spacing': [ 'error', 'always' ], - 'jsx-quotes': ['error', 'prefer-double'], - 'react/jsx-uses-react': 'error', - 'react/react-in-jsx-scope': 'error', - 'react/jsx-uses-vars': 'error', - 'react/jsx-no-undef': 'error', - 'react/jsx-pascal-case': 'error', - 'react/jsx-closing-bracket-location': ['error', 'line-aligned'], - 'react/jsx-closing-tag-location': 'error', - 'react/jsx-curly-spacing': ['error', 'never', { allowMultiline: true }], - 'react/jsx-indent-props': ['error', 2], - 'react/jsx-max-props-per-line': ['error', { maximum: 1, when: 'multiline' }], - 'react/jsx-no-duplicate-props': ['error', { ignoreCase: true }], - 'react/no-danger': 'error', - 'react/self-closing-comp': 'error', - 'react/jsx-wrap-multilines': ['error', { - declaration: true, - assignment: true, - return: true, - arrow: true, - }], - 'react/jsx-first-prop-new-line': ['error', 'multiline-multiprop'], - 'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks - 'react-hooks/exhaustive-deps': 'warn', // Checks effect dependencies - 'jsx-a11y/accessible-emoji': 'error', - 'jsx-a11y/alt-text': 'error', - 'jsx-a11y/anchor-has-content': 'error', - 'jsx-a11y/aria-activedescendant-has-tabindex': 'error', - 'jsx-a11y/aria-props': 'error', - 'jsx-a11y/aria-proptypes': 'error', - 'jsx-a11y/aria-role': 'error', - 'jsx-a11y/aria-unsupported-elements': 'error', - 'jsx-a11y/heading-has-content': 'error', - 'jsx-a11y/html-has-lang': 'error', - 'jsx-a11y/iframe-has-title': 'error', - 'jsx-a11y/interactive-supports-focus': 'error', - 'jsx-a11y/media-has-caption': 'error', - 'jsx-a11y/mouse-events-have-key-events': 'error', - 'jsx-a11y/no-access-key': 'error', - 'jsx-a11y/no-distracting-elements': 'error', - 'jsx-a11y/no-interactive-element-to-noninteractive-role': 'error', - 'jsx-a11y/no-noninteractive-element-interactions': 'error', - 'jsx-a11y/no-noninteractive-element-to-interactive-role': 'error', - 'jsx-a11y/no-redundant-roles': 'error', - 'jsx-a11y/role-has-required-aria-props': 'error', - 'jsx-a11y/role-supports-aria-props': 'error', - 'jsx-a11y/scope': 'error', - 'jsx-a11y/tabindex-no-positive': 'error', - 'jsx-a11y/label-has-associated-control': 'error', - 'react/jsx-equals-spacing': ['error', 'never'], - 'react/jsx-indent': ['error', 2], - 'react/no-will-update-set-state': 'error', - 'react/no-is-mounted': 'error', - 'react/no-multi-comp': ['error', { ignoreStateless: true }], - 'react/no-unknown-property': 'error', - 'react/prefer-es6-class': ['error', 'always'], - 'react/prefer-stateless-function': ['error', { ignorePureComponents: true }], - 'react/no-unescaped-entities': 'error', - 'mocha/handle-done-callback': 'error', 'mocha/no-exclusive-tests': 'error', diff --git a/packages/eslint-config-kibana/react.js b/packages/eslint-config-kibana/react.js new file mode 100644 index 0000000000000..163bd6ca73a07 --- /dev/null +++ b/packages/eslint-config-kibana/react.js @@ -0,0 +1,84 @@ +const semver = require('semver') +const PKG = require('../../package.json') + +module.exports = { + plugins: [ + 'react', + 'react-hooks', + 'jsx-a11y', + ], + + parserOptions: { + ecmaFeatures: { + jsx: true + } + }, + + settings: { + react: { + version: semver.valid(semver.coerce(PKG.dependencies.react)), + }, + }, + + rules: { + 'jsx-quotes': ['error', 'prefer-double'], + 'react/jsx-uses-react': 'error', + 'react/react-in-jsx-scope': 'error', + 'react/jsx-uses-vars': 'error', + 'react/jsx-no-undef': 'error', + 'react/jsx-pascal-case': 'error', + 'react/jsx-closing-bracket-location': ['error', 'line-aligned'], + 'react/jsx-closing-tag-location': 'error', + 'react/jsx-curly-spacing': ['error', 'never', { allowMultiline: true }], + 'react/jsx-indent-props': ['error', 2], + 'react/jsx-max-props-per-line': ['error', { maximum: 1, when: 'multiline' }], + 'react/jsx-no-duplicate-props': ['error', { ignoreCase: true }], + 'react/no-danger': 'error', + 'react/self-closing-comp': 'error', + 'react/jsx-wrap-multilines': ['error', { + declaration: true, + assignment: true, + return: true, + arrow: true, + }], + 'react/jsx-first-prop-new-line': ['error', 'multiline-multiprop'], + 'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks + 'react-hooks/exhaustive-deps': 'error', // Checks effect dependencies + 'jsx-a11y/accessible-emoji': 'error', + 'jsx-a11y/alt-text': 'error', + 'jsx-a11y/anchor-has-content': 'error', + 'jsx-a11y/aria-activedescendant-has-tabindex': 'error', + 'jsx-a11y/aria-props': 'error', + 'jsx-a11y/aria-proptypes': 'error', + 'jsx-a11y/aria-role': 'error', + 'jsx-a11y/aria-unsupported-elements': 'error', + 'jsx-a11y/click-events-have-key-events': 'error', + 'jsx-a11y/heading-has-content': 'error', + 'jsx-a11y/html-has-lang': 'error', + 'jsx-a11y/iframe-has-title': 'error', + 'jsx-a11y/interactive-supports-focus': 'error', + 'jsx-a11y/label-has-associated-control': 'error', + 'jsx-a11y/media-has-caption': 'error', + 'jsx-a11y/mouse-events-have-key-events': 'error', + 'jsx-a11y/no-access-key': 'error', + 'jsx-a11y/no-distracting-elements': 'error', + 'jsx-a11y/no-interactive-element-to-noninteractive-role': 'error', + 'jsx-a11y/no-noninteractive-element-interactions': 'error', + 'jsx-a11y/no-noninteractive-element-to-interactive-role': 'error', + 'jsx-a11y/no-onchange': 'error', + 'jsx-a11y/no-redundant-roles': 'error', + 'jsx-a11y/role-has-required-aria-props': 'error', + 'jsx-a11y/role-supports-aria-props': 'error', + 'jsx-a11y/scope': 'error', + 'jsx-a11y/tabindex-no-positive': 'error', + 'react/jsx-equals-spacing': ['error', 'never'], + 'react/jsx-indent': ['error', 2], + 'react/no-will-update-set-state': 'error', + 'react/no-is-mounted': 'error', + 'react/no-multi-comp': ['error', { ignoreStateless: true }], + 'react/no-unknown-property': 'error', + 'react/prefer-es6-class': ['error', 'always'], + 'react/prefer-stateless-function': ['error', { ignorePureComponents: true }], + 'react/no-unescaped-entities': 'error', + }, +} diff --git a/packages/eslint-config-kibana/typescript.js b/packages/eslint-config-kibana/typescript.js index 7653c9bb0c332..757616f36180b 100644 --- a/packages/eslint-config-kibana/typescript.js +++ b/packages/eslint-config-kibana/typescript.js @@ -18,7 +18,6 @@ module.exports = { '@typescript-eslint', 'ban', 'import', - 'jsx-a11y', 'prefer-object-spread', ], @@ -171,33 +170,6 @@ module.exports = { {'name': ['test', 'only'], 'message': 'No exclusive tests.'}, ], - 'jsx-a11y/accessible-emoji': 'error', - 'jsx-a11y/alt-text': 'error', - 'jsx-a11y/anchor-has-content': 'error', - 'jsx-a11y/aria-activedescendant-has-tabindex': 'error', - 'jsx-a11y/aria-props': 'error', - 'jsx-a11y/aria-proptypes': 'error', - 'jsx-a11y/aria-role': 'error', - 'jsx-a11y/aria-unsupported-elements': 'error', - 'jsx-a11y/click-events-have-key-events': 'error', - 'jsx-a11y/heading-has-content': 'error', - 'jsx-a11y/html-has-lang': 'error', - 'jsx-a11y/iframe-has-title': 'error', - 'jsx-a11y/interactive-supports-focus': 'error', - 'jsx-a11y/media-has-caption': 'error', - 'jsx-a11y/mouse-events-have-key-events': 'error', - 'jsx-a11y/no-access-key': 'error', - 'jsx-a11y/no-distracting-elements': 'error', - 'jsx-a11y/no-interactive-element-to-noninteractive-role': 'error', - 'jsx-a11y/no-noninteractive-element-interactions': 'error', - 'jsx-a11y/no-noninteractive-element-to-interactive-role': 'error', - 'jsx-a11y/no-onchange': 'error', - 'jsx-a11y/no-redundant-roles': 'error', - 'jsx-a11y/role-has-required-aria-props': 'error', - 'jsx-a11y/role-supports-aria-props': 'error', - 'jsx-a11y/scope': 'error', - 'jsx-a11y/tabindex-no-positive': 'error', - 'jsx-a11y/label-has-associated-control': 'error', 'import/no-default-export': 'error', }, eslintConfigPrettierTypescriptEslintRules diff --git a/src/core/public/rendering/rendering_service.test.tsx b/src/core/public/rendering/rendering_service.test.tsx index 317ab5cc8b855..ed835574a32f9 100644 --- a/src/core/public/rendering/rendering_service.test.tsx +++ b/src/core/public/rendering/rendering_service.test.tsx @@ -34,7 +34,7 @@ describe('RenderingService#start', () => { const chrome = chromeServiceMock.createStartContract(); chrome.getHeaderComponent.mockReturnValue(
Hello chrome!
); const overlays = overlayServiceMock.createStartContract(); - overlays.banners.getComponent.mockReturnValue(
I'm a banner!
); + overlays.banners.getComponent.mockReturnValue(
I'm a banner!
); const injectedMetadata = injectedMetadataServiceMock.createStartContract(); injectedMetadata.getLegacyMode.mockReturnValue(legacyMode); diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx index 62b60fa262cb8..9b77ec369c55b 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx @@ -33,14 +33,14 @@ const timefilterSetupMock = timefilterServiceMock.createSetupContract(); jest.mock('../../../../../data/public', () => { return { - FilterBar: () =>
, - QueryBarInput: () =>
, + FilterBar: () =>
, + QueryBarInput: () =>
, }; }); jest.mock('../../../query/query_bar', () => { return { - QueryBarTopRow: () =>
, + QueryBarTopRow: () =>
, }; }); diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index a03019da4e0d7..c7f8b02caf853 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -368,7 +368,7 @@ class SearchBarUI extends Component { onLoad={this.onLoadSavedQuery} savedQueryService={this.savedQueryService} onClearSavedQuery={this.props.onClearSavedQuery} - > + /> ); let queryBar; diff --git a/src/legacy/core_plugins/kbn_doc_views/public/views/table/table_row.tsx b/src/legacy/core_plugins/kbn_doc_views/public/views/table/table_row.tsx index 1d979f82d39d8..045e8093124a6 100644 --- a/src/legacy/core_plugins/kbn_doc_views/public/views/table/table_row.tsx +++ b/src/legacy/core_plugins/kbn_doc_views/public/views/table/table_row.tsx @@ -100,6 +100,7 @@ export function DocViewTableRow({ * Justification for dangerouslySetInnerHTML: * We just use values encoded by our field formatters */ + // eslint-disable-next-line react/no-danger dangerouslySetInnerHTML={{ __html: value as string }} /> diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx index 6ce987028c197..3edcda8c3bea9 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx @@ -34,7 +34,7 @@ export function ToolBarPagerButtons(props: Props) { disabled={!props.hasPreviousPage} data-test-subj="btnPrevPage" > - +
); diff --git a/src/legacy/core_plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx b/src/legacy/core_plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx index 803119bdac119..4f8c5d11f1916 100644 --- a/src/legacy/core_plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx +++ b/src/legacy/core_plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx @@ -29,7 +29,7 @@ jest.mock('ui/new_platform'); const dataShim = { ui: { - SearchBar: () =>
, + SearchBar: () =>
, }, }; diff --git a/src/legacy/ui/public/capabilities/react/inject_ui_capabilities.test.tsx b/src/legacy/ui/public/capabilities/react/inject_ui_capabilities.test.tsx index dccea2f61d567..ff46b6ec34a86 100644 --- a/src/legacy/ui/public/capabilities/react/inject_ui_capabilities.test.tsx +++ b/src/legacy/ui/public/capabilities/react/inject_ui_capabilities.test.tsx @@ -77,6 +77,7 @@ describe('injectUICapabilities', () => { uiCapabilities: UICapabilities; } + // eslint-disable-next-line react/prefer-stateless-function class MyClassComponent extends React.Component { public render() { return {this.props.uiCapabilities.uiCapability2.nestedProp}; diff --git a/src/legacy/ui/public/capabilities/react/legacy/inject_ui_capabilities.test.tsx b/src/legacy/ui/public/capabilities/react/legacy/inject_ui_capabilities.test.tsx index 21f96cf918afd..76f1dd8016313 100644 --- a/src/legacy/ui/public/capabilities/react/legacy/inject_ui_capabilities.test.tsx +++ b/src/legacy/ui/public/capabilities/react/legacy/inject_ui_capabilities.test.tsx @@ -77,6 +77,7 @@ describe('injectUICapabilities', () => { uiCapabilities: UICapabilities; } + // eslint-disable-next-line react/prefer-stateless-function class MyClassComponent extends React.Component { public render() { return {this.props.uiCapabilities.uiCapability2.nestedProp}; diff --git a/src/legacy/ui/public/capabilities/react/ui_capabilities_provider.tsx b/src/legacy/ui/public/capabilities/react/ui_capabilities_provider.tsx index fb6a1dc55c29f..3871147107439 100644 --- a/src/legacy/ui/public/capabilities/react/ui_capabilities_provider.tsx +++ b/src/legacy/ui/public/capabilities/react/ui_capabilities_provider.tsx @@ -17,22 +17,14 @@ * under the License. */ -import React, { ReactNode } from 'react'; +import React from 'react'; import { UICapabilitiesContext } from './ui_capabilities_context'; import { capabilities } from '..'; -interface Props { - children: ReactNode; -} +export const UICapabilitiesProvider: React.SFC = props => ( + + {props.children} + +); -export class UICapabilitiesProvider extends React.Component { - public static displayName: string = 'UICapabilitiesProvider'; - - public render() { - return ( - - {this.props.children} - - ); - } -} +UICapabilitiesProvider.displayName = 'UICapabilitiesProvider'; diff --git a/src/legacy/ui/public/vis/editors/default/components/agg.test.tsx b/src/legacy/ui/public/vis/editors/default/components/agg.test.tsx index 5dfb14156deee..7806b1c0f78fb 100644 --- a/src/legacy/ui/public/vis/editors/default/components/agg.test.tsx +++ b/src/legacy/ui/public/vis/editors/default/components/agg.test.tsx @@ -156,7 +156,7 @@ describe('DefaultEditorAgg component', () => { it('should add schema component', () => { defaultProps.agg.schema = { - editorComponent: () =>
, + editorComponent: () =>
, } as any; const comp = mount(); diff --git a/src/plugins/kibana_react/public/ui_settings/use_ui_setting.test.tsx b/src/plugins/kibana_react/public/ui_settings/use_ui_setting.test.tsx index cc2a8c3fbe1fc..badf568ccc193 100644 --- a/src/plugins/kibana_react/public/ui_settings/use_ui_setting.test.tsx +++ b/src/plugins/kibana_react/public/ui_settings/use_ui_setting.test.tsx @@ -106,7 +106,7 @@ describe('useUiSetting', () => { }); describe('useUiSetting$', () => { - const TestConsumer$: React.FC<{ + const TestConsumer: React.FC<{ setting: string; newValue?: string; }> = ({ setting, newValue = '' }) => { @@ -126,7 +126,7 @@ describe('useUiSetting$', () => { ReactDOM.render( - + , container ); @@ -143,7 +143,7 @@ describe('useUiSetting$', () => { ReactDOM.render( - + , container ); @@ -159,7 +159,7 @@ describe('useUiSetting$', () => { ReactDOM.render( - + , container ); @@ -174,7 +174,7 @@ describe('useUiSetting$', () => { ReactDOM.render( - + , container ); diff --git a/test/plugin_functional/plugins/core_plugin_a/public/application.tsx b/test/plugin_functional/plugins/core_plugin_a/public/application.tsx index 5d464cf0405d0..7c1406f5b20c3 100644 --- a/test/plugin_functional/plugins/core_plugin_a/public/application.tsx +++ b/test/plugin_functional/plugins/core_plugin_a/public/application.tsx @@ -76,7 +76,7 @@ const PageA = () => ( - Page A's content goes here + Page A's content goes here ); diff --git a/test/plugin_functional/plugins/kbn_tp_top_nav/public/top_nav.tsx b/test/plugin_functional/plugins/kbn_tp_top_nav/public/top_nav.tsx index d56ac5f92db88..8678ab705df9c 100644 --- a/test/plugin_functional/plugins/kbn_tp_top_nav/public/top_nav.tsx +++ b/test/plugin_functional/plugins/kbn_tp_top_nav/public/top_nav.tsx @@ -17,7 +17,7 @@ * under the License. */ -import React, { Component } from 'react'; +import React from 'react'; import { setup as navSetup, start as navStart, @@ -33,22 +33,21 @@ const customExtension = { navSetup.registerMenuItem(customExtension); -export class AppWithTopNav extends Component { - public render() { - const { TopNavMenu } = navStart.ui; - const config = [ - { - id: 'new', - label: 'New Button', - description: 'New Demo', - run() {}, - testId: 'demoNewButton', - }, - ]; - return ( - - Hey - - ); - } -} +export const AppWithTopNav = () => { + const { TopNavMenu } = navStart.ui; + const config = [ + { + id: 'new', + label: 'New Button', + description: 'New Demo', + run() {}, + testId: 'demoNewButton', + }, + ]; + + return ( + + Hey + + ); +}; diff --git a/test/plugin_functional/plugins/search_explorer/public/demo_strategy.tsx b/test/plugin_functional/plugins/search_explorer/public/demo_strategy.tsx index 665b3c9f0ae03..8a0dd31e3595f 100644 --- a/test/plugin_functional/plugins/search_explorer/public/demo_strategy.tsx +++ b/test/plugin_functional/plugins/search_explorer/public/demo_strategy.tsx @@ -127,7 +127,7 @@ export class DemoStrategy extends React.Component { }, ]} demo={this.renderDemo()} - > + /> ); } diff --git a/test/plugin_functional/plugins/search_explorer/public/es_strategy.tsx b/test/plugin_functional/plugins/search_explorer/public/es_strategy.tsx index 20de631bc5605..d35c67191a1f8 100644 --- a/test/plugin_functional/plugins/search_explorer/public/es_strategy.tsx +++ b/test/plugin_functional/plugins/search_explorer/public/es_strategy.tsx @@ -139,7 +139,7 @@ export class EsSearchTest extends React.Component { }, ]} demo={this.renderDemo()} - > + /> ); } diff --git a/test/plugin_functional/plugins/search_explorer/public/guide_section.tsx b/test/plugin_functional/plugins/search_explorer/public/guide_section.tsx index fe67e4097b2a3..1562e33b14c2f 100644 --- a/test/plugin_functional/plugins/search_explorer/public/guide_section.tsx +++ b/test/plugin_functional/plugins/search_explorer/public/guide_section.tsx @@ -110,7 +110,7 @@ export class GuideSection extends React.Component { return code.map((codeBlock, i) => ( - +

{codeBlock.description}

{this.removeLicenseBlock(codeBlock.snippet)} diff --git a/test/plugin_functional/plugins/search_explorer/public/search_api.tsx b/test/plugin_functional/plugins/search_explorer/public/search_api.tsx index 3e4768c870064..8ec6225d1f172 100644 --- a/test/plugin_functional/plugins/search_explorer/public/search_api.tsx +++ b/test/plugin_functional/plugins/search_explorer/public/search_api.tsx @@ -83,5 +83,5 @@ export const SearchApiPage = () => ( ], }, ]} - > + /> ); diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx index 2833b0476428c..79874d6648d0f 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx @@ -113,7 +113,7 @@ export function ServiceNodeMetrics() { ) }} - > + /> ) : ( @@ -129,7 +129,7 @@ export function ServiceNodeMetrics() { {host} } - > + /> {containerId} } - > + /> )} diff --git a/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/index.tsx index 4f14c4ba85932..1ecf72f6fa3fc 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/index.tsx @@ -49,7 +49,7 @@ export function TraceOverview() { return ( - + diff --git a/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/Filter/FilterBadgeList.tsx b/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/Filter/FilterBadgeList.tsx index a1140dcbbb3fc..01d2d56b6d142 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/Filter/FilterBadgeList.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/Filter/FilterBadgeList.tsx @@ -32,7 +32,7 @@ const FilterBadgeList = ({ onRemove, value }: Props) => ( > {val} - + diff --git a/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/Filter/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/Filter/index.tsx index aef8e5747d992..412b92d525aa2 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/Filter/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/Filter/index.tsx @@ -168,7 +168,7 @@ const Filter = ({ }} value={value} /> - + ) : null} diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Variables.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Variables.tsx index 34c46f84c76b9..b66c154e4be31 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Variables.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Variables.tsx @@ -24,29 +24,27 @@ interface Props { vars: IStackframe['vars']; } -export class Variables extends React.Component { - public render() { - if (!this.props.vars) { - return null; - } - - return ( - - - - - - - - - - ); +export const Variables: React.SFC = props => { + if (!props.vars) { + return null; } -} + + return ( + + + + + + + + + + ); +}; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx index 8f91b8cc5e2af..b6e783a00b5d6 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx @@ -61,7 +61,7 @@ const TransactionSummary = ({ ) : null ]; - return ; + return ; }; export { TransactionSummary }; diff --git a/x-pack/legacy/plugins/beats_management/public/components/autocomplete_field/suggestion_item.tsx b/x-pack/legacy/plugins/beats_management/public/components/autocomplete_field/suggestion_item.tsx index 64e7bf6b7f09e..2ae475475829f 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/autocomplete_field/suggestion_item.tsx +++ b/x-pack/legacy/plugins/beats_management/public/components/autocomplete_field/suggestion_item.tsx @@ -18,29 +18,23 @@ interface SuggestionItemProps { suggestion: AutocompleteSuggestion; } -export class SuggestionItem extends React.Component { - public static defaultProps: Partial = { - isSelected: false, - }; +export const SuggestionItem: React.SFC = props => { + const { isSelected, onClick, onMouseEnter, suggestion } = props; - public render() { - const { isSelected, onClick, onMouseEnter, suggestion } = this.props; + return ( + + + + + {suggestion.text} + {suggestion.description} + + ); +}; - return ( - - - - - {suggestion.text} - {suggestion.description} - - ); - } -} +SuggestionItem.defaultProps = { + isSelected: false, +}; const SuggestionItemContainer = styled.div<{ isSelected?: boolean; diff --git a/x-pack/legacy/plugins/beats_management/public/pages/walkthrough/initial/index.tsx b/x-pack/legacy/plugins/beats_management/public/pages/walkthrough/initial/index.tsx index 6ac6643755765..26f2aa80de763 100644 --- a/x-pack/legacy/plugins/beats_management/public/pages/walkthrough/initial/index.tsx +++ b/x-pack/legacy/plugins/beats_management/public/pages/walkthrough/initial/index.tsx @@ -6,93 +6,90 @@ import { EuiBetaBadge, EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; -import React, { Component } from 'react'; +import React from 'react'; import { NoDataLayout } from '../../../components/layouts/no_data'; import { WalkthroughLayout } from '../../../components/layouts/walkthrough'; import { ChildRoutes } from '../../../components/navigation/child_routes'; import { ConnectedLink } from '../../../components/navigation/connected_link'; import { AppPageProps } from '../../../frontend_types'; -class InitialWalkthroughPageComponent extends Component< - AppPageProps & { - intl: InjectedIntl; - } -> { - public render() { - const { intl } = this.props; +type Props = AppPageProps & { + intl: InjectedIntl; +}; - if (this.props.location.pathname === '/walkthrough/initial') { - return ( - - {'Beats central management '} - - - - - } - actionSection={ - - - {' '} - - - } - > -

- -

-
- ); - } +const InitialWalkthroughPageComponent: React.SFC = props => { + if (props.location.pathname === '/walkthrough/initial') { return ( - { - // FIXME implament goto - }} - activePath={this.props.location.pathname} + + {'Beats central management '} + + + + + } + actionSection={ + + + {' '} + + + } > - - +

+ +

+ ); } -} + return ( + { + // FIXME implament goto + }} + activePath={props.location.pathname} + > + + + ); +}; + export const InitialWalkthroughPage = injectI18n(InitialWalkthroughPageComponent); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/snippets_step.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/snippets_step.tsx index f933d9009d367..a65ec1ddba081 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/snippets_step.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/snippets_step.tsx @@ -58,7 +58,7 @@ export const SnippetsStep: FC<{ onCopy: OnCopyFn }> = ({ onCopy }) => ( - kbn-canvas-shareable="canvas" ({strings.getRequiredLabel()}) + kbn-canvas-shareable="canvas" ({strings.getRequiredLabel()}) {strings.getShareableParameterDescription()} diff --git a/x-pack/legacy/plugins/canvas/shareable_runtime/api/__tests__/shareable.test.tsx b/x-pack/legacy/plugins/canvas/shareable_runtime/api/__tests__/shareable.test.tsx index ea944bd30e9b8..a55c87c2d74a2 100644 --- a/x-pack/legacy/plugins/canvas/shareable_runtime/api/__tests__/shareable.test.tsx +++ b/x-pack/legacy/plugins/canvas/shareable_runtime/api/__tests__/shareable.test.tsx @@ -32,7 +32,7 @@ describe('Canvas Shareable Workpad API', () => { test('Placed successfully with default properties', async () => { const container = document.createElement('div'); document.body.appendChild(container); - const wrapper = mount(
, { + const wrapper = mount(
, { attachTo: container, }); @@ -46,11 +46,7 @@ describe('Canvas Shareable Workpad API', () => { const container = document.createElement('div'); document.body.appendChild(container); const wrapper = mount( -
, +
, { attachTo: container, } @@ -69,11 +65,7 @@ describe('Canvas Shareable Workpad API', () => { const container = document.createElement('div'); document.body.appendChild(container); const wrapper = mount( -
, +
, { attachTo: container, } @@ -97,7 +89,7 @@ describe('Canvas Shareable Workpad API', () => { kbn-canvas-width="350" kbn-canvas-height="350" kbn-canvas-url="workpad.json" - >
, + />, { attachTo: container, } @@ -116,7 +108,7 @@ describe('Canvas Shareable Workpad API', () => { const container = document.createElement('div'); document.body.appendChild(container); const wrapper = mount( -
, +
, { attachTo: container, } diff --git a/x-pack/legacy/plugins/graph/public/components/settings/blacklist_form.tsx b/x-pack/legacy/plugins/graph/public/components/settings/blacklist_form.tsx index b321ea3a0d8ed..f7ae04ef9dbcc 100644 --- a/x-pack/legacy/plugins/graph/public/components/settings/blacklist_form.tsx +++ b/x-pack/legacy/plugins/graph/public/components/settings/blacklist_form.tsx @@ -39,7 +39,7 @@ export function BlacklistForm({ }} + values={{ stopSign: }} /> } /> diff --git a/x-pack/legacy/plugins/index_management/public/sections/home/template_list/template_details/tabs/tab_aliases.tsx b/x-pack/legacy/plugins/index_management/public/sections/home/template_list/template_details/tabs/tab_aliases.tsx index d1ed816bebd45..3a1dcbc7e762d 100644 --- a/x-pack/legacy/plugins/index_management/public/sections/home/template_list/template_details/tabs/tab_aliases.tsx +++ b/x-pack/legacy/plugins/index_management/public/sections/home/template_list/template_details/tabs/tab_aliases.tsx @@ -34,6 +34,6 @@ export const TabAliases: React.FunctionComponent = ({ templateDetails }) } iconType="pin" data-test-subj="noAliasesCallout" - > + /> ); }; diff --git a/x-pack/legacy/plugins/index_management/public/sections/home/template_list/template_details/tabs/tab_mappings.tsx b/x-pack/legacy/plugins/index_management/public/sections/home/template_list/template_details/tabs/tab_mappings.tsx index 91f84a5ecf726..9439b0e31ae84 100644 --- a/x-pack/legacy/plugins/index_management/public/sections/home/template_list/template_details/tabs/tab_mappings.tsx +++ b/x-pack/legacy/plugins/index_management/public/sections/home/template_list/template_details/tabs/tab_mappings.tsx @@ -34,6 +34,6 @@ export const TabMappings: React.FunctionComponent = ({ templateDetails }) } iconType="pin" data-test-subj="noMappingsCallout" - > + /> ); }; diff --git a/x-pack/legacy/plugins/index_management/public/sections/home/template_list/template_details/tabs/tab_settings.tsx b/x-pack/legacy/plugins/index_management/public/sections/home/template_list/template_details/tabs/tab_settings.tsx index 6a83b714519d7..0370a89850721 100644 --- a/x-pack/legacy/plugins/index_management/public/sections/home/template_list/template_details/tabs/tab_settings.tsx +++ b/x-pack/legacy/plugins/index_management/public/sections/home/template_list/template_details/tabs/tab_settings.tsx @@ -34,6 +34,6 @@ export const TabSettings: React.FunctionComponent = ({ templateDetails }) } iconType="pin" data-test-subj="noSettingsCallout" - > + /> ); }; diff --git a/x-pack/legacy/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx b/x-pack/legacy/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx index ca67aec9642ac..d7c9876a07a8d 100644 --- a/x-pack/legacy/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx +++ b/x-pack/legacy/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx @@ -11,36 +11,30 @@ import React from 'react'; import { AutocompleteSuggestion } from '../../../../../../../src/plugins/data/public'; import euiStyled from '../../../../../common/eui_styled_components'; -interface SuggestionItemProps { +interface Props { isSelected?: boolean; onClick?: React.MouseEventHandler; onMouseEnter?: React.MouseEventHandler; suggestion: AutocompleteSuggestion; } -export class SuggestionItem extends React.Component { - public static defaultProps: Partial = { - isSelected: false, - }; +export const SuggestionItem: React.SFC = props => { + const { isSelected, onClick, onMouseEnter, suggestion } = props; - public render() { - const { isSelected, onClick, onMouseEnter, suggestion } = this.props; + return ( + + + + + {suggestion.text} + {suggestion.description} + + ); +}; - return ( - - - - - {suggestion.text} - {suggestion.description} - - ); - } -} +SuggestionItem.defaultProps = { + isSelected: false, +}; const SuggestionItemContainer = euiStyled.div<{ isSelected?: boolean; diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index 218b42b48c9b9..752a0d6e27a7f 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -114,29 +114,28 @@ interface ProgressEntryProps { isLoading: boolean; } -class ProgressEntry extends React.PureComponent { - public render() { - const { alignment, children, className, color, isLoading } = this.props; - // NOTE: styled-components seems to make all props in EuiProgress required, so this - // style attribute hacking replaces styled-components here for now until that can be fixed - // see: https://github.com/elastic/eui/issues/1655 - const alignmentStyle = - alignment === 'top' ? { top: 0, bottom: 'initial' } : { top: 'initial', bottom: 0 }; +const ProgressEntry: React.SFC = props => { + const { alignment, children, className, color, isLoading } = props; - return ( - - - {children} - - ); - } -} + // NOTE: styled-components seems to make all props in EuiProgress required, so this + // style attribute hacking replaces styled-components here for now until that can be fixed + // see: https://github.com/elastic/eui/issues/1655 + const alignmentStyle = + alignment === 'top' ? { top: 0, bottom: 'initial' } : { top: 'initial', bottom: 0 }; + + return ( + + + {children} + + ); +}; const ProgressEntryWrapper = euiStyled.div` align-items: center; diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/link_to.tsx b/x-pack/legacy/plugins/infra/public/pages/link_to/link_to.tsx index abb3aa6b26b15..1318e9ca6c2c5 100644 --- a/x-pack/legacy/plugins/infra/public/pages/link_to/link_to.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/link_to/link_to.tsx @@ -16,27 +16,21 @@ interface LinkToPageProps { match: RouteMatch<{}>; } -export class LinkToPage extends React.Component { - public render() { - const { match } = this.props; - - return ( - - - - - - - - ); - } -} +export const LinkToPage: React.SFC = props => ( + + + + + + + +); diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index 745db984afbd5..c340de4ad3848 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -231,7 +231,7 @@ const AnnotationTooltip: React.FunctionComponent<{ details: string }> = ({ detai const renderAnnotationTooltip = (details?: string) => { // Note: Seems to be necessary to get things typed correctly all the way through to elastic-charts components if (!details) { - return
; + return
; } return ; }; diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/popover_editor.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/popover_editor.tsx index b9baa489f3382..2a0fcfc214e40 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/popover_editor.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/popover_editor.tsx @@ -354,7 +354,7 @@ export function PopoverEditor(props: PopoverEditorProps) { defaultMessage: 'To use this function, select a field.', })} iconType="sortUp" - > + /> )} {!incompatibleSelectedOperationType && ParamEditor && ( <> diff --git a/x-pack/legacy/plugins/ml/public/components/anomalies_table/severity_cell/severity_cell.tsx b/x-pack/legacy/plugins/ml/public/components/anomalies_table/severity_cell/severity_cell.tsx index c2d60f94e144d..e74d1a73b3332 100644 --- a/x-pack/legacy/plugins/ml/public/components/anomalies_table/severity_cell/severity_cell.tsx +++ b/x-pack/legacy/plugins/ml/public/components/anomalies_table/severity_cell/severity_cell.tsx @@ -36,7 +36,7 @@ export const SeverityCell: FC = memo(({ score, multiBucketImp + /> {severity} diff --git a/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/components/actions_panel/actions_panel.tsx b/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/components/actions_panel/actions_panel.tsx index c8295a1e3d8db..fca2508cb5d14 100644 --- a/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/components/actions_panel/actions_panel.tsx +++ b/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/components/actions_panel/actions_panel.tsx @@ -64,7 +64,7 @@ export const ActionsPanel: FC = ({ indexPattern }) => { - +
diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/additional_section/additional_section.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/additional_section/additional_section.tsx index 4efac7b9ebf1a..ac2d57ea7c8a9 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/additional_section/additional_section.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/additional_section/additional_section.tsx @@ -31,7 +31,7 @@ export const AdditionalSection: FC = ({ additionalExpanded, setAdditional - + diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_view/settings.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_view/settings.tsx index 429457792d55e..bae9767a39077 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_view/settings.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/advanced_view/settings.tsx @@ -44,7 +44,7 @@ export const AdvancedSettings: FC = ({ setIsValid }) => { - + ); diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/validation_step/validation.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/validation_step/validation.tsx index b60e225cdff4d..a543dbaaf3c5d 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/validation_step/validation.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/validation_step/validation.tsx @@ -78,7 +78,7 @@ export const ValidationStep: FC = ({ setCurrentStep, isCurrentStep }) /> )} - {isCurrentStep === false && } + {isCurrentStep === false && } ); }; diff --git a/x-pack/legacy/plugins/security/public/components/authentication_state_page/authentication_state_page.tsx b/x-pack/legacy/plugins/security/public/components/authentication_state_page/authentication_state_page.tsx index f28696eb84063..2e02275b39611 100644 --- a/x-pack/legacy/plugins/security/public/components/authentication_state_page/authentication_state_page.tsx +++ b/x-pack/legacy/plugins/security/public/components/authentication_state_page/authentication_state_page.tsx @@ -5,32 +5,26 @@ */ import { EuiIcon, EuiSpacer, EuiTitle } from '@elastic/eui'; -import React, { Component } from 'react'; +import React from 'react'; interface Props { title: React.ReactNode; } -export class AuthenticationStatePage extends Component { - public render() { - return ( -
-
-
- - - - - -

{this.props.title}

-
- -
-
-
- {this.props.children} -
+export const AuthenticationStatePage: React.SFC = props => ( +
+
+
+ + + + + +

{props.title}

+
+
- ); - } -} +
+
{props.children}
+
+); diff --git a/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.tsx b/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.tsx index e66b37f8cfe5a..94a42166fbb9e 100644 --- a/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.tsx +++ b/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { EuiPage, EuiPageBody, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui'; -import React, { Component } from 'react'; +import React from 'react'; import { getUserDisplayName, AuthenticatedUser } from '../../../../common/model'; import { ChangePassword } from './change_password'; import { PersonalInfo } from './personal_info'; @@ -13,28 +13,20 @@ interface Props { user: AuthenticatedUser; } -export class AccountManagementPage extends Component { - constructor(props: Props) { - super(props); - } +export const AccountManagementPage: React.SFC = props => ( + + + + +

{getUserDisplayName(props.user)}

+
- public render() { - return ( - - - - -

{getUserDisplayName(this.props.user)}

-
+ - + - - - -
-
-
- ); - } -} + +
+
+
+); diff --git a/x-pack/legacy/plugins/security/public/views/login/components/disabled_login_form/disabled_login_form.tsx b/x-pack/legacy/plugins/security/public/views/login/components/disabled_login_form/disabled_login_form.tsx index 1b28023b6e3e0..012c16c57716e 100644 --- a/x-pack/legacy/plugins/security/public/views/login/components/disabled_login_form/disabled_login_form.tsx +++ b/x-pack/legacy/plugins/security/public/views/login/components/disabled_login_form/disabled_login_form.tsx @@ -5,24 +5,20 @@ */ import { EuiPanel, EuiText } from '@elastic/eui'; -import React, { Component, ReactNode } from 'react'; +import React, { ReactNode } from 'react'; interface Props { title: ReactNode; message: ReactNode; } -export class DisabledLoginForm extends Component { - public render() { - return ( - - -

{this.props.title}

-
- -

{this.props.message}

-
-
- ); - } -} +export const DisabledLoginForm: React.SFC = props => ( + + +

{props.title}

+
+ +

{props.message}

+
+
+); diff --git a/x-pack/legacy/plugins/siem/public/components/field_renderers/field_renderers.tsx b/x-pack/legacy/plugins/siem/public/components/field_renderers/field_renderers.tsx index ffad36c7f9396..5df961dfceeb5 100644 --- a/x-pack/legacy/plugins/siem/public/components/field_renderers/field_renderers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/field_renderers/field_renderers.tsx @@ -271,7 +271,7 @@ export const DefaultFieldRendererOverflow = React.memo + /> )} diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.test.tsx index e52d44173f656..3334447739fc5 100644 --- a/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.test.tsx @@ -12,13 +12,13 @@ import { MatrixOverTimeHistogram } from '.'; jest.mock('@elastic/eui', () => { return { EuiPanel: (children: JSX.Element) => <>{children}, - EuiLoadingContent: () =>
, + EuiLoadingContent: () =>
, }; }); jest.mock('../loader', () => { return { - Loader: () =>
, + Loader: () =>
, }; }); @@ -28,13 +28,13 @@ jest.mock('../../lib/settings/use_kibana_ui_setting', () => { jest.mock('../header_panel', () => { return { - HeaderPanel: () =>
, + HeaderPanel: () =>
, }; }); jest.mock('../charts/barchart', () => { return { - BarChart: () =>
, + BarChart: () =>
, }; }); diff --git a/x-pack/legacy/plugins/siem/public/components/skeleton_row/index.tsx b/x-pack/legacy/plugins/siem/public/components/skeleton_row/index.tsx index 4859dc0d05556..6359063798ba8 100644 --- a/x-pack/legacy/plugins/siem/public/components/skeleton_row/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/skeleton_row/index.tsx @@ -54,7 +54,7 @@ export interface SkeletonRowProps extends CellProps, RowProps { export const SkeletonRow = React.memo( ({ cellColor, cellCount = 4, cellMargin, rowHeight, rowPadding, style }) => { const cells = [...Array(cellCount)].map((_, i) => ( - + )); return ( diff --git a/x-pack/legacy/plugins/siem/public/components/stat_items/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/stat_items/index.test.tsx index 8453ec1cfb5d7..ed3eaa6cf1e91 100644 --- a/x-pack/legacy/plugins/siem/public/components/stat_items/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/stat_items/index.test.tsx @@ -38,11 +38,11 @@ const from = new Date('2019-06-15T06:00:00.000Z').valueOf(); const to = new Date('2019-06-18T06:00:00.000Z').valueOf(); jest.mock('../charts/areachart', () => { - return { AreaChart: () =>
}; + return { AreaChart: () =>
}; }); jest.mock('../charts/barchart', () => { - return { BarChart: () =>
}; + return { BarChart: () =>
}; }); describe('Stat Items Component', () => { diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/file_draggable.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/file_draggable.test.tsx index 68acc58972370..80be9fd339f51 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/file_draggable.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/file_draggable.test.tsx @@ -22,7 +22,7 @@ describe('FileDraggable', () => { eventId="1" fileName="[fileName]" filePath="[filePath]" - > + /> ); expect(wrapper.text()).toEqual('[fileName]in[filePath]'); @@ -38,7 +38,7 @@ describe('FileDraggable', () => { eventId="1" fileName={undefined} filePath={undefined} - > + /> ); expect(wrapper.text()).toEqual(''); diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx index d8e06c68af733..d75b8abbe592e 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx @@ -6,7 +6,7 @@ import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; -import React, { Component } from 'react'; +import React from 'react'; interface Props { onCancel: () => void; @@ -14,39 +14,35 @@ interface Props { intl: InjectedIntl; } -class ConfirmAlterActiveSpaceModalUI extends Component { - public render() { - return ( - - - } - defaultFocusedButton={'confirm'} - cancelButtonText={this.props.intl.formatMessage({ - id: 'xpack.spaces.management.confirmAlterActiveSpaceModal.cancelButton', - defaultMessage: 'Cancel', - })} - confirmButtonText={this.props.intl.formatMessage({ - id: 'xpack.spaces.management.confirmAlterActiveSpaceModal.updateSpaceButton', - defaultMessage: 'Update space', - })} - > -

- -

-
-
- ); - } -} +const ConfirmAlterActiveSpaceModalUI: React.SFC = props => ( + + + } + defaultFocusedButton={'confirm'} + cancelButtonText={props.intl.formatMessage({ + id: 'xpack.spaces.management.confirmAlterActiveSpaceModal.cancelButton', + defaultMessage: 'Cancel', + })} + confirmButtonText={props.intl.formatMessage({ + id: 'xpack.spaces.management.confirmAlterActiveSpaceModal.updateSpaceButton', + defaultMessage: 'Update space', + })} + > +

+ +

+
+
+); export const ConfirmAlterActiveSpaceModal = injectI18n(ConfirmAlterActiveSpaceModalUI); diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_header_nav_button.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_header_nav_button.tsx index 45bb79ee749ee..2fa0f8ca605b0 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_header_nav_button.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_header_nav_button.tsx @@ -8,22 +8,18 @@ import { // @ts-ignore EuiHeaderSectionItemButton, } from '@elastic/eui'; -import React, { Component } from 'react'; +import React from 'react'; import { ButtonProps } from '../types'; -export class SpacesHeaderNavButton extends Component { - public render() { - return ( - - {this.props.linkIcon} - - ); - } -} +export const SpacesHeaderNavButton: React.SFC = props => ( + + {props.linkIcon} + +); diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx index 4669bd3608e4f..f0e365c27b8e7 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx @@ -5,7 +5,7 @@ */ import { EuiAvatar, EuiPopover, PopoverAnchorPosition } from '@elastic/eui'; -import React, { Component, ComponentClass } from 'react'; +import React, { Component } from 'react'; import { Space } from '../../../common/model/space'; import { SpaceAvatar } from '../../components'; import { SpacesManager } from '../../lib/spaces_manager'; @@ -21,7 +21,7 @@ interface Props { space: Space; }; anchorPosition: PopoverAnchorPosition; - buttonClass: ComponentClass; + buttonClass: React.ComponentType; } interface State { diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx index d4602b99c94b2..12e57fdf6c01c 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx @@ -195,8 +195,8 @@ export class WarningsFlyoutStep extends React.Component< values={{ true: true, false: false, - yes: "yes", - on: "on", + yes: "yes", + on: "on", one: 1, }} /> diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/types.ts b/x-pack/legacy/plugins/upgrade_assistant/public/components/types.ts index c2181b783a089..dc31308a1ea34 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/types.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/public/components/types.ts @@ -21,6 +21,7 @@ export interface UpgradeAssistantTabProps { setSelectedTabIndex: (tabIndex: number) => void; } +// eslint-disable-next-line react/prefer-stateless-function export class UpgradeAssistantTabComponent< T extends UpgradeAssistantTabProps = UpgradeAssistantTabProps, S = {} diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/empty_state.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/empty_state.test.tsx index 9bb21a3ef5c71..0077292d91a46 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/empty_state.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/empty_state.test.tsx @@ -36,7 +36,7 @@ describe('EmptyState component', () => { it(`doesn't render child components when count is falsey`, () => { const component = mountWithIntl( -
Shouldn't be rendered
+
Shouldn't be rendered
); expect(component).toMatchSnapshot(); @@ -58,7 +58,7 @@ describe('EmptyState component', () => { ]; const component = mountWithIntl( -
Shouldn't appear...
+
Shouldn't appear...
); expect(component).toMatchSnapshot(); diff --git a/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/kibana_global_help.tsx b/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/kibana_global_help.tsx index 7896764d35963..8e730dcc29310 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/kibana_global_help.tsx +++ b/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/kibana_global_help.tsx @@ -11,7 +11,7 @@ import React from 'react'; export const renderUptimeKibanaGlobalHelp = (docsSiteUrl: string, docLinkVersion: string) => ( - + For Uptime specific information diff --git a/x-pack/plugins/reporting/public/components/general_error.tsx b/x-pack/plugins/reporting/public/components/general_error.tsx index 8a84b3d1741f4..dc65800ecf112 100644 --- a/x-pack/plugins/reporting/public/components/general_error.tsx +++ b/x-pack/plugins/reporting/public/components/general_error.tsx @@ -21,7 +21,7 @@ export const getGeneralErrorToast = (errorText: string, err: Error): ToastInput + /> ), iconType: undefined, From 529945bf424ef5a7fe6bb6d36c9e608f186c6627 Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Tue, 29 Oct 2019 09:22:53 -0400 Subject: [PATCH 09/25] fix query id for tls selector (#49540) --- x-pack/legacy/plugins/siem/public/containers/tls/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/siem/public/containers/tls/index.tsx b/x-pack/legacy/plugins/siem/public/containers/tls/index.tsx index 4fd24b9cb42a5..7abe14ae745c5 100644 --- a/x-pack/legacy/plugins/siem/public/containers/tls/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/tls/index.tsx @@ -143,7 +143,7 @@ class TlsComponentQuery extends QueryTemplatePaginated< const makeMapStateToProps = () => { const getTlsSelector = networkSelectors.tlsSelector(); const getQuery = inputsSelectors.globalQueryByIdSelector(); - return (state: State, { flowTarget, id = `${ID}-${flowTarget}`, type }: OwnProps) => { + return (state: State, { flowTarget, id = ID, type }: OwnProps) => { const { isInspected } = getQuery(state, id); return { ...getTlsSelector(state, type, flowTarget), From 6c4f3cbb5c3488f684aebab943347fae6c8e79c5 Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Tue, 29 Oct 2019 15:54:27 +0100 Subject: [PATCH 10/25] chore: point issue links to choose (#49616) --- README.md | 4 ++-- .../ui/public/vis/default_feedback_message.js | 13 +++++++------ .../legacy/plugins/lens/public/help_menu_util.tsx | 2 +- .../components/about_panel/welcome_content.js | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 2d33cd218b3da..03ce6a6525873 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ _Note: The version numbers below are only examples, meant to illustrate the rela ## Questions? Problems? Suggestions? -- If you've found a bug or want to request a feature, please create a [GitHub Issue](https://github.com/elastic/kibana/issues/new). -Please check to make sure someone else hasn't already created an issue for the same topic. +- If you've found a bug or want to request a feature, please create a [GitHub Issue](https://github.com/elastic/kibana/issues/new/choose). + Please check to make sure someone else hasn't already created an issue for the same topic. - Need help using Kibana? Ask away on our [Kibana Discuss Forum](https://discuss.elastic.co/c/kibana) and a fellow community member or Elastic engineer will be glad to help you out. diff --git a/src/legacy/ui/public/vis/default_feedback_message.js b/src/legacy/ui/public/vis/default_feedback_message.js index f840ba961b9c3..8b8491d397aad 100644 --- a/src/legacy/ui/public/vis/default_feedback_message.js +++ b/src/legacy/ui/public/vis/default_feedback_message.js @@ -19,9 +19,10 @@ import { i18n } from '@kbn/i18n'; -export const defaultFeedbackMessage = i18n.translate('common.ui.vis.defaultFeedbackMessage', - { - defaultMessage: 'Have feedback? Please create an issue in {link}.', - values: { link: 'GitHub' } - } -); +export const defaultFeedbackMessage = i18n.translate('common.ui.vis.defaultFeedbackMessage', { + defaultMessage: 'Have feedback? Please create an issue in {link}.', + values: { + link: + 'GitHub', + }, +}); diff --git a/x-pack/legacy/plugins/lens/public/help_menu_util.tsx b/x-pack/legacy/plugins/lens/public/help_menu_util.tsx index 3865f08862e1c..74de7fe5c4fdc 100644 --- a/x-pack/legacy/plugins/lens/public/help_menu_util.tsx +++ b/x-pack/legacy/plugins/lens/public/help_menu_util.tsx @@ -47,7 +47,7 @@ function HelpMenu() {   - + {i18n.translate('xpack.lens.helpMenu.feedbackLinkText', { defaultMessage: 'Provide feedback for the Lens application', })} diff --git a/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/components/about_panel/welcome_content.js b/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/components/about_panel/welcome_content.js index 28c616d3e74a6..5e9cd19ab388a 100644 --- a/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/components/about_panel/welcome_content.js +++ b/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/components/about_panel/welcome_content.js @@ -133,7 +133,7 @@ export function WelcomeContent() { values={{ githubLink: ( GitHub From f72be334ecc7bb21766f36a6500ac606725ddc7b Mon Sep 17 00:00:00 2001 From: John Dorlus Date: Tue, 29 Oct 2019 09:13:13 -0700 Subject: [PATCH 11/25] Snapshots title ie11 misalignment (#48802) * Removed Flexbox around header and link elements. * Removed unused EUI component * Added class to include the ellipses when name is truncated. * Fixed title in policy_details.tsx * Fixed buttons and title on policy and repository table. * Linting fixes. * Removed unused references. * Added keys to arrays of elements to resolve React errors. --- .../policy_details/policy_details.tsx | 78 +++++++++---------- .../policy_list/policy_table/policy_table.tsx | 56 +++++++------ .../repository_table/repository_table.tsx | 58 +++++++------- .../snapshot_details/snapshot_details.tsx | 42 ++++------ 4 files changed, 103 insertions(+), 131 deletions(-) diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_details/policy_details.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_details/policy_details.tsx index dfc19069b17aa..fc49a1fe464ac 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_details/policy_details.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_details/policy_details.tsx @@ -20,6 +20,7 @@ import { EuiContextMenu, EuiButtonIcon, EuiLink, + EuiSpacer, } from '@elastic/eui'; import { SlmPolicy } from '../../../../../../common/types'; @@ -302,49 +303,40 @@ export const PolicyDetails: React.FunctionComponent = ({ maxWidth={550} > - - - - - -

- {policyName} -

-
- - reload()} - /> - -
-
-
- {policyDetails && policyDetails.policy && policyDetails.policy.inProgress ? ( - - - - - - - - ) : null} -
+ +

+ {policyName}{' '} + reload()} + /> +

+
+ {policyDetails && policyDetails.policy && policyDetails.policy.inProgress ? ( + <> + + + + + + + + ) : null} {renderTabs()}
diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_table/policy_table.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_table/policy_table.tsx index b544666b67159..01fc904906bf1 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_table/policy_table.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_table/policy_table.tsx @@ -327,36 +327,32 @@ export const PolicyTable: React.FunctionComponent = ({ ) : ( undefined ), - toolsRight: ( - - - - - - - - - - - - - ), + toolsRight: [ + + + , + + + , + ], box: { incremental: true, schema: true, diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/repository_list/repository_table/repository_table.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/repository_list/repository_table/repository_table.tsx index 119733d891ab4..e8df533cb3496 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/repository_list/repository_table/repository_table.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/repository_list/repository_table/repository_table.tsx @@ -9,8 +9,6 @@ import { EuiBadge, EuiButton, EuiButtonIcon, - EuiFlexGroup, - EuiFlexItem, EuiInMemoryTable, EuiLink, EuiToolTip, @@ -235,36 +233,32 @@ export const RepositoryTable: React.FunctionComponent = ({ ) : ( undefined ), - toolsRight: ( - - - - - - - - - - - - - ), + toolsRight: [ + + + , + + + , + ], box: { incremental: true, schema: true, diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_details/snapshot_details.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_details/snapshot_details.tsx index e9ce9cbe0ac74..955cade353d2e 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_details/snapshot_details.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_details/snapshot_details.tsx @@ -18,7 +18,6 @@ import { EuiTab, EuiTabs, EuiText, - EuiTitle, } from '@elastic/eui'; import React, { Fragment, useState, useEffect } from 'react'; @@ -256,33 +255,24 @@ export const SnapshotDetails: React.FunctionComponent = ({ maxWidth={550} > - - - -

- {snapshotId} -

-
-
- - - -

- - - -

-
-
-
- + +

+ {snapshotId} +

+

+ + + + + +

+
{tabs}
- {content} {renderFooter()} From ebb494478a4be6274261e51312bdf035931efdd4 Mon Sep 17 00:00:00 2001 From: Chris Davies Date: Tue, 29 Oct 2019 12:48:30 -0400 Subject: [PATCH 12/25] [Lens] Make date histogram interval more customizable (#49217) --- src/legacy/ui/public/agg_types/utils.ts | 2 +- .../editor_frame/editor_frame.test.tsx | 47 ++-- .../editor_frame/editor_frame.tsx | 11 +- .../editor_frame/suggestion_panel.tsx | 7 +- .../lens/public/editor_frame_plugin/mocks.tsx | 2 +- .../public/indexpattern_plugin/auto_date.ts | 29 +- .../dimension_panel/dimension_panel.test.tsx | 1 + .../dimension_panel/dimension_panel.tsx | 2 + .../dimension_panel/popover_editor.tsx | 1 + .../indexpattern_plugin/indexpattern.test.ts | 22 +- .../indexpattern_plugin/indexpattern.tsx | 13 +- .../definitions/date_histogram.test.tsx | 219 ++++++++++----- .../operations/definitions/date_histogram.tsx | 256 ++++++++++++------ .../operations/definitions/index.ts | 2 + .../operations/definitions/terms.test.tsx | 38 +-- .../lens/public/indexpattern_plugin/types.ts | 28 +- x-pack/legacy/plugins/lens/public/types.ts | 22 +- 17 files changed, 463 insertions(+), 239 deletions(-) diff --git a/src/legacy/ui/public/agg_types/utils.ts b/src/legacy/ui/public/agg_types/utils.ts index c6452cf46e0c0..6721262d265f4 100644 --- a/src/legacy/ui/public/agg_types/utils.ts +++ b/src/legacy/ui/public/agg_types/utils.ts @@ -49,7 +49,7 @@ function isValidJson(value: string): boolean { } } -function isValidInterval(value: string, baseInterval: string) { +function isValidInterval(value: string, baseInterval?: string) { if (baseInterval) { return _parseWithBase(value, baseInterval); } else { diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx index fb3f8774be92a..c9d44b315abd9 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx @@ -807,30 +807,38 @@ describe('editor_frame', () => { await waitForPromises(); expect(mockDatasource.getPublicAPI).toHaveBeenCalledWith( - datasource1State, - expect.anything(), - 'first' + expect.objectContaining({ + state: datasource1State, + setState: expect.anything(), + layerId: 'first', + }) ); expect(mockDatasource2.getPublicAPI).toHaveBeenCalledWith( - datasource2State, - expect.anything(), - 'second' + expect.objectContaining({ + state: datasource2State, + setState: expect.anything(), + layerId: 'second', + }) ); expect(mockDatasource2.getPublicAPI).toHaveBeenCalledWith( - datasource2State, - expect.anything(), - 'third' + expect.objectContaining({ + state: datasource2State, + setState: expect.anything(), + layerId: 'third', + }) ); }); it('should give access to the datasource state in the datasource factory function', async () => { const datasourceState = {}; + const dateRange = { fromDate: 'now-1w', toDate: 'now' }; mockDatasource.initialize.mockResolvedValue(datasourceState); mockDatasource.getLayers.mockReturnValue(['first']); mount( { await waitForPromises(); - expect(mockDatasource.getPublicAPI).toHaveBeenCalledWith( - datasourceState, - expect.any(Function), - 'first' - ); + expect(mockDatasource.getPublicAPI).toHaveBeenCalledWith({ + dateRange, + state: datasourceState, + setState: expect.any(Function), + layerId: 'first', + }); }); it('should re-create the public api after state has been set', async () => { @@ -872,15 +881,17 @@ describe('editor_frame', () => { await waitForPromises(); const updatedState = {}; - const setDatasourceState = mockDatasource.getPublicAPI.mock.calls[0][1]; + const setDatasourceState = mockDatasource.getPublicAPI.mock.calls[0][0].setState; act(() => { setDatasourceState(updatedState); }); expect(mockDatasource.getPublicAPI).toHaveBeenLastCalledWith( - updatedState, - expect.any(Function), - 'first' + expect.objectContaining({ + state: updatedState, + setState: expect.any(Function), + layerId: 'first', + }) ); }); }); 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 04c0b22c378d7..8e89d8edc9f23 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 @@ -89,9 +89,9 @@ export function EditorFrame(props: EditorFrameProps) { const layers = datasource.getLayers(datasourceState); layers.forEach(layer => { - const publicAPI = props.datasourceMap[id].getPublicAPI( - datasourceState, - (newState: unknown) => { + const publicAPI = props.datasourceMap[id].getPublicAPI({ + state: datasourceState, + setState: (newState: unknown) => { dispatch({ type: 'UPDATE_DATASOURCE_STATE', datasourceId: id, @@ -99,8 +99,9 @@ export function EditorFrame(props: EditorFrameProps) { clearStagedPreview: true, }); }, - layer - ); + layerId: layer, + dateRange: props.dateRange, + }); datasourceLayers[layer] = publicAPI; }); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/suggestion_panel.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/suggestion_panel.tsx index 6aee215d11591..e29ac84486d0b 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/suggestion_panel.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/suggestion_panel.tsx @@ -367,7 +367,12 @@ function getPreviewExpression( const changedLayers = datasource.getLayers(visualizableState.datasourceState); changedLayers.forEach(layerId => { if (updatedLayerApis[layerId]) { - updatedLayerApis[layerId] = datasource.getPublicAPI(datasourceState, () => {}, layerId); + updatedLayerApis[layerId] = datasource.getPublicAPI({ + layerId, + dateRange: frame.dateRange, + state: datasourceState, + setState: () => {}, + }); } }); } diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx index f349585ce88a4..c3e91c9debcd0 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx @@ -53,7 +53,7 @@ export function createMockDatasource(): DatasourceMock { getDatasourceSuggestionsForField: jest.fn((_state, item) => []), getDatasourceSuggestionsFromCurrentState: jest.fn(_state => []), getPersistableState: jest.fn(), - getPublicAPI: jest.fn((_state, _setState, _layerId) => publicAPIMock), + getPublicAPI: jest.fn().mockReturnValue(publicAPIMock), initialize: jest.fn((_state?) => Promise.resolve()), renderDataPanel: jest.fn(), toExpression: jest.fn((_frame, _state) => null), diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/auto_date.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/auto_date.ts index 359c6d7c35c3a..566e5bece096d 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/auto_date.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/auto_date.ts @@ -10,25 +10,38 @@ import { ExpressionFunction, KibanaContext, } from '../../../../../../src/plugins/expressions/common'; +import { DateRange } from '../../common'; interface LensAutoDateProps { aggConfigs: string; } -export function getAutoInterval(ctx?: KibanaContext | null) { - if (!ctx || !ctx.timeRange) { - return; +export function autoIntervalFromDateRange(dateRange?: DateRange, defaultValue: string = '1h') { + if (!dateRange) { + return defaultValue; } - const { timeRange } = ctx; const buckets = new TimeBuckets(); buckets.setInterval('auto'); buckets.setBounds({ - min: dateMath.parse(timeRange.from), - max: dateMath.parse(timeRange.to, { roundUp: true }), + min: dateMath.parse(dateRange.fromDate), + max: dateMath.parse(dateRange.toDate, { roundUp: true }), }); - return buckets.getInterval(); + return buckets.getInterval().expression; +} + +function autoIntervalFromContext(ctx?: KibanaContext | null) { + if (!ctx || !ctx.timeRange) { + return; + } + + const { timeRange } = ctx; + + return autoIntervalFromDateRange({ + fromDate: timeRange.from, + toDate: timeRange.to, + }); } /** @@ -56,7 +69,7 @@ export const autoDate: ExpressionFunction< }, }, fn(ctx: KibanaContext, args: LensAutoDateProps) { - const interval = getAutoInterval(ctx); + const interval = autoIntervalFromContext(ctx); if (!interval) { return args.aggConfigs; diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.test.tsx index a6ccc6361c3d0..81e0a214f93e3 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.test.tsx @@ -129,6 +129,7 @@ describe('IndexPatternDimensionPanel', () => { dragDropContext, state, setState, + dateRange: { fromDate: 'now-1d', toDate: 'now' }, columnId: 'col1', layerId: 'first', uniqueLabel: 'stuff', diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.tsx index a1242947a87d3..fd4acef41f0bf 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.tsx @@ -23,6 +23,7 @@ import { changeColumn, deleteColumn } from '../state_helpers'; import { isDraggedField, hasField } from '../utils'; import { IndexPatternPrivateState, IndexPatternField } from '../types'; import { trackUiEvent } from '../../lens_ui_telemetry'; +import { DateRange } from '../../../common'; export type IndexPatternDimensionPanelProps = DatasourceDimensionPanelProps & { state: IndexPatternPrivateState; @@ -34,6 +35,7 @@ export type IndexPatternDimensionPanelProps = DatasourceDimensionPanelProps & { layerId: string; http: HttpServiceBase; uniqueLabel: string; + dateRange: DateRange; }; export interface OperationFieldSupportMatrix { diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/popover_editor.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/popover_editor.tsx index 2a0fcfc214e40..ce30e1195f4ca 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/popover_editor.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/popover_editor.tsx @@ -368,6 +368,7 @@ export function PopoverEditor(props: PopoverEditorProps) { savedObjectsClient={props.savedObjectsClient} layerId={layerId} http={props.http} + dateRange={props.dateRange} /> diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.test.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.test.ts index 2edf9dd0b5acb..9fb7be01642e5 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.test.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.test.ts @@ -414,7 +414,15 @@ describe('IndexPattern Data Source', () => { beforeEach(async () => { const initialState = stateFromPersistedState(persistedState); - publicAPI = indexPatternDatasource.getPublicAPI(initialState, () => {}, 'first'); + publicAPI = indexPatternDatasource.getPublicAPI({ + state: initialState, + setState: () => {}, + layerId: 'first', + dateRange: { + fromDate: 'now-30d', + toDate: 'now', + }, + }); }); describe('getTableSpec', () => { @@ -453,8 +461,8 @@ describe('IndexPattern Data Source', () => { suggestedPriority: 2, }, }; - const api = indexPatternDatasource.getPublicAPI( - { + const api = indexPatternDatasource.getPublicAPI({ + state: { ...initialState, layers: { first: { @@ -465,8 +473,12 @@ describe('IndexPattern Data Source', () => { }, }, setState, - 'first' - ); + layerId: 'first', + dateRange: { + fromDate: 'now-1y', + toDate: 'now', + }, + }); api.removeColumnInTableSpec('b'); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx index 1ab6ab6307e8d..bde5ce01aecda 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx @@ -16,6 +16,7 @@ import { DatasourceDataPanelProps, Operation, DatasourceLayerPanelProps, + PublicAPIProps, } from '../types'; import { loadInitialState, changeIndexPattern, changeLayerIndexPattern } from './loader'; import { toExpression } from './to_expression'; @@ -196,11 +197,12 @@ export function getIndexPatternDatasource({ ); }, - getPublicAPI( - state: IndexPatternPrivateState, - setState: StateSetter, - layerId: string - ) { + getPublicAPI({ + state, + setState, + layerId, + dateRange, + }: PublicAPIProps) { const columnLabelMap = uniqueLabels(state.layers); return { @@ -237,6 +239,7 @@ export function getIndexPatternDatasource({ layerId={props.layerId} http={core.http} uniqueLabel={columnLabelMap[props.columnId]} + dateRange={dateRange} {...props} /> diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.test.tsx index 0b2243a01bf0a..d19493d579b64 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { DateHistogramIndexPatternColumn } from './date_histogram'; import { dateHistogramOperation } from '.'; import { shallow } from 'enzyme'; -import { EuiRange, EuiSwitch } from '@elastic/eui'; +import { EuiSwitch } from '@elastic/eui'; import { UiSettingsClientContract, SavedObjectsClientContract, @@ -19,6 +19,26 @@ import { createMockedIndexPattern } from '../../mocks'; import { IndexPatternPrivateState } from '../../types'; jest.mock('ui/new_platform'); +jest.mock('ui/chrome', () => ({ + getUiSettingsClient: () => ({ + get(path: string) { + if (path === 'histogram:maxBars') { + return 10; + } + }, + }), +})); + +const defaultOptions = { + storage: {} as IStorageWrapper, + uiSettings: {} as UiSettingsClientContract, + savedObjectsClient: {} as SavedObjectsClientContract, + dateRange: { + fromDate: 'now-1y', + toDate: 'now', + }, + http: {} as HttpServiceBase, +}; describe('date_histogram', () => { let state: IndexPatternPrivateState; @@ -72,7 +92,7 @@ describe('date_histogram', () => { // Private operationType: 'date_histogram', params: { - interval: 'w', + interval: '42w', }, sourceField: 'timestamp', }, @@ -118,6 +138,27 @@ describe('date_histogram', () => { }; }); + function stateWithInterval(interval: string) { + return ({ + ...state, + layers: { + ...state.layers, + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: { + ...state.layers.first.columns.col1, + params: { + interval, + }, + }, + }, + }, + }, + } as unknown) as IndexPatternPrivateState; + } + describe('buildColumn', () => { it('should create column object with auto interval for primary time field', () => { const column = dateHistogramOperation.buildColumn({ @@ -188,7 +229,7 @@ describe('date_histogram', () => { expect(esAggsConfig).toEqual( expect.objectContaining({ params: expect.objectContaining({ - interval: 'w', + interval: '42w', field: 'timestamp', }), }) @@ -217,7 +258,7 @@ describe('date_histogram', () => { expect(column.label).toContain('start_date'); }); - it('should change interval from auto when switching to a non primary time field', () => { + it('should not change interval from auto when switching to a non primary time field', () => { const oldColumn: DateHistogramIndexPatternColumn = { operationType: 'date_histogram', sourceField: 'timestamp', @@ -233,7 +274,7 @@ describe('date_histogram', () => { const column = dateHistogramOperation.onFieldChange(oldColumn, indexPattern, newDateField); expect(column).toHaveProperty('sourceField', 'start_date'); - expect(column).toHaveProperty('params.interval', 'd'); + expect(column).toHaveProperty('params.interval', 'auto'); expect(column.label).toContain('start_date'); }); }); @@ -281,7 +322,7 @@ describe('date_histogram', () => { ); }); - it('should remove time zone param and normalize interval param', () => { + it('should retain interval', () => { const transferedColumn = dateHistogramOperation.transfer!( { dataType: 'date', @@ -309,7 +350,7 @@ describe('date_histogram', () => { expect(transferedColumn).toEqual( expect.objectContaining({ params: { - interval: 'M', + interval: '20s', timeZone: undefined, }, }) @@ -322,55 +363,49 @@ describe('date_histogram', () => { const setStateSpy = jest.fn(); const instance = shallow( ); - expect(instance.find(EuiRange).prop('value')).toEqual(1); + expect(instance.find('[data-test-subj="lensDateHistogramValue"]').prop('value')).toEqual(42); + expect(instance.find('[data-test-subj="lensDateHistogramUnit"]').prop('value')).toEqual('w'); }); it('should render current value for other index pattern', () => { const setStateSpy = jest.fn(); const instance = shallow( ); - expect(instance.find(EuiRange).prop('value')).toEqual(2); + expect(instance.find('[data-test-subj="lensDateHistogramValue"]').prop('value')).toEqual(''); + expect(instance.find('[data-test-subj="lensDateHistogramUnit"]').prop('value')).toEqual('d'); }); - it('should render disabled switch and no time intervals control for auto interval', () => { + it('should render disabled switch and no time interval control for auto interval', () => { const instance = shallow( ); - expect(instance.find(EuiRange).exists()).toBe(false); + expect(instance.find('[data-test-subj="lensDateHistogramValue"]').exists()).toBeFalsy(); + expect(instance.find('[data-test-subj="lensDateHistogramUnit"]').exists()).toBeFalsy(); expect(instance.find(EuiSwitch).prop('checked')).toBe(false); }); @@ -378,15 +413,12 @@ describe('date_histogram', () => { const setStateSpy = jest.fn(); const instance = shallow( ); instance.find(EuiSwitch).prop('onChange')!({ @@ -394,57 +426,124 @@ describe('date_histogram', () => { } as React.ChangeEvent); expect(setStateSpy).toHaveBeenCalled(); const newState = setStateSpy.mock.calls[0][0]; - expect(newState).toHaveProperty('layers.third.columns.col1.params.interval', 'd'); + expect(newState).toHaveProperty('layers.third.columns.col1.params.interval', '30d'); }); - it('should update state with the interval value', () => { + it('should force calendar values to 1', () => { const setStateSpy = jest.fn(); const instance = shallow( ); + instance.find('[data-test-subj="lensDateHistogramValue"]').prop('onChange')!({ + target: { + value: '2', + }, + } as React.ChangeEvent); + expect(setStateSpy).toHaveBeenCalledWith(stateWithInterval('1w')); + }); - instance.find(EuiRange).prop('onChange')!( - { - target: { - value: '2', - }, - } as React.ChangeEvent, - true + it('should display error if an invalid interval is specified', () => { + const setStateSpy = jest.fn(); + const testState = stateWithInterval('4quid'); + const instance = shallow( + ); - expect(setStateSpy).toHaveBeenCalledWith({ - ...state, - layers: { - ...state.layers, - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - ...state.layers.first.columns.col1, - params: { - interval: 'd', - }, - }, - }, - }, + expect(instance.find('[data-test-subj="lensDateHistogramError"]').exists()).toBeTruthy(); + }); + + it('should not display error if interval value is blank', () => { + const setStateSpy = jest.fn(); + const testState = stateWithInterval('d'); + const instance = shallow( + + ); + expect(instance.find('[data-test-subj="lensDateHistogramError"]').exists()).toBeFalsy(); + }); + + it('should display error if interval value is 0', () => { + const setStateSpy = jest.fn(); + const testState = stateWithInterval('0d'); + const instance = shallow( + + ); + expect(instance.find('[data-test-subj="lensDateHistogramError"]').exists()).toBeTruthy(); + }); + + it('should update the unit', () => { + const setStateSpy = jest.fn(); + const instance = shallow( + + ); + instance.find('[data-test-subj="lensDateHistogramUnit"]').prop('onChange')!({ + target: { + value: 'd', }, - }); + } as React.ChangeEvent); + expect(setStateSpy).toHaveBeenCalledWith(stateWithInterval('42d')); + }); + + it('should update the value', () => { + const setStateSpy = jest.fn(); + const testState = stateWithInterval('42d'); + + const instance = shallow( + + ); + instance.find('[data-test-subj="lensDateHistogramValue"]').prop('onChange')!({ + target: { + value: '9', + }, + } as React.ChangeEvent); + expect(setStateSpy).toHaveBeenCalledWith(stateWithInterval('9d')); }); it('should not render options if they are restricted', () => { const setStateSpy = jest.fn(); const instance = shallow( { columnId="col1" layerId="first" currentColumn={state.layers.first.columns.col1 as DateHistogramIndexPatternColumn} - storage={{} as IStorageWrapper} - uiSettings={{} as UiSettingsClientContract} - savedObjectsClient={{} as SavedObjectsClientContract} - http={{} as HttpServiceBase} /> ); - expect(instance.find(EuiRange)).toHaveLength(0); + expect(instance.find('[data-test-subj="lensDateHistogramValue"]').exists()).toBeFalsy(); }); }); }); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.tsx index e5c00542df7d3..017dccab64607 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.tsx @@ -7,31 +7,29 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiForm, EuiFormRow, EuiRange, EuiSwitch } from '@elastic/eui'; + +// TODO: make this new-platform compatible +import { isValidInterval } from 'ui/agg_types/utils'; + +import { + EuiForm, + EuiFormRow, + EuiSwitch, + EuiFieldNumber, + EuiSelect, + EuiFlexItem, + EuiFlexGroup, + EuiTextColor, + EuiSpacer, +} from '@elastic/eui'; import { updateColumnParam } from '../../state_helpers'; import { OperationDefinition } from '.'; import { FieldBasedIndexPatternColumn } from './column_types'; -import { IndexPattern } from '../../types'; - -type PropType = C extends React.ComponentType ? P : unknown; +import { autoIntervalFromDateRange } from '../../auto_date'; +import { AggregationRestrictions } from '../../types'; const autoInterval = 'auto'; -const supportedIntervals = ['M', 'w', 'd', 'h', 'm']; -const defaultCustomInterval = supportedIntervals[2]; - -// Add ticks to EuiRange component props -const FixedEuiRange = (EuiRange as unknown) as React.ComponentType< - PropType & { - ticks?: Array<{ - label: string; - value: number; - }>; - } ->; - -function supportsAutoInterval(fieldName: string, indexPattern: IndexPattern): boolean { - return indexPattern.timeFieldName ? indexPattern.timeFieldName === fieldName : false; -} +const calendarOnlyIntervals = new Set(['w', 'M', 'q', 'y']); export interface DateHistogramIndexPatternColumn extends FieldBasedIndexPatternColumn { operationType: 'date_histogram'; @@ -59,12 +57,11 @@ export const dateHistogramOperation: OperationDefinition { return { ...oldColumn, label: field.name, sourceField: field.name, - params: { - ...oldColumn.params, - // If we have an "auto" interval but the field we're switching to doesn't support auto intervals - // we use the default custom interval instead - interval: - oldColumn.params.interval === 'auto' && !supportsAutoInterval(field.name, indexPattern) - ? defaultCustomInterval - : oldColumn.params.interval, - }, }; }, toEsAggsConfig: (column, columnId) => ({ @@ -157,7 +135,7 @@ export const dateHistogramOperation: OperationDefinition { + paramEditor: ({ state, setState, currentColumn: currentColumn, layerId, dateRange }) => { const field = currentColumn && state.indexPatterns[state.layers[layerId].indexPatternId].fields.find( @@ -166,20 +144,35 @@ export const dateHistogramOperation: OperationDefinition) { - const interval = ev.target.checked ? defaultCustomInterval : autoInterval; + const value = ev.target.checked ? autoIntervalFromDateRange(dateRange) : autoInterval; + setState(updateColumnParam({ state, layerId, currentColumn, paramName: 'interval', value })); + } + + const setInterval = (newInterval: typeof interval) => { + const isCalendarInterval = calendarOnlyIntervals.has(newInterval.unit); + const value = `${isCalendarInterval ? '1' : newInterval.value}${newInterval.unit || 'd'}`; + setState( - updateColumnParam({ state, layerId, currentColumn, paramName: 'interval', value: interval }) + updateColumnParam({ + state, + layerId, + currentColumn, + value, + paramName: 'interval', + }) ); - } + }; return ( @@ -187,7 +180,7 @@ export const dateHistogramOperation: OperationDefinition {intervalIsRestricted ? ( @@ -209,33 +202,101 @@ export const dateHistogramOperation: OperationDefinition ) : ( - ({ - label: interval, - value: index, - }))} - onChange={( - e: React.ChangeEvent | React.MouseEvent - ) => - setState( - updateColumnParam({ - state, - layerId, - currentColumn, - paramName: 'interval', - value: numericToInterval(Number((e.target as HTMLInputElement).value)), - }) - ) - } - aria-label={i18n.translate('xpack.lens.indexPattern.dateHistogram.interval', { - defaultMessage: 'Time intervals', - })} - /> + <> + + + { + setInterval({ + ...interval, + value: e.target.value, + }); + }} + /> + + + { + setInterval({ + ...interval, + unit: e.target.value, + }); + }} + isInvalid={!isValid} + options={[ + { + value: 'ms', + text: i18n.translate( + 'xpack.lens.indexPattern.dateHistogram.milliseconds', + { + defaultMessage: 'milliseconds', + } + ), + }, + { + value: 's', + text: i18n.translate('xpack.lens.indexPattern.dateHistogram.seconds', { + defaultMessage: 'seconds', + }), + }, + { + value: 'm', + text: i18n.translate('xpack.lens.indexPattern.dateHistogram.minutes', { + defaultMessage: 'minutes', + }), + }, + { + value: 'h', + text: i18n.translate('xpack.lens.indexPattern.dateHistogram.hours', { + defaultMessage: 'hours', + }), + }, + { + value: 'd', + text: i18n.translate('xpack.lens.indexPattern.dateHistogram.days', { + defaultMessage: 'days', + }), + }, + { + value: 'w', + text: i18n.translate('xpack.lens.indexPattern.dateHistogram.week', { + defaultMessage: 'week', + }), + }, + { + value: 'M', + text: i18n.translate('xpack.lens.indexPattern.dateHistogram.month', { + defaultMessage: 'month', + }), + }, + // Quarterly intervals appear to be unsupported by esaggs + { + value: 'y', + text: i18n.translate('xpack.lens.indexPattern.dateHistogram.year', { + defaultMessage: 'year', + }), + }, + ]} + /> + + + {!isValid && ( + <> + + + {i18n.translate('xpack.lens.indexPattern.invalidInterval', { + defaultMessage: 'Invalid interval value', + })} + + + )} + )} )} @@ -243,3 +304,26 @@ export const dateHistogramOperation: OperationDefinition { storage: IStorageWrapper; savedObjectsClient: SavedObjectsClientContract; http: HttpServiceBase; + dateRange: DateRange; } interface BaseOperationDefinitionProps { diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/terms.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/terms.test.tsx index 9630f850dc247..9fba3e205dd45 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/terms.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/terms.test.tsx @@ -20,6 +20,14 @@ import { IndexPatternPrivateState } from '../../types'; jest.mock('ui/new_platform'); +const defaultProps = { + storage: {} as IStorageWrapper, + uiSettings: {} as UiSettingsClientContract, + savedObjectsClient: {} as SavedObjectsClientContract, + dateRange: { fromDate: 'now-1d', toDate: 'now' }, + http: {} as HttpServiceBase, +}; + describe('terms', () => { let state: IndexPatternPrivateState; const InlineOptions = termsOperation.paramEditor!; @@ -324,15 +332,12 @@ describe('terms', () => { const setStateSpy = jest.fn(); const instance = shallow( ); @@ -350,15 +355,12 @@ describe('terms', () => { const setStateSpy = jest.fn(); const instance = shallow( ); @@ -398,15 +400,12 @@ describe('terms', () => { const setStateSpy = jest.fn(); const instance = shallow( ); @@ -422,15 +421,12 @@ describe('terms', () => { const setStateSpy = jest.fn(); const instance = shallow( ); @@ -467,15 +463,12 @@ describe('terms', () => { const setStateSpy = jest.fn(); const instance = shallow( ); @@ -486,15 +479,12 @@ describe('terms', () => { const setStateSpy = jest.fn(); const instance = shallow( ); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts index 3bf3be41f4c89..9ed5083633314 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts @@ -20,25 +20,27 @@ export interface IndexPattern { >; } +export type AggregationRestrictions = Partial< + Record< + string, + { + agg: string; + interval?: number; + fixed_interval?: string; + calendar_interval?: string; + delay?: string; + time_zone?: string; + } + > +>; + export interface IndexPatternField { name: string; type: string; esTypes?: string[]; aggregatable: boolean; searchable: boolean; - aggregationRestrictions?: Partial< - Record< - string, - { - agg: string; - interval?: number; - fixed_interval?: string; - calendar_interval?: string; - delay?: string; - time_zone?: string; - } - > - >; + aggregationRestrictions?: AggregationRestrictions; } export interface IndexPatternLayer { diff --git a/x-pack/legacy/plugins/lens/public/types.ts b/x-pack/legacy/plugins/lens/public/types.ts index 1efa123f4f0a3..93b8f93cbd2ff 100644 --- a/x-pack/legacy/plugins/lens/public/types.ts +++ b/x-pack/legacy/plugins/lens/public/types.ts @@ -13,19 +13,24 @@ import { SavedQuery } from 'src/legacy/core_plugins/data/public'; import { KibanaDatatable } from '../../../../../src/legacy/core_plugins/interpreter/common'; import { DragContextState } from './drag_drop'; import { Document } from './persistence'; +import { DateRange } from '../common'; // eslint-disable-next-line export interface EditorFrameOptions {} export type ErrorCallback = (e: { message: string }) => void; +export interface PublicAPIProps { + state: T; + setState: StateSetter; + layerId: string; + dateRange: DateRange; +} + export interface EditorFrameProps { onError: ErrorCallback; doc?: Document; - dateRange: { - fromDate: string; - toDate: string; - }; + dateRange: DateRange; query: Query; filters: Filter[]; savedQuery?: SavedQuery; @@ -138,7 +143,7 @@ export interface Datasource { getDatasourceSuggestionsForField: (state: T, field: unknown) => Array>; getDatasourceSuggestionsFromCurrentState: (state: T) => Array>; - getPublicAPI: (state: T, setState: StateSetter, layerId: string) => DatasourcePublicAPI; + getPublicAPI: (props: PublicAPIProps) => DatasourcePublicAPI; } /** @@ -171,7 +176,7 @@ export interface DatasourceDataPanelProps { setState: StateSetter; core: Pick; query: Query; - dateRange: FramePublicAPI['dateRange']; + dateRange: DateRange; filters: Filter[]; } @@ -296,10 +301,7 @@ export interface VisualizationSuggestion { export interface FramePublicAPI { datasourceLayers: Record; - dateRange: { - fromDate: string; - toDate: string; - }; + dateRange: DateRange; query: Query; filters: Filter[]; From 8bd0aacc5f5c77b43f80523207502f42ddcaa0ad Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Tue, 29 Oct 2019 12:54:21 -0400 Subject: [PATCH 13/25] Spaces - fix react hook deps (#49618) * fix react hook deps * removing lint override for spaces --- .eslintrc.js | 6 ------ .../copy_saved_objects_to_space/copy_to_space_flyout.tsx | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 9c81ba6efbd2b..a7b712dabb6c2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -229,12 +229,6 @@ module.exports = { 'react-hooks/exhaustive-deps': 'off', }, }, - { - files: ['x-pack/legacy/plugins/spaces/**/*.{js,ts,tsx}'], - rules: { - 'react-hooks/exhaustive-deps': 'off', - }, - }, { files: ['x-pack/legacy/plugins/transform/**/*.{js,ts,tsx}'], rules: { diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout.tsx b/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout.tsx index 4663b73f1cb7e..7461edcff10e9 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout.tsx @@ -72,7 +72,7 @@ export const CopySavedObjectsToSpaceFlyout = (props: Props) => { }), }); }); - }, []); + }, [spacesManager, toastNotifications]); const eligibleSpaces = spaces.filter(space => space.id !== props.activeSpace.id); const [copyInProgress, setCopyInProgress] = useState(false); From 94202a9eab966eb22d643d9d79919e5c8ece1dd5 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Tue, 29 Oct 2019 11:04:24 -0600 Subject: [PATCH 14/25] Upgrade EUI to v14.8.0 (#49375) * Upgrade EUI to v14.8.0 * update snapshot for determinism --- package.json | 2 +- .../__snapshots__/new_vis_modal.test.tsx.snap | 12 ++++++++++-- .../plugins/kbn_tp_run_pipeline/package.json | 2 +- .../kbn_tp_custom_visualizations/package.json | 2 +- .../kbn_tp_embeddable_explorer/package.json | 2 +- .../kbn_tp_sample_panel_action/package.json | 2 +- .../kbn_tp_visualize_embedding/package.json | 2 +- .../__snapshots__/settings.test.tsx.snap | 13 +++++++++++-- .../footer/settings/__tests__/settings.test.tsx | 5 +++++ .../__jest__/components/policy_table.test.js | 2 +- .../__jest__/components/index_table.test.js | 2 +- .../__snapshots__/upload_license.test.js.snap | 6 +++++- .../report_info_button.test.tsx.snap | 12 ++++++++++-- .../open_timeline/timelines_table/index.test.tsx | 6 +++--- x-pack/package.json | 2 +- yarn.lock | 15 ++++++++++----- 16 files changed, 63 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index 01067ab02cb55..4a3b3bfdc2985 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "@elastic/charts": "^13.5.9", "@elastic/datemath": "5.0.2", "@elastic/ems-client": "1.0.5", - "@elastic/eui": "14.7.0", + "@elastic/eui": "14.8.0", "@elastic/filesaver": "1.1.2", "@elastic/good": "8.1.1-kibana2", "@elastic/numeral": "2.3.3", diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/__snapshots__/new_vis_modal.test.tsx.snap b/src/legacy/core_plugins/kibana/public/visualize/wizard/__snapshots__/new_vis_modal.test.tsx.snap index 341a839e9c776..4aa614b68ea23 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/__snapshots__/new_vis_modal.test.tsx.snap +++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/__snapshots__/new_vis_modal.test.tsx.snap @@ -349,7 +349,11 @@ exports[`NewVisModal filter for visualization types should render as expected 1` as="div" autoFocus={true} disabled={false} - lockProps={Object {}} + lockProps={ + Object { + "style": undefined, + } + } noFocusGuards={false} persistentFocus={false} returnFocus={true} @@ -1627,7 +1631,11 @@ exports[`NewVisModal should render as expected 1`] = ` as="div" autoFocus={true} disabled={false} - lockProps={Object {}} + lockProps={ + Object { + "style": undefined, + } + } noFocusGuards={false} persistentFocus={false} returnFocus={true} diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json index 21b49a9823f62..766e6168002c2 100644 --- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json +++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json @@ -7,7 +7,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "14.7.0", + "@elastic/eui": "14.8.0", "react": "^16.8.0", "react-dom": "^16.8.0" } diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json index 7aacfbc22ceee..7c5b6f6be58af 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json @@ -7,7 +7,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "14.7.0", + "@elastic/eui": "14.8.0", "react": "^16.8.0" } } diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json index 80ec89fb0078a..ef472b4026957 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json @@ -8,7 +8,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "14.7.0", + "@elastic/eui": "14.8.0", "react": "^16.8.0" }, "scripts": { diff --git a/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json b/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json index 54aba07079eeb..277bb09ac745c 100644 --- a/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json +++ b/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json @@ -8,7 +8,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "14.7.0", + "@elastic/eui": "14.8.0", "react": "^16.8.0" }, "scripts": { diff --git a/test/plugin_functional/plugins/kbn_tp_visualize_embedding/package.json b/test/plugin_functional/plugins/kbn_tp_visualize_embedding/package.json index 121f69ada329b..f248a7e4d1f2d 100644 --- a/test/plugin_functional/plugins/kbn_tp_visualize_embedding/package.json +++ b/test/plugin_functional/plugins/kbn_tp_visualize_embedding/package.json @@ -7,7 +7,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "14.7.0", + "@elastic/eui": "14.8.0", "react": "^16.8.0", "react-dom": "^16.8.0" } diff --git a/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/settings/__tests__/__snapshots__/settings.test.tsx.snap b/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/settings/__tests__/__snapshots__/settings.test.tsx.snap index baa0b509ccb73..072cf01255a0d 100644 --- a/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/settings/__tests__/__snapshots__/settings.test.tsx.snap +++ b/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/settings/__tests__/__snapshots__/settings.test.tsx.snap @@ -17,8 +17,11 @@ exports[` can navigate Autoplay Settings 1`] = ` data-focus-lock-disabled="disabled" >