From edfc3bc525f829ec8ffb5b0858dad3e124fb8178 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 1 Oct 2020 09:30:25 +0200 Subject: [PATCH 01/27] Initial refactorings --- .../public/application/angular/discover.js | 369 +++++------------- .../angular/helpers/resolve_index_pattern.ts | 74 ++++ .../components/top_nav/get_top_nav_links.ts | 104 +++++ .../components/top_nav/on_save_search.tsx | 80 ++++ ...nel.test.js => open_search_panel.test.tsx} | 0 ..._search_panel.js => open_search_panel.tsx} | 14 +- ...ch_panel.js => show_open_search_panel.tsx} | 0 .../application/helpers/get_sharing_data.ts | 85 ++++ .../discover/public/saved_searches/types.ts | 1 + 9 files changed, 437 insertions(+), 290 deletions(-) create mode 100644 src/plugins/discover/public/application/angular/helpers/resolve_index_pattern.ts create mode 100644 src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts create mode 100644 src/plugins/discover/public/application/components/top_nav/on_save_search.tsx rename src/plugins/discover/public/application/components/top_nav/{open_search_panel.test.js => open_search_panel.test.tsx} (100%) rename src/plugins/discover/public/application/components/top_nav/{open_search_panel.js => open_search_panel.tsx} (94%) rename src/plugins/discover/public/application/components/top_nav/{show_open_search_panel.js => show_open_search_panel.tsx} (100%) create mode 100644 src/plugins/discover/public/application/helpers/get_sharing_data.ts diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 92b96d11723e09..f454feacf2a41e 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -18,8 +18,7 @@ */ import _ from 'lodash'; -import React from 'react'; -import { Subscription, Subject, merge } from 'rxjs'; +import { merge, Subject, Subscription } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; import moment from 'moment'; import dateMath from '@elastic/datemath'; @@ -28,12 +27,11 @@ import { getState, splitState } from './discover_state'; import { RequestAdapter } from '../../../../inspector/public'; import { + connectToQueryState, esFilters, indexPatterns as indexPatternsUtils, - connectToQueryState, syncQueryStateWithUrl, } from '../../../../data/public'; -import { SavedObjectSaveModal, showSaveModal } from '../../../../saved_objects/public'; import { getSortArray, getSortForSearchSource } from './doc_table'; import { createFixedScroll } from './directives/fixed_scroll'; import * as columnActions from './doc_table/actions/columns'; @@ -42,17 +40,33 @@ import { showOpenSearchPanel } from '../components/top_nav/show_open_search_pane import { addHelpMenuToAppChrome } from '../components/help_menu/help_menu_util'; import { discoverResponseHandler } from './response_handler'; import { + getAngularModule, + getHeaderActionMenuMounter, getRequestInspectorStats, getResponseInspectorStats, getServices, - getHeaderActionMenuMounter, getUrlTracker, - unhashUrl, + redirectWhenMissing, subscribeWithScope, tabifyAggResponse, - getAngularModule, - redirectWhenMissing, + unhashUrl, } from '../../kibana_services'; +import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../helpers/breadcrumbs'; +import { validateTimeRange } from '../helpers/validate_time_range'; +import { popularizeField } from '../helpers/popularize_field'; + +import { getIndexPatternId } from '../helpers/get_index_pattern_id'; +import { addFatalError } from '../../../../kibana_legacy/public'; +import { + DEFAULT_COLUMNS_SETTING, + SAMPLE_SIZE_SETTING, + SEARCH_ON_PAGE_LOAD_SETTING, + SORT_DEFAULT_ORDER_SETTING, +} from '../../../common'; +import { resolveIndexPatternLoading } from './helpers/resolve_index_pattern'; +import { getTopNavLinks } from '../components/top_nav/get_top_nav_links'; +import { onSaveSearch } from '../components/top_nav/on_save_search'; +import { getSharingData } from '../helpers/get_sharing_data'; const { core, @@ -68,20 +82,6 @@ const { visualizations, } = getServices(); -import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../helpers/breadcrumbs'; -import { validateTimeRange } from '../helpers/validate_time_range'; -import { popularizeField } from '../helpers/popularize_field'; - -import { getIndexPatternId } from '../helpers/get_index_pattern_id'; -import { addFatalError } from '../../../../kibana_legacy/public'; -import { - DEFAULT_COLUMNS_SETTING, - SAMPLE_SIZE_SETTING, - SORT_DEFAULT_ORDER_SETTING, - SEARCH_ON_PAGE_LOAD_SETTING, - DOC_HIDE_TIME_COLUMN_SETTING, -} from '../../../common'; - const fetchStatuses = { UNINITIALIZED: 'uninitialized', LOADING: 'loading', @@ -196,7 +196,11 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise let inspectorRequest; const savedSearch = $route.current.locals.savedObjects.savedSearch; $scope.searchSource = savedSearch.searchSource; - $scope.indexPattern = resolveIndexPatternLoading(); + $scope.indexPattern = resolveIndexPatternLoading( + $route.current.locals.savedObjects.ip, + $scope.searchSource, + toastNotifications + ); //used for functional testing $scope.fetchCounter = 0; @@ -323,145 +327,73 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise unlistenHistoryBasePath(); }); - const getTopNavLinks = () => { - const newSearch = { - id: 'new', - label: i18n.translate('discover.localMenu.localMenu.newSearchTitle', { - defaultMessage: 'New', - }), - description: i18n.translate('discover.localMenu.newSearchDescription', { - defaultMessage: 'New Search', - }), - run: function () { - $scope.$evalAsync(() => { - history.push('/'); - }); - }, - testId: 'discoverNewButton', - }; + const getFieldCounts = async () => { + // the field counts aren't set until we have the data back, + // so we wait for the fetch to be done before proceeding + if ($scope.fetchStatus === fetchStatuses.COMPLETE) { + return $scope.fieldCounts; + } - const saveSearch = { - id: 'save', - label: i18n.translate('discover.localMenu.saveTitle', { - defaultMessage: 'Save', - }), - description: i18n.translate('discover.localMenu.saveSearchDescription', { - defaultMessage: 'Save Search', - }), - testId: 'discoverSaveButton', - run: async () => { - const onSave = ({ - newTitle, - newCopyOnSave, - isTitleDuplicateConfirmed, - onTitleDuplicate, - }) => { - const currentTitle = savedSearch.title; - savedSearch.title = newTitle; - savedSearch.copyOnSave = newCopyOnSave; - const saveOptions = { - confirmOverwrite: false, - isTitleDuplicateConfirmed, - onTitleDuplicate, - }; - return saveDataSource(saveOptions).then((response) => { - // If the save wasn't successful, put the original values back. - if (!response.id || response.error) { - savedSearch.title = currentTitle; - } else { - resetInitialAppState(); - } - return response; - }); - }; - - const saveModal = ( - {}} - title={savedSearch.title} - showCopyOnSave={!!savedSearch.id} - objectType="search" - description={i18n.translate('discover.localMenu.saveSaveSearchDescription', { - defaultMessage: - 'Save your Discover search so you can use it in visualizations and dashboards', - })} - showDescription={false} - /> - ); - showSaveModal(saveModal, core.i18n.Context); - }, - }; + return await new Promise((resolve) => { + const unwatch = $scope.$watch('fetchStatus', (newValue) => { + if (newValue === fetchStatuses.COMPLETE) { + unwatch(); + resolve($scope.fieldCounts); + } + }); + }); + }; - const openSearch = { - id: 'open', - label: i18n.translate('discover.localMenu.openTitle', { - defaultMessage: 'Open', - }), - description: i18n.translate('discover.localMenu.openSavedSearchDescription', { - defaultMessage: 'Open Saved Search', - }), - testId: 'discoverOpenButton', - run: () => { - showOpenSearchPanel({ - makeUrl: (searchId) => `#/view/${encodeURIComponent(searchId)}`, - I18nContext: core.i18n.Context, - }); - }, - }; + const onNewSearch = function () { + $scope.$evalAsync(() => { + history.push('/'); + }); + }; - const shareSearch = { - id: 'share', - label: i18n.translate('discover.localMenu.shareTitle', { - defaultMessage: 'Share', - }), - description: i18n.translate('discover.localMenu.shareSearchDescription', { - defaultMessage: 'Share Search', - }), - testId: 'shareTopNavButton', - run: async (anchorElement) => { - const sharingData = await this.getSharingData(); - share.toggleShareContextMenu({ - anchorElement, - allowEmbed: false, - allowShortUrl: uiCapabilities.discover.createShortUrl, - shareableUrl: unhashUrl(window.location.href), - objectId: savedSearch.id, - objectType: 'search', - sharingData: { - ...sharingData, - title: savedSearch.title, - }, - isDirty: !savedSearch.id || isAppStateDirty(), - }); - }, - }; + const onOpenSearch = () => { + showOpenSearchPanel({ + makeUrl: (searchId) => `#/view/${encodeURIComponent(searchId)}`, + I18nContext: core.i18n.Context, + }); + }; - const inspectSearch = { - id: 'inspect', - label: i18n.translate('discover.localMenu.inspectTitle', { - defaultMessage: 'Inspect', - }), - description: i18n.translate('discover.localMenu.openInspectorForSearchDescription', { - defaultMessage: 'Open Inspector for search', - }), - testId: 'openInspectorButton', - run() { - getServices().inspector.open(inspectorAdapters, { - title: savedSearch.title, - }); + const onShareSearch = async (anchorElement) => { + const sharingData = await getSharingData( + $scope.searchSource, + $scope.state, + $scope.indexPattern, + config, + getFieldCounts + ); + share.toggleShareContextMenu({ + anchorElement, + allowEmbed: false, + allowShortUrl: uiCapabilities.discover.createShortUrl, + shareableUrl: unhashUrl(window.location.href), + objectId: savedSearch.id, + objectType: 'search', + sharingData: { + ...sharingData, + title: savedSearch.title, }, - }; + isDirty: !savedSearch.id || isAppStateDirty(), + }); + }; - return [ - newSearch, - ...(uiCapabilities.discover.save ? [saveSearch] : []), - openSearch, - shareSearch, - inspectSearch, - ]; + const onInspectSearch = () => { + getServices().inspector.open(inspectorAdapters, { + title: savedSearch.title, + }); }; - $scope.topNavMenu = getTopNavLinks(); + + $scope.topNavMenu = getTopNavLinks( + onNewSearch, + () => onSaveSearch(savedSearch, saveDataSource, resetInitialAppState, core.i18n), + onOpenSearch, + onShareSearch, + onInspectSearch, + uiCapabilities + ); $scope.searchSource .setField('index', $scope.indexPattern) @@ -505,77 +437,6 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise ]); } - const getFieldCounts = async () => { - // the field counts aren't set until we have the data back, - // so we wait for the fetch to be done before proceeding - if ($scope.fetchStatus === fetchStatuses.COMPLETE) { - return $scope.fieldCounts; - } - - return await new Promise((resolve) => { - const unwatch = $scope.$watch('fetchStatus', (newValue) => { - if (newValue === fetchStatuses.COMPLETE) { - unwatch(); - resolve($scope.fieldCounts); - } - }); - }); - }; - - const getSharingDataFields = async (selectedFields, timeFieldName, hideTimeColumn) => { - if (selectedFields.length === 1 && selectedFields[0] === '_source') { - const fieldCounts = await getFieldCounts(); - return { - searchFields: null, - selectFields: _.keys(fieldCounts).sort(), - }; - } - - const fields = - timeFieldName && !hideTimeColumn ? [timeFieldName, ...selectedFields] : selectedFields; - return { - searchFields: fields, - selectFields: fields, - }; - }; - - this.getSharingData = async () => { - const searchSource = $scope.searchSource.createCopy(); - - const { searchFields, selectFields } = await getSharingDataFields( - $scope.state.columns, - $scope.indexPattern.timeFieldName, - config.get(DOC_HIDE_TIME_COLUMN_SETTING) - ); - searchSource.setField('fields', searchFields); - searchSource.setField( - 'sort', - getSortForSearchSource( - $scope.state.sort, - $scope.indexPattern, - config.get(SORT_DEFAULT_ORDER_SETTING) - ) - ); - searchSource.setField('highlight', null); - searchSource.setField('highlightAll', null); - searchSource.setField('aggs', null); - searchSource.setField('size', null); - - const body = await searchSource.getSearchRequestBody(); - return { - searchRequest: { - index: searchSource.getField('index').title, - body, - }, - fields: selectFields, - metaFields: $scope.indexPattern.metaFields, - conflictedTypesFields: $scope.indexPattern.fields - .filter((f) => f.type === 'conflict') - .map((f) => f.name), - indexPatternId: searchSource.getField('index').id, - }; - }; - function getStateDefaults() { const query = $scope.searchSource.getField('query') || data.query.queryString.getDefaultQuery(); return { @@ -1041,62 +902,6 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise }); } - function getIndexPatternWarning(index) { - return i18n.translate('discover.valueIsNotConfiguredIndexPatternIDWarningTitle', { - defaultMessage: '{stateVal} is not a configured index pattern ID', - values: { - stateVal: `"${index}"`, - }, - }); - } - - function resolveIndexPatternLoading() { - const { - loaded: loadedIndexPattern, - stateVal, - stateValFound, - } = $route.current.locals.savedObjects.ip; - - const ownIndexPattern = $scope.searchSource.getOwnField('index'); - - if (ownIndexPattern && !stateVal) { - return ownIndexPattern; - } - - if (stateVal && !stateValFound) { - const warningTitle = getIndexPatternWarning(); - - if (ownIndexPattern) { - toastNotifications.addWarning({ - title: warningTitle, - text: i18n.translate('discover.showingSavedIndexPatternWarningDescription', { - defaultMessage: - 'Showing the saved index pattern: "{ownIndexPatternTitle}" ({ownIndexPatternId})', - values: { - ownIndexPatternTitle: ownIndexPattern.title, - ownIndexPatternId: ownIndexPattern.id, - }, - }), - }); - return ownIndexPattern; - } - - toastNotifications.addWarning({ - title: warningTitle, - text: i18n.translate('discover.showingDefaultIndexPatternWarningDescription', { - defaultMessage: - 'Showing the default index pattern: "{loadedIndexPatternTitle}" ({loadedIndexPatternId})', - values: { - loadedIndexPatternTitle: loadedIndexPattern.title, - loadedIndexPatternId: loadedIndexPattern.id, - }, - }), - }); - } - - return loadedIndexPattern; - } - addHelpMenuToAppChrome(chrome); init(); diff --git a/src/plugins/discover/public/application/angular/helpers/resolve_index_pattern.ts b/src/plugins/discover/public/application/angular/helpers/resolve_index_pattern.ts new file mode 100644 index 00000000000000..8fb7fdc099e3c6 --- /dev/null +++ b/src/plugins/discover/public/application/angular/helpers/resolve_index_pattern.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 { i18n } from '@kbn/i18n'; +import { ToastsStart } from 'kibana/public'; +import { IndexPattern } from '../../../kibana_services'; +import { SearchSource } from '../../../../../data/common'; + +export function resolveIndexPatternLoading( + ip: { loaded: IndexPattern; stateVal: string; stateValFound: string }, + searchSource: SearchSource, + toastNotifications: ToastsStart +) { + const { loaded: loadedIndexPattern, stateVal, stateValFound } = ip; + + const ownIndexPattern = searchSource.getOwnField('index'); + + if (ownIndexPattern && !stateVal) { + return ownIndexPattern; + } + + if (stateVal && !stateValFound) { + const warningTitle = i18n.translate('discover.valueIsNotConfiguredIndexPatternIDWarningTitle', { + defaultMessage: '{stateVal} is not a configured index pattern ID', + values: { + stateVal: `"${stateVal}"`, + }, + }); + + if (ownIndexPattern) { + toastNotifications.addWarning({ + title: warningTitle, + text: i18n.translate('discover.showingSavedIndexPatternWarningDescription', { + defaultMessage: + 'Showing the saved index pattern: "{ownIndexPatternTitle}" ({ownIndexPatternId})', + values: { + ownIndexPatternTitle: ownIndexPattern.title, + ownIndexPatternId: ownIndexPattern.id, + }, + }), + }); + return ownIndexPattern; + } + + toastNotifications.addWarning({ + title: warningTitle, + text: i18n.translate('discover.showingDefaultIndexPatternWarningDescription', { + defaultMessage: + 'Showing the default index pattern: "{loadedIndexPatternTitle}" ({loadedIndexPatternId})', + values: { + loadedIndexPatternTitle: loadedIndexPattern.title, + loadedIndexPatternId: loadedIndexPattern.id, + }, + }), + }); + } + + return loadedIndexPattern; +} diff --git a/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts b/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts new file mode 100644 index 00000000000000..95ed3df1c427f8 --- /dev/null +++ b/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.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 { i18n } from '@kbn/i18n'; +import { Capabilities } from '../../../../../../core/types'; + +export const getTopNavLinks = ( + onNewSearch, + onSaveSearch, + onOpenSearch, + onShareSearch, + onInspectSearch, + uiCapabilities +): { + onNewSearch: () => void; + onSaveSearch: () => void; + onOpenSearch: () => void; + onShareSearch: () => void; + onInspectSearch: () => void; + uiCapabilities: Capabilities; +} => { + const newSearch = { + id: 'new', + label: i18n.translate('discover.localMenu.localMenu.newSearchTitle', { + defaultMessage: 'New', + }), + description: i18n.translate('discover.localMenu.newSearchDescription', { + defaultMessage: 'New Search', + }), + run: onNewSearch, + testId: 'discoverNewButton', + }; + + const saveSearch = { + id: 'save', + label: i18n.translate('discover.localMenu.saveTitle', { + defaultMessage: 'Save', + }), + description: i18n.translate('discover.localMenu.saveSearchDescription', { + defaultMessage: 'Save Search', + }), + testId: 'discoverSaveButton', + run: onSaveSearch, + }; + + const openSearch = { + id: 'open', + label: i18n.translate('discover.localMenu.openTitle', { + defaultMessage: 'Open', + }), + description: i18n.translate('discover.localMenu.openSavedSearchDescription', { + defaultMessage: 'Open Saved Search', + }), + testId: 'discoverOpenButton', + run: onOpenSearch, + }; + + const shareSearch = { + id: 'share', + label: i18n.translate('discover.localMenu.shareTitle', { + defaultMessage: 'Share', + }), + description: i18n.translate('discover.localMenu.shareSearchDescription', { + defaultMessage: 'Share Search', + }), + testId: 'shareTopNavButton', + run: onShareSearch, + }; + + const inspectSearch = { + id: 'inspect', + label: i18n.translate('discover.localMenu.inspectTitle', { + defaultMessage: 'Inspect', + }), + description: i18n.translate('discover.localMenu.openInspectorForSearchDescription', { + defaultMessage: 'Open Inspector for search', + }), + testId: 'openInspectorButton', + run: onInspectSearch, + }; + + return [ + newSearch, + ...(uiCapabilities.discover.save ? [saveSearch] : []), + openSearch, + shareSearch, + inspectSearch, + ]; +}; diff --git a/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx b/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx new file mode 100644 index 00000000000000..b568f3107c25ff --- /dev/null +++ b/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx @@ -0,0 +1,80 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { I18nStart } from 'kibana/public'; +import { i18n } from '@kbn/i18n'; +import { SavedObjectSaveModal, showSaveModal } from '../../../../../saved_objects/public'; +import { SavedSearch } from '../../../saved_searches'; + +export const onSaveSearch = async ( + savedSearch: SavedSearch, + saveDataSource: (args: { + confirmOverwrite: boolean; + isTitleDuplicateConfirmed: boolean; + onTitleDuplicate: () => void; + }) => Promise<{ id: string; error: string }>, + onSaveDataSourceSuccess: () => void, + i18nStart: I18nStart +) => { + const onSave = ({ + newTitle, + newCopyOnSave, + isTitleDuplicateConfirmed, + onTitleDuplicate, + }: { + newTitle: string; + newCopyOnSave: boolean; + isTitleDuplicateConfirmed: boolean; + onTitleDuplicate: () => void; + }) => { + const currentTitle = savedSearch.title; + savedSearch.title = newTitle; + savedSearch.copyOnSave = newCopyOnSave; + const saveOptions = { + confirmOverwrite: false, + isTitleDuplicateConfirmed, + onTitleDuplicate, + }; + const response = await saveDataSource(saveOptions); + // If the save wasn't successful, put the original values back. + if (!response.id || response.error) { + savedSearch.title = currentTitle; + } else { + onSaveDataSourceSuccess(); + } + return response; + }; + + const saveModal = ( + {}} + title={savedSearch.title} + showCopyOnSave={!!savedSearch.id} + objectType="search" + description={i18n.translate('discover.localMenu.saveSaveSearchDescription', { + defaultMessage: + 'Save your Discover search so you can use it in visualizations and dashboards', + })} + showDescription={false} + /> + ); + showSaveModal(saveModal, i18nStart.Context); +}; diff --git a/src/plugins/discover/public/application/components/top_nav/open_search_panel.test.js b/src/plugins/discover/public/application/components/top_nav/open_search_panel.test.tsx similarity index 100% rename from src/plugins/discover/public/application/components/top_nav/open_search_panel.test.js rename to src/plugins/discover/public/application/components/top_nav/open_search_panel.test.tsx diff --git a/src/plugins/discover/public/application/components/top_nav/open_search_panel.js b/src/plugins/discover/public/application/components/top_nav/open_search_panel.tsx similarity index 94% rename from src/plugins/discover/public/application/components/top_nav/open_search_panel.js rename to src/plugins/discover/public/application/components/top_nav/open_search_panel.tsx index 9a6840c29bf1c6..62441f7d827d92 100644 --- a/src/plugins/discover/public/application/components/top_nav/open_search_panel.js +++ b/src/plugins/discover/public/application/components/top_nav/open_search_panel.tsx @@ -16,9 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - import React from 'react'; -import PropTypes from 'prop-types'; import rison from 'rison-node'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -37,7 +35,12 @@ import { getServices } from '../../../kibana_services'; const SEARCH_OBJECT_TYPE = 'search'; -export function OpenSearchPanel(props) { +interface OpenSearchPanelProps { + onClose: () => void; + makeUrl: (id: string) => string; +} + +export function OpenSearchPanel(props: OpenSearchPanelProps) { const { core: { uiSettings, savedObjects }, addBasePath, @@ -102,8 +105,3 @@ export function OpenSearchPanel(props) { ); } - -OpenSearchPanel.propTypes = { - onClose: PropTypes.func.isRequired, - makeUrl: PropTypes.func.isRequired, -}; diff --git a/src/plugins/discover/public/application/components/top_nav/show_open_search_panel.js b/src/plugins/discover/public/application/components/top_nav/show_open_search_panel.tsx similarity index 100% rename from src/plugins/discover/public/application/components/top_nav/show_open_search_panel.js rename to src/plugins/discover/public/application/components/top_nav/show_open_search_panel.tsx diff --git a/src/plugins/discover/public/application/helpers/get_sharing_data.ts b/src/plugins/discover/public/application/helpers/get_sharing_data.ts new file mode 100644 index 00000000000000..10700ed77ddae0 --- /dev/null +++ b/src/plugins/discover/public/application/helpers/get_sharing_data.ts @@ -0,0 +1,85 @@ +/* + * 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 { IUiSettingsClient } from 'kibana/public'; +import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common'; +import { getSortForSearchSource } from '../angular/doc_table'; +import { IndexPattern, SearchSource } from '../../../../data/common'; +import { AppState } from '../angular/discover_state'; + +const getSharingDataFields = async ( + getFieldCounts, + selectedFields, + timeFieldName, + hideTimeColumn +) => { + if (selectedFields.length === 1 && selectedFields[0] === '_source') { + const fieldCounts = await getFieldCounts(); + return { + searchFields: null, + selectFields: Object.keys(fieldCounts).sort(), + }; + } + + const fields = + timeFieldName && !hideTimeColumn ? [timeFieldName, ...selectedFields] : selectedFields; + return { + searchFields: fields, + selectFields: fields, + }; +}; + +export async function getSharingData( + currentSearchSource: SearchSource, + state: AppState, + indexPattern: IndexPattern, + config: IUiSettingsClient, + getFieldCounts: any +) { + const searchSource = currentSearchSource.createCopy(); + + const { searchFields, selectFields } = await getSharingDataFields( + getFieldCounts, + state.columns, + indexPattern.timeFieldName, + config.get(DOC_HIDE_TIME_COLUMN_SETTING) + ); + searchSource.setField('fields', searchFields); + searchSource.setField( + 'sort', + getSortForSearchSource(state.sort, indexPattern, config.get(SORT_DEFAULT_ORDER_SETTING)) + ); + searchSource.setField('highlight', null); + searchSource.setField('highlightAll', null); + searchSource.setField('aggs', null); + searchSource.setField('size', null); + + const body = await searchSource.getSearchRequestBody(); + return { + searchRequest: { + index: searchSource.getField('index').title, + body, + }, + fields: selectFields, + metaFields: indexPattern.metaFields, + conflictedTypesFields: indexPattern.fields + .filter((f) => f.type === 'conflict') + .map((f) => f.name), + indexPatternId: searchSource.getField('index').id, + }; +} diff --git a/src/plugins/discover/public/saved_searches/types.ts b/src/plugins/discover/public/saved_searches/types.ts index 13361cb647ddc0..0111cfd88ab802 100644 --- a/src/plugins/discover/public/saved_searches/types.ts +++ b/src/plugins/discover/public/saved_searches/types.ts @@ -29,6 +29,7 @@ export interface SavedSearch { sort: SortOrder[]; destroy: () => void; lastSavedTitle?: string; + copyOnSave?: boolean; } export interface SavedSearchLoader { get: (id: string) => Promise; From 430d6465c3166a64624a645592dfe6c45f891452 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 1 Oct 2020 18:19:05 +0200 Subject: [PATCH 02/27] Fix async - await issue --- .../application/components/top_nav/on_save_search.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx b/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx index b568f3107c25ff..a27f6da9f7602a 100644 --- a/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx +++ b/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { SavedObjectSaveModal, showSaveModal } from '../../../../../saved_objects/public'; import { SavedSearch } from '../../../saved_searches'; -export const onSaveSearch = async ( +export async function onSaveSearch( savedSearch: SavedSearch, saveDataSource: (args: { confirmOverwrite: boolean; @@ -32,8 +32,8 @@ export const onSaveSearch = async ( }) => Promise<{ id: string; error: string }>, onSaveDataSourceSuccess: () => void, i18nStart: I18nStart -) => { - const onSave = ({ +) { + const onSave = async ({ newTitle, newCopyOnSave, isTitleDuplicateConfirmed, @@ -77,4 +77,4 @@ export const onSaveSearch = async ( /> ); showSaveModal(saveModal, i18nStart.Context); -}; +} From 0db09eae5609195882241a78a671ed4cec0a1685 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 1 Oct 2020 19:01:24 +0200 Subject: [PATCH 03/27] Refactor updateSearchSource to update_search_source.ts --- .../public/application/angular/discover.js | 24 +++------ .../helpers/update_search_source.ts | 54 +++++++++++++++++++ 2 files changed, 62 insertions(+), 16 deletions(-) create mode 100644 src/plugins/discover/public/application/helpers/update_search_source.ts diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index f454feacf2a41e..10dee4a4c6e92d 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -32,7 +32,7 @@ import { indexPatterns as indexPatternsUtils, syncQueryStateWithUrl, } from '../../../../data/public'; -import { getSortArray, getSortForSearchSource } from './doc_table'; +import { getSortArray } from './doc_table'; import { createFixedScroll } from './directives/fixed_scroll'; import * as columnActions from './doc_table/actions/columns'; import indexTemplateLegacy from './discover_legacy.html'; @@ -61,12 +61,12 @@ import { DEFAULT_COLUMNS_SETTING, SAMPLE_SIZE_SETTING, SEARCH_ON_PAGE_LOAD_SETTING, - SORT_DEFAULT_ORDER_SETTING, } from '../../../common'; import { resolveIndexPatternLoading } from './helpers/resolve_index_pattern'; import { getTopNavLinks } from '../components/top_nav/get_top_nav_links'; import { onSaveSearch } from '../components/top_nav/on_save_search'; import { getSharingData } from '../helpers/get_sharing_data'; +import { updateSearchSource } from '../helpers/update_search_source'; const { core, @@ -791,20 +791,12 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise }; $scope.updateDataSource = () => { - const { indexPattern, searchSource } = $scope; - searchSource - .setField('index', $scope.indexPattern) - .setField('size', $scope.opts.sampleSize) - .setField( - 'sort', - getSortForSearchSource( - $scope.state.sort, - indexPattern, - config.get(SORT_DEFAULT_ORDER_SETTING) - ) - ) - .setField('query', data.query.queryString.getQuery() || null) - .setField('filter', filterManager.getFilters()); + updateSearchSource($scope.searchSource, { + indexPattern: $scope.indexPattern, + config, + state: $scope.state, + data, + }); return Promise.resolve(); }; diff --git a/src/plugins/discover/public/application/helpers/update_search_source.ts b/src/plugins/discover/public/application/helpers/update_search_source.ts new file mode 100644 index 00000000000000..cb2937799bd97d --- /dev/null +++ b/src/plugins/discover/public/application/helpers/update_search_source.ts @@ -0,0 +1,54 @@ +/* + * 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 { IUiSettingsClient } from 'kibana/public'; +import { getSortForSearchSource } from '../angular/doc_table'; +import { SAMPLE_SIZE_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common'; +import { IndexPattern, SearchSource } from '../../../../data/common/'; +import { AppState } from '../angular/discover_state'; +import { SortOrder } from '../../saved_searches/types'; +import { DataPublicPluginStart } from '../../../../data/public'; + +export function updateSearchSource( + searchSource: SearchSource, + { + state, + indexPattern, + data, + config, + }: { + config: IUiSettingsClient; + data: DataPublicPluginStart; + indexPattern: IndexPattern; + state: AppState; + } +) { + searchSource + .setField('index', indexPattern) + .setField('size', config.get(SAMPLE_SIZE_SETTING)) + .setField( + 'sort', + getSortForSearchSource( + state.sort as SortOrder[], + indexPattern, + config.get(SORT_DEFAULT_ORDER_SETTING) + ) + ) + .setField('query', data.query.queryString.getQuery() || null) + .setField('filter', data.query.filterManager.getFilters()); +} From c075d5ff69bf8ef84d8eae03dbba0f77c19885f9 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 5 Oct 2020 14:42:17 +0200 Subject: [PATCH 04/27] Fix types & jest tests --- ...s.snap => open_search_panel.test.tsx.snap} | 4 +-- .../components/top_nav/get_top_nav_links.ts | 21 +++++-------- .../top_nav/open_search_panel.test.tsx | 4 +-- .../top_nav/show_open_search_panel.tsx | 9 +++++- .../application/helpers/get_sharing_data.ts | 30 +++++++++++-------- 5 files changed, 37 insertions(+), 31 deletions(-) rename src/plugins/discover/public/application/components/top_nav/__snapshots__/{open_search_panel.test.js.snap => open_search_panel.test.tsx.snap} (96%) diff --git a/src/plugins/discover/public/application/components/top_nav/__snapshots__/open_search_panel.test.js.snap b/src/plugins/discover/public/application/components/top_nav/__snapshots__/open_search_panel.test.tsx.snap similarity index 96% rename from src/plugins/discover/public/application/components/top_nav/__snapshots__/open_search_panel.test.js.snap rename to src/plugins/discover/public/application/components/top_nav/__snapshots__/open_search_panel.test.tsx.snap index 42cd8613b1de01..2c2674b158bfc1 100644 --- a/src/plugins/discover/public/application/components/top_nav/__snapshots__/open_search_panel.test.js.snap +++ b/src/plugins/discover/public/application/components/top_nav/__snapshots__/open_search_panel.test.tsx.snap @@ -3,7 +3,7 @@ exports[`render 1`] = ` void; - onSaveSearch: () => void; - onOpenSearch: () => void; - onShareSearch: () => void; - onInspectSearch: () => void; - uiCapabilities: Capabilities; -} => { + onNewSearch: () => void, + onSaveSearch: () => void, + onOpenSearch: () => void, + onShareSearch: () => void, + onInspectSearch: () => void, + uiCapabilities: Capabilities +) => { const newSearch = { id: 'new', label: i18n.translate('discover.localMenu.localMenu.newSearchTitle', { diff --git a/src/plugins/discover/public/application/components/top_nav/open_search_panel.test.tsx b/src/plugins/discover/public/application/components/top_nav/open_search_panel.test.tsx index 50ab02c8e273d3..4b06964c7bc39e 100644 --- a/src/plugins/discover/public/application/components/top_nav/open_search_panel.test.tsx +++ b/src/plugins/discover/public/application/components/top_nav/open_search_panel.test.tsx @@ -24,7 +24,7 @@ jest.mock('../../../kibana_services', () => { return { getServices: () => ({ core: { uiSettings: {}, savedObjects: {} }, - addBasePath: (path) => path, + addBasePath: (path: string) => path, }), }; }); @@ -32,6 +32,6 @@ jest.mock('../../../kibana_services', () => { import { OpenSearchPanel } from './open_search_panel'; test('render', () => { - const component = shallow( {}} makeUrl={() => {}} />); + const component = shallow(); expect(component).toMatchSnapshot(); }); diff --git a/src/plugins/discover/public/application/components/top_nav/show_open_search_panel.tsx b/src/plugins/discover/public/application/components/top_nav/show_open_search_panel.tsx index e40d700b488853..d9a5cdcb063d3a 100644 --- a/src/plugins/discover/public/application/components/top_nav/show_open_search_panel.tsx +++ b/src/plugins/discover/public/application/components/top_nav/show_open_search_panel.tsx @@ -19,11 +19,18 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import { I18nStart } from 'kibana/public'; import { OpenSearchPanel } from './open_search_panel'; let isOpen = false; -export function showOpenSearchPanel({ makeUrl, I18nContext }) { +export function showOpenSearchPanel({ + makeUrl, + I18nContext, +}: { + makeUrl: (path: string) => string; + I18nContext: I18nStart['Context']; +}) { if (isOpen) { return; } diff --git a/src/plugins/discover/public/application/helpers/get_sharing_data.ts b/src/plugins/discover/public/application/helpers/get_sharing_data.ts index 10700ed77ddae0..fdba020dee5f07 100644 --- a/src/plugins/discover/public/application/helpers/get_sharing_data.ts +++ b/src/plugins/discover/public/application/helpers/get_sharing_data.ts @@ -21,17 +21,18 @@ import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../ import { getSortForSearchSource } from '../angular/doc_table'; import { IndexPattern, SearchSource } from '../../../../data/common'; import { AppState } from '../angular/discover_state'; +import { SortOrder } from '../../saved_searches/types'; const getSharingDataFields = async ( - getFieldCounts, - selectedFields, - timeFieldName, - hideTimeColumn + getFieldCounts: () => Promise>, + selectedFields: string[], + timeFieldName: string, + hideTimeColumn: boolean ) => { if (selectedFields.length === 1 && selectedFields[0] === '_source') { const fieldCounts = await getFieldCounts(); return { - searchFields: null, + searchFields: undefined, selectFields: Object.keys(fieldCounts).sort(), }; } @@ -55,24 +56,29 @@ export async function getSharingData( const { searchFields, selectFields } = await getSharingDataFields( getFieldCounts, - state.columns, - indexPattern.timeFieldName, + state.columns || [], + indexPattern.timeFieldName || '', config.get(DOC_HIDE_TIME_COLUMN_SETTING) ); searchSource.setField('fields', searchFields); searchSource.setField( 'sort', - getSortForSearchSource(state.sort, indexPattern, config.get(SORT_DEFAULT_ORDER_SETTING)) + getSortForSearchSource( + state.sort as SortOrder[], + indexPattern, + config.get(SORT_DEFAULT_ORDER_SETTING) + ) ); searchSource.setField('highlight', null); - searchSource.setField('highlightAll', null); + searchSource.setField('highlightAll', undefined); searchSource.setField('aggs', null); - searchSource.setField('size', null); + searchSource.setField('size', undefined); const body = await searchSource.getSearchRequestBody(); + const index = searchSource.getField('index')!; return { searchRequest: { - index: searchSource.getField('index').title, + index: index.title, body, }, fields: selectFields, @@ -80,6 +86,6 @@ export async function getSharingData( conflictedTypesFields: indexPattern.fields .filter((f) => f.type === 'conflict') .map((f) => f.name), - indexPatternId: searchSource.getField('index').id, + indexPatternId: index.id, }; } From b2803aa87e7fa4a215ac6957330d973c266f1411 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Tue, 6 Oct 2020 09:51:17 +0200 Subject: [PATCH 05/27] Extract persistSavedSearch function --- .../public/application/angular/discover.js | 30 +++++---- .../application/helpers/get_field_counts.ts | 32 +++++++++ .../application/helpers/get_sharing_data.ts | 2 +- .../helpers/persist_saved_search.ts | 67 +++++++++++++++++++ .../discover/public/saved_searches/types.ts | 2 + 5 files changed, 119 insertions(+), 14 deletions(-) create mode 100644 src/plugins/discover/public/application/helpers/get_field_counts.ts create mode 100644 src/plugins/discover/public/application/helpers/persist_saved_search.ts diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 10dee4a4c6e92d..d20f9c009f2d62 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -67,6 +67,7 @@ import { getTopNavLinks } from '../components/top_nav/get_top_nav_links'; import { onSaveSearch } from '../components/top_nav/on_save_search'; import { getSharingData } from '../helpers/get_sharing_data'; import { updateSearchSource } from '../helpers/update_search_source'; +import { persistSavedSearch } from '../helpers/persist_saved_search'; const { core, @@ -566,15 +567,8 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise } }); }); - async function saveDataSource(saveOptions) { - await $scope.updateDataSource(); - - savedSearch.columns = $scope.state.columns; - savedSearch.sort = $scope.state.sort; - - try { - const id = await savedSearch.save(saveOptions); + function onSuccess(id) { $scope.$evalAsync(() => { if (id) { toastNotifications.addSuccess({ @@ -603,8 +597,9 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise } } }); - return { id }; - } catch (saveError) { + } + + function onError(error, savedSearch) { toastNotifications.addDanger({ title: i18n.translate('discover.notifications.notSavedSearchTitle', { defaultMessage: `Search '{savedSearchTitle}' was not saved.`, @@ -612,12 +607,21 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise savedSearchTitle: savedSearch.title, }, }), - text: saveError.message, + text: error.message, }); - return { error: saveError }; } + return persistSavedSearch({ + config, + data, + indexPattern: $scope.indexPattern, + onError, + onSuccess, + searchSource: $scope.searchSource, + savedSearch, + saveOptions, + state: $scope.state, + }); } - $scope.opts.fetch = $scope.fetch = function () { // ignore requests to fetch before the app inits if (!init.complete) return; diff --git a/src/plugins/discover/public/application/helpers/get_field_counts.ts b/src/plugins/discover/public/application/helpers/get_field_counts.ts new file mode 100644 index 00000000000000..13f3d4f8b11743 --- /dev/null +++ b/src/plugins/discover/public/application/helpers/get_field_counts.ts @@ -0,0 +1,32 @@ +/* + * 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 { IndexPattern } from '../../kibana_services'; + +export function getFieldCounts(rows: Array>, indexPattern: IndexPattern) { + const counts: Record = {}; + + for (const hit of rows) { + const fields = Object.keys(indexPattern.flattenHit(hit)); + for (const fieldName of fields) { + counts[fieldName] = (counts[fieldName] || 0) + 1; + } + } + + return counts; +} diff --git a/src/plugins/discover/public/application/helpers/get_sharing_data.ts b/src/plugins/discover/public/application/helpers/get_sharing_data.ts index fdba020dee5f07..cf8220d263d4dd 100644 --- a/src/plugins/discover/public/application/helpers/get_sharing_data.ts +++ b/src/plugins/discover/public/application/helpers/get_sharing_data.ts @@ -50,7 +50,7 @@ export async function getSharingData( state: AppState, indexPattern: IndexPattern, config: IUiSettingsClient, - getFieldCounts: any + getFieldCounts: () => Promise> ) { const searchSource = currentSearchSource.createCopy(); diff --git a/src/plugins/discover/public/application/helpers/persist_saved_search.ts b/src/plugins/discover/public/application/helpers/persist_saved_search.ts new file mode 100644 index 00000000000000..933d7e64c19c13 --- /dev/null +++ b/src/plugins/discover/public/application/helpers/persist_saved_search.ts @@ -0,0 +1,67 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { IUiSettingsClient } from 'kibana/public'; +import { updateSearchSource } from './update_search_source'; +import { SearchSource } from '../../../../data/common/search/search_source'; +import { DataPublicPluginStart, IndexPattern } from '../../../../data/public'; +import { SavedSearch } from '../../saved_searches'; +import { AppState } from '../angular/discover_state'; +import { SortOrder } from '../../saved_searches/types'; +import { SavedObjectSaveOpts } from '../../../../saved_objects/public'; + +export async function persistSavedSearch({ + config, + data, + indexPattern, + onError, + onSuccess, + savedSearch, + saveOptions, + searchSource, + state, +}: { + config: IUiSettingsClient; + data: DataPublicPluginStart; + indexPattern: IndexPattern; + onError: (error: Error, savedSearch: SavedSearch) => void; + onSuccess: (id: string) => void; + savedSearch: SavedSearch; + saveOptions: SavedObjectSaveOpts; + searchSource: SearchSource; + state: AppState; +}) { + updateSearchSource(searchSource, { + indexPattern, + config, + state, + data, + }); + + savedSearch.columns = state.columns || []; + savedSearch.sort = (state.sort as SortOrder[]) || []; + + try { + const id = await savedSearch.save(saveOptions); + onSuccess(id); + return { id }; + } catch (saveError) { + onError(saveError, savedSearch); + return { error: saveError }; + } +} diff --git a/src/plugins/discover/public/saved_searches/types.ts b/src/plugins/discover/public/saved_searches/types.ts index 0111cfd88ab802..0fd78257596e43 100644 --- a/src/plugins/discover/public/saved_searches/types.ts +++ b/src/plugins/discover/public/saved_searches/types.ts @@ -18,6 +18,7 @@ */ import { ISearchSource } from '../../../data/public'; +import { SavedObjectSaveOpts } from '../../../saved_objects/public'; export type SortOrder = [string, string]; export interface SavedSearch { @@ -28,6 +29,7 @@ export interface SavedSearch { columns: string[]; sort: SortOrder[]; destroy: () => void; + save: (saveOptions: SavedObjectSaveOpts) => Promise; lastSavedTitle?: string; copyOnSave?: boolean; } From 34de86199aa7870625b2561bcd3b26b6cc96d2ae Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Sat, 10 Oct 2020 12:14:13 +0200 Subject: [PATCH 06/27] Update types --- .../public/application/helpers/update_search_source.ts | 4 ++-- src/plugins/discover/public/saved_searches/types.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/discover/public/application/helpers/update_search_source.ts b/src/plugins/discover/public/application/helpers/update_search_source.ts index cb2937799bd97d..e97c0a57bbf4c6 100644 --- a/src/plugins/discover/public/application/helpers/update_search_source.ts +++ b/src/plugins/discover/public/application/helpers/update_search_source.ts @@ -19,13 +19,13 @@ import { IUiSettingsClient } from 'kibana/public'; import { getSortForSearchSource } from '../angular/doc_table'; import { SAMPLE_SIZE_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common'; -import { IndexPattern, SearchSource } from '../../../../data/common/'; +import { IndexPattern, ISearchSource } from '../../../../data/common/'; import { AppState } from '../angular/discover_state'; import { SortOrder } from '../../saved_searches/types'; import { DataPublicPluginStart } from '../../../../data/public'; export function updateSearchSource( - searchSource: SearchSource, + searchSource: ISearchSource, { state, indexPattern, diff --git a/src/plugins/discover/public/saved_searches/types.ts b/src/plugins/discover/public/saved_searches/types.ts index 0fd78257596e43..d5e5dd765a364d 100644 --- a/src/plugins/discover/public/saved_searches/types.ts +++ b/src/plugins/discover/public/saved_searches/types.ts @@ -17,14 +17,14 @@ * under the License. */ -import { ISearchSource } from '../../../data/public'; +import { SearchSource } from '../../../data/public'; import { SavedObjectSaveOpts } from '../../../saved_objects/public'; export type SortOrder = [string, string]; export interface SavedSearch { readonly id: string; title: string; - searchSource: ISearchSource; + searchSource: SearchSource; description?: string; columns: string[]; sort: SortOrder[]; From d4b246ba6e5afc3848bf98774dcbde3d45a5e851 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Sat, 10 Oct 2020 22:24:49 +0200 Subject: [PATCH 07/27] Refactor --- .../public/__mocks__/index_pattern.ts | 26 +++ .../public/__mocks__/index_patterns.ts | 34 ++++ .../discover/public/__mocks__/saved_search.ts | 39 ++++ .../public/application/angular/discover.js | 173 +++--------------- .../angular/discover_state.test.ts | 4 +- .../application/angular/discover_state.ts | 15 +- .../components/discover_legacy.tsx | 17 +- .../top_nav/get_top_nav_links.test.ts | 85 +++++++++ .../components/top_nav/get_top_nav_links.ts | 79 ++++++-- .../components/top_nav/on_save_search.tsx | 102 +++++++++-- .../helpers/persist_saved_search.ts | 5 +- .../helpers/set_breadcrumbs_title.ts | 47 +++++ .../helpers/update_search_source.ts | 6 +- 13 files changed, 433 insertions(+), 199 deletions(-) create mode 100644 src/plugins/discover/public/__mocks__/index_pattern.ts create mode 100644 src/plugins/discover/public/__mocks__/index_patterns.ts create mode 100644 src/plugins/discover/public/__mocks__/saved_search.ts create mode 100644 src/plugins/discover/public/application/components/top_nav/get_top_nav_links.test.ts create mode 100644 src/plugins/discover/public/application/helpers/set_breadcrumbs_title.ts diff --git a/src/plugins/discover/public/__mocks__/index_pattern.ts b/src/plugins/discover/public/__mocks__/index_pattern.ts new file mode 100644 index 00000000000000..4079c5a13e6c0e --- /dev/null +++ b/src/plugins/discover/public/__mocks__/index_pattern.ts @@ -0,0 +1,26 @@ +/* + * 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 { IndexPattern } from '../kibana_services'; + +export const indexPatternMock = ({ + id: 'the-index-pattern-id', + title: 'the-index-pattern-title', + fields: [], +} as unknown) as IndexPattern; diff --git a/src/plugins/discover/public/__mocks__/index_patterns.ts b/src/plugins/discover/public/__mocks__/index_patterns.ts new file mode 100644 index 00000000000000..1281640a0fa696 --- /dev/null +++ b/src/plugins/discover/public/__mocks__/index_patterns.ts @@ -0,0 +1,34 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { IndexPatternsContract } from '../kibana_services'; + +export const indexPatternsMock = (new (class { + fieldFormats = []; + config = {}; + savedObjectsClient = {}; + refreshSavedObjectsCache = {}; + clearCache = jest.fn(); + get = jest.fn(); + getDefault = jest.fn(); + getFields = jest.fn(); + getIds = jest.fn(); + getTitles = jest.fn(); + make = jest.fn(); +})() as unknown) as IndexPatternsContract; diff --git a/src/plugins/discover/public/__mocks__/saved_search.ts b/src/plugins/discover/public/__mocks__/saved_search.ts new file mode 100644 index 00000000000000..7501c25f514e86 --- /dev/null +++ b/src/plugins/discover/public/__mocks__/saved_search.ts @@ -0,0 +1,39 @@ +/* + * 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 const savedSearchMock: any = { + id: 'the-saved-search-id', + type: 'search', + attributes: { + title: 'the-saved-search-title', + kibanaSavedObjectMeta: { + searchSourceJSON: + '{"highlightAll":true,"version":true,"query":{"query":"foo : \\"bar\\" ","language":"kuery"},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}', + }, + }, + references: [ + { + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + type: 'index-pattern', + id: 'the-index-pattern-id', + }, + ], + migrationVersion: { search: '7.5.0' }, + error: undefined, +}; diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 71ec102a473b88..e4d226ca79bb18 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -33,10 +33,8 @@ import { syncQueryStateWithUrl, } from '../../../../data/public'; import { getSortArray } from './doc_table'; -import { createFixedScroll } from './directives/fixed_scroll'; import * as columnActions from './doc_table/actions/columns'; import indexTemplateLegacy from './discover_legacy.html'; -import { showOpenSearchPanel } from '../components/top_nav/show_open_search_panel'; import { addHelpMenuToAppChrome } from '../components/help_menu/help_menu_util'; import { discoverResponseHandler } from './response_handler'; import { @@ -49,7 +47,6 @@ import { redirectWhenMissing, subscribeWithScope, tabifyAggResponse, - unhashUrl, } from '../../kibana_services'; import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../helpers/breadcrumbs'; import { validateTimeRange } from '../helpers/validate_time_range'; @@ -59,16 +56,16 @@ import { getIndexPatternId } from '../helpers/get_index_pattern_id'; import { addFatalError } from '../../../../kibana_legacy/public'; import { DEFAULT_COLUMNS_SETTING, + MODIFY_COLUMNS_ON_SWITCH, SAMPLE_SIZE_SETTING, SEARCH_ON_PAGE_LOAD_SETTING, - MODIFY_COLUMNS_ON_SWITCH, } from '../../../common'; import { resolveIndexPatternLoading } from './helpers/resolve_index_pattern'; import { getTopNavLinks } from '../components/top_nav/get_top_nav_links'; -import { onSaveSearch } from '../components/top_nav/on_save_search'; -import { getSharingData } from '../helpers/get_sharing_data'; import { updateSearchSource } from '../helpers/update_search_source'; -import { persistSavedSearch } from '../helpers/persist_saved_search'; +import { setBreadcrumbsTitle } from '../helpers/set_breadcrumbs_title'; + +const services = getServices(); const { core, @@ -77,12 +74,11 @@ const { history: getHistory, indexPatterns, filterManager, - share, timefilter, toastNotifications, uiSettings: config, visualizations, -} = getServices(); +} = services; const fetchStatuses = { UNINITIALIZED: 'uninitialized', @@ -192,6 +188,7 @@ app.directive('discoverApp', function () { }); function discoverController($element, $route, $scope, $timeout, $window, Promise, uiCapabilities) { + console.log(123); const { isDefault: isDefaultType } = indexPatternsUtils; const subscriptions = new Subscription(); const $fetchObservable = new Subject(); @@ -212,22 +209,22 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise const history = getHistory(); + const state = getState({ + getStateDefaults, + storeInSessionStorage: config.get('state:storeInSessionStorage'), + history, + toasts: core.notifications.toasts, + }); const { appStateContainer, startSync: startStateSync, stopSync: stopStateSync, setAppState, replaceUrlAppState, - isAppStateDirty, kbnUrlStateStorage, getPreviousAppState, - resetInitialAppState, - } = getState({ - defaultAppState: getStateDefaults(), - storeInSessionStorage: config.get('state:storeInSessionStorage'), - history, - toasts: core.notifications.toasts, - }); + } = state; + if (appStateContainer.getState().index !== $scope.indexPattern.id) { //used index pattern is different than the given by url/state which is invalid setAppState({ index: $scope.indexPattern.id }); @@ -361,56 +358,19 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise }); }; - const onNewSearch = function () { - $scope.$evalAsync(() => { - history.push('/'); - }); - }; - - const onOpenSearch = () => { - showOpenSearchPanel({ - makeUrl: (searchId) => `#/view/${encodeURIComponent(searchId)}`, - I18nContext: core.i18n.Context, - }); - }; - - const onShareSearch = async (anchorElement) => { - const sharingData = await getSharingData( - $scope.searchSource, - $scope.state, - $scope.indexPattern, - config, - getFieldCounts - ); - share.toggleShareContextMenu({ - anchorElement, - allowEmbed: false, - allowShortUrl: uiCapabilities.discover.createShortUrl, - shareableUrl: unhashUrl(window.location.href), - objectId: savedSearch.id, - objectType: 'search', - sharingData: { - ...sharingData, - title: savedSearch.title, - }, - isDirty: !savedSearch.id || isAppStateDirty(), - }); - }; - - const onInspectSearch = () => { - getServices().inspector.open(inspectorAdapters, { - title: savedSearch.title, - }); - }; - - $scope.topNavMenu = getTopNavLinks( - onNewSearch, - () => onSaveSearch(savedSearch, saveDataSource, resetInitialAppState, core.i18n), - onOpenSearch, - onShareSearch, - onInspectSearch, - uiCapabilities - ); + $scope.topNavMenu = getTopNavLinks({ + getFieldCounts, + indexPattern: $scope.indexPattern, + inspectorAdapters, + navigateTo: (path) => { + $scope.$evalAsync(() => { + history.push(path); + }); + }, + savedSearch, + services, + state, + }); $scope.searchSource .setField('index', $scope.indexPattern) @@ -434,25 +394,8 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise const pageTitleSuffix = savedSearch.id && savedSearch.title ? `: ${savedSearch.title}` : ''; chrome.docTitle.change(`Discover${pageTitleSuffix}`); - const discoverBreadcrumbsTitle = i18n.translate('discover.discoverBreadcrumbTitle', { - defaultMessage: 'Discover', - }); - if (savedSearch.id && savedSearch.title) { - chrome.setBreadcrumbs([ - { - text: discoverBreadcrumbsTitle, - href: '#/', - }, - { text: savedSearch.title }, - ]); - } else { - chrome.setBreadcrumbs([ - { - text: discoverBreadcrumbsTitle, - }, - ]); - } + setBreadcrumbsTitle(savedSearch, chrome); function getStateDefaults() { const query = $scope.searchSource.getField('query') || data.query.queryString.getDefaultQuery(); @@ -479,7 +422,6 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise savedSearch: savedSearch, indexPatternList: $route.current.locals.savedObjects.ip.list, config: config, - fixedScroll: createFixedScroll($scope, $timeout), setHeaderActionMenu: getHeaderActionMenuMounter(), }; @@ -583,61 +525,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise } }); }); - async function saveDataSource(saveOptions) { - function onSuccess(id) { - $scope.$evalAsync(() => { - if (id) { - toastNotifications.addSuccess({ - title: i18n.translate('discover.notifications.savedSearchTitle', { - defaultMessage: `Search '{savedSearchTitle}' was saved`, - values: { - savedSearchTitle: savedSearch.title, - }, - }), - 'data-test-subj': 'saveSearchSuccess', - }); - - if (savedSearch.id !== $route.current.params.id) { - history.push(`/view/${encodeURIComponent(savedSearch.id)}`); - } else { - // Update defaults so that "reload saved query" functions correctly - setAppState(getStateDefaults()); - chrome.docTitle.change(savedSearch.lastSavedTitle); - chrome.setBreadcrumbs([ - { - text: discoverBreadcrumbsTitle, - href: '#/', - }, - { text: savedSearch.title }, - ]); - } - } - }); - } - function onError(error, savedSearch) { - toastNotifications.addDanger({ - title: i18n.translate('discover.notifications.notSavedSearchTitle', { - defaultMessage: `Search '{savedSearchTitle}' was not saved.`, - values: { - savedSearchTitle: savedSearch.title, - }, - }), - text: error.message, - }); - } - return persistSavedSearch({ - config, - data, - indexPattern: $scope.indexPattern, - onError, - onSuccess, - searchSource: $scope.searchSource, - savedSearch, - saveOptions, - state: $scope.state, - }); - } $scope.opts.fetch = $scope.fetch = function () { // ignore requests to fetch before the app inits if (!init.complete) return; @@ -865,11 +753,6 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise const columns = columnActions.moveColumn($scope.state.columns, columnName, newIndex); setAppState({ columns }); }; - - $scope.scrollToTop = function () { - $window.scrollTo(0, 0); - }; - async function setupVisualization() { // If no timefield has been specified we don't create a histogram of messages if (!getTimeField()) return; diff --git a/src/plugins/discover/public/application/angular/discover_state.test.ts b/src/plugins/discover/public/application/angular/discover_state.test.ts index b7b36ca960167e..2914ce8f17a09f 100644 --- a/src/plugins/discover/public/application/angular/discover_state.test.ts +++ b/src/plugins/discover/public/application/angular/discover_state.test.ts @@ -29,7 +29,7 @@ describe('Test discover state', () => { history = createBrowserHistory(); history.push('/'); state = getState({ - defaultAppState: { index: 'test' }, + getStateDefaults: () => ({ index: 'test' }), history, }); await state.replaceUrlAppState({}); @@ -84,7 +84,7 @@ describe('Test discover state with legacy migration', () => { "/#?_a=(query:(query_string:(analyze_wildcard:!t,query:'type:nice%20name:%22yeah%22')))" ); state = getState({ - defaultAppState: { index: 'test' }, + getStateDefaults: () => ({ index: 'test' }), history, }); expect(state.appStateContainer.getState()).toMatchInlineSnapshot(` diff --git a/src/plugins/discover/public/application/angular/discover_state.ts b/src/plugins/discover/public/application/angular/discover_state.ts index 5ddb6a92b5fd48..3c6ef1d3e4334e 100644 --- a/src/plugins/discover/public/application/angular/discover_state.ts +++ b/src/plugins/discover/public/application/angular/discover_state.ts @@ -65,7 +65,7 @@ interface GetStateParams { /** * Default state used for merging with with URL state to get the initial state */ - defaultAppState?: AppState; + getStateDefaults?: () => AppState; /** * Determins the use of long vs. short/hashed urls */ @@ -123,7 +123,11 @@ export interface GetStateReturn { /** * Returns whether the current app state is different to the initial state */ - isAppStateDirty: () => void; + isAppStateDirty: () => boolean; + /** + * Reset AppState to default, discarding all changes + */ + resetAppState: () => void; } const APP_STATE_URL_KEY = '_a'; @@ -132,11 +136,12 @@ const APP_STATE_URL_KEY = '_a'; * Used to sync URL with UI state */ export function getState({ - defaultAppState = {}, + getStateDefaults, storeInSessionStorage = false, history, toasts, }: GetStateParams): GetStateReturn { + const defaultAppState = getStateDefaults ? getStateDefaults() : {}; const stateStorage = createKbnUrlStateStorage({ useHash: storeInSessionStorage, history, @@ -185,6 +190,10 @@ export function getState({ resetInitialAppState: () => { initialAppState = appStateContainer.getState(); }, + resetAppState: () => { + const defaultState = getStateDefaults ? getStateDefaults() : {}; + setState(appStateContainerModified, defaultState); + }, getPreviousAppState: () => previousAppState, flushToUrl: () => stateStorage.flush(), isAppStateDirty: () => !isEqualState(initialAppState, appStateContainer.getState()), diff --git a/src/plugins/discover/public/application/components/discover_legacy.tsx b/src/plugins/discover/public/application/components/discover_legacy.tsx index de1faaf9fc19d4..a472806d4b38d1 100644 --- a/src/plugins/discover/public/application/components/discover_legacy.tsx +++ b/src/plugins/discover/public/application/components/discover_legacy.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import React, { useState, useCallback, useEffect } from 'react'; +import React, { useState } from 'react'; import classNames from 'classnames'; import { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -71,7 +71,6 @@ export interface DiscoverLegacyProps { indexPatternList: Array>; timefield: string; sampleSize: number; - fixedScroll: (el: HTMLElement) => void; setHeaderActionMenu: (menuMount: MountPoint | undefined) => void; }; resetQuery: () => void; @@ -127,20 +126,7 @@ export function DiscoverLegacy({ bucketAggConfig && search.aggs.isDateHistogramBucketAggConfig(bucketAggConfig) ? bucketAggConfig.buckets?.getInterval() : undefined; - const [fixedScrollEl, setFixedScrollEl] = useState(); - useEffect(() => (fixedScrollEl ? opts.fixedScroll(fixedScrollEl) : undefined), [ - fixedScrollEl, - opts, - ]); - const fixedScrollRef = useCallback( - (node: HTMLElement) => { - if (node !== null) { - setFixedScrollEl(node); - } - }, - [setFixedScrollEl] - ); const sidebarClassName = classNames({ closed: isSidebarClosed, }); @@ -258,7 +244,6 @@ export function DiscoverLegacy({

{ + const topNavLinks = getTopNavLinks({ + getFieldCounts: jest.fn(), + indexPattern: indexPatternMock, + inspectorAdapters: inspectorPluginMock, + navigateTo: jest.fn(), + savedSearch: savedSearchMock, + services, + state, + }); + expect(topNavLinks).toMatchInlineSnapshot(` + Array [ + Object { + "description": "New Search", + "id": "new", + "label": "New", + "run": [Function], + "testId": "discoverNewButton", + }, + Object { + "description": "Save Search", + "id": "save", + "label": "Save", + "run": [Function], + "testId": "discoverSaveButton", + }, + Object { + "description": "Open Saved Search", + "id": "open", + "label": "Open", + "run": [Function], + "testId": "discoverOpenButton", + }, + Object { + "description": "Share Search", + "id": "share", + "label": "Share", + "run": [Function], + "testId": "shareTopNavButton", + }, + Object { + "description": "Open Inspector for search", + "id": "inspect", + "label": "Inspect", + "run": [Function], + "testId": "openInspectorButton", + }, + ] + `); +}); diff --git a/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts b/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts index 92ccd22e10945d..a67760020f6ed6 100644 --- a/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts +++ b/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts @@ -17,19 +17,36 @@ * under the License. */ import { i18n } from '@kbn/i18n'; -import { Capabilities } from '../../../../../../core/types'; +import { showOpenSearchPanel } from './show_open_search_panel'; +import { getSharingData } from '../../helpers/get_sharing_data'; +import { unhashUrl } from '../../../../../kibana_utils/public'; +import { DiscoverServices } from '../../../build_services'; +import { Adapters } from '../../../../../inspector/common/adapters'; +import { SavedSearch } from '../../../saved_searches'; +import { onSaveSearch } from './on_save_search'; +import { GetStateReturn } from '../../angular/discover_state'; +import { IndexPattern } from '../../../kibana_services'; /** * Helper function to build the top nav links */ -export const getTopNavLinks = ( - onNewSearch: () => void, - onSaveSearch: () => void, - onOpenSearch: () => void, - onShareSearch: () => void, - onInspectSearch: () => void, - uiCapabilities: Capabilities -) => { +export const getTopNavLinks = ({ + getFieldCounts, + indexPattern, + inspectorAdapters, + navigateTo, + savedSearch, + services, + state, +}: { + getFieldCounts: () => Promise>; + indexPattern: IndexPattern; + inspectorAdapters: Adapters; + navigateTo: (url: string) => void; + savedSearch: SavedSearch; + services: DiscoverServices; + state: GetStateReturn; +}) => { const newSearch = { id: 'new', label: i18n.translate('discover.localMenu.localMenu.newSearchTitle', { @@ -38,7 +55,7 @@ export const getTopNavLinks = ( description: i18n.translate('discover.localMenu.newSearchDescription', { defaultMessage: 'New Search', }), - run: onNewSearch, + run: () => navigateTo('/'), testId: 'discoverNewButton', }; @@ -51,7 +68,7 @@ export const getTopNavLinks = ( defaultMessage: 'Save Search', }), testId: 'discoverSaveButton', - run: onSaveSearch, + run: () => onSaveSearch({ savedSearch, services, indexPattern, navigateTo, state }), }; const openSearch = { @@ -63,7 +80,11 @@ export const getTopNavLinks = ( defaultMessage: 'Open Saved Search', }), testId: 'discoverOpenButton', - run: onOpenSearch, + run: () => + showOpenSearchPanel({ + makeUrl: (searchId) => `#/view/${encodeURIComponent(searchId)}`, + I18nContext: services.core.i18n.Context, + }), }; const shareSearch = { @@ -75,7 +96,31 @@ export const getTopNavLinks = ( defaultMessage: 'Share Search', }), testId: 'shareTopNavButton', - run: onShareSearch, + run: async (anchorElement: HTMLElement) => { + if (!services.share) { + return; + } + const sharingData = await getSharingData( + savedSearch.searchSource, + state.appStateContainer.getState(), + indexPattern, + services.uiSettings, + getFieldCounts + ); + services.share.toggleShareContextMenu({ + anchorElement, + allowEmbed: false, + allowShortUrl: !!services.capabilities.discover.createShortUrl, + shareableUrl: unhashUrl(window.location.href), + objectId: savedSearch.id, + objectType: 'search', + sharingData: { + ...sharingData, + title: savedSearch.title, + }, + isDirty: !savedSearch.id || state.isAppStateDirty(), + }); + }, }; const inspectSearch = { @@ -87,12 +132,16 @@ export const getTopNavLinks = ( defaultMessage: 'Open Inspector for search', }), testId: 'openInspectorButton', - run: onInspectSearch, + run: () => { + services.inspector.open(inspectorAdapters, { + title: savedSearch.title, + }); + }, }; return [ newSearch, - ...(uiCapabilities.discover.save ? [saveSearch] : []), + ...(services.capabilities.discover.save ? [saveSearch] : []), openSearch, shareSearch, inspectSearch, diff --git a/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx b/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx index a27f6da9f7602a..7c139d05dfff46 100644 --- a/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx +++ b/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx @@ -18,21 +18,94 @@ */ import React from 'react'; -import { I18nStart } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { SavedObjectSaveModal, showSaveModal } from '../../../../../saved_objects/public'; import { SavedSearch } from '../../../saved_searches'; +import { IndexPattern } from '../../../../../data/common/index_patterns/index_patterns'; +import { DiscoverServices } from '../../../build_services'; +import { GetStateReturn } from '../../angular/discover_state'; +import { setBreadcrumbsTitle } from '../../helpers/set_breadcrumbs_title'; +import { persistSavedSearch } from '../../helpers/persist_saved_search'; -export async function onSaveSearch( - savedSearch: SavedSearch, - saveDataSource: (args: { +async function saveDataSource({ + indexPattern, + navigateTo, + savedSearch, + saveOptions, + services, + state, +}: { + indexPattern: IndexPattern; + navigateTo: (url: string) => void; + savedSearch: SavedSearch; + saveOptions: { confirmOverwrite: boolean; isTitleDuplicateConfirmed: boolean; onTitleDuplicate: () => void; - }) => Promise<{ id: string; error: string }>, - onSaveDataSourceSuccess: () => void, - i18nStart: I18nStart -) { + }; + services: DiscoverServices; + state: GetStateReturn; +}) { + const prevSavedSearchId = savedSearch.id; + function onSuccess(id: string) { + if (id) { + services.toastNotifications.addSuccess({ + title: i18n.translate('discover.notifications.savedSearchTitle', { + defaultMessage: `Search '{savedSearchTitle}' was saved`, + values: { + savedSearchTitle: savedSearch.title, + }, + }), + 'data-test-subj': 'saveSearchSuccess', + }); + + if (savedSearch.id !== prevSavedSearchId) { + navigateTo(`/view/${encodeURIComponent(savedSearch.id)}`); + } else { + // Update defaults so that "reload saved query" functions correctly + state.resetAppState(); + services.chrome.docTitle.change(savedSearch.lastSavedTitle!); + setBreadcrumbsTitle(savedSearch, services.chrome); + } + } + } + + function onError(error: Error) { + services.toastNotifications.addDanger({ + title: i18n.translate('discover.notifications.notSavedSearchTitle', { + defaultMessage: `Search '{savedSearchTitle}' was not saved.`, + values: { + savedSearchTitle: savedSearch.title, + }, + }), + text: error.message, + }); + } + return persistSavedSearch({ + config: services.uiSettings, + data: services.data, + indexPattern, + onError, + onSuccess, + savedSearch, + saveOptions, + state: state.appStateContainer.getState(), + }); +} + +export async function onSaveSearch({ + indexPattern, + navigateTo, + savedSearch, + services, + state, +}: { + indexPattern: IndexPattern; + navigateTo: (path: string) => void; + savedSearch: SavedSearch; + services: DiscoverServices; + state: GetStateReturn; +}) { const onSave = async ({ newTitle, newCopyOnSave, @@ -52,12 +125,19 @@ export async function onSaveSearch( isTitleDuplicateConfirmed, onTitleDuplicate, }; - const response = await saveDataSource(saveOptions); + const response = await saveDataSource({ + indexPattern, + saveOptions, + services, + navigateTo, + savedSearch, + state, + }); // If the save wasn't successful, put the original values back. if (!response.id || response.error) { savedSearch.title = currentTitle; } else { - onSaveDataSourceSuccess(); + state.resetInitialAppState(); } return response; }; @@ -76,5 +156,5 @@ export async function onSaveSearch( showDescription={false} /> ); - showSaveModal(saveModal, i18nStart.Context); + showSaveModal(saveModal, services.core.i18n.Context); } diff --git a/src/plugins/discover/public/application/helpers/persist_saved_search.ts b/src/plugins/discover/public/application/helpers/persist_saved_search.ts index 933d7e64c19c13..06fa78838ae145 100644 --- a/src/plugins/discover/public/application/helpers/persist_saved_search.ts +++ b/src/plugins/discover/public/application/helpers/persist_saved_search.ts @@ -18,7 +18,6 @@ */ import { IUiSettingsClient } from 'kibana/public'; import { updateSearchSource } from './update_search_source'; -import { SearchSource } from '../../../../data/common/search/search_source'; import { DataPublicPluginStart, IndexPattern } from '../../../../data/public'; import { SavedSearch } from '../../saved_searches'; import { AppState } from '../angular/discover_state'; @@ -33,7 +32,6 @@ export async function persistSavedSearch({ onSuccess, savedSearch, saveOptions, - searchSource, state, }: { config: IUiSettingsClient; @@ -43,10 +41,9 @@ export async function persistSavedSearch({ onSuccess: (id: string) => void; savedSearch: SavedSearch; saveOptions: SavedObjectSaveOpts; - searchSource: SearchSource; state: AppState; }) { - updateSearchSource(searchSource, { + updateSearchSource(savedSearch.searchSource, { indexPattern, config, state, diff --git a/src/plugins/discover/public/application/helpers/set_breadcrumbs_title.ts b/src/plugins/discover/public/application/helpers/set_breadcrumbs_title.ts new file mode 100644 index 00000000000000..cb062de474aa75 --- /dev/null +++ b/src/plugins/discover/public/application/helpers/set_breadcrumbs_title.ts @@ -0,0 +1,47 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ChromeStart } from 'kibana/public'; +import { SavedSearch } from '../../saved_searches'; + +/** + * Helper function to set the Discover's breadcrumb + * if there's an active savedSearch, its title is appended + */ +export function setBreadcrumbsTitle(savedSearch: SavedSearch, chrome: ChromeStart) { + const discoverBreadcrumbsTitle = i18n.translate('discover.discoverBreadcrumbTitle', { + defaultMessage: 'Discover', + }); + + if (savedSearch.id && savedSearch.title) { + chrome.setBreadcrumbs([ + { + text: discoverBreadcrumbsTitle, + href: '#/', + }, + { text: savedSearch.title }, + ]); + } else { + chrome.setBreadcrumbs([ + { + text: discoverBreadcrumbsTitle, + }, + ]); + } +} diff --git a/src/plugins/discover/public/application/helpers/update_search_source.ts b/src/plugins/discover/public/application/helpers/update_search_source.ts index e97c0a57bbf4c6..916d39f39c5fca 100644 --- a/src/plugins/discover/public/application/helpers/update_search_source.ts +++ b/src/plugins/discover/public/application/helpers/update_search_source.ts @@ -27,10 +27,10 @@ import { DataPublicPluginStart } from '../../../../data/public'; export function updateSearchSource( searchSource: ISearchSource, { - state, - indexPattern, - data, config, + data, + indexPattern, + state, }: { config: IUiSettingsClient; data: DataPublicPluginStart; From ca127f8a4565d35a7191309059eb02d4723c34d4 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 12 Oct 2020 14:19:06 +0200 Subject: [PATCH 08/27] Improve code --- .../public/__mocks__/index_patterns.ts | 34 -------------- .../public/application/angular/discover.js | 3 +- .../components/top_nav/on_save_search.tsx | 6 +-- .../helpers/persist_saved_search.ts | 47 ++++++++++--------- .../helpers/update_search_source.ts | 18 +++---- 5 files changed, 37 insertions(+), 71 deletions(-) delete mode 100644 src/plugins/discover/public/__mocks__/index_patterns.ts diff --git a/src/plugins/discover/public/__mocks__/index_patterns.ts b/src/plugins/discover/public/__mocks__/index_patterns.ts deleted file mode 100644 index 1281640a0fa696..00000000000000 --- a/src/plugins/discover/public/__mocks__/index_patterns.ts +++ /dev/null @@ -1,34 +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 { IndexPatternsContract } from '../kibana_services'; - -export const indexPatternsMock = (new (class { - fieldFormats = []; - config = {}; - savedObjectsClient = {}; - refreshSavedObjectsCache = {}; - clearCache = jest.fn(); - get = jest.fn(); - getDefault = jest.fn(); - getFields = jest.fn(); - getIds = jest.fn(); - getTitles = jest.fn(); - make = jest.fn(); -})() as unknown) as IndexPatternsContract; diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index e4d226ca79bb18..628bf9d974cdb4 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -701,9 +701,8 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise $scope.updateDataSource = () => { updateSearchSource($scope.searchSource, { indexPattern: $scope.indexPattern, - config, + services, state: $scope.state, - data, }); return Promise.resolve(); }; diff --git a/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx b/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx index 7c139d05dfff46..e05e3d806fb6e9 100644 --- a/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx +++ b/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx @@ -81,14 +81,12 @@ async function saveDataSource({ text: error.message, }); } - return persistSavedSearch({ - config: services.uiSettings, - data: services.data, + return persistSavedSearch(savedSearch, { indexPattern, onError, onSuccess, - savedSearch, saveOptions, + services, state: state.appStateContainer.getState(), }); } diff --git a/src/plugins/discover/public/application/helpers/persist_saved_search.ts b/src/plugins/discover/public/application/helpers/persist_saved_search.ts index 06fa78838ae145..bcb9743ca42abe 100644 --- a/src/plugins/discover/public/application/helpers/persist_saved_search.ts +++ b/src/plugins/discover/public/application/helpers/persist_saved_search.ts @@ -16,38 +16,39 @@ * specific language governing permissions and limitations * under the License. */ -import { IUiSettingsClient } from 'kibana/public'; import { updateSearchSource } from './update_search_source'; -import { DataPublicPluginStart, IndexPattern } from '../../../../data/public'; +import { IndexPattern } from '../../../../data/public'; import { SavedSearch } from '../../saved_searches'; import { AppState } from '../angular/discover_state'; import { SortOrder } from '../../saved_searches/types'; import { SavedObjectSaveOpts } from '../../../../saved_objects/public'; +import { DiscoverServices } from '../../build_services'; -export async function persistSavedSearch({ - config, - data, - indexPattern, - onError, - onSuccess, - savedSearch, - saveOptions, - state, -}: { - config: IUiSettingsClient; - data: DataPublicPluginStart; - indexPattern: IndexPattern; - onError: (error: Error, savedSearch: SavedSearch) => void; - onSuccess: (id: string) => void; - savedSearch: SavedSearch; - saveOptions: SavedObjectSaveOpts; - state: AppState; -}) { +/** + * Helper function to update and persist the given savedSearch + */ +export async function persistSavedSearch( + savedSearch: SavedSearch, + { + indexPattern, + onError, + onSuccess, + services, + saveOptions, + state, + }: { + indexPattern: IndexPattern; + onError: (error: Error, savedSearch: SavedSearch) => void; + onSuccess: (id: string) => void; + saveOptions: SavedObjectSaveOpts; + services: DiscoverServices; + state: AppState; + } +) { updateSearchSource(savedSearch.searchSource, { indexPattern, - config, + services, state, - data, }); savedSearch.columns = state.columns || []; diff --git a/src/plugins/discover/public/application/helpers/update_search_source.ts b/src/plugins/discover/public/application/helpers/update_search_source.ts index 916d39f39c5fca..7edd62b3143496 100644 --- a/src/plugins/discover/public/application/helpers/update_search_source.ts +++ b/src/plugins/discover/public/application/helpers/update_search_source.ts @@ -16,37 +16,39 @@ * specific language governing permissions and limitations * under the License. */ -import { IUiSettingsClient } from 'kibana/public'; import { getSortForSearchSource } from '../angular/doc_table'; import { SAMPLE_SIZE_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common'; import { IndexPattern, ISearchSource } from '../../../../data/common/'; import { AppState } from '../angular/discover_state'; import { SortOrder } from '../../saved_searches/types'; -import { DataPublicPluginStart } from '../../../../data/public'; +import { DiscoverServices } from '../../build_services'; +/** + * Helper function to update the given searchSource before fetching/sharing/persisting + */ export function updateSearchSource( searchSource: ISearchSource, { - config, - data, indexPattern, + services, state, }: { - config: IUiSettingsClient; - data: DataPublicPluginStart; + services: DiscoverServices; indexPattern: IndexPattern; state: AppState; } ) { + const { uiSettings, data } = services; + searchSource .setField('index', indexPattern) - .setField('size', config.get(SAMPLE_SIZE_SETTING)) + .setField('size', uiSettings.get(SAMPLE_SIZE_SETTING)) .setField( 'sort', getSortForSearchSource( state.sort as SortOrder[], indexPattern, - config.get(SORT_DEFAULT_ORDER_SETTING) + uiSettings.get(SORT_DEFAULT_ORDER_SETTING) ) ) .setField('query', data.query.queryString.getQuery() || null) From f4fbf2b7c3762b944e71183b6e6672a7dfc8f648 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 12 Oct 2020 14:30:13 +0200 Subject: [PATCH 09/27] Remove console.log --- src/plugins/discover/public/application/angular/discover.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 628bf9d974cdb4..0b1456dc695d2e 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -188,7 +188,6 @@ app.directive('discoverApp', function () { }); function discoverController($element, $route, $scope, $timeout, $window, Promise, uiCapabilities) { - console.log(123); const { isDefault: isDefaultType } = indexPatternsUtils; const subscriptions = new Subscription(); const $fetchObservable = new Subject(); From 1496644379adb332ff181fe1ff0e13f038d8bef3 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Tue, 3 Nov 2020 16:03:43 +0100 Subject: [PATCH 10/27] Improve index pattern loading code --- .../public/application/angular/discover.js | 24 ++------- .../angular/helpers/resolve_index_pattern.ts | 50 +++++++++++++++++-- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index adb083e8a926e2..46a1fd3fcf2118 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -52,7 +52,6 @@ import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../helpers/breadc import { validateTimeRange } from '../helpers/validate_time_range'; import { popularizeField } from '../helpers/popularize_field'; import { getSwitchIndexPatternAppState } from '../helpers/get_switch_index_pattern_app_state'; -import { getIndexPatternId } from '../helpers/get_index_pattern_id'; import { addFatalError } from '../../../../kibana_legacy/public'; import { DEFAULT_COLUMNS_SETTING, @@ -60,7 +59,7 @@ import { SAMPLE_SIZE_SETTING, SEARCH_ON_PAGE_LOAD_SETTING, } from '../../../common'; -import { resolveIndexPatternLoading } from './helpers/resolve_index_pattern'; +import { resolveIndexPattern, loadIndexPattern } from './helpers/resolve_index_pattern'; import { getTopNavLinks } from '../components/top_nav/get_top_nav_links'; import { updateSearchSource } from '../helpers/update_search_source'; import { setBreadcrumbsTitle } from '../helpers/set_breadcrumbs_title'; @@ -123,24 +122,7 @@ app.config(($routeProvider) => { const { appStateContainer } = getState({ history }); const { index } = appStateContainer.getState(); return Promise.props({ - ip: indexPatterns.getCache().then((indexPatternList) => { - /** - * In making the indexPattern modifiable it was placed in appState. Unfortunately, - * the load order of AppState conflicts with the load order of many other things - * so in order to get the name of the index we should use, and to switch to the - * default if necessary, we parse the appState with a temporary State object and - * then destroy it immediatly after we're done - * - * @type {State} - */ - const id = getIndexPatternId(index, indexPatternList, config.get('defaultIndex')); - return Promise.props({ - list: indexPatternList, - loaded: indexPatterns.get(id), - stateVal: index, - stateValFound: !!index && id === index, - }); - }), + ip: loadIndexPattern(index, data.indexPatterns, config), savedSearch: getServices() .getSavedSearchById(savedSearchId) .then((savedSearch) => { @@ -195,7 +177,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise let inspectorRequest; const savedSearch = $route.current.locals.savedObjects.savedSearch; $scope.searchSource = savedSearch.searchSource; - $scope.indexPattern = resolveIndexPatternLoading( + $scope.indexPattern = resolveIndexPattern( $route.current.locals.savedObjects.ip, $scope.searchSource, toastNotifications diff --git a/src/plugins/discover/public/application/angular/helpers/resolve_index_pattern.ts b/src/plugins/discover/public/application/angular/helpers/resolve_index_pattern.ts index 8fb7fdc099e3c6..6629fb7a1fcac7 100644 --- a/src/plugins/discover/public/application/angular/helpers/resolve_index_pattern.ts +++ b/src/plugins/discover/public/application/angular/helpers/resolve_index_pattern.ts @@ -19,10 +19,54 @@ import { i18n } from '@kbn/i18n'; import { ToastsStart } from 'kibana/public'; import { IndexPattern } from '../../../kibana_services'; -import { SearchSource } from '../../../../../data/common'; +import { IndexPatternsService, SearchSource } from '../../../../../data/common'; +import { getIndexPatternId } from '../../helpers/get_index_pattern_id'; +import { UiActionsStart } from '../../../../../ui_actions/public'; -export function resolveIndexPatternLoading( - ip: { loaded: IndexPattern; stateVal: string; stateValFound: string }, +interface IndexPatternData { + /** + * List of existing index patterns + */ + list: IndexPattern[]; + /** + * Loaded index pattern (might be default index pattern if requested was not found) + */ + loaded: IndexPattern; + /** + * Id of the requested index pattern + */ + stateVal: string; + /** + * Determines if requested index pattern was found + */ + stateValFound: boolean; +} + +/** + * Function to load the given index pattern by id, providing a fallback if it doesn't exist + */ +export async function loadIndexPattern( + id: string, + indexPatterns: IndexPatternsService, + config: UiActionsStart +): Promise { + const indexPatternList = await indexPatterns.getCache(); + + const actualId = getIndexPatternId(id, indexPatternList, config.get('defaultIndex')); + return { + list: indexPatternList, + loaded: await indexPatterns.get(actualId), + stateVal: id, + stateValFound: !!id && actualId === id, + }; +} + +/** + * Function used in the discover controller to message the user about the state of the current + * index pattern + */ +export function resolveIndexPattern( + ip: IndexPatternData, searchSource: SearchSource, toastNotifications: ToastsStart ) { From fe778516cfd58190fa821f7cc20e31e16c529a0e Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Wed, 4 Nov 2020 09:13:29 +0100 Subject: [PATCH 11/27] Refactor index pattern code and add test --- .../discover/public/__mocks__/config.ts | 30 +++++++++ .../public/__mocks__/index_patterns.ts | 32 +++++++++ .../public/application/angular/discover.js | 2 +- .../helpers/get_index_pattern_id.ts | 60 ----------------- .../helpers/resolve_index_pattern.test.ts | 67 +++++++++++++++++++ .../helpers/resolve_index_pattern.ts | 58 +++++++++++++--- 6 files changed, 179 insertions(+), 70 deletions(-) create mode 100644 src/plugins/discover/public/__mocks__/config.ts create mode 100644 src/plugins/discover/public/__mocks__/index_patterns.ts delete mode 100644 src/plugins/discover/public/application/helpers/get_index_pattern_id.ts create mode 100644 src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts rename src/plugins/discover/public/application/{angular => }/helpers/resolve_index_pattern.ts (67%) diff --git a/src/plugins/discover/public/__mocks__/config.ts b/src/plugins/discover/public/__mocks__/config.ts new file mode 100644 index 00000000000000..fab1d7f7a3a102 --- /dev/null +++ b/src/plugins/discover/public/__mocks__/config.ts @@ -0,0 +1,30 @@ +/* + * 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 { UiSettingsClient } from '../../../../core/public'; + +export const configMock = ({ + get: (key: string) => { + if (key === 'defaultIndex') { + return 'the-index-pattern-id'; + } + + return ''; + }, +} as unknown) as UiSettingsClient; diff --git a/src/plugins/discover/public/__mocks__/index_patterns.ts b/src/plugins/discover/public/__mocks__/index_patterns.ts new file mode 100644 index 00000000000000..f413a111a1d795 --- /dev/null +++ b/src/plugins/discover/public/__mocks__/index_patterns.ts @@ -0,0 +1,32 @@ +/* + * 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 { IndexPatternsService } from '../../../data/common'; +import { indexPatternMock } from './index_pattern'; + +export const indexPatternsMock = ({ + getCache: () => { + return [indexPatternMock]; + }, + get: (id: string) => { + if (id === 'the-index-pattern-id') { + return indexPatternMock; + } + }, +} as unknown) as IndexPatternsService; diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 46a1fd3fcf2118..070b34bcea4092 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -59,7 +59,7 @@ import { SAMPLE_SIZE_SETTING, SEARCH_ON_PAGE_LOAD_SETTING, } from '../../../common'; -import { resolveIndexPattern, loadIndexPattern } from './helpers/resolve_index_pattern'; +import { resolveIndexPattern, loadIndexPattern } from '../helpers/resolve_index_pattern'; import { getTopNavLinks } from '../components/top_nav/get_top_nav_links'; import { updateSearchSource } from '../helpers/update_search_source'; import { setBreadcrumbsTitle } from '../helpers/set_breadcrumbs_title'; diff --git a/src/plugins/discover/public/application/helpers/get_index_pattern_id.ts b/src/plugins/discover/public/application/helpers/get_index_pattern_id.ts deleted file mode 100644 index 601f892e3c56a7..00000000000000 --- a/src/plugins/discover/public/application/helpers/get_index_pattern_id.ts +++ /dev/null @@ -1,60 +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 { IIndexPattern } from '../../../../data/common/index_patterns'; - -export function findIndexPatternById( - indexPatterns: IIndexPattern[], - id: string -): IIndexPattern | undefined { - if (!Array.isArray(indexPatterns) || !id) { - return; - } - return indexPatterns.find((o) => o.id === id); -} - -/** - * Checks if the given defaultIndex exists and returns - * the first available index pattern id if not - */ -export function getFallbackIndexPatternId( - indexPatterns: IIndexPattern[], - defaultIndex: string = '' -): string { - if (defaultIndex && findIndexPatternById(indexPatterns, defaultIndex)) { - return defaultIndex; - } - return !indexPatterns || !indexPatterns.length || !indexPatterns[0].id ? '' : indexPatterns[0].id; -} - -/** - * A given index pattern id is checked for existence and a fallback is provided if it doesn't exist - * The provided defaultIndex is usually configured in Advanced Settings, if it's also invalid - * the first entry of the given list of Indexpatterns is used - */ -export function getIndexPatternId( - id: string = '', - indexPatterns: IIndexPattern[], - defaultIndex: string = '' -): string { - if (!id || !findIndexPatternById(indexPatterns, id)) { - return getFallbackIndexPatternId(indexPatterns, defaultIndex); - } - return id; -} diff --git a/src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts b/src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts new file mode 100644 index 00000000000000..8dfb9e11fa2eab --- /dev/null +++ b/src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts @@ -0,0 +1,67 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { loadIndexPattern } from './resolve_index_pattern'; +import { indexPatternsMock } from '../../__mocks__/index_patterns'; +import { configMock } from '../../__mocks__/config'; + +describe('Resolve index pattern tests', () => { + test('returns valid data for an existing index pattern', async () => { + const result = await loadIndexPattern('the-index-pattern-id', indexPatternsMock, configMock); + expect(result).toMatchInlineSnapshot(` + Object { + "list": Array [ + Object { + "fields": Array [], + "id": "the-index-pattern-id", + "title": "the-index-pattern-title", + }, + ], + "loaded": Object { + "fields": Array [], + "id": "the-index-pattern-id", + "title": "the-index-pattern-title", + }, + "stateVal": "the-index-pattern-id", + "stateValFound": true, + } + `); + }); + test('returns fallback data for an invalid index pattern', async () => { + const result = await loadIndexPattern('invalid-id', indexPatternsMock, configMock); + expect(result).toMatchInlineSnapshot(` + Object { + "list": Array [ + Object { + "fields": Array [], + "id": "the-index-pattern-id", + "title": "the-index-pattern-title", + }, + ], + "loaded": Object { + "fields": Array [], + "id": "the-index-pattern-id", + "title": "the-index-pattern-title", + }, + "stateVal": "invalid-id", + "stateValFound": false, + } + `); + }); +}); diff --git a/src/plugins/discover/public/application/angular/helpers/resolve_index_pattern.ts b/src/plugins/discover/public/application/helpers/resolve_index_pattern.ts similarity index 67% rename from src/plugins/discover/public/application/angular/helpers/resolve_index_pattern.ts rename to src/plugins/discover/public/application/helpers/resolve_index_pattern.ts index 6629fb7a1fcac7..2ae99c7fee429a 100644 --- a/src/plugins/discover/public/application/angular/helpers/resolve_index_pattern.ts +++ b/src/plugins/discover/public/application/helpers/resolve_index_pattern.ts @@ -17,17 +17,17 @@ * under the License. */ import { i18n } from '@kbn/i18n'; -import { ToastsStart } from 'kibana/public'; -import { IndexPattern } from '../../../kibana_services'; -import { IndexPatternsService, SearchSource } from '../../../../../data/common'; -import { getIndexPatternId } from '../../helpers/get_index_pattern_id'; -import { UiActionsStart } from '../../../../../ui_actions/public'; +import { IUiSettingsClient, SavedObject, ToastsStart } from 'kibana/public'; +import { IndexPattern } from '../../kibana_services'; +import { IndexPatternsService, SearchSource } from '../../../../data/common'; + +type IndexPatternSavedObject = SavedObject & { title: string }; interface IndexPatternData { /** * List of existing index patterns */ - list: IndexPattern[]; + list: IndexPatternSavedObject[]; /** * Loaded index pattern (might be default index pattern if requested was not found) */ @@ -42,19 +42,59 @@ interface IndexPatternData { stateValFound: boolean; } +export function findIndexPatternById( + indexPatterns: IndexPatternSavedObject[], + id: string +): IndexPatternSavedObject | undefined { + if (!Array.isArray(indexPatterns) || !id) { + return; + } + return indexPatterns.find((o) => o.id === id); +} + +/** + * Checks if the given defaultIndex exists and returns + * the first available index pattern id if not + */ +export function getFallbackIndexPatternId( + indexPatterns: IndexPatternSavedObject[], + defaultIndex: string = '' +): string { + if (defaultIndex && findIndexPatternById(indexPatterns, defaultIndex)) { + return defaultIndex; + } + return !indexPatterns || !indexPatterns.length || !indexPatterns[0].id ? '' : indexPatterns[0].id; +} + +/** + * A given index pattern id is checked for existence and a fallback is provided if it doesn't exist + * The provided defaultIndex is usually configured in Advanced Settings, if it's also invalid + * the first entry of the given list of Indexpatterns is used + */ +export function getIndexPatternId( + id: string = '', + indexPatterns: IndexPatternSavedObject[] = [], + defaultIndex: string = '' +): string { + if (!id || !findIndexPatternById(indexPatterns, id)) { + return getFallbackIndexPatternId(indexPatterns, defaultIndex); + } + return id; +} + /** * Function to load the given index pattern by id, providing a fallback if it doesn't exist */ export async function loadIndexPattern( id: string, indexPatterns: IndexPatternsService, - config: UiActionsStart + config: IUiSettingsClient ): Promise { - const indexPatternList = await indexPatterns.getCache(); + const indexPatternList = ((await indexPatterns.getCache()) as unknown) as IndexPatternSavedObject[]; const actualId = getIndexPatternId(id, indexPatternList, config.get('defaultIndex')); return { - list: indexPatternList, + list: indexPatternList || [], loaded: await indexPatterns.get(actualId), stateVal: id, stateValFound: !!id && actualId === id, From 828db70ed1d9e9620e11235f6a0d772c42d8d411 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Wed, 4 Nov 2020 11:58:56 +0100 Subject: [PATCH 12/27] Fix type --- src/plugins/discover/public/__mocks__/config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/discover/public/__mocks__/config.ts b/src/plugins/discover/public/__mocks__/config.ts index fab1d7f7a3a102..a6cdfedd795b51 100644 --- a/src/plugins/discover/public/__mocks__/config.ts +++ b/src/plugins/discover/public/__mocks__/config.ts @@ -17,7 +17,7 @@ * under the License. */ -import { UiSettingsClient } from '../../../../core/public'; +import { IUiSettingsClient } from '../../../../core/public'; export const configMock = ({ get: (key: string) => { @@ -27,4 +27,4 @@ export const configMock = ({ return ''; }, -} as unknown) as UiSettingsClient; +} as unknown) as IUiSettingsClient; From ce3ada94fe6c61a9195306ecdbbc9fd79211348d Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 5 Nov 2020 13:17:59 +0100 Subject: [PATCH 13/27] Add another test --- .../discover/public/__mocks__/saved_search.ts | 6 ++- .../top_nav/on_save_search.test.tsx | 47 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 src/plugins/discover/public/application/components/top_nav/on_save_search.test.tsx diff --git a/src/plugins/discover/public/__mocks__/saved_search.ts b/src/plugins/discover/public/__mocks__/saved_search.ts index 7501c25f514e86..11f36fdfde67cc 100644 --- a/src/plugins/discover/public/__mocks__/saved_search.ts +++ b/src/plugins/discover/public/__mocks__/saved_search.ts @@ -17,7 +17,9 @@ * under the License. */ -export const savedSearchMock: any = { +import { SavedSearch } from '../saved_searches'; + +export const savedSearchMock = ({ id: 'the-saved-search-id', type: 'search', attributes: { @@ -36,4 +38,4 @@ export const savedSearchMock: any = { ], migrationVersion: { search: '7.5.0' }, error: undefined, -}; +} as unknown) as SavedSearch; diff --git a/src/plugins/discover/public/application/components/top_nav/on_save_search.test.tsx b/src/plugins/discover/public/application/components/top_nav/on_save_search.test.tsx new file mode 100644 index 00000000000000..b96af355fafd0d --- /dev/null +++ b/src/plugins/discover/public/application/components/top_nav/on_save_search.test.tsx @@ -0,0 +1,47 @@ +/* + * 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 { showSaveModal } from '../../../../../saved_objects/public'; +jest.mock('../../../../../saved_objects/public'); + +import { onSaveSearch } from './on_save_search'; +import { indexPatternMock } from '../../../__mocks__/index_pattern'; +import { savedSearchMock } from '../../../__mocks__/saved_search'; +import { DiscoverServices } from '../../../build_services'; +import { GetStateReturn } from '../../angular/discover_state'; +import { i18nServiceMock } from '../../../../../../core/public/mocks'; + +test('onSaveSearch', async () => { + const serviceMock = ({ + core: { + i18n: i18nServiceMock.create(), + }, + } as unknown) as DiscoverServices; + const stateMock = ({} as unknown) as GetStateReturn; + + await onSaveSearch({ + indexPattern: indexPatternMock, + navigateTo: jest.fn(), + savedSearch: savedSearchMock, + services: serviceMock, + state: stateMock, + }); + + expect(showSaveModal).toHaveBeenCalled(); +}); From 886b7c9c15b7b1eae07572ef86378c9e661c16b0 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 9 Nov 2020 08:36:07 +0100 Subject: [PATCH 14/27] Add tests --- .../public/__mocks__/index_pattern.ts | 3 + .../components/top_nav/get_top_nav_links.ts | 1 - .../helpers/get_sharing_data.test.ts | 67 ++++++++++++++ .../application/helpers/get_sharing_data.ts | 20 ++--- .../helpers/update_search_source.test.ts | 87 +++++++++++++++++++ .../helpers/update_search_source.ts | 1 + 6 files changed, 165 insertions(+), 14 deletions(-) create mode 100644 src/plugins/discover/public/application/helpers/get_sharing_data.test.ts create mode 100644 src/plugins/discover/public/application/helpers/update_search_source.test.ts diff --git a/src/plugins/discover/public/__mocks__/index_pattern.ts b/src/plugins/discover/public/__mocks__/index_pattern.ts index 4079c5a13e6c0e..76f1afcea7297c 100644 --- a/src/plugins/discover/public/__mocks__/index_pattern.ts +++ b/src/plugins/discover/public/__mocks__/index_pattern.ts @@ -23,4 +23,7 @@ export const indexPatternMock = ({ id: 'the-index-pattern-id', title: 'the-index-pattern-title', fields: [], + getComputedFields: () => ({}), + getSourceFiltering: () => ({}), + getFieldByName: () => ({}), } as unknown) as IndexPattern; diff --git a/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts b/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts index a67760020f6ed6..62542e9ace4dd7 100644 --- a/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts +++ b/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts @@ -103,7 +103,6 @@ export const getTopNavLinks = ({ const sharingData = await getSharingData( savedSearch.searchSource, state.appStateContainer.getState(), - indexPattern, services.uiSettings, getFieldCounts ); diff --git a/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts new file mode 100644 index 00000000000000..e3216bc371bf9e --- /dev/null +++ b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts @@ -0,0 +1,67 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { getSharingData } from './get_sharing_data'; +import { IUiSettingsClient } from 'kibana/public'; +import { createSearchSourceMock } from '../../../../data/common/search/search_source/mocks'; +import { indexPatternMock } from '../../__mocks__/index_pattern'; + +describe('getSharingData', () => { + test('returns valid data for sharing', async () => { + const searchSourceMock = createSearchSourceMock({ index: indexPatternMock }); + const result = await getSharingData( + searchSourceMock, + { columns: [] }, + ({ + get: () => { + return false; + }, + } as unknown) as IUiSettingsClient, + () => Promise.resolve({}) + ); + expect(result).toMatchInlineSnapshot(` + Object { + "conflictedTypesFields": Array [], + "fields": Array [], + "indexPatternId": "the-index-pattern-id", + "metaFields": undefined, + "searchRequest": Object { + "body": Object { + "_source": Object { + "includes": Array [], + }, + "docvalue_fields": Array [], + "query": Object { + "bool": Object { + "filter": Array [], + "must": Array [], + "must_not": Array [], + "should": Array [], + }, + }, + "script_fields": Object {}, + "sort": Array [], + "stored_fields": Array [], + }, + "index": "the-index-pattern-title", + }, + } + `); + }); +}); diff --git a/src/plugins/discover/public/application/helpers/get_sharing_data.ts b/src/plugins/discover/public/application/helpers/get_sharing_data.ts index 4d2f4ef308c0a9..db25a1feaaee82 100644 --- a/src/plugins/discover/public/application/helpers/get_sharing_data.ts +++ b/src/plugins/discover/public/application/helpers/get_sharing_data.ts @@ -19,7 +19,7 @@ import { IUiSettingsClient } from 'kibana/public'; import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common'; import { getSortForSearchSource } from '../angular/doc_table'; -import { IndexPattern, SearchSource } from '../../../../data/common'; +import { SearchSource } from '../../../../data/common'; import { AppState } from '../angular/discover_state'; import { SortOrder } from '../../saved_searches/types'; @@ -51,26 +51,22 @@ const getSharingDataFields = async ( export async function getSharingData( currentSearchSource: SearchSource, state: AppState, - indexPattern: IndexPattern, config: IUiSettingsClient, getFieldCounts: () => Promise> ) { const searchSource = currentSearchSource.createCopy(); + const index = searchSource.getField('index')!; const { searchFields, selectFields } = await getSharingDataFields( getFieldCounts, state.columns || [], - indexPattern.timeFieldName || '', + index.timeFieldName || '', config.get(DOC_HIDE_TIME_COLUMN_SETTING) ); searchSource.setField('fields', searchFields); searchSource.setField( 'sort', - getSortForSearchSource( - state.sort as SortOrder[], - indexPattern, - config.get(SORT_DEFAULT_ORDER_SETTING) - ) + getSortForSearchSource(state.sort as SortOrder[], index, config.get(SORT_DEFAULT_ORDER_SETTING)) ); searchSource.setField('highlight', null); searchSource.setField('highlightAll', undefined); @@ -78,17 +74,15 @@ export async function getSharingData( searchSource.setField('size', undefined); const body = await searchSource.getSearchRequestBody(); - const index = searchSource.getField('index')!; + return { searchRequest: { index: index.title, body, }, fields: selectFields, - metaFields: indexPattern.metaFields, - conflictedTypesFields: indexPattern.fields - .filter((f) => f.type === 'conflict') - .map((f) => f.name), + metaFields: index.metaFields, + conflictedTypesFields: index.fields.filter((f) => f.type === 'conflict').map((f) => f.name), indexPatternId: index.id, }; } diff --git a/src/plugins/discover/public/application/helpers/update_search_source.test.ts b/src/plugins/discover/public/application/helpers/update_search_source.test.ts new file mode 100644 index 00000000000000..96fc5767af5fc5 --- /dev/null +++ b/src/plugins/discover/public/application/helpers/update_search_source.test.ts @@ -0,0 +1,87 @@ +/* + * 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 { updateSearchSource } from './update_search_source'; +import { createSearchSourceMock } from '../../../../data/common/search/search_source/mocks'; +import { indexPatternMock } from '../../__mocks__/index_pattern'; +import { AppState } from '../angular/discover_state'; +import { IUiSettingsClient } from 'kibana/public'; +import { DiscoverServices } from '../../build_services'; +import { dataPluginMock } from '../../../../data/public/mocks'; +import { SAMPLE_SIZE_SETTING } from '../../../common'; + +describe('updateSearchSource', () => { + test('updates a given search source', async () => { + const searchSourceMock = createSearchSourceMock({}); + const result = updateSearchSource(searchSourceMock, { + indexPattern: indexPatternMock, + services: ({ + data: dataPluginMock.createStartContract(), + uiSettings: ({ + get: (key: string) => { + if (key === SAMPLE_SIZE_SETTING) { + return 500; + } + return false; + }, + } as unknown) as IUiSettingsClient, + } as unknown) as DiscoverServices, + state: ({ sort: [] } as unknown) as AppState, + }); + expect(result).toMatchInlineSnapshot(` + SearchSource { + "dependencies": Object { + "getConfig": [MockFunction], + "legacy": Object { + "callMsearch": [MockFunction], + "loadingCount$": BehaviorSubject { + "_isScalar": false, + "_value": 0, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, + "onResponse": [MockFunction], + "search": [MockFunction], + }, + "fields": Object { + "index": Object { + "fields": Array [], + "getComputedFields": [Function], + "getFieldByName": [Function], + "getSourceFiltering": [Function], + "id": "the-index-pattern-id", + "title": "the-index-pattern-title", + }, + "size": 500, + "sort": Array [], + }, + "history": Array [], + "id": "data_source1", + "inheritOptions": Object {}, + "parent": undefined, + "requestStartHandlers": Array [], + "searchStrategyId": undefined, + } + `); + }); +}); diff --git a/src/plugins/discover/public/application/helpers/update_search_source.ts b/src/plugins/discover/public/application/helpers/update_search_source.ts index 7edd62b3143496..dc8e61e79b637c 100644 --- a/src/plugins/discover/public/application/helpers/update_search_source.ts +++ b/src/plugins/discover/public/application/helpers/update_search_source.ts @@ -53,4 +53,5 @@ export function updateSearchSource( ) .setField('query', data.query.queryString.getQuery() || null) .setField('filter', data.query.filterManager.getFilters()); + return searchSource; } From 81fa22e9fb624e443945aea42621565fd509a09a Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 9 Nov 2020 13:19:07 +0100 Subject: [PATCH 15/27] Update snapshot --- .../helpers/resolve_index_pattern.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts b/src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts index 8dfb9e11fa2eab..84e540219bc46c 100644 --- a/src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts +++ b/src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts @@ -29,12 +29,18 @@ describe('Resolve index pattern tests', () => { "list": Array [ Object { "fields": Array [], + "getComputedFields": [Function], + "getFieldByName": [Function], + "getSourceFiltering": [Function], "id": "the-index-pattern-id", "title": "the-index-pattern-title", }, ], "loaded": Object { "fields": Array [], + "getComputedFields": [Function], + "getFieldByName": [Function], + "getSourceFiltering": [Function], "id": "the-index-pattern-id", "title": "the-index-pattern-title", }, @@ -50,12 +56,18 @@ describe('Resolve index pattern tests', () => { "list": Array [ Object { "fields": Array [], + "getComputedFields": [Function], + "getFieldByName": [Function], + "getSourceFiltering": [Function], "id": "the-index-pattern-id", "title": "the-index-pattern-title", }, ], "loaded": Object { "fields": Array [], + "getComputedFields": [Function], + "getFieldByName": [Function], + "getSourceFiltering": [Function], "id": "the-index-pattern-id", "title": "the-index-pattern-title", }, From 3735b50fc8f170d6d9765a5670ddcbefc6724f0d Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 9 Nov 2020 16:01:14 +0100 Subject: [PATCH 16/27] Undo fixed scroll changes --- .../public/application/angular/discover.js | 2 ++ .../application/components/discover_legacy.tsx | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 0b25ab261c5827..9b83eeb8eb2e88 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -33,6 +33,7 @@ import { syncQueryStateWithUrl, } from '../../../../data/public'; import { getSortArray } from './doc_table'; +import { createFixedScroll } from './directives/fixed_scroll'; import * as columnActions from './doc_table/actions/columns'; import indexTemplateLegacy from './discover_legacy.html'; import { addHelpMenuToAppChrome } from '../components/help_menu/help_menu_util'; @@ -404,6 +405,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise savedSearch: savedSearch, indexPatternList: $route.current.locals.savedObjects.ip.list, config: config, + fixedScroll: createFixedScroll($scope, $timeout), setHeaderActionMenu: getHeaderActionMenuMounter(), data, }; diff --git a/src/plugins/discover/public/application/components/discover_legacy.tsx b/src/plugins/discover/public/application/components/discover_legacy.tsx index 07bee432bfcaae..e9de4c08a177b5 100644 --- a/src/plugins/discover/public/application/components/discover_legacy.tsx +++ b/src/plugins/discover/public/application/components/discover_legacy.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import React, { useState } from 'react'; +import React, { useState, useCallback, useEffect } from 'react'; import classNames from 'classnames'; import { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -69,6 +69,7 @@ export interface DiscoverLegacyProps { chartAggConfigs?: AggConfigs; config: IUiSettingsClient; data: DataPublicPluginStart; + fixedScroll: (el: HTMLElement) => void; indexPatternList: Array>; sampleSize: number; savedSearch: SavedSearch; @@ -128,7 +129,20 @@ export function DiscoverLegacy({ bucketAggConfig && search.aggs.isDateHistogramBucketAggConfig(bucketAggConfig) ? bucketAggConfig.buckets?.getInterval() : undefined; + const [fixedScrollEl, setFixedScrollEl] = useState(); + useEffect(() => (fixedScrollEl ? opts.fixedScroll(fixedScrollEl) : undefined), [ + fixedScrollEl, + opts, + ]); + const fixedScrollRef = useCallback( + (node: HTMLElement) => { + if (node !== null) { + setFixedScrollEl(node); + } + }, + [setFixedScrollEl] + ); const sidebarClassName = classNames({ closed: isSidebarClosed, }); @@ -244,6 +258,7 @@ export function DiscoverLegacy({

Date: Thu, 12 Nov 2020 06:30:18 +0100 Subject: [PATCH 17/27] Add more tests and refactor --- .../public/__mocks__/index_pattern.ts | 51 +++++- .../public/application/angular/discover.js | 30 ++-- .../components/top_nav/on_save_search.tsx | 2 +- .../public/application/helpers/breadcrumbs.ts | 28 ++++ .../helpers/calc_field_counts.test.ts | 58 +++++++ ...t_field_counts.ts => calc_field_counts.ts} | 12 +- .../helpers/get_sharing_data.test.ts | 5 +- .../helpers/resolve_index_pattern.test.ts | 156 +++++++++++++++++- .../helpers/set_breadcrumbs_title.ts | 47 ------ .../helpers/update_search_source.test.ts | 39 ++++- 10 files changed, 349 insertions(+), 79 deletions(-) create mode 100644 src/plugins/discover/public/application/helpers/calc_field_counts.test.ts rename src/plugins/discover/public/application/helpers/{get_field_counts.ts => calc_field_counts.ts} (76%) delete mode 100644 src/plugins/discover/public/application/helpers/set_breadcrumbs_title.ts diff --git a/src/plugins/discover/public/__mocks__/index_pattern.ts b/src/plugins/discover/public/__mocks__/index_pattern.ts index 76f1afcea7297c..696079ec72a732 100644 --- a/src/plugins/discover/public/__mocks__/index_pattern.ts +++ b/src/plugins/discover/public/__mocks__/index_pattern.ts @@ -17,13 +17,58 @@ * under the License. */ -import { IndexPattern } from '../kibana_services'; +import { IndexPattern, indexPatterns } from '../kibana_services'; +import { IIndexPatternFieldList } from '../../../data/common/index_patterns/fields'; -export const indexPatternMock = ({ +const fields = [ + { + name: '_index', + type: 'string', + scripted: false, + filterable: true, + }, + { + name: 'message', + type: 'string', + scripted: false, + filterable: false, + }, + { + name: 'extension', + type: 'string', + scripted: false, + filterable: true, + }, + { + name: 'bytes', + type: 'number', + scripted: false, + filterable: true, + }, + { + name: 'scripted', + type: 'number', + scripted: true, + filterable: false, + }, +] as IIndexPatternFieldList; + +fields.getByName = (name: string) => { + return fields.find((field) => field.name === name); +}; + +const indexPattern = ({ id: 'the-index-pattern-id', title: 'the-index-pattern-title', - fields: [], + metaFields: ['_index', '_score'], + flattenHit: undefined, + formatHit: jest.fn((hit) => hit._source), + fields, getComputedFields: () => ({}), getSourceFiltering: () => ({}), getFieldByName: () => ({}), } as unknown) as IndexPattern; + +indexPattern.flattenHit = indexPatterns.flattenHitWrapper(indexPattern, indexPattern.metaFields); + +export const indexPatternMock = indexPattern; diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 9b83eeb8eb2e88..91f089c7484ac4 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -49,7 +49,11 @@ import { subscribeWithScope, tabifyAggResponse, } from '../../kibana_services'; -import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../helpers/breadcrumbs'; +import { + getRootBreadcrumbs, + getSavedSearchBreadcrumbs, + setBreadcrumbsTitle, +} from '../helpers/breadcrumbs'; import { validateTimeRange } from '../helpers/validate_time_range'; import { popularizeField } from '../helpers/popularize_field'; import { getSwitchIndexPatternAppState } from '../helpers/get_switch_index_pattern_app_state'; @@ -63,7 +67,7 @@ import { import { resolveIndexPattern, loadIndexPattern } from '../helpers/resolve_index_pattern'; import { getTopNavLinks } from '../components/top_nav/get_top_nav_links'; import { updateSearchSource } from '../helpers/update_search_source'; -import { setBreadcrumbsTitle } from '../helpers/set_breadcrumbs_title'; +import { calcFieldCounts } from '../helpers/calc_field_counts'; const services = getServices(); @@ -621,16 +625,11 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise $scope.hits = resp.hits.total; $scope.rows = resp.hits.hits; - // if we haven't counted yet, reset the counts - const counts = ($scope.fieldCounts = $scope.fieldCounts || {}); - - $scope.rows.forEach((hit) => { - const fields = Object.keys($scope.indexPattern.flattenHit(hit)); - fields.forEach((fieldName) => { - counts[fieldName] = (counts[fieldName] || 0) + 1; - }); - }); - + $scope.fieldCounts = calcFieldCounts( + $scope.fieldCounts || {}, + resp.hits.hits, + $scope.indexPattern + ); $scope.fetchStatus = fetchStatuses.COMPLETE; } @@ -658,13 +657,6 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise }; }; - $scope.toMoment = function (datetime) { - if (!datetime) { - return; - } - return moment(datetime).format(config.get('dateFormat')); - }; - $scope.resetQuery = function () { history.push( $route.current.params.id ? `/view/${encodeURIComponent($route.current.params.id)}` : '/' diff --git a/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx b/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx index e05e3d806fb6e9..c3343968a46855 100644 --- a/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx +++ b/src/plugins/discover/public/application/components/top_nav/on_save_search.tsx @@ -24,7 +24,7 @@ import { SavedSearch } from '../../../saved_searches'; import { IndexPattern } from '../../../../../data/common/index_patterns/index_patterns'; import { DiscoverServices } from '../../../build_services'; import { GetStateReturn } from '../../angular/discover_state'; -import { setBreadcrumbsTitle } from '../../helpers/set_breadcrumbs_title'; +import { setBreadcrumbsTitle } from '../../helpers/breadcrumbs'; import { persistSavedSearch } from '../../helpers/persist_saved_search'; async function saveDataSource({ diff --git a/src/plugins/discover/public/application/helpers/breadcrumbs.ts b/src/plugins/discover/public/application/helpers/breadcrumbs.ts index 17492b02f7eab1..96a9f546a06363 100644 --- a/src/plugins/discover/public/application/helpers/breadcrumbs.ts +++ b/src/plugins/discover/public/application/helpers/breadcrumbs.ts @@ -17,7 +17,9 @@ * under the License. */ +import { ChromeStart } from 'kibana/public'; import { i18n } from '@kbn/i18n'; +import { SavedSearch } from '../../saved_searches'; export function getRootBreadcrumbs() { return [ @@ -38,3 +40,29 @@ export function getSavedSearchBreadcrumbs($route: any) { }, ]; } + +/** + * Helper function to set the Discover's breadcrumb + * if there's an active savedSearch, its title is appended + */ +export function setBreadcrumbsTitle(savedSearch: SavedSearch, chrome: ChromeStart) { + const discoverBreadcrumbsTitle = i18n.translate('discover.discoverBreadcrumbTitle', { + defaultMessage: 'Discover', + }); + + if (savedSearch.id && savedSearch.title) { + chrome.setBreadcrumbs([ + { + text: discoverBreadcrumbsTitle, + href: '#/', + }, + { text: savedSearch.title }, + ]); + } else { + chrome.setBreadcrumbs([ + { + text: discoverBreadcrumbsTitle, + }, + ]); + } +} diff --git a/src/plugins/discover/public/application/helpers/calc_field_counts.test.ts b/src/plugins/discover/public/application/helpers/calc_field_counts.test.ts new file mode 100644 index 00000000000000..ce3319bf8a667d --- /dev/null +++ b/src/plugins/discover/public/application/helpers/calc_field_counts.test.ts @@ -0,0 +1,58 @@ +/* + * 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 { calcFieldCounts } from './calc_field_counts'; +import { indexPatternMock } from '../../__mocks__/index_pattern'; + +describe('calcFieldCounts', () => { + test('returns valid field count data', async () => { + const rows = [ + { _id: 1, _source: { message: 'test1', bytes: 20 } }, + { _id: 2, _source: { name: 'test2', extension: 'jpg' } }, + ]; + const result = calcFieldCounts({}, rows, indexPatternMock); + expect(result).toMatchInlineSnapshot(` + Object { + "_index": 2, + "_score": 2, + "bytes": 1, + "extension": 1, + "message": 1, + "name": 1, + } + `); + }); + test('updates field count data', async () => { + const rows = [ + { _id: 1, _source: { message: 'test1', bytes: 20 } }, + { _id: 2, _source: { name: 'test2', extension: 'jpg' } }, + ]; + const result = calcFieldCounts({ message: 2 }, rows, indexPatternMock); + expect(result).toMatchInlineSnapshot(` + Object { + "_index": 2, + "_score": 2, + "bytes": 1, + "extension": 1, + "message": 3, + "name": 1, + } + `); + }); +}); diff --git a/src/plugins/discover/public/application/helpers/get_field_counts.ts b/src/plugins/discover/public/application/helpers/calc_field_counts.ts similarity index 76% rename from src/plugins/discover/public/application/helpers/get_field_counts.ts rename to src/plugins/discover/public/application/helpers/calc_field_counts.ts index 13f3d4f8b11743..02c0299995e191 100644 --- a/src/plugins/discover/public/application/helpers/get_field_counts.ts +++ b/src/plugins/discover/public/application/helpers/calc_field_counts.ts @@ -18,9 +18,15 @@ */ import { IndexPattern } from '../../kibana_services'; -export function getFieldCounts(rows: Array>, indexPattern: IndexPattern) { - const counts: Record = {}; - +/** + * This function is recording stats of the available fields, for usage in sidebar and sharing + * Note that this values aren't displayed, but used for internal calculations + */ +export function calcFieldCounts( + counts = {} as Record, + rows: Array>, + indexPattern: IndexPattern +) { for (const hit of rows) { const fields = Object.keys(indexPattern.flattenHit(hit)); for (const fieldName of fields) { diff --git a/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts index e3216bc371bf9e..8ce9789d1dc840 100644 --- a/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts +++ b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts @@ -40,7 +40,10 @@ describe('getSharingData', () => { "conflictedTypesFields": Array [], "fields": Array [], "indexPatternId": "the-index-pattern-id", - "metaFields": undefined, + "metaFields": Array [ + "_index", + "_score", + ], "searchRequest": Object { "body": Object { "_source": Object { diff --git a/src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts b/src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts index 84e540219bc46c..0a51892bb2ab2e 100644 --- a/src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts +++ b/src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts @@ -28,20 +28,94 @@ describe('Resolve index pattern tests', () => { Object { "list": Array [ Object { - "fields": Array [], + "fields": Array [ + Object { + "filterable": true, + "name": "_index", + "scripted": false, + "type": "string", + }, + Object { + "filterable": false, + "name": "message", + "scripted": false, + "type": "string", + }, + Object { + "filterable": true, + "name": "extension", + "scripted": false, + "type": "string", + }, + Object { + "filterable": true, + "name": "bytes", + "scripted": false, + "type": "number", + }, + Object { + "filterable": false, + "name": "scripted", + "scripted": true, + "type": "number", + }, + ], + "flattenHit": [Function], + "formatHit": [MockFunction], "getComputedFields": [Function], "getFieldByName": [Function], "getSourceFiltering": [Function], "id": "the-index-pattern-id", + "metaFields": Array [ + "_index", + "_score", + ], "title": "the-index-pattern-title", }, ], "loaded": Object { - "fields": Array [], + "fields": Array [ + Object { + "filterable": true, + "name": "_index", + "scripted": false, + "type": "string", + }, + Object { + "filterable": false, + "name": "message", + "scripted": false, + "type": "string", + }, + Object { + "filterable": true, + "name": "extension", + "scripted": false, + "type": "string", + }, + Object { + "filterable": true, + "name": "bytes", + "scripted": false, + "type": "number", + }, + Object { + "filterable": false, + "name": "scripted", + "scripted": true, + "type": "number", + }, + ], + "flattenHit": [Function], + "formatHit": [MockFunction], "getComputedFields": [Function], "getFieldByName": [Function], "getSourceFiltering": [Function], "id": "the-index-pattern-id", + "metaFields": Array [ + "_index", + "_score", + ], "title": "the-index-pattern-title", }, "stateVal": "the-index-pattern-id", @@ -55,20 +129,94 @@ describe('Resolve index pattern tests', () => { Object { "list": Array [ Object { - "fields": Array [], + "fields": Array [ + Object { + "filterable": true, + "name": "_index", + "scripted": false, + "type": "string", + }, + Object { + "filterable": false, + "name": "message", + "scripted": false, + "type": "string", + }, + Object { + "filterable": true, + "name": "extension", + "scripted": false, + "type": "string", + }, + Object { + "filterable": true, + "name": "bytes", + "scripted": false, + "type": "number", + }, + Object { + "filterable": false, + "name": "scripted", + "scripted": true, + "type": "number", + }, + ], + "flattenHit": [Function], + "formatHit": [MockFunction], "getComputedFields": [Function], "getFieldByName": [Function], "getSourceFiltering": [Function], "id": "the-index-pattern-id", + "metaFields": Array [ + "_index", + "_score", + ], "title": "the-index-pattern-title", }, ], "loaded": Object { - "fields": Array [], + "fields": Array [ + Object { + "filterable": true, + "name": "_index", + "scripted": false, + "type": "string", + }, + Object { + "filterable": false, + "name": "message", + "scripted": false, + "type": "string", + }, + Object { + "filterable": true, + "name": "extension", + "scripted": false, + "type": "string", + }, + Object { + "filterable": true, + "name": "bytes", + "scripted": false, + "type": "number", + }, + Object { + "filterable": false, + "name": "scripted", + "scripted": true, + "type": "number", + }, + ], + "flattenHit": [Function], + "formatHit": [MockFunction], "getComputedFields": [Function], "getFieldByName": [Function], "getSourceFiltering": [Function], "id": "the-index-pattern-id", + "metaFields": Array [ + "_index", + "_score", + ], "title": "the-index-pattern-title", }, "stateVal": "invalid-id", diff --git a/src/plugins/discover/public/application/helpers/set_breadcrumbs_title.ts b/src/plugins/discover/public/application/helpers/set_breadcrumbs_title.ts deleted file mode 100644 index cb062de474aa75..00000000000000 --- a/src/plugins/discover/public/application/helpers/set_breadcrumbs_title.ts +++ /dev/null @@ -1,47 +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 { i18n } from '@kbn/i18n'; -import { ChromeStart } from 'kibana/public'; -import { SavedSearch } from '../../saved_searches'; - -/** - * Helper function to set the Discover's breadcrumb - * if there's an active savedSearch, its title is appended - */ -export function setBreadcrumbsTitle(savedSearch: SavedSearch, chrome: ChromeStart) { - const discoverBreadcrumbsTitle = i18n.translate('discover.discoverBreadcrumbTitle', { - defaultMessage: 'Discover', - }); - - if (savedSearch.id && savedSearch.title) { - chrome.setBreadcrumbs([ - { - text: discoverBreadcrumbsTitle, - href: '#/', - }, - { text: savedSearch.title }, - ]); - } else { - chrome.setBreadcrumbs([ - { - text: discoverBreadcrumbsTitle, - }, - ]); - } -} diff --git a/src/plugins/discover/public/application/helpers/update_search_source.test.ts b/src/plugins/discover/public/application/helpers/update_search_source.test.ts index 96fc5767af5fc5..0fd4f82708f875 100644 --- a/src/plugins/discover/public/application/helpers/update_search_source.test.ts +++ b/src/plugins/discover/public/application/helpers/update_search_source.test.ts @@ -65,11 +65,48 @@ describe('updateSearchSource', () => { }, "fields": Object { "index": Object { - "fields": Array [], + "fields": Array [ + Object { + "filterable": true, + "name": "_index", + "scripted": false, + "type": "string", + }, + Object { + "filterable": false, + "name": "message", + "scripted": false, + "type": "string", + }, + Object { + "filterable": true, + "name": "extension", + "scripted": false, + "type": "string", + }, + Object { + "filterable": true, + "name": "bytes", + "scripted": false, + "type": "number", + }, + Object { + "filterable": false, + "name": "scripted", + "scripted": true, + "type": "number", + }, + ], + "flattenHit": [Function], + "formatHit": [MockFunction], "getComputedFields": [Function], "getFieldByName": [Function], "getSourceFiltering": [Function], "id": "the-index-pattern-id", + "metaFields": Array [ + "_index", + "_score", + ], "title": "the-index-pattern-title", }, "size": 500, From 3fb09824bfb740b0a8a00c3562916d79aa1de02e Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 12 Nov 2020 11:33:37 +0100 Subject: [PATCH 18/27] Improve test code --- .../helpers/update_search_source.test.ts | 81 +------------------ 1 file changed, 4 insertions(+), 77 deletions(-) diff --git a/src/plugins/discover/public/application/helpers/update_search_source.test.ts b/src/plugins/discover/public/application/helpers/update_search_source.test.ts index 0fd4f82708f875..673cecb5e261e2 100644 --- a/src/plugins/discover/public/application/helpers/update_search_source.test.ts +++ b/src/plugins/discover/public/application/helpers/update_search_source.test.ts @@ -29,6 +29,7 @@ import { SAMPLE_SIZE_SETTING } from '../../../common'; describe('updateSearchSource', () => { test('updates a given search source', async () => { const searchSourceMock = createSearchSourceMock({}); + const sampleSize = 250; const result = updateSearchSource(searchSourceMock, { indexPattern: indexPatternMock, services: ({ @@ -36,7 +37,7 @@ describe('updateSearchSource', () => { uiSettings: ({ get: (key: string) => { if (key === SAMPLE_SIZE_SETTING) { - return 500; + return sampleSize; } return false; }, @@ -44,81 +45,7 @@ describe('updateSearchSource', () => { } as unknown) as DiscoverServices, state: ({ sort: [] } as unknown) as AppState, }); - expect(result).toMatchInlineSnapshot(` - SearchSource { - "dependencies": Object { - "getConfig": [MockFunction], - "legacy": Object { - "callMsearch": [MockFunction], - "loadingCount$": BehaviorSubject { - "_isScalar": false, - "_value": 0, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - }, - }, - "onResponse": [MockFunction], - "search": [MockFunction], - }, - "fields": Object { - "index": Object { - "fields": Array [ - Object { - "filterable": true, - "name": "_index", - "scripted": false, - "type": "string", - }, - Object { - "filterable": false, - "name": "message", - "scripted": false, - "type": "string", - }, - Object { - "filterable": true, - "name": "extension", - "scripted": false, - "type": "string", - }, - Object { - "filterable": true, - "name": "bytes", - "scripted": false, - "type": "number", - }, - Object { - "filterable": false, - "name": "scripted", - "scripted": true, - "type": "number", - }, - ], - "flattenHit": [Function], - "formatHit": [MockFunction], - "getComputedFields": [Function], - "getFieldByName": [Function], - "getSourceFiltering": [Function], - "id": "the-index-pattern-id", - "metaFields": Array [ - "_index", - "_score", - ], - "title": "the-index-pattern-title", - }, - "size": 500, - "sort": Array [], - }, - "history": Array [], - "id": "data_source1", - "inheritOptions": Object {}, - "parent": undefined, - "requestStartHandlers": Array [], - "searchStrategyId": undefined, - } - `); + expect(result.getField('index')).toEqual(indexPatternMock); + expect(result.getField('size')).toEqual(sampleSize); }); }); From 3e83e59953a96673278b7abb311affaa4bd03e20 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Tue, 17 Nov 2020 20:10:12 +0100 Subject: [PATCH 19/27] Improve index pattern code --- .../helpers/resolve_index_pattern.test.ts | 227 +++--------------- .../helpers/resolve_index_pattern.ts | 4 +- 2 files changed, 30 insertions(+), 201 deletions(-) diff --git a/src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts b/src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts index 0a51892bb2ab2e..826f738c381a47 100644 --- a/src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts +++ b/src/plugins/discover/public/application/helpers/resolve_index_pattern.test.ts @@ -17,211 +17,40 @@ * under the License. */ -import { loadIndexPattern } from './resolve_index_pattern'; +import { + loadIndexPattern, + getFallbackIndexPatternId, + IndexPatternSavedObject, +} from './resolve_index_pattern'; import { indexPatternsMock } from '../../__mocks__/index_patterns'; +import { indexPatternMock } from '../../__mocks__/index_pattern'; import { configMock } from '../../__mocks__/config'; describe('Resolve index pattern tests', () => { test('returns valid data for an existing index pattern', async () => { - const result = await loadIndexPattern('the-index-pattern-id', indexPatternsMock, configMock); - expect(result).toMatchInlineSnapshot(` - Object { - "list": Array [ - Object { - "fields": Array [ - Object { - "filterable": true, - "name": "_index", - "scripted": false, - "type": "string", - }, - Object { - "filterable": false, - "name": "message", - "scripted": false, - "type": "string", - }, - Object { - "filterable": true, - "name": "extension", - "scripted": false, - "type": "string", - }, - Object { - "filterable": true, - "name": "bytes", - "scripted": false, - "type": "number", - }, - Object { - "filterable": false, - "name": "scripted", - "scripted": true, - "type": "number", - }, - ], - "flattenHit": [Function], - "formatHit": [MockFunction], - "getComputedFields": [Function], - "getFieldByName": [Function], - "getSourceFiltering": [Function], - "id": "the-index-pattern-id", - "metaFields": Array [ - "_index", - "_score", - ], - "title": "the-index-pattern-title", - }, - ], - "loaded": Object { - "fields": Array [ - Object { - "filterable": true, - "name": "_index", - "scripted": false, - "type": "string", - }, - Object { - "filterable": false, - "name": "message", - "scripted": false, - "type": "string", - }, - Object { - "filterable": true, - "name": "extension", - "scripted": false, - "type": "string", - }, - Object { - "filterable": true, - "name": "bytes", - "scripted": false, - "type": "number", - }, - Object { - "filterable": false, - "name": "scripted", - "scripted": true, - "type": "number", - }, - ], - "flattenHit": [Function], - "formatHit": [MockFunction], - "getComputedFields": [Function], - "getFieldByName": [Function], - "getSourceFiltering": [Function], - "id": "the-index-pattern-id", - "metaFields": Array [ - "_index", - "_score", - ], - "title": "the-index-pattern-title", - }, - "stateVal": "the-index-pattern-id", - "stateValFound": true, - } - `); + const indexPatternId = 'the-index-pattern-id'; + const result = await loadIndexPattern(indexPatternId, indexPatternsMock, configMock); + expect(result.loaded).toEqual(indexPatternMock); + expect(result.stateValFound).toEqual(true); + expect(result.stateVal).toEqual(indexPatternId); }); test('returns fallback data for an invalid index pattern', async () => { - const result = await loadIndexPattern('invalid-id', indexPatternsMock, configMock); - expect(result).toMatchInlineSnapshot(` - Object { - "list": Array [ - Object { - "fields": Array [ - Object { - "filterable": true, - "name": "_index", - "scripted": false, - "type": "string", - }, - Object { - "filterable": false, - "name": "message", - "scripted": false, - "type": "string", - }, - Object { - "filterable": true, - "name": "extension", - "scripted": false, - "type": "string", - }, - Object { - "filterable": true, - "name": "bytes", - "scripted": false, - "type": "number", - }, - Object { - "filterable": false, - "name": "scripted", - "scripted": true, - "type": "number", - }, - ], - "flattenHit": [Function], - "formatHit": [MockFunction], - "getComputedFields": [Function], - "getFieldByName": [Function], - "getSourceFiltering": [Function], - "id": "the-index-pattern-id", - "metaFields": Array [ - "_index", - "_score", - ], - "title": "the-index-pattern-title", - }, - ], - "loaded": Object { - "fields": Array [ - Object { - "filterable": true, - "name": "_index", - "scripted": false, - "type": "string", - }, - Object { - "filterable": false, - "name": "message", - "scripted": false, - "type": "string", - }, - Object { - "filterable": true, - "name": "extension", - "scripted": false, - "type": "string", - }, - Object { - "filterable": true, - "name": "bytes", - "scripted": false, - "type": "number", - }, - Object { - "filterable": false, - "name": "scripted", - "scripted": true, - "type": "number", - }, - ], - "flattenHit": [Function], - "formatHit": [MockFunction], - "getComputedFields": [Function], - "getFieldByName": [Function], - "getSourceFiltering": [Function], - "id": "the-index-pattern-id", - "metaFields": Array [ - "_index", - "_score", - ], - "title": "the-index-pattern-title", - }, - "stateVal": "invalid-id", - "stateValFound": false, - } - `); + const indexPatternId = 'invalid-id'; + const result = await loadIndexPattern(indexPatternId, indexPatternsMock, configMock); + expect(result.loaded).toEqual(indexPatternMock); + expect(result.stateValFound).toBe(false); + expect(result.stateVal).toBe(indexPatternId); + }); + test('getFallbackIndexPatternId with an empty indexPatterns array', async () => { + const result = await getFallbackIndexPatternId([], ''); + expect(result).toBe(''); + }); + test('getFallbackIndexPatternId with an indexPatterns array', async () => { + const list = await indexPatternsMock.getCache(); + const result = await getFallbackIndexPatternId( + (list as unknown) as IndexPatternSavedObject[], + '' + ); + expect(result).toBe('the-index-pattern-id'); }); }); diff --git a/src/plugins/discover/public/application/helpers/resolve_index_pattern.ts b/src/plugins/discover/public/application/helpers/resolve_index_pattern.ts index 2ae99c7fee429a..61167ac2f067da 100644 --- a/src/plugins/discover/public/application/helpers/resolve_index_pattern.ts +++ b/src/plugins/discover/public/application/helpers/resolve_index_pattern.ts @@ -21,7 +21,7 @@ import { IUiSettingsClient, SavedObject, ToastsStart } from 'kibana/public'; import { IndexPattern } from '../../kibana_services'; import { IndexPatternsService, SearchSource } from '../../../../data/common'; -type IndexPatternSavedObject = SavedObject & { title: string }; +export type IndexPatternSavedObject = SavedObject & { title: string }; interface IndexPatternData { /** @@ -63,7 +63,7 @@ export function getFallbackIndexPatternId( if (defaultIndex && findIndexPatternById(indexPatterns, defaultIndex)) { return defaultIndex; } - return !indexPatterns || !indexPatterns.length || !indexPatterns[0].id ? '' : indexPatterns[0].id; + return indexPatterns[0]?.id ? indexPatterns[0].id : ''; } /** From 4e1f56b0cede0f35a64bb2d51b44e6ef7c680170 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 19 Nov 2020 11:49:25 +0100 Subject: [PATCH 20/27] Add removeField to SearchSource --- .../data/common/search/search_source/search_source.ts | 9 +++++++++ .../public/application/helpers/get_sharing_data.ts | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index 9bc65ca3419806..a8512d45c43fad 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -149,6 +149,15 @@ export class SearchSource { return this; } + /** + * remove field + * @param field: field name + */ + removeField(field: K) { + delete this.fields[field]; + return this; + } + /** * Internal, do not use. Overrides all search source fields with the new field array. * diff --git a/src/plugins/discover/public/application/helpers/get_sharing_data.ts b/src/plugins/discover/public/application/helpers/get_sharing_data.ts index db25a1feaaee82..0edaa356cba7de 100644 --- a/src/plugins/discover/public/application/helpers/get_sharing_data.ts +++ b/src/plugins/discover/public/application/helpers/get_sharing_data.ts @@ -68,10 +68,10 @@ export async function getSharingData( 'sort', getSortForSearchSource(state.sort as SortOrder[], index, config.get(SORT_DEFAULT_ORDER_SETTING)) ); - searchSource.setField('highlight', null); - searchSource.setField('highlightAll', undefined); - searchSource.setField('aggs', null); - searchSource.setField('size', undefined); + searchSource.removeField('highlight'); + searchSource.removeField('highlightAll'); + searchSource.removeField('aggs'); + searchSource.removeField('size'); const body = await searchSource.getSearchRequestBody(); From 2c362e9f12dc82cdd28505701389f272b84bf9f1 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 19 Nov 2020 12:15:58 +0100 Subject: [PATCH 21/27] Address review comments --- .../public/application/helpers/resolve_index_pattern.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/discover/public/application/helpers/resolve_index_pattern.ts b/src/plugins/discover/public/application/helpers/resolve_index_pattern.ts index 61167ac2f067da..61f7f087501baa 100644 --- a/src/plugins/discover/public/application/helpers/resolve_index_pattern.ts +++ b/src/plugins/discover/public/application/helpers/resolve_index_pattern.ts @@ -63,7 +63,7 @@ export function getFallbackIndexPatternId( if (defaultIndex && findIndexPatternById(indexPatterns, defaultIndex)) { return defaultIndex; } - return indexPatterns[0]?.id ? indexPatterns[0].id : ''; + return indexPatterns && indexPatterns[0]?.id ? indexPatterns[0].id : ''; } /** From 9c873141b9fc2f53e8e5b8381991b0d602914206 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 19 Nov 2020 16:12:34 +0100 Subject: [PATCH 22/27] Address review comments --- .../data/common/search/search_source/mocks.ts | 1 + .../search/search_source/search_source.ts | 2 +- .../helpers/persist_saved_search.ts | 2 +- .../helpers/update_search_source.test.ts | 4 ++-- .../helpers/update_search_source.ts | 21 ++++++++----------- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/plugins/data/common/search/search_source/mocks.ts b/src/plugins/data/common/search/search_source/mocks.ts index ea7d6b4441ccfe..dd2b0eaccc86e1 100644 --- a/src/plugins/data/common/search/search_source/mocks.ts +++ b/src/plugins/data/common/search/search_source/mocks.ts @@ -28,6 +28,7 @@ export const searchSourceInstanceMock: MockedKeys = { setPreferredSearchStrategyId: jest.fn(), setFields: jest.fn().mockReturnThis(), setField: jest.fn().mockReturnThis(), + removeField: jest.fn().mockReturnThis(), getId: jest.fn(), getFields: jest.fn(), getField: jest.fn(), diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index a8512d45c43fad..0334c4041c3bbd 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -142,7 +142,7 @@ export class SearchSource { */ setField(field: K, value: SearchSourceFields[K]) { if (value == null) { - delete this.fields[field]; + return this.removeField(field); } else { this.fields[field] = value; } diff --git a/src/plugins/discover/public/application/helpers/persist_saved_search.ts b/src/plugins/discover/public/application/helpers/persist_saved_search.ts index bcb9743ca42abe..8e956eff598f3c 100644 --- a/src/plugins/discover/public/application/helpers/persist_saved_search.ts +++ b/src/plugins/discover/public/application/helpers/persist_saved_search.ts @@ -48,7 +48,7 @@ export async function persistSavedSearch( updateSearchSource(savedSearch.searchSource, { indexPattern, services, - state, + sort: state.sort as SortOrder[], }); savedSearch.columns = state.columns || []; diff --git a/src/plugins/discover/public/application/helpers/update_search_source.test.ts b/src/plugins/discover/public/application/helpers/update_search_source.test.ts index 673cecb5e261e2..91832325432ef3 100644 --- a/src/plugins/discover/public/application/helpers/update_search_source.test.ts +++ b/src/plugins/discover/public/application/helpers/update_search_source.test.ts @@ -20,11 +20,11 @@ import { updateSearchSource } from './update_search_source'; import { createSearchSourceMock } from '../../../../data/common/search/search_source/mocks'; import { indexPatternMock } from '../../__mocks__/index_pattern'; -import { AppState } from '../angular/discover_state'; import { IUiSettingsClient } from 'kibana/public'; import { DiscoverServices } from '../../build_services'; import { dataPluginMock } from '../../../../data/public/mocks'; import { SAMPLE_SIZE_SETTING } from '../../../common'; +import { SortOrder } from '../../saved_searches/types'; describe('updateSearchSource', () => { test('updates a given search source', async () => { @@ -43,7 +43,7 @@ describe('updateSearchSource', () => { }, } as unknown) as IUiSettingsClient, } as unknown) as DiscoverServices, - state: ({ sort: [] } as unknown) as AppState, + sort: [] as SortOrder[], }); expect(result.getField('index')).toEqual(indexPatternMock); expect(result.getField('size')).toEqual(sampleSize); diff --git a/src/plugins/discover/public/application/helpers/update_search_source.ts b/src/plugins/discover/public/application/helpers/update_search_source.ts index dc8e61e79b637c..324dc8a48457a5 100644 --- a/src/plugins/discover/public/application/helpers/update_search_source.ts +++ b/src/plugins/discover/public/application/helpers/update_search_source.ts @@ -19,7 +19,6 @@ import { getSortForSearchSource } from '../angular/doc_table'; import { SAMPLE_SIZE_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common'; import { IndexPattern, ISearchSource } from '../../../../data/common/'; -import { AppState } from '../angular/discover_state'; import { SortOrder } from '../../saved_searches/types'; import { DiscoverServices } from '../../build_services'; @@ -31,26 +30,24 @@ export function updateSearchSource( { indexPattern, services, - state, + sort, }: { - services: DiscoverServices; indexPattern: IndexPattern; - state: AppState; + services: DiscoverServices; + sort: SortOrder[]; } ) { const { uiSettings, data } = services; + const usedSort = getSortForSearchSource( + sort, + indexPattern, + uiSettings.get(SORT_DEFAULT_ORDER_SETTING) + ); searchSource .setField('index', indexPattern) .setField('size', uiSettings.get(SAMPLE_SIZE_SETTING)) - .setField( - 'sort', - getSortForSearchSource( - state.sort as SortOrder[], - indexPattern, - uiSettings.get(SORT_DEFAULT_ORDER_SETTING) - ) - ) + .setField('sort', usedSort) .setField('query', data.query.queryString.getQuery() || null) .setField('filter', data.query.filterManager.getFilters()); return searchSource; From 748bf48bf5e3927313a3a17cd2d7fb2efa522ffa Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 19 Nov 2020 16:51:21 +0100 Subject: [PATCH 23/27] Generate API --- .../public/kibana-plugin-plugins-data-public.searchsource.md | 1 + src/plugins/data/public/public.api.md | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md index 548fa66e6e518d..df302e9f3b0d39 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md @@ -41,6 +41,7 @@ export declare class SearchSource | [getSearchRequestBody()](./kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md) | | Returns body contents of the search request, often referred as query DSL. | | [getSerializedFields()](./kibana-plugin-plugins-data-public.searchsource.getserializedfields.md) | | serializes search source fields (which can later be passed to [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md)) | | [onRequestStart(handler)](./kibana-plugin-plugins-data-public.searchsource.onrequeststart.md) | | Add a handler that will be notified whenever requests start | +| [removeField(field)](./kibana-plugin-plugins-data-public.searchsource.removefield.md) | | remove field | | [serialize()](./kibana-plugin-plugins-data-public.searchsource.serialize.md) | | Serializes the instance to a JSON string and a set of referenced objects. Use this method to get a representation of the search source which can be stored in a saved object.The references returned by this function can be mixed with other references in the same object, however make sure there are no name-collisions. The references will be named kibanaSavedObjectMeta.searchSourceJSON.index and kibanaSavedObjectMeta.searchSourceJSON.filter[<number>].meta.index.Using createSearchSource, the instance can be re-created. | | [setField(field, value)](./kibana-plugin-plugins-data-public.searchsource.setfield.md) | | sets value to a single search source field | | [setFields(newFields)](./kibana-plugin-plugins-data-public.searchsource.setfields.md) | | Internal, do not use. Overrides all search source fields with the new field array. | diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 6c4609e5506c23..53eff604a59e70 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -2157,6 +2157,7 @@ export class SearchSource { // (undocumented) history: SearchRequest[]; onRequestStart(handler: (searchSource: SearchSource, options?: ISearchOptions) => Promise): void; + removeField(field: K): this; serialize(): { searchSourceJSON: string; references: import("src/core/server").SavedObjectReference[]; From 797063fa726490fc012eea78debfcf086d9496ef Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 19 Nov 2020 16:58:23 +0100 Subject: [PATCH 24/27] Generate API part 2 --- ...ns-data-public.searchsource.removefield.md | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.removefield.md diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.removefield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.removefield.md new file mode 100644 index 00000000000000..1e6b63be997ffa --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.removefield.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [removeField](./kibana-plugin-plugins-data-public.searchsource.removefield.md) + +## SearchSource.removeField() method + +remove field + +Signature: + +```typescript +removeField(field: K): this; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| field | K | | + +Returns: + +`this` + From 4a5b11cec6873dfd08203f7f8c4b62fd547f5aa9 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 19 Nov 2020 19:53:12 +0100 Subject: [PATCH 25/27] Seems I've broken sorting ... time to fix --- src/plugins/discover/public/application/angular/discover.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 347e2e9ecd538b..272c2f2ca61874 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -710,7 +710,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise updateSearchSource($scope.searchSource, { indexPattern: $scope.indexPattern, services, - state: $scope.state, + sort: $scope.state.sort, }); return Promise.resolve(); }; From 2acc85201512160f96355c983b65b86804675a75 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Tue, 24 Nov 2020 17:32:31 +0100 Subject: [PATCH 26/27] Add jest test to search_source.test.ts --- .../common/search/search_source/search_source.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/plugins/data/common/search/search_source/search_source.test.ts b/src/plugins/data/common/search/search_source/search_source.test.ts index 98d66310c040e1..e7bdcb159f3cbb 100644 --- a/src/plugins/data/common/search/search_source/search_source.test.ts +++ b/src/plugins/data/common/search/search_source/search_source.test.ts @@ -82,6 +82,15 @@ describe('SearchSource', () => { }); }); + describe('#removeField()', () => { + test('remove property', () => { + const searchSource = new SearchSource({}, searchSourceDependencies); + searchSource.setField('aggs', 5); + searchSource.removeField('aggs'); + expect(searchSource.getField('aggs')).toBeFalsy(); + }); + }); + describe(`#setField('index')`, () => { describe('auto-sourceFiltering', () => { describe('new index pattern assigned', () => { From 910b5da65baed2117eeec4f09d6d88ac52b54667 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Wed, 25 Nov 2020 09:18:58 +0100 Subject: [PATCH 27/27] Improve SearchSource code --- src/plugins/data/common/search/search_source/search_source.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index 0334c4041c3bbd..79ef3a3f11ca5a 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -143,9 +143,8 @@ export class SearchSource { setField(field: K, value: SearchSourceFields[K]) { if (value == null) { return this.removeField(field); - } else { - this.fields[field] = value; } + this.fields[field] = value; return this; }