From 0aa4101d7e65720760ceeacd2619a2cc63833b45 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 23 Sep 2021 14:00:47 +0300 Subject: [PATCH] [Discover] Step 2 - remove SavedObjectLoader --- src/plugins/discover/kibana.json | 2 +- .../discover/public/__mocks__/saved_search.ts | 34 -------- .../discover/public/__mocks__/services.ts | 2 - .../apps/context/services/context.ts | 11 ++- .../components/layout/discover_documents.tsx | 4 +- .../components/layout/discover_layout.tsx | 8 +- .../components/top_nav/on_save_search.tsx | 31 +++---- .../apps/main/discover_main_route.tsx | 44 +++++----- .../apps/main/services/discover_state.test.ts | 2 +- .../apps/main/services/use_discover_state.ts | 28 ++++--- .../apps/main/services/use_saved_search.ts | 4 +- .../application/apps/main/utils/fetch_all.ts | 6 +- .../apps/main/utils/fetch_chart.ts | 6 +- .../apps/main/utils/fetch_documents.ts | 4 +- .../apps/main/utils/fetch_total_hits.ts | 4 +- .../main/utils/get_chart_agg_config.test.ts | 4 +- .../apps/main/utils/get_chart_agg_configs.ts | 4 +- .../apps/main/utils/get_dimensions.test.ts | 4 +- .../apps/main/utils/get_sharing_data.ts | 2 +- .../apps/main/utils/get_state_defaults.ts | 9 ++- .../apps/main/utils/persist_saved_search.ts | 7 +- .../apps/main/utils/resolve_index_pattern.ts | 4 +- .../main/utils/update_search_source.test.ts | 2 +- .../apps/main/utils/update_search_source.ts | 2 +- .../helpers/update_search_source.test.ts | 2 +- .../embeddable/saved_search_embeddable.tsx | 5 +- .../embeddable/search_embeddable_factory.ts | 19 +++-- .../public/application/embeddable/types.ts | 2 +- .../embeddable/view_saved_search_action.ts | 3 +- src/plugins/discover/public/build_services.ts | 14 +--- src/plugins/discover/public/index.ts | 11 ++- src/plugins/discover/public/mocks.ts | 4 +- src/plugins/discover/public/plugin.tsx | 26 +++--- .../public/saved_searches/constants.ts | 10 +++ .../saved_searches/get_saved_searches.ts | 75 +++++++++++++++++ .../discover/public/saved_searches/index.ts | 17 +++- .../{ => legacy}/_saved_search.ts | 11 ++- .../public/saved_searches/legacy/index.ts | 10 +++ .../{ => legacy}/saved_searches.ts | 8 +- .../public/saved_searches/legacy/types.ts | 34 ++++++++ .../saved_searches/save_saved_searches.ts | 80 +++++++++++++++++++ .../saved_search_alias_match_redirect.ts | 48 +++++++++++ .../saved_search_url_conflict_callout.ts | 45 +++++++++++ .../saved_searches/saved_searches_utils.ts | 43 ++++++++++ .../discover/public/saved_searches/types.ts | 48 ++++++----- src/plugins/discover/tsconfig.json | 3 +- src/plugins/vis_default_editor/kibana.json | 2 +- .../public/components/sidebar/sidebar.tsx | 4 +- .../components/sidebar/sidebar_title.tsx | 8 +- src/plugins/visualizations/public/plugin.ts | 9 +-- .../public/saved_visualizations/_saved_vis.ts | 6 -- src/plugins/visualizations/public/services.ts | 4 - src/plugins/visualizations/public/vis.ts | 19 +++-- .../visualize/public/application/types.ts | 18 ++--- .../utils/get_visualization_instance.test.ts | 24 +++--- .../utils/get_visualization_instance.ts | 14 ++-- .../get_csv_panel_action.test.ts | 4 +- .../panel_actions/get_csv_panel_action.tsx | 2 +- .../public/__mocks__/shared_imports.ts | 2 +- .../app/hooks/use_search_items/common.ts | 7 -- .../use_search_items/use_search_items.ts | 12 ++- .../transform/public/shared_imports.ts | 2 +- 62 files changed, 622 insertions(+), 261 deletions(-) create mode 100644 src/plugins/discover/public/saved_searches/constants.ts create mode 100644 src/plugins/discover/public/saved_searches/get_saved_searches.ts rename src/plugins/discover/public/saved_searches/{ => legacy}/_saved_search.ts (80%) create mode 100644 src/plugins/discover/public/saved_searches/legacy/index.ts rename src/plugins/discover/public/saved_searches/{ => legacy}/saved_searches.ts (78%) create mode 100644 src/plugins/discover/public/saved_searches/legacy/types.ts create mode 100644 src/plugins/discover/public/saved_searches/save_saved_searches.ts create mode 100644 src/plugins/discover/public/saved_searches/saved_search_alias_match_redirect.ts create mode 100644 src/plugins/discover/public/saved_searches/saved_search_url_conflict_callout.ts create mode 100644 src/plugins/discover/public/saved_searches/saved_searches_utils.ts diff --git a/src/plugins/discover/kibana.json b/src/plugins/discover/kibana.json index 46eeb5af1470d58..09557ecddea6ae0 100644 --- a/src/plugins/discover/kibana.json +++ b/src/plugins/discover/kibana.json @@ -15,7 +15,7 @@ "savedObjects", "indexPatternFieldEditor" ], - "optionalPlugins": ["home", "share", "usageCollection"], + "optionalPlugins": ["home", "share", "usageCollection", "spaces"], "requiredBundles": ["kibanaUtils", "home", "kibanaReact", "fieldFormats"], "extraPublicDirs": ["common"], "owner": { diff --git a/src/plugins/discover/public/__mocks__/saved_search.ts b/src/plugins/discover/public/__mocks__/saved_search.ts index ebe65a57703562e..a488fe7e04c503d 100644 --- a/src/plugins/discover/public/__mocks__/saved_search.ts +++ b/src/plugins/discover/public/__mocks__/saved_search.ts @@ -13,44 +13,10 @@ import { indexPatternWithTimefieldMock } from './index_pattern_with_timefield'; export const savedSearchMock = { 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, searchSource: createSearchSourceMock({ index: indexPatternMock }), } as unknown as SavedSearch; export const savedSearchMockWithTimeField = { id: 'the-saved-search-id-with-timefield', - 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, searchSource: createSearchSourceMock({ index: indexPatternWithTimefieldMock }), } as unknown as SavedSearch; diff --git a/src/plugins/discover/public/__mocks__/services.ts b/src/plugins/discover/public/__mocks__/services.ts index d24aefb6c8192af..087796f89f48707 100644 --- a/src/plugins/discover/public/__mocks__/services.ts +++ b/src/plugins/discover/public/__mocks__/services.ts @@ -16,7 +16,6 @@ import { SAMPLE_SIZE_SETTING, SORT_DEFAULT_ORDER_SETTING, } from '../../common'; -import { savedSearchMock } from './saved_search'; import { UI_SETTINGS } from '../../../data/common'; import { TopNavMenu } from '../../../navigation/public'; import { FORMATS_UI_SETTINGS } from 'src/plugins/field_formats/common'; @@ -78,7 +77,6 @@ export const discoverServiceMock = { editIndexPattern: jest.fn(), }, }, - getSavedSearchById: (id?: string) => Promise.resolve(savedSearchMock), navigation: { ui: { TopNavMenu }, }, diff --git a/src/plugins/discover/public/application/apps/context/services/context.ts b/src/plugins/discover/public/application/apps/context/services/context.ts index 237de8e52e656f3..f7fd65cda44c593 100644 --- a/src/plugins/discover/public/application/apps/context/services/context.ts +++ b/src/plugins/discover/public/application/apps/context/services/context.ts @@ -5,7 +5,12 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { Filter, IndexPattern, IndexPatternsContract, SearchSource } from 'src/plugins/data/public'; +import { + Filter, + IndexPattern, + IndexPatternsContract, + ISearchSource, +} from 'src/plugins/data/public'; import { reverseSortDir, SortDirection } from './utils/sorting'; import { convertIsoToMillis, extractNanos } from './utils/date_conversion'; import { fetchHitsInInterval } from './utils/fetch_hits_in_interval'; @@ -58,7 +63,7 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract, useNewFields } const indexPattern = await indexPatterns.get(indexPatternId); const { data } = getServices(); - const searchSource = data.search.searchSource.createEmpty() as SearchSource; + const searchSource = data.search.searchSource.createEmpty(); updateSearchSource(searchSource, indexPattern, filters, Boolean(useNewFieldsApi)); const sortDirToApply = type === SurrDocType.SUCCESSORS ? sortDir : reverseSortDir(sortDir); @@ -110,7 +115,7 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract, useNewFields } export function updateSearchSource( - searchSource: SearchSource, + searchSource: ISearchSource, indexPattern: IndexPattern, filters: Filter[], useNewFieldsApi: boolean diff --git a/src/plugins/discover/public/application/apps/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/apps/main/components/layout/discover_documents.tsx index e0e0c9c6f8831c0..d6ede9aa7fe5fe5 100644 --- a/src/plugins/discover/public/application/apps/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/apps/main/components/layout/discover_documents.tsx @@ -134,7 +134,7 @@ function DiscoverDocumentsComponent({ sort={state.sort || []} isLoading={isLoading} searchDescription={savedSearch.description} - sharedItemTitle={savedSearch.lastSavedTitle} + sharedItemTitle={savedSearch.title} onAddColumn={onAddColumn} onFilter={onAddFilter as DocViewFilterFn} onMoveColumn={onMoveColumn} @@ -156,7 +156,7 @@ function DiscoverDocumentsComponent({ sort={(state.sort as SortPairArr[]) || []} sampleSize={sampleSize} searchDescription={savedSearch.description} - searchTitle={savedSearch.lastSavedTitle} + searchTitle={savedSearch.title} setExpandedDoc={setExpandedDoc} showTimeCol={showTimeCol} services={services} diff --git a/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx index c2d09f31e3e0ac3..b904dd012e79ea2 100644 --- a/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx @@ -39,7 +39,10 @@ import { useDataGridColumns } from '../../../../helpers/use_data_grid_columns'; import { DiscoverDocuments } from './discover_documents'; import { FetchStatus } from '../../../../types'; import { useDataState } from '../../utils/use_data_state'; - +import { + SavedSearchURLConflictCallout, + useSavedSearchAliasMatchRedirect, +} from '../../../../../saved_searches'; const SidebarMemoized = React.memo(DiscoverSidebarResponsive); const TopNavMemoized = React.memo(DiscoverTopNav); const DiscoverChartMemoized = React.memo(DiscoverChart); @@ -74,6 +77,8 @@ export function DiscoverLayout({ } }, [dataState.fetchStatus]); + useSavedSearchAliasMatchRedirect({ savedSearch, spaces: services.spaces }); + const timeField = useMemo(() => { return indexPattern.type !== 'rollup' ? indexPattern.timeFieldName : undefined; }, [indexPattern]); @@ -162,6 +167,7 @@ export function DiscoverLayout({ resetSavedSearch={resetSavedSearch} /> +

{savedSearch.title}

diff --git a/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx b/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx index c68b1ab7b844c0a..1f28e9ae771143a 100644 --- a/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx +++ b/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { SavedObjectSaveModal, showSaveModal } from '../../../../../../../saved_objects/public'; -import { SavedSearch } from '../../../../../saved_searches'; +import { SavedSearch, SaveSavedSearchOptions } from '../../../../../saved_searches'; import { IndexPattern } from '../../../../../../../data/common'; import { DiscoverServices } from '../../../../../build_services'; import { GetStateReturn } from '../../services/discover_state'; @@ -27,11 +27,7 @@ async function saveDataSource({ indexPattern: IndexPattern; navigateTo: (url: string) => void; savedSearch: SavedSearch; - saveOptions: { - confirmOverwrite: boolean; - isTitleDuplicateConfirmed: boolean; - onTitleDuplicate: () => void; - }; + saveOptions: SaveSavedSearchOptions; services: DiscoverServices; state: GetStateReturn; }) { @@ -47,14 +43,20 @@ async function saveDataSource({ }), 'data-test-subj': 'saveSearchSuccess', }); - - if (savedSearch.id !== prevSavedSearchId) { - navigateTo(`/view/${encodeURIComponent(savedSearch.id)}`); + if (id !== prevSavedSearchId) { + navigateTo(`/view/${encodeURIComponent(id)}`); } else { // Update defaults so that "reload saved query" functions correctly state.resetAppState(); - services.chrome.docTitle.change(savedSearch.lastSavedTitle!); - setBreadcrumbsTitle(savedSearch, services.chrome); + services.chrome.docTitle.change(savedSearch.title!); + + setBreadcrumbsTitle( + { + ...savedSearch, + id: prevSavedSearchId ?? id, + }, + services.chrome + ); } } } @@ -106,11 +108,10 @@ export async function onSaveSearch({ }) => { const currentTitle = savedSearch.title; savedSearch.title = newTitle; - savedSearch.copyOnSave = newCopyOnSave; - const saveOptions = { + const saveOptions: SaveSavedSearchOptions = { confirmOverwrite: false, - isTitleDuplicateConfirmed, onTitleDuplicate, + copyOnSave: newCopyOnSave, }; const response = await saveDataSource({ indexPattern, @@ -133,7 +134,7 @@ export async function onSaveSearch({ {}} - title={savedSearch.title} + title={savedSearch.title ?? ''} showCopyOnSave={!!savedSearch.id} objectType={i18n.translate('discover.localMenu.saveSaveSearchObjectType', { defaultMessage: 'search', diff --git a/src/plugins/discover/public/application/apps/main/discover_main_route.tsx b/src/plugins/discover/public/application/apps/main/discover_main_route.tsx index 5141908e44adead..d7580ccf7b92a7b 100644 --- a/src/plugins/discover/public/application/apps/main/discover_main_route.tsx +++ b/src/plugins/discover/public/application/apps/main/discover_main_route.tsx @@ -8,10 +8,9 @@ import React, { useEffect, useState, memo } from 'react'; import { History } from 'history'; import { useParams } from 'react-router-dom'; -import type { SavedObject as SavedObjectDeprecated } from 'src/plugins/saved_objects/public'; -import { IndexPatternAttributes, SavedObject } from 'src/plugins/data/common'; +import { IndexPatternAttributes, ISearchSource, SavedObject } from 'src/plugins/data/common'; import { DiscoverServices } from '../../../build_services'; -import { SavedSearch } from '../../../saved_searches'; +import { SavedSearch, getSavedSearch, getSavedSearchFullPathUrl } from '../../../saved_searches'; import { getState } from './services/discover_state'; import { loadIndexPattern, resolveIndexPattern } from './utils/resolve_index_pattern'; import { DiscoverMainApp } from './discover_main_app'; @@ -58,18 +57,16 @@ export function DiscoverMainRoute({ services, history }: DiscoverMainProps) { useEffect(() => { const savedSearchId = id; - async function loadDefaultOrCurrentIndexPattern(usedSavedSearch: SavedSearch) { + async function loadDefaultOrCurrentIndexPattern(searchSource: ISearchSource) { await data.indexPatterns.ensureDefaultDataView(); const { appStateContainer } = getState({ history, uiSettings: config }); const { index } = appStateContainer.getState(); const ip = await loadIndexPattern(index || '', data.indexPatterns, config); const ipList = ip.list as Array>; - const indexPatternData = await resolveIndexPattern( - ip, - usedSavedSearch.searchSource, - toastNotifications - ); + const indexPatternData = await resolveIndexPattern(ip, searchSource, toastNotifications); + setIndexPatternList(ipList); + return indexPatternData; } @@ -77,17 +74,27 @@ export function DiscoverMainRoute({ services, history }: DiscoverMainProps) { try { // force a refresh if a given saved search without id was saved setSavedSearch(undefined); - const loadedSavedSearch = await services.getSavedSearchById(savedSearchId); - const loadedIndexPattern = await loadDefaultOrCurrentIndexPattern(loadedSavedSearch); - if (loadedSavedSearch && !loadedSavedSearch?.searchSource.getField('index')) { - loadedSavedSearch.searchSource.setField('index', loadedIndexPattern); + + const currentSavedSearch = await getSavedSearch(savedSearchId, { + search: services.data.search, + savedObjectsClient: core.savedObjects.client, + }); + + const loadedIndexPattern = await loadDefaultOrCurrentIndexPattern( + currentSavedSearch.searchSource + ); + + if (!currentSavedSearch.searchSource.getField('index')) { + currentSavedSearch.searchSource.setField('index', loadedIndexPattern); } - setSavedSearch(loadedSavedSearch); - if (savedSearchId) { + + setSavedSearch(currentSavedSearch); + + if (currentSavedSearch.id) { chrome.recentlyAccessed.add( - (loadedSavedSearch as unknown as SavedObjectDeprecated).getFullPath(), - loadedSavedSearch.title, - loadedSavedSearch.id + getSavedSearchFullPathUrl(currentSavedSearch.id), + currentSavedSearch.title ?? '', + currentSavedSearch.id ); } } catch (e) { @@ -112,6 +119,7 @@ export function DiscoverMainRoute({ services, history }: DiscoverMainProps) { loadSavedSearch(); }, [ + core.savedObjects.client, basePath, chrome.recentlyAccessed, config, diff --git a/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts b/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts index 905d81a6fc71602..9968ca6f1f63fe0 100644 --- a/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts +++ b/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts @@ -14,7 +14,7 @@ import { } from './discover_state'; import { createBrowserHistory, History } from 'history'; import { dataPluginMock } from '../../../../../../data/public/mocks'; -import { SavedSearch } from '../../../../saved_searches'; +import type { SavedSearch } from '../../../../saved_searches'; import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../../common'; let history: History; diff --git a/src/plugins/discover/public/application/apps/main/services/use_discover_state.ts b/src/plugins/discover/public/application/apps/main/services/use_discover_state.ts index e11a9937111a192..bdf40bebed193fd 100644 --- a/src/plugins/discover/public/application/apps/main/services/use_discover_state.ts +++ b/src/plugins/discover/public/application/apps/main/services/use_discover_state.ts @@ -11,7 +11,7 @@ import { History } from 'history'; import { getState } from './discover_state'; import { getStateDefaults } from '../utils/get_state_defaults'; import { DiscoverServices } from '../../../../build_services'; -import { SavedSearch } from '../../../../saved_searches'; +import { SavedSearch, getSavedSearch } from '../../../../saved_searches'; import { loadIndexPattern } from '../utils/resolve_index_pattern'; import { useSavedSearch as useSavedSearchData } from './use_saved_search'; import { @@ -147,16 +147,22 @@ export function useDiscoverState({ */ const resetSavedSearch = useCallback( async (id?: string) => { - const newSavedSearch = await services.getSavedSearchById(id); - const newIndexPattern = newSavedSearch.searchSource.getField('index') || indexPattern; - newSavedSearch.searchSource.setField('index', newIndexPattern); - const newAppState = getStateDefaults({ - config, - data, - savedSearch: newSavedSearch, - }); - await stateContainer.replaceUrlAppState(newAppState); - setState(newAppState); + if (id) { + const newSavedSearch = await getSavedSearch(id, { + search: services.data.search, + savedObjectsClient: services.core.savedObjects.client, + }); + + const newIndexPattern = newSavedSearch.searchSource.getField('index') || indexPattern; + newSavedSearch.searchSource.setField('index', newIndexPattern); + const newAppState = getStateDefaults({ + config, + data, + savedSearch: newSavedSearch, + }); + await stateContainer.replaceUrlAppState(newAppState); + setState(newAppState); + } }, [indexPattern, services, config, data, stateContainer] ); diff --git a/src/plugins/discover/public/application/apps/main/services/use_saved_search.ts b/src/plugins/discover/public/application/apps/main/services/use_saved_search.ts index 164dff862779052..bfdb046f6934b7d 100644 --- a/src/plugins/discover/public/application/apps/main/services/use_saved_search.ts +++ b/src/plugins/discover/public/application/apps/main/services/use_saved_search.ts @@ -10,7 +10,7 @@ import { BehaviorSubject, merge, Subject } from 'rxjs'; import { debounceTime, filter, tap } from 'rxjs/operators'; import { DiscoverServices } from '../../../../build_services'; import { DiscoverSearchSessionManager } from './discover_search_session'; -import { SearchSource } from '../../../../../../data/common'; +import { ISearchSource } from '../../../../../../data/common'; import { GetStateReturn } from './discover_state'; import { ElasticSearchHit } from '../../../doc_views/doc_views_types'; import { RequestAdapter } from '../../../../../../inspector/public'; @@ -91,7 +91,7 @@ export const useSavedSearch = ({ }: { initialFetchStatus: FetchStatus; searchSessionManager: DiscoverSearchSessionManager; - searchSource: SearchSource; + searchSource: ISearchSource; services: DiscoverServices; stateContainer: GetStateReturn; useNewFieldsApi: boolean; diff --git a/src/plugins/discover/public/application/apps/main/utils/fetch_all.ts b/src/plugins/discover/public/application/apps/main/utils/fetch_all.ts index 53d13ee547b0ffb..e9d9335abcda052 100644 --- a/src/plugins/discover/public/application/apps/main/utils/fetch_all.ts +++ b/src/plugins/discover/public/application/apps/main/utils/fetch_all.ts @@ -14,11 +14,11 @@ import { sendResetMsg, } from '../services/use_saved_search_messages'; import { updateSearchSource } from './update_search_source'; -import { SortOrder } from '../../../../saved_searches/types'; +import type { SortOrder } from '../../../../saved_searches'; import { fetchDocuments } from './fetch_documents'; import { fetchTotalHits } from './fetch_total_hits'; import { fetchChart } from './fetch_chart'; -import { SearchSource } from '../../../../../../data/common'; +import { ISearchSource } from '../../../../../../data/common'; import { Adapters } from '../../../../../../inspector'; import { AppState } from '../services/discover_state'; import { FetchStatus } from '../../../types'; @@ -29,7 +29,7 @@ import { ReduxLikeStateContainer } from '../../../../../../kibana_utils/common'; export function fetchAll( dataSubjects: SavedSearchData, - searchSource: SearchSource, + searchSource: ISearchSource, reset = false, fetchDeps: { abortController: AbortController; diff --git a/src/plugins/discover/public/application/apps/main/utils/fetch_chart.ts b/src/plugins/discover/public/application/apps/main/utils/fetch_chart.ts index 67f34c7503c59c7..50f3a1b8bfea763 100644 --- a/src/plugins/discover/public/application/apps/main/utils/fetch_chart.ts +++ b/src/plugins/discover/public/application/apps/main/utils/fetch_chart.ts @@ -11,7 +11,7 @@ import { DataPublicPluginStart, isCompleteResponse, search, - SearchSource, + ISearchSource, } from '../../../../../../data/public'; import { Adapters } from '../../../../../../inspector'; import { getChartAggConfigs, getDimensions } from './index'; @@ -25,7 +25,7 @@ import { sendErrorMsg, sendLoadingMsg } from '../services/use_saved_search_messa export function fetchChart( data$: SavedSearchData, - searchSource: SearchSource, + searchSource: ISearchSource, { abortController, appStateContainer, @@ -114,7 +114,7 @@ export function fetchChart( } export function updateSearchSource( - searchSource: SearchSource, + searchSource: ISearchSource, interval: string, data: DataPublicPluginStart ) { diff --git a/src/plugins/discover/public/application/apps/main/utils/fetch_documents.ts b/src/plugins/discover/public/application/apps/main/utils/fetch_documents.ts index 2f06a9dbbb3db37..6c5eff7cff70225 100644 --- a/src/plugins/discover/public/application/apps/main/utils/fetch_documents.ts +++ b/src/plugins/discover/public/application/apps/main/utils/fetch_documents.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { filter } from 'rxjs/operators'; import { Adapters } from '../../../../../../inspector/common'; -import { isCompleteResponse, SearchSource } from '../../../../../../data/common'; +import { isCompleteResponse, ISearchSource } from '../../../../../../data/common'; import { FetchStatus } from '../../../types'; import { SavedSearchData } from '../services/use_saved_search'; import { sendErrorMsg, sendLoadingMsg } from '../services/use_saved_search_messages'; @@ -17,7 +17,7 @@ import { DiscoverServices } from '../../../../build_services'; export const fetchDocuments = ( data$: SavedSearchData, - searchSource: SearchSource, + searchSource: ISearchSource, { abortController, inspectorAdapters, diff --git a/src/plugins/discover/public/application/apps/main/utils/fetch_total_hits.ts b/src/plugins/discover/public/application/apps/main/utils/fetch_total_hits.ts index 9688f5ddd614d43..cfab0d17fcd549a 100644 --- a/src/plugins/discover/public/application/apps/main/utils/fetch_total_hits.ts +++ b/src/plugins/discover/public/application/apps/main/utils/fetch_total_hits.ts @@ -11,7 +11,7 @@ import { filter } from 'rxjs/operators'; import { DataPublicPluginStart, isCompleteResponse, - SearchSource, + ISearchSource, } from '../../../../../../data/public'; import { Adapters } from '../../../../../../inspector/common'; import { FetchStatus } from '../../../types'; @@ -20,7 +20,7 @@ import { sendErrorMsg, sendLoadingMsg } from '../services/use_saved_search_messa export function fetchTotalHits( data$: SavedSearchData, - searchSource: SearchSource, + searchSource: ISearchSource, { abortController, data, diff --git a/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_config.test.ts b/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_config.test.ts index 3eef49fe6ddcb05..515565f0062c958 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_config.test.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_config.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ import { indexPatternWithTimefieldMock } from '../../../../__mocks__/index_pattern_with_timefield'; -import { SearchSource } from '../../../../../../data/public'; +import { ISearchSource } from '../../../../../../data/public'; import { dataPluginMock } from '../../../../../../data/public/mocks'; import { getChartAggConfigs } from './get_chart_agg_configs'; @@ -22,7 +22,7 @@ describe('getChartAggConfigs', () => { } }, removeField: jest.fn(), - } as unknown as SearchSource; + } as unknown as ISearchSource; const dataMock = dataPluginMock.createStartContract(); diff --git a/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_configs.ts b/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_configs.ts index 2665254027fd916..65f98f72beec059 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_configs.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_chart_agg_configs.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { SearchSource } from '../../../../../../data/common'; +import { ISearchSource } from '../../../../../../data/common'; import { DataPublicPluginStart } from '../../../../../../data/public'; /** @@ -13,7 +13,7 @@ import { DataPublicPluginStart } from '../../../../../../data/public'; * for Discover's histogram vis */ export function getChartAggConfigs( - searchSource: SearchSource, + searchSource: ISearchSource, histogramInterval: string, data: DataPublicPluginStart ) { diff --git a/src/plugins/discover/public/application/apps/main/utils/get_dimensions.test.ts b/src/plugins/discover/public/application/apps/main/utils/get_dimensions.test.ts index b98662f2db3b5b1..35a6e955fe5b233 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_dimensions.test.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_dimensions.test.ts @@ -9,7 +9,7 @@ import { dataPluginMock } from '../../../../../../data/public/mocks'; import { getDimensions } from './get_dimensions'; import { indexPatternWithTimefieldMock } from '../../../../__mocks__/index_pattern_with_timefield'; -import { SearchSource, calculateBounds } from '../../../../../../data/common'; +import { ISearchSource, calculateBounds } from '../../../../../../data/common'; import { getChartAggConfigs } from './get_chart_agg_configs'; test('getDimensions', () => { @@ -23,7 +23,7 @@ test('getDimensions', () => { return indexPattern; } }, - } as unknown as SearchSource; + } as unknown as ISearchSource; const dataMock = dataPluginMock.createStartContract(); dataMock.query.timefilter.timefilter.getTime = () => { diff --git a/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts b/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts index 420ff0fa11eeb93..33bf61c194d9b32 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts @@ -11,7 +11,7 @@ import type { IUiSettingsClient } from 'src/core/public'; import type { DataPublicPluginStart } from 'src/plugins/data/public'; import type { ISearchSource } from 'src/plugins/data/common'; import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../../../common'; -import type { SavedSearch, SortOrder } from '../../../../saved_searches/types'; +import type { SavedSearch, SortOrder } from '../../../../saved_searches'; import { getSortForSearchSource } from '../components/doc_table'; import { AppState } from '../services/discover_state'; diff --git a/src/plugins/discover/public/application/apps/main/utils/get_state_defaults.ts b/src/plugins/discover/public/application/apps/main/utils/get_state_defaults.ts index 4061d9a61f0a39c..ae9a97c4385c487 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_state_defaults.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_state_defaults.ts @@ -31,10 +31,11 @@ export function getStateDefaults({ data: DataPublicPluginStart; savedSearch: SavedSearch; }) { - const searchSource = savedSearch.searchSource; - const indexPattern = savedSearch.searchSource.getField('index'); + const { searchSource } = savedSearch; + const indexPattern = searchSource.getField('index'); + const query = searchSource.getField('query') || data.query.queryString.getDefaultQuery(); - const sort = getSortArray(savedSearch.sort, indexPattern!); + const sort = getSortArray(savedSearch.sort ?? [], indexPattern!); const columns = getDefaultColumns(savedSearch, config); const defaultState = { @@ -43,7 +44,7 @@ export function getStateDefaults({ ? getDefaultSort(indexPattern, config.get(SORT_DEFAULT_ORDER_SETTING, 'desc')) : sort, columns, - index: indexPattern!.id, + index: indexPattern?.id, interval: 'auto', filters: cloneDeep(searchSource.getOwnField('filter')), hideChart: undefined, diff --git a/src/plugins/discover/public/application/apps/main/utils/persist_saved_search.ts b/src/plugins/discover/public/application/apps/main/utils/persist_saved_search.ts index a5e1e2bb6c2ea6d..d42f5595489a9f7 100644 --- a/src/plugins/discover/public/application/apps/main/utils/persist_saved_search.ts +++ b/src/plugins/discover/public/application/apps/main/utils/persist_saved_search.ts @@ -10,9 +10,10 @@ import { updateSearchSource } from './update_search_source'; import { IndexPattern } from '../../../../../../data/public'; import { SavedSearch } from '../../../../saved_searches'; import { AppState } from '../services/discover_state'; -import { SortOrder } from '../../../../saved_searches/types'; +import type { SortOrder } from '../../../../saved_searches'; import { SavedObjectSaveOpts } from '../../../../../../saved_objects/public'; import { DiscoverServices } from '../../../../build_services'; +import { saveSavedSearch } from '../../../../saved_searches'; /** * Helper function to update and persist the given savedSearch @@ -52,8 +53,8 @@ export async function persistSavedSearch( } try { - const id = await savedSearch.save(saveOptions); - onSuccess(id); + const id = await saveSavedSearch(savedSearch, saveOptions, services.core.savedObjects.client); + onSuccess(id!); return { id }; } catch (saveError) { onError(saveError, savedSearch); diff --git a/src/plugins/discover/public/application/apps/main/utils/resolve_index_pattern.ts b/src/plugins/discover/public/application/apps/main/utils/resolve_index_pattern.ts index d30b67db3118650..613615446ee08c3 100644 --- a/src/plugins/discover/public/application/apps/main/utils/resolve_index_pattern.ts +++ b/src/plugins/discover/public/application/apps/main/utils/resolve_index_pattern.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import type { IndexPattern, IndexPatternsContract, SearchSource } from 'src/plugins/data/common'; +import type { IndexPattern, IndexPatternsContract, ISearchSource } from 'src/plugins/data/common'; import type { IUiSettingsClient, SavedObject, ToastsStart } from 'kibana/public'; export type IndexPatternSavedObject = SavedObject & { title: string }; @@ -95,7 +95,7 @@ export async function loadIndexPattern( */ export function resolveIndexPattern( ip: IndexPatternData, - searchSource: SearchSource, + searchSource: ISearchSource, toastNotifications: ToastsStart ) { const { loaded: loadedIndexPattern, stateVal, stateValFound } = ip; diff --git a/src/plugins/discover/public/application/apps/main/utils/update_search_source.test.ts b/src/plugins/discover/public/application/apps/main/utils/update_search_source.test.ts index 945140e0586abf5..22f3b6ad86f6c0e 100644 --- a/src/plugins/discover/public/application/apps/main/utils/update_search_source.test.ts +++ b/src/plugins/discover/public/application/apps/main/utils/update_search_source.test.ts @@ -9,7 +9,7 @@ import { updateSearchSource } from './update_search_source'; import { createSearchSourceMock } from '../../../../../../data/common/search/search_source/mocks'; import { indexPatternMock } from '../../../../__mocks__/index_pattern'; -import { SortOrder } from '../../../../saved_searches/types'; +import type { SortOrder } from '../../../../saved_searches'; import { discoverServiceMock } from '../../../../__mocks__/services'; describe('updateSearchSource', () => { diff --git a/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts b/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts index 4dfcbc7b79712f2..6d592e176afe50c 100644 --- a/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts +++ b/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts @@ -8,7 +8,7 @@ import { SORT_DEFAULT_ORDER_SETTING } from '../../../../../common'; import { IndexPattern, ISearchSource } from '../../../../../../data/common'; -import { SortOrder } from '../../../../saved_searches/types'; +import type { SortOrder } from '../../../../saved_searches'; import { DiscoverServices } from '../../../../build_services'; import { getSortForSearchSource } from '../components/doc_table'; diff --git a/src/plugins/discover/public/application/embeddable/helpers/update_search_source.test.ts b/src/plugins/discover/public/application/embeddable/helpers/update_search_source.test.ts index f3edc523f446466..f09131cb5c926c1 100644 --- a/src/plugins/discover/public/application/embeddable/helpers/update_search_source.test.ts +++ b/src/plugins/discover/public/application/embeddable/helpers/update_search_source.test.ts @@ -8,7 +8,7 @@ import { createSearchSourceMock } from '../../../../../data/common/search/search_source/mocks'; import { updateSearchSource } from './update_search_source'; import { indexPatternMock } from '../../../__mocks__/index_pattern'; -import { SortOrder } from '../../../saved_searches/types'; +import type { SortOrder } from '../../../saved_searches'; describe('updateSearchSource', () => { const defaults = { diff --git a/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx b/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx index ef8670f97667243..8849806cf5959d9 100644 --- a/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx +++ b/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx @@ -165,7 +165,7 @@ export class SavedSearchEmbeddable const executionContext = { type: this.type, name: 'discover', - id: this.savedSearch.id, + id: this.savedSearch.id!, description: this.output.title || this.output.defaultTitle || '', url: this.output.editUrl, parent: this.input.executionContext, @@ -232,7 +232,7 @@ export class SavedSearchEmbeddable searchDescription: this.savedSearch.description, description: this.savedSearch.description, inspectorAdapters: this.inspectorAdapters, - searchTitle: this.savedSearch.lastSavedTitle, + searchTitle: this.savedSearch.title, services: this.services, onAddColumn: (columnName: string) => { if (!props.columns) { @@ -404,7 +404,6 @@ export class SavedSearchEmbeddable public destroy() { super.destroy(); - this.savedSearch.destroy(); if (this.searchProps) { delete this.searchProps; } diff --git a/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts b/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts index 95f5b2d3ce2845b..645e3c5c6735700 100644 --- a/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts +++ b/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts @@ -20,6 +20,7 @@ import { TimeRange } from '../../../../data/public'; import { SearchInput, SearchOutput } from './types'; import { SEARCH_EMBEDDABLE_TYPE } from './constants'; import { SavedSearchEmbeddable } from './saved_search_embeddable'; +import { getSavedSearch, getSavedSearchUrl } from '../../saved_searches'; interface StartServices { executeTriggerActions: UiActionsStart['executeTriggerActions']; @@ -59,20 +60,24 @@ export class SearchEmbeddableFactory input: Partial & { id: string; timeRange: TimeRange }, parent?: Container ): Promise => { - const filterManager = getServices().filterManager; - - const url = await getServices().getSavedSearchUrlById(savedObjectId); - const editUrl = getServices().addBasePath(`/app/discover${url}`); + const services = getServices(); + const filterManager = services.filterManager; + const url = getSavedSearchUrl(savedObjectId); + const editUrl = services.addBasePath(`/app/discover${url}`); try { - const savedObject = await getServices().getSavedSearchById(savedObjectId); - const indexPattern = savedObject.searchSource.getField('index'); + const savedSearch = await getSavedSearch(savedObjectId, { + search: services.data.search, + savedObjectsClient: services.core.savedObjects.client, + }); + + const indexPattern = savedSearch.searchSource.getField('index'); const { executeTriggerActions } = await this.getStartServices(); const { SavedSearchEmbeddable: SavedSearchEmbeddableClass } = await import( './saved_search_embeddable' ); return new SavedSearchEmbeddableClass( { - savedSearch: savedObject, + savedSearch, editUrl, editPath: url, filterManager, diff --git a/src/plugins/discover/public/application/embeddable/types.ts b/src/plugins/discover/public/application/embeddable/types.ts index 5a08534918d4f55..de109e3fa787943 100644 --- a/src/plugins/discover/public/application/embeddable/types.ts +++ b/src/plugins/discover/public/application/embeddable/types.ts @@ -13,7 +13,7 @@ import { IEmbeddable, } from 'src/plugins/embeddable/public'; import { Filter, IndexPattern, TimeRange, Query } from '../../../../data/public'; -import { SavedSearch } from '../..'; +import { SavedSearch } from '../../saved_searches'; import { SortOrder } from '../apps/main/components/doc_table/components/table_header/helpers'; export interface SearchInput extends EmbeddableInput { diff --git a/src/plugins/discover/public/application/embeddable/view_saved_search_action.ts b/src/plugins/discover/public/application/embeddable/view_saved_search_action.ts index 69c273f326c617d..e4b97d011ff6458 100644 --- a/src/plugins/discover/public/application/embeddable/view_saved_search_action.ts +++ b/src/plugins/discover/public/application/embeddable/view_saved_search_action.ts @@ -12,6 +12,7 @@ import { IEmbeddable, ViewMode } from '../../../../embeddable/public'; import { Action } from '../../../../ui_actions/public'; import { SavedSearchEmbeddable } from './saved_search_embeddable'; import { SEARCH_EMBEDDABLE_TYPE } from '../../../common'; +import { getSavedSearchUrl } from '../../saved_searches'; export const ACTION_VIEW_SAVED_SEARCH = 'ACTION_VIEW_SAVED_SEARCH'; @@ -28,7 +29,7 @@ export class ViewSavedSearchAction implements Action { async execute(context: ActionExecutionContext): Promise { const { embeddable } = context; const savedSearchId = (embeddable as SavedSearchEmbeddable).getSavedSearch().id; - const path = `#/view/${encodeURIComponent(savedSearchId)}`; + const path = getSavedSearchUrl(savedSearchId); const app = embeddable ? embeddable.getOutput().editApp : undefined; await this.application.navigateToApp(app ? app : 'discover', { path }); } diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index 1efae6cd2c0ec42..006404bb9cf84dd 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -30,13 +30,14 @@ import { ChartsPluginStart } from 'src/plugins/charts/public'; import { UiCounterMetricType } from '@kbn/analytics'; import { DiscoverStartPlugins } from './plugin'; -import { createSavedSearchesLoader, SavedSearch } from './saved_searches'; import { getHistory } from './kibana_services'; import { KibanaLegacyStart } from '../../kibana_legacy/public'; import { UrlForwardingStart } from '../../url_forwarding/public'; import { NavigationPublicPluginStart } from '../../navigation/public'; import { IndexPatternFieldEditorStart } from '../../index_pattern_field_editor/public'; +import type { SpacesApi } from '../../../../x-pack/plugins/spaces/public'; + export interface DiscoverServices { addBasePath: (path: string) => string; capabilities: Capabilities; @@ -56,12 +57,11 @@ export interface DiscoverServices { urlForwarding: UrlForwardingStart; timefilter: TimefilterContract; toastNotifications: ToastsStart; - getSavedSearchById: (id?: string) => Promise; - getSavedSearchUrlById: (id: string) => Promise; uiSettings: IUiSettingsClient; trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; indexPatternFieldEditor: IndexPatternFieldEditorStart; http: HttpStart; + spaces?: SpacesApi; } export function buildServices( @@ -69,11 +69,6 @@ export function buildServices( plugins: DiscoverStartPlugins, context: PluginInitializerContext ): DiscoverServices { - const services = { - savedObjectsClient: core.savedObjects.client, - savedObjects: plugins.savedObjects, - }; - const savedObjectService = createSavedSearchesLoader(services); const { usageCollection } = plugins; return { @@ -85,8 +80,6 @@ export function buildServices( docLinks: core.docLinks, theme: plugins.charts.theme, filterManager: plugins.data.query.filterManager, - getSavedSearchById: async (id?: string) => savedObjectService.get(id), - getSavedSearchUrlById: async (id: string) => savedObjectService.urlFor(id), history: getHistory, indexPatterns: plugins.data.indexPatterns, inspector: plugins.inspector, @@ -103,5 +96,6 @@ export function buildServices( trackUiMetric: usageCollection?.reportUiCounter.bind(usageCollection, 'discover'), indexPatternFieldEditor: plugins.indexPatternFieldEditor, http: core.http, + spaces: plugins.spaces, }; } diff --git a/src/plugins/discover/public/index.ts b/src/plugins/discover/public/index.ts index 3840df4353faf8b..5215b67784c88df 100644 --- a/src/plugins/discover/public/index.ts +++ b/src/plugins/discover/public/index.ts @@ -9,12 +9,21 @@ import { PluginInitializerContext } from 'kibana/public'; import { DiscoverPlugin } from './plugin'; +export { + getSavedSearch, + getSavedSearchFullPathUrl, + getSavedSearchUrl, + SavedSearch, + LegacySavedSearch, + SavedSearchLoader, + __LEGACY, +} from './saved_searches'; + export { DiscoverSetup, DiscoverStart } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { return new DiscoverPlugin(initializerContext); } -export { SavedSearch, SavedSearchLoader, createSavedSearchesLoader } from './saved_searches'; export { ISearchEmbeddable, SEARCH_EMBEDDABLE_TYPE, SearchInput } from './application/embeddable'; export { loadSharingDataHelpers } from './shared'; diff --git a/src/plugins/discover/public/mocks.ts b/src/plugins/discover/public/mocks.ts index 71de630132b0ac6..6a3c703ea0da872 100644 --- a/src/plugins/discover/public/mocks.ts +++ b/src/plugins/discover/public/mocks.ts @@ -24,7 +24,9 @@ const createSetupContract = (): Setup => { const createStartContract = (): Start => { const startContract: Start = { - savedSearchLoader: {} as DiscoverStart['savedSearchLoader'], + __LEGACY: { + savedSearchLoader: {} as DiscoverStart['__LEGACY']['savedSearchLoader'], + }, urlGenerator: { createUrl: jest.fn(), } as unknown as DiscoverStart['urlGenerator'], diff --git a/src/plugins/discover/public/plugin.tsx b/src/plugins/discover/public/plugin.tsx index afb83d6cbd6678d..fa4eb2656dfaa7d 100644 --- a/src/plugins/discover/public/plugin.tsx +++ b/src/plugins/discover/public/plugin.tsx @@ -45,7 +45,7 @@ import { getScopedHistory, syncHistoryLocations, } from './kibana_services'; -import { createSavedSearchesLoader } from './saved_searches'; +import { __LEGACY } from './saved_searches'; import { registerFeature } from './register_feature'; import { buildServices } from './build_services'; import { @@ -61,6 +61,7 @@ import { replaceUrlHashQuery } from '../../kibana_utils/public/'; import { IndexPatternFieldEditorStart } from '../../../plugins/index_pattern_field_editor/public'; import { DeferredSpinner } from './shared'; import { ViewSavedSearchAction } from './application/embeddable/view_saved_search_action'; +import { SpacesPluginStart } from '../../../../x-pack/plugins/spaces/public'; declare module '../../share/public' { export interface UrlGeneratorStateMapping { @@ -120,7 +121,9 @@ export interface DiscoverSetup { } export interface DiscoverStart { - savedSearchLoader: SavedObjectLoader; + __LEGACY: { + savedSearchLoader: SavedObjectLoader; + }; /** * @deprecated Use URL locator instead. URL generator will be removed. @@ -189,6 +192,7 @@ export interface DiscoverStartPlugins { savedObjects: SavedObjectsStart; usageCollection?: UsageCollectionSetup; indexPatternFieldEditor: IndexPatternFieldEditorStart; + spaces?: SpacesPluginStart; } /** @@ -333,7 +337,10 @@ export class DiscoverPlugin defaultPath: '#/', category: DEFAULT_APP_CATEGORIES.kibana, mount: async (params: AppMountParameters) => { - const [, depsStart] = await core.getStartServices(); + const [coreStart, depsStart] = await core.getStartServices(); + const services = buildServices(coreStart, depsStart, this.initializerContext); + + setServices(services); setScopedHistory(params.history); setHeaderActionMenuMounter(params.setHeaderActionMenu); syncHistoryLocations(); @@ -404,16 +411,15 @@ export class DiscoverPlugin uiActions.addTriggerAction('CONTEXT_MENU_TRIGGER', viewSavedSearchAction); setUiActions(plugins.uiActions); - const services = buildServices(core, plugins, this.initializerContext); - setServices(services); - return { urlGenerator: this.urlGenerator, locator: this.locator, - savedSearchLoader: createSavedSearchesLoader({ - savedObjectsClient: core.savedObjects.client, - savedObjects: plugins.savedObjects, - }), + __LEGACY: { + savedSearchLoader: __LEGACY.createSavedSearchesLoader({ + savedObjectsClient: core.savedObjects.client, + savedObjects: plugins.savedObjects, + }), + }, }; } diff --git a/src/plugins/discover/public/saved_searches/constants.ts b/src/plugins/discover/public/saved_searches/constants.ts new file mode 100644 index 000000000000000..f8e191c263bd72b --- /dev/null +++ b/src/plugins/discover/public/saved_searches/constants.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** @internal **/ +export const SAVED_SEARCH_TYPE = 'search'; diff --git a/src/plugins/discover/public/saved_searches/get_saved_searches.ts b/src/plugins/discover/public/saved_searches/get_saved_searches.ts new file mode 100644 index 000000000000000..aee51c68bcf532d --- /dev/null +++ b/src/plugins/discover/public/saved_searches/get_saved_searches.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { SavedObjectsStart } from '../../../../core/public'; +import type { DataPublicPluginStart } from '../../../data/public'; +import type { SavedSearchAttributes, SavedSearch } from './types'; + +import { SAVED_SEARCH_TYPE } from './constants'; +import { fromSavedSearchAttributes } from './saved_searches_utils'; +import { injectSearchSourceReferences, parseSearchSourceJSON } from '../../../data/public'; + +interface GetSavedSearchDependencies { + search: DataPublicPluginStart['search']; + savedObjectsClient: SavedObjectsStart['client']; +} + +const getEmptySavedSearch = ({ + search, +}: { + search: DataPublicPluginStart['search']; +}): SavedSearch => ({ + searchSource: search.searchSource.createEmpty(), +}); + +const findSavedSearch = async ( + savedSearchId: string, + { search, savedObjectsClient }: GetSavedSearchDependencies +) => { + const so = await savedObjectsClient.resolve( + SAVED_SEARCH_TYPE, + savedSearchId + ); + + if (so) { + const savedSearch = so.saved_object; + const parsedSearchSourceJSON = parseSearchSourceJSON( + savedSearch.attributes.kibanaSavedObjectMeta?.searchSourceJSON ?? '{}' + ); + const searchSourceValues = injectSearchSourceReferences( + parsedSearchSourceJSON as Parameters[0], + savedSearch.references + ); + + return fromSavedSearchAttributes( + savedSearchId, + savedSearch.attributes, + await search.searchSource.create(searchSourceValues), + { + outcome: so.outcome, + aliasTargetId: so.alias_target_id, + } + ); + } +}; + +/** @public **/ +export const getSavedSearch = async ( + savedSearchId: string | undefined, + dependencies: GetSavedSearchDependencies +) => { + if (savedSearchId) { + const savedSearch = await findSavedSearch(savedSearchId, dependencies); + + if (savedSearch) { + return savedSearch; + } + } + + return getEmptySavedSearch(dependencies); +}; diff --git a/src/plugins/discover/public/saved_searches/index.ts b/src/plugins/discover/public/saved_searches/index.ts index 86564cc5d6eca36..30d4decdd5dcab1 100644 --- a/src/plugins/discover/public/saved_searches/index.ts +++ b/src/plugins/discover/public/saved_searches/index.ts @@ -6,5 +6,18 @@ * Side Public License, v 1. */ -export { createSavedSearchesLoader } from './saved_searches'; -export { SavedSearch, SavedSearchLoader } from './types'; +import { createSavedSearchesLoader } from './legacy/saved_searches'; + +export { getSavedSearch } from './get_saved_searches'; +export { getSavedSearchUrl, getSavedSearchFullPathUrl } from './saved_searches_utils'; +export { useSavedSearchAliasMatchRedirect } from './saved_search_alias_match_redirect'; +export { SavedSearchURLConflictCallout } from './saved_search_url_conflict_callout'; +export { saveSavedSearch, SaveSavedSearchOptions } from './save_saved_searches'; + +export type { SavedSearch } from './types'; +export type { LegacySavedSearch, SavedSearchLoader, SortOrder } from './legacy/types'; + +/** @deprecated __LEGACY object will be removed in v8**/ +export const __LEGACY = { + createSavedSearchesLoader, +}; diff --git a/src/plugins/discover/public/saved_searches/_saved_search.ts b/src/plugins/discover/public/saved_searches/legacy/_saved_search.ts similarity index 80% rename from src/plugins/discover/public/saved_searches/_saved_search.ts rename to src/plugins/discover/public/saved_searches/legacy/_saved_search.ts index 56533ed20b31e2c..154f91f5582b3fa 100644 --- a/src/plugins/discover/public/saved_searches/_saved_search.ts +++ b/src/plugins/discover/public/saved_searches/legacy/_saved_search.ts @@ -6,11 +6,14 @@ * Side Public License, v 1. */ -import { SavedObject, SavedObjectsStart } from '../../../saved_objects/public'; +import type { SavedObject, SavedObjectsStart } from '../../../../saved_objects/public'; +import { SAVED_SEARCH_TYPE } from '../constants'; +import { getSavedSearchFullPathUrl } from '../saved_searches_utils'; +/** @deprecated **/ export function createSavedSearchClass(savedObjects: SavedObjectsStart) { class SavedSearch extends savedObjects.SavedObjectClass { - public static type: string = 'search'; + public static type: string = SAVED_SEARCH_TYPE; public static mapping = { title: 'text', description: 'text', @@ -31,7 +34,7 @@ export function createSavedSearchClass(savedObjects: SavedObjectsStart) { constructor(id: string) { super({ id, - type: 'search', + type: SAVED_SEARCH_TYPE, mapping: { title: 'text', description: 'text', @@ -54,7 +57,7 @@ export function createSavedSearchClass(savedObjects: SavedObjectsStart) { }); this.showInRecentlyAccessed = true; this.id = id; - this.getFullPath = () => `/app/discover#/view/${String(id)}`; + this.getFullPath = () => getSavedSearchFullPathUrl(String(id)); } } diff --git a/src/plugins/discover/public/saved_searches/legacy/index.ts b/src/plugins/discover/public/saved_searches/legacy/index.ts new file mode 100644 index 000000000000000..0bfed6f57b9f5cb --- /dev/null +++ b/src/plugins/discover/public/saved_searches/legacy/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { createSavedSearchesLoader } from './saved_searches'; +export { LegacySavedSearch, SavedSearchLoader } from './types'; diff --git a/src/plugins/discover/public/saved_searches/saved_searches.ts b/src/plugins/discover/public/saved_searches/legacy/saved_searches.ts similarity index 78% rename from src/plugins/discover/public/saved_searches/saved_searches.ts rename to src/plugins/discover/public/saved_searches/legacy/saved_searches.ts index 0fe693dcd2ebe6f..58bed080d0249cd 100644 --- a/src/plugins/discover/public/saved_searches/saved_searches.ts +++ b/src/plugins/discover/public/saved_searches/legacy/saved_searches.ts @@ -6,15 +6,17 @@ * Side Public License, v 1. */ -import { SavedObjectsClientContract } from 'kibana/public'; -import { SavedObjectLoader, SavedObjectsStart } from '../../../saved_objects/public'; +import type { SavedObjectsClientContract } from 'kibana/public'; +import { SavedObjectLoader, SavedObjectsStart } from '../../../../saved_objects/public'; import { createSavedSearchClass } from './_saved_search'; +import { getSavedSearchUrl } from '../saved_searches_utils'; interface Services { savedObjectsClient: SavedObjectsClientContract; savedObjects: SavedObjectsStart; } +/** @deprecated **/ export function createSavedSearchesLoader({ savedObjectsClient, savedObjects }: Services) { const SavedSearchClass = createSavedSearchClass(savedObjects); const savedSearchLoader = new SavedObjectLoader(SavedSearchClass, savedObjectsClient); @@ -25,7 +27,7 @@ export function createSavedSearchesLoader({ savedObjectsClient, savedObjects }: nouns: 'saved searches', }; - savedSearchLoader.urlFor = (id: string) => (id ? `#/view/${encodeURIComponent(id)}` : '#/'); + savedSearchLoader.urlFor = getSavedSearchUrl; return savedSearchLoader; } diff --git a/src/plugins/discover/public/saved_searches/legacy/types.ts b/src/plugins/discover/public/saved_searches/legacy/types.ts new file mode 100644 index 000000000000000..e55422ff26a7bee --- /dev/null +++ b/src/plugins/discover/public/saved_searches/legacy/types.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ISearchSource } from '../../../../data/public'; +import type { SavedObjectSaveOpts } from '../../../../saved_objects/public'; +import type { DiscoverGridSettings } from '../../application/components/discover_grid/types'; + +export type SortOrder = [string, string]; + +/** @deprecated **/ +export interface LegacySavedSearch { + readonly id: string; + title: string; + searchSource: ISearchSource; + description?: string; + columns: string[]; + sort: SortOrder[]; + grid: DiscoverGridSettings; + destroy: () => void; + save: (saveOptions: SavedObjectSaveOpts) => Promise; + copyOnSave?: boolean; + hideChart?: boolean; +} + +/** @deprecated **/ +export interface SavedSearchLoader { + get: (id: string) => Promise; + urlFor: (id: string) => string; +} diff --git a/src/plugins/discover/public/saved_searches/save_saved_searches.ts b/src/plugins/discover/public/saved_searches/save_saved_searches.ts new file mode 100644 index 000000000000000..a455a93f7c4eb08 --- /dev/null +++ b/src/plugins/discover/public/saved_searches/save_saved_searches.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import type { SavedObjectsStart } from 'kibana/public'; +import type { SavedSearch, SavedSearchAttributes } from './types'; + +import { SAVED_SEARCH_TYPE } from './constants'; +import { toSavedSearchAttributes } from './saved_searches_utils'; + +export interface SaveSavedSearchOptions { + confirmOverwrite?: boolean; + onTitleDuplicate?: () => void; + returnToOrigin?: boolean; + copyOnSave?: boolean; +} + +const hasDuplicatedTitle = async ( + title: string, + savedObjectsClient: SavedObjectsStart['client'] +): Promise => { + if (!title) { + return; + } + + const response = await savedObjectsClient.find({ + type: SAVED_SEARCH_TYPE, + perPage: 10, + search: `"${title}"`, + searchFields: ['title'], + fields: ['title'], + }); + + return response.savedObjects.some( + (obj) => obj.get('title').toLowerCase() === title.toLowerCase() + ); +}; + +/** @internal **/ +export const saveSavedSearch = async ( + savedSearch: SavedSearch, + options: SaveSavedSearchOptions, + savedObjectsClient: SavedObjectsStart['client'] +): Promise => { + const isNew = options.copyOnSave || !savedSearch.id; + + if (savedSearch.title) { + if ( + isNew && + options.onTitleDuplicate && + (await hasDuplicatedTitle(savedSearch.title, savedObjectsClient)) + ) { + options.onTitleDuplicate(); + return; + } + } + + const { searchSourceJSON, references } = savedSearch.searchSource.serialize(); + const resp = isNew + ? await savedObjectsClient.create( + SAVED_SEARCH_TYPE, + toSavedSearchAttributes(savedSearch, searchSourceJSON), + { + references, + } + ) + : await savedObjectsClient.update( + SAVED_SEARCH_TYPE, + savedSearch.id!, + toSavedSearchAttributes(savedSearch, searchSourceJSON), + { + references, + } + ); + + return resp.id; +}; diff --git a/src/plugins/discover/public/saved_searches/saved_search_alias_match_redirect.ts b/src/plugins/discover/public/saved_searches/saved_search_alias_match_redirect.ts new file mode 100644 index 000000000000000..5683edcd2091b57 --- /dev/null +++ b/src/plugins/discover/public/saved_searches/saved_search_alias_match_redirect.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; +import { getSavedSearchFullPathUrl } from './saved_searches_utils'; +import { SAVED_SEARCH_TYPE } from './constants'; + +import type { SavedSearch } from './types'; +import type { SpacesApi } from '../../../../../x-pack/plugins/spaces/public'; + +interface SavedSearchAliasMatchRedirectProps { + savedSearch?: SavedSearch; + spaces?: SpacesApi; +} + +export const useSavedSearchAliasMatchRedirect = ({ + savedSearch, + spaces, +}: SavedSearchAliasMatchRedirectProps) => { + useEffect(() => { + async function aliasMatchRedirect() { + if (savedSearch) { + const { aliasTargetId, outcome } = savedSearch.sharingSavedObject ?? {}; + + if (spaces && aliasTargetId && outcome === 'aliasMatch') { + await spaces.ui.redirectLegacyUrl( + getSavedSearchFullPathUrl(aliasTargetId), + i18n.translate('discover.savedSearchAliasMatchRedirect.objectNoun', { + defaultMessage: '{savedSearch} {type}', + values: { + savedSearch: savedSearch.title, + type: SAVED_SEARCH_TYPE, + }, + }) + ); + } + } + } + + aliasMatchRedirect(); + }, [savedSearch, spaces]); +}; diff --git a/src/plugins/discover/public/saved_searches/saved_search_url_conflict_callout.ts b/src/plugins/discover/public/saved_searches/saved_search_url_conflict_callout.ts new file mode 100644 index 000000000000000..1ea56de62d2e161 --- /dev/null +++ b/src/plugins/discover/public/saved_searches/saved_search_url_conflict_callout.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { getSavedSearchUrl } from './saved_searches_utils'; +import { SAVED_SEARCH_TYPE } from './constants'; + +import type { SavedSearch } from './types'; +import type { SpacesApi } from '../../../../../x-pack/plugins/spaces/public'; + +interface SavedSearchURLConflictCalloutProps { + savedSearch?: SavedSearch; + spaces?: SpacesApi; +} + +export const SavedSearchURLConflictCallout = ({ + savedSearch, + spaces, +}: SavedSearchURLConflictCalloutProps) => { + if (spaces && savedSearch?.id && savedSearch?.sharingSavedObject?.outcome === 'conflict') { + const otherObjectId = savedSearch.sharingSavedObject?.aliasTargetId; + + if (otherObjectId) { + return spaces.ui.components.getLegacyUrlConflict({ + objectNoun: i18n.translate('discover.savedSearchURLConflictCallout.objectNoun', { + defaultMessage: '{savedSearch} {type}', + values: { + savedSearch: savedSearch.title, + type: SAVED_SEARCH_TYPE, + }, + }), + currentObjectId: savedSearch.id, + otherObjectPath: getSavedSearchUrl(otherObjectId), + otherObjectId, + }); + } + } + + return null; +}; diff --git a/src/plugins/discover/public/saved_searches/saved_searches_utils.ts b/src/plugins/discover/public/saved_searches/saved_searches_utils.ts new file mode 100644 index 000000000000000..a24413f79d6b091 --- /dev/null +++ b/src/plugins/discover/public/saved_searches/saved_searches_utils.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { SavedSearchAttributes, SavedSearch } from './types'; + +export const getSavedSearchUrl = (id?: string) => (id ? `#/view/${encodeURIComponent(id)}` : '#/'); + +export const getSavedSearchFullPathUrl = (id?: string) => `/app/discover${getSavedSearchUrl(id)}`; + +export const fromSavedSearchAttributes = ( + id: string, + attributes: SavedSearchAttributes, + searchSource: SavedSearch['searchSource'], + sharingSavedObject: SavedSearch['sharingSavedObject'] +): SavedSearch => ({ + id, + searchSource, + sharingSavedObject, + title: attributes.title, + sort: attributes.sort, + columns: attributes.columns, + description: attributes.description, + grid: attributes.grid, + hideChart: attributes.hideChart, +}); + +export const toSavedSearchAttributes = ( + savedSearch: SavedSearch, + searchSourceJSON: string +): SavedSearchAttributes => ({ + kibanaSavedObjectMeta: { searchSourceJSON }, + title: savedSearch.title ?? '', + sort: savedSearch.sort ?? [], + columns: savedSearch.columns ?? [], + description: savedSearch.description ?? '', + grid: savedSearch.grid ?? {}, + hideChart: savedSearch.hideChart ?? false, +}); diff --git a/src/plugins/discover/public/saved_searches/types.ts b/src/plugins/discover/public/saved_searches/types.ts index b1c7b48d696b344..860d9fed02ac468 100644 --- a/src/plugins/discover/public/saved_searches/types.ts +++ b/src/plugins/discover/public/saved_searches/types.ts @@ -6,26 +6,38 @@ * Side Public License, v 1. */ -import { SearchSource } from '../../../data/public'; -import { SavedObjectSaveOpts } from '../../../saved_objects/public'; -import { DiscoverGridSettings } from '../application/components/discover_grid/types'; +import type { ISearchSource } from '../../../data/public'; +import { DiscoverGridSettingsColumn } from '../application/components/discover_grid/types'; -export type SortOrder = [string, string]; -export interface SavedSearch { - readonly id: string; +/** @internal **/ +export interface SavedSearchAttributes { title: string; - searchSource: SearchSource; - description?: string; + sort: Array<[string, string]>; columns: string[]; - sort: SortOrder[]; - grid: DiscoverGridSettings; - destroy: () => void; - save: (saveOptions: SavedObjectSaveOpts) => Promise; - lastSavedTitle?: string; - copyOnSave?: boolean; - hideChart?: boolean; + description: string; + grid: { + columns?: Record; + }; + hideChart: boolean; + kibanaSavedObjectMeta: { + searchSourceJSON: string; + }; } -export interface SavedSearchLoader { - get: (id: string) => Promise; - urlFor: (id: string) => string; + +/** @public **/ +export interface SavedSearch { + searchSource: ISearchSource; + id?: string; + title?: string; + sort?: Array<[string, string]>; + columns?: string[]; + description?: string; + grid?: { + columns?: Record; + }; + hideChart?: boolean; + sharingSavedObject?: { + outcome?: 'aliasMatch' | 'exactMatch' | 'conflict'; + aliasTargetId?: string; + }; } diff --git a/src/plugins/discover/tsconfig.json b/src/plugins/discover/tsconfig.json index b3f1ad5d0bc1e8b..7a6736b30d9f305 100644 --- a/src/plugins/discover/tsconfig.json +++ b/src/plugins/discover/tsconfig.json @@ -24,6 +24,7 @@ { "path": "../kibana_react/tsconfig.json" }, { "path": "../kibana_legacy/tsconfig.json" }, { "path": "../index_pattern_field_editor/tsconfig.json"}, - { "path": "../field_formats/tsconfig.json" } + { "path": "../field_formats/tsconfig.json" }, + { "path": "../../../x-pack/plugins/spaces/tsconfig.json" } ] } diff --git a/src/plugins/vis_default_editor/kibana.json b/src/plugins/vis_default_editor/kibana.json index e85c5713eb82c3f..efed1eab1e4948b 100644 --- a/src/plugins/vis_default_editor/kibana.json +++ b/src/plugins/vis_default_editor/kibana.json @@ -3,7 +3,7 @@ "version": "kibana", "ui": true, "optionalPlugins": ["visualize"], - "requiredBundles": ["kibanaUtils", "kibanaReact", "data", "fieldFormats"], + "requiredBundles": ["kibanaUtils", "kibanaReact", "data", "fieldFormats", "discover"], "owner": { "name": "Vis Editors", "githubTeam": "kibana-vis-editors" diff --git a/src/plugins/vis_default_editor/public/components/sidebar/sidebar.tsx b/src/plugins/vis_default_editor/public/components/sidebar/sidebar.tsx index dab982e5a8070f1..f1eebbbdf211618 100644 --- a/src/plugins/vis_default_editor/public/components/sidebar/sidebar.tsx +++ b/src/plugins/vis_default_editor/public/components/sidebar/sidebar.tsx @@ -26,7 +26,7 @@ import { } from 'src/plugins/visualizations/public'; import type { Schema } from 'src/plugins/visualizations/public'; import { TimeRange } from 'src/plugins/data/public'; -import { SavedObject } from 'src/plugins/saved_objects/public'; +import { SavedSearch } from 'src/plugins/discover/public'; import { DefaultEditorNavBar } from './navbar'; import { DefaultEditorControls } from './controls'; import { setStateParamValue, useEditorReducer, useEditorFormState, discardChanges } from './state'; @@ -42,7 +42,7 @@ interface DefaultEditorSideBarProps { vis: Vis; isLinkedSearch: boolean; eventEmitter: EventEmitter; - savedSearch?: SavedObject; + savedSearch?: SavedSearch; timeRange: TimeRange; } diff --git a/src/plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx b/src/plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx index cab27d53b827de0..2740f4ff50b4e2a 100644 --- a/src/plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx +++ b/src/plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx @@ -25,18 +25,18 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { Vis } from 'src/plugins/visualizations/public'; -import { SavedObject } from 'src/plugins/saved_objects/public'; +import { SavedSearch, getSavedSearchUrl } from '../../../../discover/public'; import { ApplicationStart } from '../../../../../core/public'; import { useKibana } from '../../../../kibana_react/public'; interface LinkedSearchProps { - savedSearch: SavedObject; + savedSearch: SavedSearch; eventEmitter: EventEmitter; } interface SidebarTitleProps { isLinkedSearch: boolean; - savedSearch?: SavedObject; + savedSearch?: SavedSearch; vis: Vis; eventEmitter: EventEmitter; } @@ -55,7 +55,7 @@ export function LinkedSearch({ savedSearch, eventEmitter }: LinkedSearchProps) { }, [eventEmitter]); const onClickViewInDiscover = useCallback(() => { application.navigateToApp('discover', { - path: `#/view/${savedSearch.id}`, + path: getSavedSearchUrl(savedSearch.id), }); }, [application, savedSearch.id]); diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index ee3e914aa4bc6bb..d8002a60256d71a 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -21,7 +21,6 @@ import { setAggs, setChrome, setOverlays, - setSavedSearchLoader, setEmbeddable, setDocLinks, } from './services'; @@ -45,8 +44,6 @@ import { convertToSerializedVis, } from './saved_visualizations/_saved_vis'; -import { createSavedSearchesLoader } from '../../discover/public'; - import type { PluginInitializerContext, CoreSetup, @@ -173,11 +170,7 @@ export class VisualizationsPlugin visualizationTypes: types, }); setSavedVisualizationsLoader(savedVisualizationsLoader); - const savedSearchLoader = createSavedSearchesLoader({ - savedObjectsClient: core.savedObjects.client, - savedObjects, - }); - setSavedSearchLoader(savedSearchLoader); + return { ...types, showNewVisModal, diff --git a/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts b/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts index fb6c99ac8ef02ed..90dd774c4acac2d 100644 --- a/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts +++ b/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts @@ -17,7 +17,6 @@ import type { SavedObjectsStart, SavedObject } from '../../../../plugins/saved_o // @ts-ignore import { updateOldState } from '../legacy/vis_update_state'; import { extractReferences, injectReferences } from './saved_visualization_references'; -import { createSavedSearchesLoader } from '../../../discover/public'; import type { SavedObjectsClientContract } from '../../../../core/public'; import type { IndexPatternsContract } from '../../../../plugins/data/public'; import type { ISavedVis, SerializedVis } from '../types'; @@ -66,8 +65,6 @@ export const convertFromSerializedVis = (vis: SerializedVis): ISavedVis => { }; export function createSavedVisClass(services: SavedVisServices) { - const savedSearch = createSavedSearchesLoader(services); - class SavedVis extends services.savedObjects.SavedObjectClass { public static type: string = 'visualization'; public static mapping: Record = { @@ -108,9 +105,6 @@ export function createSavedVisClass(services: SavedVisServices) { if (savedVis.searchSourceFields?.index) { await services.indexPatterns.get(savedVis.searchSourceFields.index as any); } - if (savedVis.savedSearchId) { - await savedSearch.get(savedVis.savedSearchId); - } return savedVis as any as SavedObject; }, }); diff --git a/src/plugins/visualizations/public/services.ts b/src/plugins/visualizations/public/services.ts index f1ab9077cd207b1..f6b8145b3e35261 100644 --- a/src/plugins/visualizations/public/services.ts +++ b/src/plugins/visualizations/public/services.ts @@ -23,7 +23,6 @@ import { UsageCollectionSetup } from '../../../plugins/usage_collection/public'; import { ExpressionsStart } from '../../../plugins/expressions/public'; import { UiActionsStart } from '../../../plugins/ui_actions/public'; import { SavedVisualizationsLoader } from './saved_visualizations'; -import { SavedObjectLoader } from '../../saved_objects/public'; import { EmbeddableStart } from '../../embeddable/public'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); @@ -63,6 +62,3 @@ export const [getAggs, setAggs] = export const [getOverlays, setOverlays] = createGetterSetter('Overlays'); export const [getChrome, setChrome] = createGetterSetter('Chrome'); - -export const [getSavedSearchLoader, setSavedSearchLoader] = - createGetterSetter('savedSearchLoader'); diff --git a/src/plugins/visualizations/public/vis.ts b/src/plugins/visualizations/public/vis.ts index dfab4ecfc3cd816..b879b683bf7b0da 100644 --- a/src/plugins/visualizations/public/vis.ts +++ b/src/plugins/visualizations/public/vis.ts @@ -21,17 +21,19 @@ import { Assign } from '@kbn/utility-types'; import { i18n } from '@kbn/i18n'; import { PersistedState } from './persisted_state'; -import { getTypes, getAggs, getSearch, getSavedSearchLoader } from './services'; +import { getTypes, getAggs, getSearch, getSavedObjects } from './services'; import { IAggConfigs, IndexPattern, ISearchSource, AggConfigSerialized, SearchSourceFields, -} from '../../../plugins/data/public'; +} from '../../data/public'; import { BaseVisType } from './vis_types'; import { VisParams } from '../common/types'; +import { getSavedSearch } from '../../discover/public'; + export interface SerializedVisData { expression?: string; aggs: AggConfigSerialized[]; @@ -58,14 +60,17 @@ export interface VisData { } const getSearchSource = async (inputSearchSource: ISearchSource, savedSearchId?: string) => { - const searchSource = inputSearchSource.createCopy(); if (savedSearchId) { - const savedSearch = await getSavedSearchLoader().get(savedSearchId); + const savedSearchSearch = await getSavedSearch(savedSearchId, { + search: getSearch(), + savedObjectsClient: getSavedObjects().client, + }); - searchSource.setParent(savedSearch.searchSource); + if (savedSearchSearch?.searchSource) { + return savedSearchSearch?.searchSource; + } } - searchSource.setField('size', 0); - return searchSource; + return inputSearchSource; }; type PartialVisState = Assign }>; diff --git a/src/plugins/visualize/public/application/types.ts b/src/plugins/visualize/public/application/types.ts index 7e9f69163f5a6da..be49895a941145b 100644 --- a/src/plugins/visualize/public/application/types.ts +++ b/src/plugins/visualize/public/application/types.ts @@ -38,13 +38,14 @@ import type { import type { NavigationPublicPluginStart as NavigationStart } from 'src/plugins/navigation/public'; import type { Query, Filter, DataPublicPluginStart, TimeRange } from 'src/plugins/data/public'; import type { SharePluginStart } from 'src/plugins/share/public'; -import type { SavedObjectsStart, SavedObject } from 'src/plugins/saved_objects/public'; +import type { SavedObjectsStart } from 'src/plugins/saved_objects/public'; import type { EmbeddableStart, EmbeddableStateTransfer } from 'src/plugins/embeddable/public'; import type { UrlForwardingStart } from 'src/plugins/url_forwarding/public'; import type { PresentationUtilPluginStart } from 'src/plugins/presentation_util/public'; import type { DashboardStart } from '../../../dashboard/public'; import type { SavedObjectsTaggingApi } from '../../../saved_objects_tagging_oss/public'; import type { UsageCollectionStart } from '../../../usage_collection/public'; +import type { SavedSearch } from '../../../discover/public'; import { PureVisState } from '../../common/types'; @@ -107,20 +108,15 @@ export interface VisualizeServices extends CoreStart { getKibanaVersion: () => string; } -export interface SavedVisInstance { +export interface VisInstance { vis: Vis; savedVis: VisSavedObject; - savedSearch?: SavedObject; - embeddableHandler: VisualizeEmbeddableContract; -} - -export interface ByValueVisInstance { - vis: Vis; - savedVis: VisSavedObject; - savedSearch?: SavedObject; + savedSearch?: SavedSearch; embeddableHandler: VisualizeEmbeddableContract; } +export type SavedVisInstance = VisInstance; +export type ByValueVisInstance = VisInstance; export type VisualizeEditorVisInstance = SavedVisInstance | ByValueVisInstance; export type VisEditorConstructor = new ( @@ -141,7 +137,7 @@ export interface EditorRenderProps { filters: Filter[]; timeRange: TimeRange; query?: Query; - savedSearch?: SavedObject; + savedSearch?: SavedSearch; uiState: PersistedState; /** * Flag to determine if visualiztion is linked to the saved search diff --git a/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts b/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts index 83462ba9ae7b93e..337f9d5c1000010 100644 --- a/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts +++ b/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { createSavedSearchesLoader } from '../../../../discover/public'; +import { getSavedSearch } from '../../../../discover/public'; import type { VisualizeInput, VisSavedObject, @@ -21,13 +21,12 @@ import { createVisualizeServicesMock } from './mocks'; import { VisualizeServices } from '../types'; import { BehaviorSubject } from 'rxjs'; -const mockSavedSearchObj = {}; -const mockGetSavedSearch = jest.fn(() => mockSavedSearchObj); - jest.mock('../../../../discover/public', () => ({ - createSavedSearchesLoader: jest.fn(() => ({ - get: mockGetSavedSearch, - })), + getSavedSearch: jest.fn().mockResolvedValue({ + id: 'savedSearch', + title: 'savedSearchTitle', + searchSource: {}, + }), })); describe('getVisualizationInstance', () => { @@ -108,9 +107,14 @@ describe('getVisualizationInstance', () => { visMock.data.savedSearchId = 'saved_search_id'; const { savedSearch } = await getVisualizationInstance(mockServices, 'saved_vis_id'); - expect(createSavedSearchesLoader).toHaveBeenCalled(); - expect(mockGetSavedSearch).toHaveBeenCalledWith(visMock.data.savedSearchId); - expect(savedSearch).toBe(mockSavedSearchObj); + expect(getSavedSearch).toHaveBeenCalled(); + expect(savedSearch).toMatchInlineSnapshot(` + Object { + "id": "savedSearch", + "searchSource": Object {}, + "title": "savedSearchTitle", + } + `); }); test('should subscribe on embeddable handler updates and send toasts on errors', async () => { diff --git a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts index 88797ce264e25db..9ac86469b0da333 100644 --- a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts +++ b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts @@ -14,10 +14,9 @@ import { VisualizeInput, } from 'src/plugins/visualizations/public'; import { SearchSourceFields } from 'src/plugins/data/public'; -import { SavedObject } from 'src/plugins/saved_objects/public'; import { cloneDeep } from 'lodash'; import { ExpressionValueError } from 'src/plugins/expressions/public'; -import { createSavedSearchesLoader } from '../../../../discover/public'; +import { getSavedSearch, SavedSearch } from '../../../../discover/public'; import { SavedFieldNotFound, SavedFieldTypeInvalidForAgg } from '../../../../kibana_utils/common'; import { VisualizeServices } from '../types'; @@ -33,8 +32,7 @@ const createVisualizeEmbeddableAndLinkSavedSearch = async ( vis: Vis, visualizeServices: VisualizeServices ) => { - const { data, createVisEmbeddableFromObject, savedObjects, savedObjectsPublic } = - visualizeServices; + const { data, createVisEmbeddableFromObject, savedObjects } = visualizeServices; const embeddableHandler = (await createVisEmbeddableFromObject(vis, { id: '', timeRange: data.query.timefilter.timefilter.getTime(), @@ -50,13 +48,13 @@ const createVisualizeEmbeddableAndLinkSavedSearch = async ( } }); - let savedSearch: SavedObject | undefined; + let savedSearch: SavedSearch | undefined; if (vis.data.savedSearchId) { - savedSearch = await createSavedSearchesLoader({ + savedSearch = await getSavedSearch(vis.data.savedSearchId, { + search: data.search, savedObjectsClient: savedObjects.client, - savedObjects: savedObjectsPublic, - }).get(vis.data.savedSearchId); + }); } return { savedSearch, embeddableHandler }; diff --git a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts index bedf310725ae251..a4fe1835ce5f705 100644 --- a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts +++ b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts @@ -107,7 +107,7 @@ describe('GetCsvReportPanelAction', () => { columns: [], objectType: 'downloadCsv', searchSource: {}, - title: undefined, + title: '', version: '7.15.0', }); }); @@ -144,7 +144,7 @@ describe('GetCsvReportPanelAction', () => { columns: ['column_a', 'column_b'], objectType: 'downloadCsv', searchSource: { testData: 'testDataValue' }, - title: undefined, + title: '', version: '7.15.0', }); }); diff --git a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx index ef32e6474176598..6bf52aa6e328125 100644 --- a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx +++ b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx @@ -122,7 +122,7 @@ export class ReportingCsvPanelAction implements ActionDefinition const immediateJobParams = this.apiClient.getDecoratedJobParams({ searchSource, columns, - title: savedSearch.title, + title: savedSearch.title || '', objectType: 'downloadCsv', // FIXME: added for typescript, but immediate download job does not need objectType }); diff --git a/x-pack/plugins/transform/public/__mocks__/shared_imports.ts b/x-pack/plugins/transform/public/__mocks__/shared_imports.ts index ae072e6666e4aa9..e0788d7747e6552 100644 --- a/x-pack/plugins/transform/public/__mocks__/shared_imports.ts +++ b/x-pack/plugins/transform/public/__mocks__/shared_imports.ts @@ -13,7 +13,7 @@ export const useRequest = jest.fn(() => ({ error: null, data: undefined, })); -export const createSavedSearchesLoader = jest.fn(); +export const getSavedSearch = jest.fn(); // just passing through the reimports export { getMlSharedImports, ES_CLIENT_TOTAL_HITS_RELATION } from '../../../ml/public'; diff --git a/x-pack/plugins/transform/public/app/hooks/use_search_items/common.ts b/x-pack/plugins/transform/public/app/hooks/use_search_items/common.ts index 3e85f5d4d49a4fe..db5fd82808a911b 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_search_items/common.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_search_items/common.ts @@ -20,12 +20,10 @@ import { isIndexPattern } from '../../../../common/types/index_pattern'; export type SavedSearchQuery = object; type IndexPatternId = string; -type SavedSearchId = string; let indexPatternCache: Array>> = []; let fullIndexPatterns; let currentIndexPattern = null; -let currentSavedSearch = null; export let refreshIndexPatterns: () => Promise; @@ -76,11 +74,6 @@ export function loadCurrentIndexPattern( return currentIndexPattern; } -export function loadCurrentSavedSearch(savedSearches: any, savedSearchId: SavedSearchId) { - currentSavedSearch = savedSearches.get(savedSearchId); - return currentSavedSearch; -} - export interface SearchItems { indexPattern: IndexPattern; savedSearch: any; diff --git a/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts b/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts index 44e3cc347824d8b..28cff69a6ef9018 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import { isIndexPattern } from '../../../../common/types/index_pattern'; -import { createSavedSearchesLoader } from '../../../shared_imports'; +import { getSavedSearch } from '../../../shared_imports'; import { useAppDependencies } from '../../app_dependencies'; @@ -20,7 +20,6 @@ import { getIndexPatternIdByTitle, loadCurrentIndexPattern, loadIndexPatterns, - loadCurrentSavedSearch, SearchItems, } from './common'; @@ -32,10 +31,6 @@ export const useSearchItems = (defaultSavedObjectId: string | undefined) => { const indexPatterns = appDeps.data.indexPatterns; const uiSettings = appDeps.uiSettings; const savedObjectsClient = appDeps.savedObjects.client; - const savedSearches = createSavedSearchesLoader({ - savedObjectsClient, - savedObjects: appDeps.savedObjectsPlugin, - }); const [searchItems, setSearchItems] = useState(undefined); @@ -52,7 +47,10 @@ export const useSearchItems = (defaultSavedObjectId: string | undefined) => { } try { - fetchedSavedSearch = await loadCurrentSavedSearch(savedSearches, id); + fetchedSavedSearch = await getSavedSearch(id, { + search: appDeps.data.search, + savedObjectsClient: appDeps.savedObjects.client, + }); } catch (e) { // Just let fetchedSavedSearch stay undefined in case it doesn't exist. } diff --git a/x-pack/plugins/transform/public/shared_imports.ts b/x-pack/plugins/transform/public/shared_imports.ts index edd27fd43c2af93..b3662466a53f52f 100644 --- a/x-pack/plugins/transform/public/shared_imports.ts +++ b/x-pack/plugins/transform/public/shared_imports.ts @@ -5,9 +5,9 @@ * 2.0. */ -export { createSavedSearchesLoader } from '../../../../src/plugins/discover/public'; export { XJsonMode } from '@kbn/ace'; export { UseRequestConfig, useRequest } from '../../../../src/plugins/es_ui_shared/public'; +export { getSavedSearch } from '../../../../src/plugins/discover/public'; export { getMlSharedImports,