mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Discover] Removing SavedObject usage for savedSearch (#112983)
* [Discover] Step 2 - remove SavedObjectLoader * Fix PR comments * fix test names * fix ts error * add handling of missed 'so' * add Embeddable error * fix jest * add DiscoverError component * fix Joe comments * add search params * add throwErrorOnUrlConflict util method * add error handling into transform plugin * do some updates * add spaces into visualize, visualizations * fix Tim's comment * pass false into createGetterSetter for getSpaces * Fix comments * Fix lint Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Uladzislau Lasitsa <Uladzislau_Lasitsa@epam.com>
This commit is contained in:
parent
62d39cc1fd
commit
d9ef453b26
74 changed files with 1318 additions and 282 deletions
|
@ -15,8 +15,8 @@
|
|||
"savedObjects",
|
||||
"indexPatternFieldEditor"
|
||||
],
|
||||
"optionalPlugins": ["home", "share", "usageCollection"],
|
||||
"requiredBundles": ["kibanaUtils", "home", "kibanaReact", "fieldFormats"],
|
||||
"optionalPlugins": ["home", "share", "usageCollection", "spaces"],
|
||||
"requiredBundles": ["kibanaUtils", "home", "kibanaReact", "fieldFormats", "dataViews"],
|
||||
"extraPublicDirs": ["common"],
|
||||
"owner": {
|
||||
"name": "Data Discovery",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 },
|
||||
},
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { Filter, IndexPattern, SearchSource } from 'src/plugins/data/public';
|
||||
import { Filter, IndexPattern, 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';
|
||||
|
@ -53,7 +53,7 @@ export async function fetchSurroundingDocs(
|
|||
}
|
||||
const { data } = getServices();
|
||||
const timeField = indexPattern.timeFieldName!;
|
||||
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);
|
||||
|
||||
|
@ -104,7 +104,7 @@ export async function fetchSurroundingDocs(
|
|||
}
|
||||
|
||||
export function updateSearchSource(
|
||||
searchSource: SearchSource,
|
||||
searchSource: ISearchSource,
|
||||
indexPattern: IndexPattern,
|
||||
filters: Filter[],
|
||||
useNewFieldsApi: boolean
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -39,6 +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';
|
||||
|
||||
/**
|
||||
* Local storage key for sidebar persistence state
|
||||
|
@ -65,10 +69,18 @@ export function DiscoverLayout({
|
|||
state,
|
||||
stateContainer,
|
||||
}: DiscoverLayoutProps) {
|
||||
const { trackUiMetric, capabilities, indexPatterns, data, uiSettings, filterManager, storage } =
|
||||
services;
|
||||
const {
|
||||
trackUiMetric,
|
||||
capabilities,
|
||||
indexPatterns,
|
||||
data,
|
||||
uiSettings,
|
||||
filterManager,
|
||||
storage,
|
||||
history,
|
||||
spaces,
|
||||
} = services;
|
||||
const { main$, charts$, totalHits$ } = savedSearchData$;
|
||||
|
||||
const [expandedDoc, setExpandedDoc] = useState<ElasticSearchHit | undefined>(undefined);
|
||||
const [inspectorSession, setInspectorSession] = useState<InspectorSession | undefined>(undefined);
|
||||
const fetchCounter = useRef<number>(0);
|
||||
|
@ -80,6 +92,8 @@ export function DiscoverLayout({
|
|||
}
|
||||
}, [dataState.fetchStatus]);
|
||||
|
||||
useSavedSearchAliasMatchRedirect({ savedSearch, spaces, history });
|
||||
|
||||
const timeField = useMemo(() => {
|
||||
return indexPattern.type !== 'rollup' ? indexPattern.timeFieldName : undefined;
|
||||
}, [indexPattern]);
|
||||
|
@ -174,6 +188,11 @@ export function DiscoverLayout({
|
|||
resetSavedSearch={resetSavedSearch}
|
||||
/>
|
||||
<EuiPageBody className="dscPageBody" aria-describedby="savedSearchTitle">
|
||||
<SavedSearchURLConflictCallout
|
||||
savedSearch={savedSearch}
|
||||
spaces={spaces}
|
||||
history={history}
|
||||
/>
|
||||
<h1 id="savedSearchTitle" className="euiScreenReaderOnly">
|
||||
{savedSearch.title}
|
||||
</h1>
|
||||
|
|
|
@ -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 = {
|
||||
confirmOverwrite: false,
|
||||
isTitleDuplicateConfirmed,
|
||||
const saveOptions: SaveSavedSearchOptions = {
|
||||
onTitleDuplicate,
|
||||
copyOnSave: newCopyOnSave,
|
||||
isTitleDuplicateConfirmed,
|
||||
};
|
||||
const response = await saveDataSource({
|
||||
indexPattern,
|
||||
|
@ -133,7 +134,7 @@ export async function onSaveSearch({
|
|||
<SavedObjectSaveModal
|
||||
onSave={onSave}
|
||||
onClose={() => {}}
|
||||
title={savedSearch.title}
|
||||
title={savedSearch.title ?? ''}
|
||||
showCopyOnSave={!!savedSearch.id}
|
||||
objectType={i18n.translate('discover.localMenu.saveSaveSearchObjectType', {
|
||||
defaultMessage: 'search',
|
||||
|
|
|
@ -8,15 +8,18 @@
|
|||
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 { i18n } from '@kbn/i18n';
|
||||
import { EuiEmptyPrompt } from '@elastic/eui';
|
||||
|
||||
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';
|
||||
import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../../helpers/breadcrumbs';
|
||||
import { redirectWhenMissing } from '../../../../../kibana_utils/public';
|
||||
import { DataViewSavedObjectConflictError } from '../../../../../data_views/common';
|
||||
import { getUrlTracker } from '../../../kibana_services';
|
||||
import { LoadingIndicator } from '../../components/common/loading_indicator';
|
||||
|
||||
|
@ -37,6 +40,21 @@ interface DiscoverLandingParams {
|
|||
id: string;
|
||||
}
|
||||
|
||||
const DiscoverError = ({ error }: { error: Error }) => (
|
||||
<EuiEmptyPrompt
|
||||
iconType="alert"
|
||||
iconColor="danger"
|
||||
title={
|
||||
<h2>
|
||||
{i18n.translate('discover.discoverError.title', {
|
||||
defaultMessage: 'Error loading Discover',
|
||||
})}
|
||||
</h2>
|
||||
}
|
||||
body={<p>{error.message}</p>}
|
||||
/>
|
||||
);
|
||||
|
||||
export function DiscoverMainRoute({ services, history }: DiscoverMainProps) {
|
||||
const {
|
||||
core,
|
||||
|
@ -46,7 +64,7 @@ export function DiscoverMainRoute({ services, history }: DiscoverMainProps) {
|
|||
toastNotifications,
|
||||
http: { basePath },
|
||||
} = services;
|
||||
|
||||
const [error, setError] = useState<Error>();
|
||||
const [savedSearch, setSavedSearch] = useState<SavedSearch>();
|
||||
const indexPattern = savedSearch?.searchSource?.getField('index');
|
||||
const [indexPatternList, setIndexPatternList] = useState<
|
||||
|
@ -58,58 +76,76 @@ export function DiscoverMainRoute({ services, history }: DiscoverMainProps) {
|
|||
useEffect(() => {
|
||||
const savedSearchId = id;
|
||||
|
||||
async function loadDefaultOrCurrentIndexPattern(usedSavedSearch: SavedSearch) {
|
||||
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<SavedObject<IndexPatternAttributes>>;
|
||||
const indexPatternData = await resolveIndexPattern(
|
||||
ip,
|
||||
usedSavedSearch.searchSource,
|
||||
toastNotifications
|
||||
);
|
||||
setIndexPatternList(ipList);
|
||||
return indexPatternData;
|
||||
async function loadDefaultOrCurrentIndexPattern(searchSource: ISearchSource) {
|
||||
try {
|
||||
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<SavedObject<IndexPatternAttributes>>;
|
||||
const indexPatternData = await resolveIndexPattern(ip, searchSource, toastNotifications);
|
||||
|
||||
setIndexPatternList(ipList);
|
||||
|
||||
return indexPatternData;
|
||||
} catch (e) {
|
||||
setError(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSavedSearch() {
|
||||
try {
|
||||
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,
|
||||
spaces: services.spaces,
|
||||
});
|
||||
|
||||
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) {
|
||||
redirectWhenMissing({
|
||||
history,
|
||||
navigateToApp: core.application.navigateToApp,
|
||||
basePath,
|
||||
mapping: {
|
||||
search: '/',
|
||||
'index-pattern': {
|
||||
app: 'management',
|
||||
path: `kibana/objects/savedSearches/${id}`,
|
||||
if (e instanceof DataViewSavedObjectConflictError) {
|
||||
setError(e);
|
||||
} else {
|
||||
redirectWhenMissing({
|
||||
history,
|
||||
navigateToApp: core.application.navigateToApp,
|
||||
basePath,
|
||||
mapping: {
|
||||
search: '/',
|
||||
'index-pattern': {
|
||||
app: 'management',
|
||||
path: `kibana/objects/savedSearches/${id}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
toastNotifications,
|
||||
onBeforeRedirect() {
|
||||
getUrlTracker().setTrackedUrl('/');
|
||||
},
|
||||
})(e);
|
||||
toastNotifications,
|
||||
onBeforeRedirect() {
|
||||
getUrlTracker().setTrackedUrl('/');
|
||||
},
|
||||
})(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadSavedSearch();
|
||||
}, [
|
||||
core.savedObjects.client,
|
||||
basePath,
|
||||
chrome.recentlyAccessed,
|
||||
config,
|
||||
|
@ -129,6 +165,10 @@ export function DiscoverMainRoute({ services, history }: DiscoverMainProps) {
|
|||
);
|
||||
}, [chrome, savedSearch]);
|
||||
|
||||
if (error) {
|
||||
return <DiscoverError error={error} />;
|
||||
}
|
||||
|
||||
if (!indexPattern || !savedSearch) {
|
||||
return <LoadingIndicator />;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -15,6 +15,20 @@ import { indexPatternMock } from '../../../../__mocks__/index_pattern';
|
|||
import { SearchSource } from '../../../../../../data/common';
|
||||
|
||||
describe('test useDiscoverState', () => {
|
||||
const originalSavedObjectsClient = discoverServiceMock.core.savedObjects.client;
|
||||
|
||||
beforeAll(() => {
|
||||
discoverServiceMock.core.savedObjects.client.resolve = jest.fn().mockReturnValue({
|
||||
saved_object: {
|
||||
attributes: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
discoverServiceMock.core.savedObjects.client = originalSavedObjectsClient;
|
||||
});
|
||||
|
||||
test('return is valid', async () => {
|
||||
const { history } = createSearchSessionMock();
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
@ -148,7 +148,12 @@ export function useDiscoverState({
|
|||
*/
|
||||
const resetSavedSearch = useCallback(
|
||||
async (id?: string) => {
|
||||
const newSavedSearch = await services.getSavedSearchById(id);
|
||||
const newSavedSearch = await getSavedSearch(id, {
|
||||
search: services.data.search,
|
||||
savedObjectsClient: services.core.savedObjects.client,
|
||||
spaces: services.spaces,
|
||||
});
|
||||
|
||||
const newIndexPattern = newSavedSearch.searchSource.getField('index') || indexPattern;
|
||||
newSavedSearch.searchSource.setField('index', newIndexPattern);
|
||||
const newAppState = getStateDefaults({
|
||||
|
|
|
@ -9,7 +9,7 @@ import { useCallback, useEffect, useMemo, useRef } from 'react';
|
|||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
) {
|
||||
|
|
|
@ -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 = () => {
|
||||
|
|
|
@ -12,7 +12,7 @@ import { FetchStatus } from '../../../types';
|
|||
import type {
|
||||
AutoRefreshDoneFn,
|
||||
DataPublicPluginStart,
|
||||
SearchSource,
|
||||
ISearchSource,
|
||||
} from '../../../../../../data/public';
|
||||
import { DataMain$, DataRefetch$ } from '../services/use_saved_search';
|
||||
import { DiscoverSearchSessionManager } from '../services/discover_search_session';
|
||||
|
@ -33,7 +33,7 @@ export function getFetch$({
|
|||
main$: DataMain$;
|
||||
refetch$: DataRefetch$;
|
||||
searchSessionManager: DiscoverSearchSessionManager;
|
||||
searchSource: SearchSource;
|
||||
searchSource: ISearchSource;
|
||||
initialFetchStatus: FetchStatus;
|
||||
}) {
|
||||
const { timefilter } = data.query.timefilter;
|
||||
|
|
|
@ -11,7 +11,7 @@ import type { IUiSettingsClient } from 'src/core/public';
|
|||
import type { DataPublicPluginStart } from 'src/plugins/data/public';
|
||||
import type { ISearchSource, SearchSourceFields } 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';
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,10 @@ export async function persistSavedSearch(
|
|||
}
|
||||
|
||||
try {
|
||||
const id = await savedSearch.save(saveOptions);
|
||||
onSuccess(id);
|
||||
const id = await saveSavedSearch(savedSearch, saveOptions, services.core.savedObjects.client);
|
||||
if (id) {
|
||||
onSuccess(id);
|
||||
}
|
||||
return { id };
|
||||
} catch (saveError) {
|
||||
onError(saveError, savedSearch);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,11 @@ 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,
|
||||
throwErrorOnSavedSearchUrlConflict,
|
||||
} from '../../saved_searches';
|
||||
|
||||
interface StartServices {
|
||||
executeTriggerActions: UiActionsStart['executeTriggerActions'];
|
||||
|
@ -59,20 +64,27 @@ export class SearchEmbeddableFactory
|
|||
input: Partial<SearchInput> & { id: string; timeRange: TimeRange },
|
||||
parent?: Container
|
||||
): Promise<SavedSearchEmbeddable | ErrorEmbeddable> => {
|
||||
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,
|
||||
spaces: services.spaces,
|
||||
});
|
||||
|
||||
await throwErrorOnSavedSearchUrlConflict(savedSearch);
|
||||
|
||||
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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<ViewSearchContext> {
|
|||
async execute(context: ActionExecutionContext<ViewSearchContext>): Promise<void> {
|
||||
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 });
|
||||
}
|
||||
|
|
|
@ -31,13 +31,14 @@ import { UiCounterMetricType } from '@kbn/analytics';
|
|||
import { Storage } from '../../kibana_utils/public';
|
||||
|
||||
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;
|
||||
|
@ -57,13 +58,12 @@ export interface DiscoverServices {
|
|||
urlForwarding: UrlForwardingStart;
|
||||
timefilter: TimefilterContract;
|
||||
toastNotifications: ToastsStart;
|
||||
getSavedSearchById: (id?: string) => Promise<SavedSearch>;
|
||||
getSavedSearchUrlById: (id: string) => Promise<string>;
|
||||
uiSettings: IUiSettingsClient;
|
||||
trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void;
|
||||
indexPatternFieldEditor: IndexPatternFieldEditorStart;
|
||||
http: HttpStart;
|
||||
storage: Storage;
|
||||
spaces?: SpacesApi;
|
||||
}
|
||||
|
||||
export function buildServices(
|
||||
|
@ -71,11 +71,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;
|
||||
const storage = new Storage(localStorage);
|
||||
|
||||
|
@ -88,8 +83,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,
|
||||
|
@ -107,5 +100,6 @@ export function buildServices(
|
|||
trackUiMetric: usageCollection?.reportUiCounter.bind(usageCollection, 'discover'),
|
||||
indexPatternFieldEditor: plugins.indexPatternFieldEditor,
|
||||
http: core.http,
|
||||
spaces: plugins.spaces,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,12 +9,23 @@
|
|||
import { PluginInitializerContext } from 'kibana/public';
|
||||
import { DiscoverPlugin } from './plugin';
|
||||
|
||||
export {
|
||||
getSavedSearch,
|
||||
getSavedSearchFullPathUrl,
|
||||
getSavedSearchUrl,
|
||||
getSavedSearchUrlConflictMessage,
|
||||
throwErrorOnSavedSearchUrlConflict,
|
||||
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';
|
||||
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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 type { 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -410,10 +414,12 @@ export class DiscoverPlugin
|
|||
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,
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
10
src/plugins/discover/public/saved_searches/constants.ts
Normal file
10
src/plugins/discover/public/saved_searches/constants.ts
Normal file
|
@ -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';
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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 { savedObjectsServiceMock } from '../../../../core/public/mocks';
|
||||
import { dataPluginMock } from '../../../data/public/mocks';
|
||||
|
||||
import { getSavedSearch } from './get_saved_searches';
|
||||
|
||||
describe('getSavedSearch', () => {
|
||||
let search: DataPublicPluginStart['search'];
|
||||
let savedObjectsClient: SavedObjectsStart['client'];
|
||||
|
||||
beforeEach(() => {
|
||||
savedObjectsClient = savedObjectsServiceMock.createStartContract().client;
|
||||
search = dataPluginMock.createStartContract().search;
|
||||
});
|
||||
|
||||
test('should return empty saved search in case of no id', async () => {
|
||||
const savedSearch = await getSavedSearch(undefined, { savedObjectsClient, search });
|
||||
|
||||
expect(search.searchSource.createEmpty).toHaveBeenCalled();
|
||||
expect(savedSearch).toHaveProperty('searchSource');
|
||||
});
|
||||
|
||||
test('should throw an error if so not found', async () => {
|
||||
let errorMessage = 'No error thrown.';
|
||||
savedObjectsClient.resolve = jest.fn().mockReturnValue({
|
||||
saved_object: {
|
||||
attributes: {},
|
||||
error: {
|
||||
statusCode: 404,
|
||||
error: 'Not Found',
|
||||
message: 'Saved object [search/ccf1af80-2297-11ec-86e0-1155ffb9c7a7] not found',
|
||||
},
|
||||
id: 'ccf1af80-2297-11ec-86e0-1155ffb9c7a7',
|
||||
type: 'search',
|
||||
references: [],
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
await getSavedSearch('ccf1af80-2297-11ec-86e0-1155ffb9c7a7', {
|
||||
savedObjectsClient,
|
||||
search,
|
||||
});
|
||||
} catch (error) {
|
||||
errorMessage = error.message;
|
||||
}
|
||||
|
||||
expect(errorMessage).toBe(
|
||||
'Could not locate that search (id: ccf1af80-2297-11ec-86e0-1155ffb9c7a7)'
|
||||
);
|
||||
});
|
||||
|
||||
test('should find saved search', async () => {
|
||||
savedObjectsClient.resolve = jest.fn().mockReturnValue({
|
||||
saved_object: {
|
||||
attributes: {
|
||||
kibanaSavedObjectMeta: {
|
||||
searchSourceJSON:
|
||||
'{"query":{"query":"","language":"kuery"},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}',
|
||||
},
|
||||
title: 'test1',
|
||||
sort: [['order_date', 'desc']],
|
||||
columns: ['_source'],
|
||||
description: 'description',
|
||||
grid: {},
|
||||
hideChart: false,
|
||||
},
|
||||
id: 'ccf1af80-2297-11ec-86e0-1155ffb9c7a7',
|
||||
type: 'search',
|
||||
references: [
|
||||
{
|
||||
name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
|
||||
id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f',
|
||||
type: 'index-pattern',
|
||||
},
|
||||
],
|
||||
namespaces: ['default'],
|
||||
},
|
||||
outcome: 'exactMatch',
|
||||
});
|
||||
|
||||
const savedSearch = await getSavedSearch('ccf1af80-2297-11ec-86e0-1155ffb9c7a7', {
|
||||
savedObjectsClient,
|
||||
search,
|
||||
});
|
||||
|
||||
expect(savedObjectsClient.resolve).toHaveBeenCalled();
|
||||
expect(savedSearch).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"columns": Array [
|
||||
"_source",
|
||||
],
|
||||
"description": "description",
|
||||
"grid": Object {},
|
||||
"hideChart": false,
|
||||
"id": "ccf1af80-2297-11ec-86e0-1155ffb9c7a7",
|
||||
"searchSource": Object {
|
||||
"create": [MockFunction],
|
||||
"createChild": [MockFunction],
|
||||
"createCopy": [MockFunction],
|
||||
"destroy": [MockFunction],
|
||||
"fetch": [MockFunction],
|
||||
"fetch$": [MockFunction],
|
||||
"getField": [MockFunction],
|
||||
"getFields": [MockFunction],
|
||||
"getId": [MockFunction],
|
||||
"getOwnField": [MockFunction],
|
||||
"getParent": [MockFunction],
|
||||
"getSearchRequestBody": [MockFunction],
|
||||
"getSerializedFields": [MockFunction],
|
||||
"history": Array [],
|
||||
"onRequestStart": [MockFunction],
|
||||
"removeField": [MockFunction],
|
||||
"serialize": [MockFunction],
|
||||
"setField": [MockFunction],
|
||||
"setFields": [MockFunction],
|
||||
"setParent": [MockFunction],
|
||||
"setPreferredSearchStrategyId": [MockFunction],
|
||||
},
|
||||
"sharingSavedObjectProps": Object {
|
||||
"aliasTargetId": undefined,
|
||||
"errorJSON": undefined,
|
||||
"outcome": "exactMatch",
|
||||
},
|
||||
"sort": Array [
|
||||
Array [
|
||||
"order_date",
|
||||
"desc",
|
||||
],
|
||||
],
|
||||
"title": "test1",
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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';
|
||||
import { SavedObjectNotFound } from '../../../kibana_utils/public';
|
||||
|
||||
import type { SpacesApi } from '../../../../../x-pack/plugins/spaces/public';
|
||||
|
||||
interface GetSavedSearchDependencies {
|
||||
search: DataPublicPluginStart['search'];
|
||||
savedObjectsClient: SavedObjectsStart['client'];
|
||||
spaces?: SpacesApi;
|
||||
}
|
||||
|
||||
const getEmptySavedSearch = ({
|
||||
search,
|
||||
}: {
|
||||
search: DataPublicPluginStart['search'];
|
||||
}): SavedSearch => ({
|
||||
searchSource: search.searchSource.createEmpty(),
|
||||
});
|
||||
|
||||
const findSavedSearch = async (
|
||||
savedSearchId: string,
|
||||
{ search, savedObjectsClient, spaces }: GetSavedSearchDependencies
|
||||
) => {
|
||||
const so = await savedObjectsClient.resolve<SavedSearchAttributes>(
|
||||
SAVED_SEARCH_TYPE,
|
||||
savedSearchId
|
||||
);
|
||||
|
||||
if (!so.saved_object || so.saved_object.error) {
|
||||
throw new SavedObjectNotFound(SAVED_SEARCH_TYPE, savedSearchId);
|
||||
}
|
||||
|
||||
const savedSearch = so.saved_object;
|
||||
|
||||
const parsedSearchSourceJSON = parseSearchSourceJSON(
|
||||
savedSearch.attributes.kibanaSavedObjectMeta?.searchSourceJSON ?? '{}'
|
||||
);
|
||||
|
||||
const searchSourceValues = injectSearchSourceReferences(
|
||||
parsedSearchSourceJSON as Parameters<typeof injectSearchSourceReferences>[0],
|
||||
savedSearch.references
|
||||
);
|
||||
|
||||
return fromSavedSearchAttributes(
|
||||
savedSearchId,
|
||||
savedSearch.attributes,
|
||||
await search.searchSource.create(searchSourceValues),
|
||||
{
|
||||
outcome: so.outcome,
|
||||
aliasTargetId: so.alias_target_id,
|
||||
errorJSON:
|
||||
so.outcome === 'conflict' && spaces
|
||||
? JSON.stringify({
|
||||
targetType: SAVED_SEARCH_TYPE,
|
||||
sourceId: savedSearchId,
|
||||
targetSpace: (await spaces.getActiveSpace()).id,
|
||||
})
|
||||
: undefined,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/** @public **/
|
||||
export const getSavedSearch = async (
|
||||
savedSearchId: string | undefined,
|
||||
dependencies: GetSavedSearchDependencies
|
||||
) => {
|
||||
return savedSearchId
|
||||
? findSavedSearch(savedSearchId, dependencies)
|
||||
: getEmptySavedSearch(dependencies);
|
||||
};
|
|
@ -6,5 +6,25 @@
|
|||
* 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,
|
||||
getSavedSearchUrlConflictMessage,
|
||||
throwErrorOnSavedSearchUrlConflict,
|
||||
} 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 { SAVED_SEARCH_TYPE } from './constants';
|
||||
|
||||
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,
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
10
src/plugins/discover/public/saved_searches/legacy/index.ts
Normal file
10
src/plugins/discover/public/saved_searches/legacy/index.ts
Normal file
|
@ -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';
|
|
@ -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;
|
||||
}
|
34
src/plugins/discover/public/saved_searches/legacy/types.ts
Normal file
34
src/plugins/discover/public/saved_searches/legacy/types.ts
Normal file
|
@ -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<string>;
|
||||
copyOnSave?: boolean;
|
||||
hideChart?: boolean;
|
||||
}
|
||||
|
||||
/** @deprecated **/
|
||||
export interface SavedSearchLoader {
|
||||
get: (id: string) => Promise<LegacySavedSearch>;
|
||||
urlFor: (id: string) => string;
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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 { savedObjectsServiceMock } from '../../../../core/public/mocks';
|
||||
import { dataPluginMock } from '../../../data/public/mocks';
|
||||
|
||||
import { saveSavedSearch } from './save_saved_searches';
|
||||
import type { SavedSearch } from './types';
|
||||
|
||||
describe('saveSavedSearch', () => {
|
||||
let savedObjectsClient: SavedObjectsStart['client'];
|
||||
let savedSearch: SavedSearch;
|
||||
|
||||
beforeEach(() => {
|
||||
savedObjectsClient = savedObjectsServiceMock.createStartContract().client;
|
||||
const searchSource = dataPluginMock.createStartContract().search.searchSource.createEmpty();
|
||||
|
||||
savedSearch = {
|
||||
id: 'id',
|
||||
title: 'title',
|
||||
searchSource: {
|
||||
...searchSource,
|
||||
serialize: () => ({
|
||||
searchSourceJSON: '{}',
|
||||
references: [],
|
||||
}),
|
||||
},
|
||||
sharingSavedObjectProps: {
|
||||
outcome: 'aliasMatch',
|
||||
},
|
||||
} as SavedSearch;
|
||||
});
|
||||
|
||||
describe('onTitleDuplicate', () => {
|
||||
test('should check for title duplicating', async () => {
|
||||
savedObjectsClient.find = jest.fn().mockReturnValue({
|
||||
savedObjects: [{ get: () => 'title' }],
|
||||
});
|
||||
const onTitleDuplicate = jest.fn();
|
||||
|
||||
await saveSavedSearch(
|
||||
savedSearch,
|
||||
{
|
||||
onTitleDuplicate,
|
||||
copyOnSave: true,
|
||||
},
|
||||
savedObjectsClient
|
||||
);
|
||||
|
||||
expect(onTitleDuplicate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should not check for title duplicating for saving existing search', async () => {
|
||||
savedObjectsClient.find = jest.fn().mockReturnValue({
|
||||
savedObjects: [{ get: () => 'title' }],
|
||||
});
|
||||
const onTitleDuplicate = jest.fn();
|
||||
|
||||
await saveSavedSearch(
|
||||
savedSearch,
|
||||
{
|
||||
onTitleDuplicate,
|
||||
copyOnSave: false,
|
||||
},
|
||||
savedObjectsClient
|
||||
);
|
||||
|
||||
expect(onTitleDuplicate).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('should call savedObjectsClient.create for saving new search', async () => {
|
||||
delete savedSearch.id;
|
||||
|
||||
await saveSavedSearch(savedSearch, {}, savedObjectsClient);
|
||||
|
||||
expect(savedObjectsClient.create).toHaveBeenCalledWith(
|
||||
'search',
|
||||
{
|
||||
columns: [],
|
||||
description: '',
|
||||
grid: {},
|
||||
hideChart: false,
|
||||
kibanaSavedObjectMeta: { searchSourceJSON: '{}' },
|
||||
sort: [],
|
||||
title: 'title',
|
||||
},
|
||||
{ references: [] }
|
||||
);
|
||||
});
|
||||
|
||||
test('should call savedObjectsClient.update for saving existing search', async () => {
|
||||
await saveSavedSearch(savedSearch, {}, savedObjectsClient);
|
||||
|
||||
expect(savedObjectsClient.update).toHaveBeenCalledWith(
|
||||
'search',
|
||||
'id',
|
||||
{
|
||||
columns: [],
|
||||
description: '',
|
||||
grid: {},
|
||||
hideChart: false,
|
||||
kibanaSavedObjectMeta: { searchSourceJSON: '{}' },
|
||||
sort: [],
|
||||
title: 'title',
|
||||
},
|
||||
{ references: [] }
|
||||
);
|
||||
});
|
||||
});
|
|
@ -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 {
|
||||
onTitleDuplicate?: () => void;
|
||||
isTitleDuplicateConfirmed?: boolean;
|
||||
copyOnSave?: boolean;
|
||||
}
|
||||
|
||||
const hasDuplicatedTitle = async (
|
||||
title: string,
|
||||
savedObjectsClient: SavedObjectsStart['client']
|
||||
): Promise<boolean | void> => {
|
||||
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<string | undefined> => {
|
||||
const isNew = options.copyOnSave || !savedSearch.id;
|
||||
|
||||
if (savedSearch.title) {
|
||||
if (
|
||||
isNew &&
|
||||
!options.isTitleDuplicateConfirmed &&
|
||||
options.onTitleDuplicate &&
|
||||
(await hasDuplicatedTitle(savedSearch.title, savedObjectsClient))
|
||||
) {
|
||||
options.onTitleDuplicate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const { searchSourceJSON, references } = savedSearch.searchSource.serialize();
|
||||
const resp = isNew
|
||||
? await savedObjectsClient.create<SavedSearchAttributes>(
|
||||
SAVED_SEARCH_TYPE,
|
||||
toSavedSearchAttributes(savedSearch, searchSourceJSON),
|
||||
{
|
||||
references,
|
||||
}
|
||||
)
|
||||
: await savedObjectsClient.update<SavedSearchAttributes>(
|
||||
SAVED_SEARCH_TYPE,
|
||||
savedSearch.id!,
|
||||
toSavedSearchAttributes(savedSearch, searchSourceJSON),
|
||||
{
|
||||
references,
|
||||
}
|
||||
);
|
||||
|
||||
return resp?.id;
|
||||
};
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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 { renderHook } from '@testing-library/react-hooks';
|
||||
import type { History } from 'history';
|
||||
|
||||
import { useSavedSearchAliasMatchRedirect } from './saved_search_alias_match_redirect';
|
||||
import type { SavedSearch } from './types';
|
||||
|
||||
import { spacesPluginMock } from '../../../../../x-pack/plugins/spaces/public/mocks';
|
||||
|
||||
describe('useSavedSearchAliasMatchRedirect', () => {
|
||||
let spaces: ReturnType<typeof spacesPluginMock.createStartContract>;
|
||||
let history: () => History;
|
||||
|
||||
beforeEach(() => {
|
||||
spaces = spacesPluginMock.createStartContract();
|
||||
history = () =>
|
||||
({
|
||||
location: {
|
||||
search: '?_g=foo',
|
||||
},
|
||||
} as History);
|
||||
});
|
||||
|
||||
test('should redirect in case of aliasMatch', () => {
|
||||
const savedSearch = {
|
||||
id: 'id',
|
||||
sharingSavedObjectProps: {
|
||||
outcome: 'aliasMatch',
|
||||
aliasTargetId: 'aliasTargetId',
|
||||
},
|
||||
} as SavedSearch;
|
||||
|
||||
renderHook(() => useSavedSearchAliasMatchRedirect({ spaces, savedSearch, history }));
|
||||
|
||||
expect(spaces.ui.redirectLegacyUrl).toHaveBeenCalledWith(
|
||||
'#/view/aliasTargetId?_g=foo',
|
||||
' search'
|
||||
);
|
||||
});
|
||||
|
||||
test('should not redirect if outcome !== aliasMatch', () => {
|
||||
const savedSearch = {
|
||||
id: 'id',
|
||||
sharingSavedObjectProps: {
|
||||
outcome: 'exactMatch',
|
||||
},
|
||||
} as SavedSearch;
|
||||
|
||||
renderHook(() => useSavedSearchAliasMatchRedirect({ spaces, savedSearch, history }));
|
||||
|
||||
expect(spaces.ui.redirectLegacyUrl).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should not redirect if aliasTargetId is not defined', () => {
|
||||
const savedSearch = {
|
||||
id: 'id',
|
||||
sharingSavedObjectProps: {
|
||||
outcome: 'aliasMatch',
|
||||
},
|
||||
} as SavedSearch;
|
||||
|
||||
renderHook(() => useSavedSearchAliasMatchRedirect({ spaces, savedSearch, history }));
|
||||
|
||||
expect(spaces.ui.redirectLegacyUrl).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 { History } from 'history';
|
||||
import { useEffect } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getSavedSearchUrl } from './saved_searches_utils';
|
||||
|
||||
import type { SavedSearch } from './types';
|
||||
import type { SpacesApi } from '../../../../../x-pack/plugins/spaces/public';
|
||||
|
||||
interface SavedSearchAliasMatchRedirectProps {
|
||||
savedSearch?: SavedSearch;
|
||||
spaces?: SpacesApi;
|
||||
history: () => History;
|
||||
}
|
||||
|
||||
export const useSavedSearchAliasMatchRedirect = ({
|
||||
savedSearch,
|
||||
spaces,
|
||||
history,
|
||||
}: SavedSearchAliasMatchRedirectProps) => {
|
||||
useEffect(() => {
|
||||
async function aliasMatchRedirect() {
|
||||
if (savedSearch) {
|
||||
const { aliasTargetId, outcome } = savedSearch.sharingSavedObjectProps ?? {};
|
||||
|
||||
if (spaces && aliasTargetId && outcome === 'aliasMatch') {
|
||||
await spaces.ui.redirectLegacyUrl(
|
||||
`${getSavedSearchUrl(aliasTargetId)}${history().location.search}`,
|
||||
i18n.translate('discover.savedSearchAliasMatchRedirect.objectNoun', {
|
||||
defaultMessage: '{savedSearch} search',
|
||||
values: {
|
||||
savedSearch: savedSearch.title,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aliasMatchRedirect();
|
||||
}, [savedSearch, spaces, history]);
|
||||
};
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import type { History } from 'history';
|
||||
|
||||
import { mountWithIntl } from '@kbn/test/jest';
|
||||
import { SavedSearchURLConflictCallout } from './saved_search_url_conflict_callout';
|
||||
import type { SavedSearch } from './types';
|
||||
|
||||
import { spacesPluginMock } from '../../../../../x-pack/plugins/spaces/public/mocks';
|
||||
|
||||
describe('SavedSearchURLConflictCallout', () => {
|
||||
let spaces: ReturnType<typeof spacesPluginMock.createStartContract>;
|
||||
let history: () => History;
|
||||
|
||||
beforeEach(() => {
|
||||
spaces = spacesPluginMock.createStartContract();
|
||||
spaces.ui.components.getLegacyUrlConflict = jest.fn().mockReturnValue('callout');
|
||||
history = () =>
|
||||
({
|
||||
location: {
|
||||
search: '?_g=foo',
|
||||
},
|
||||
} as History);
|
||||
});
|
||||
|
||||
test("should render URLConflictCallout in case of id's conflicts", () => {
|
||||
const savedSearch = {
|
||||
id: 'id',
|
||||
sharingSavedObjectProps: {
|
||||
outcome: 'conflict',
|
||||
aliasTargetId: 'aliasTargetId',
|
||||
},
|
||||
} as SavedSearch;
|
||||
|
||||
const component = mountWithIntl(
|
||||
<SavedSearchURLConflictCallout spaces={spaces} savedSearch={savedSearch} history={history} />
|
||||
);
|
||||
|
||||
expect(component.children()).toMatchInlineSnapshot(`"callout"`);
|
||||
});
|
||||
|
||||
test('should not render URLConflictCallout in case of no conflicts', () => {
|
||||
const savedSearch = {
|
||||
id: 'id',
|
||||
sharingSavedObjectProps: {},
|
||||
} as SavedSearch;
|
||||
|
||||
const component = mountWithIntl(
|
||||
<SavedSearchURLConflictCallout spaces={spaces} savedSearch={savedSearch} history={history} />
|
||||
);
|
||||
|
||||
expect(component.children()).toMatchInlineSnapshot(`null`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 type { History } from 'history';
|
||||
import { getSavedSearchUrl } from './saved_searches_utils';
|
||||
|
||||
import type { SavedSearch } from './types';
|
||||
import type { SpacesApi } from '../../../../../x-pack/plugins/spaces/public';
|
||||
|
||||
interface SavedSearchURLConflictCalloutProps {
|
||||
savedSearch?: SavedSearch;
|
||||
spaces?: SpacesApi;
|
||||
history: () => History;
|
||||
}
|
||||
|
||||
export const SavedSearchURLConflictCallout = ({
|
||||
savedSearch,
|
||||
spaces,
|
||||
history,
|
||||
}: SavedSearchURLConflictCalloutProps) => {
|
||||
if (spaces && savedSearch?.id && savedSearch?.sharingSavedObjectProps?.outcome === 'conflict') {
|
||||
const otherObjectId = savedSearch.sharingSavedObjectProps?.aliasTargetId;
|
||||
|
||||
if (otherObjectId) {
|
||||
return spaces.ui.components.getLegacyUrlConflict({
|
||||
objectNoun: i18n.translate('discover.savedSearchURLConflictCallout.objectNoun', {
|
||||
defaultMessage: '{savedSearch} search',
|
||||
values: {
|
||||
savedSearch: savedSearch.title,
|
||||
},
|
||||
}),
|
||||
currentObjectId: savedSearch.id,
|
||||
otherObjectPath: `${getSavedSearchUrl(otherObjectId)}${history().location.search}`,
|
||||
otherObjectId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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 {
|
||||
getSavedSearchUrl,
|
||||
getSavedSearchFullPathUrl,
|
||||
fromSavedSearchAttributes,
|
||||
toSavedSearchAttributes,
|
||||
throwErrorOnSavedSearchUrlConflict,
|
||||
} from './saved_searches_utils';
|
||||
|
||||
import { createSearchSourceMock } from '../../../data/public/mocks';
|
||||
|
||||
import type { SavedSearchAttributes, SavedSearch } from './types';
|
||||
|
||||
describe('saved_searches_utils', () => {
|
||||
describe('getSavedSearchUrl', () => {
|
||||
test('should return valid saved search url', () => {
|
||||
expect(getSavedSearchUrl()).toBe('#/');
|
||||
expect(getSavedSearchUrl('id')).toBe('#/view/id');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSavedSearchFullPathUrl', () => {
|
||||
test('should return valid full path url', () => {
|
||||
expect(getSavedSearchFullPathUrl()).toBe('/app/discover#/');
|
||||
expect(getSavedSearchFullPathUrl('id')).toBe('/app/discover#/view/id');
|
||||
});
|
||||
});
|
||||
|
||||
describe('fromSavedSearchAttributes', () => {
|
||||
test('should convert attributes into SavedSearch', () => {
|
||||
const attributes: SavedSearchAttributes = {
|
||||
kibanaSavedObjectMeta: { searchSourceJSON: '{}' },
|
||||
title: 'saved search',
|
||||
sort: [],
|
||||
columns: ['a', 'b'],
|
||||
description: 'foo',
|
||||
grid: {},
|
||||
hideChart: true,
|
||||
};
|
||||
|
||||
expect(fromSavedSearchAttributes('id', attributes, createSearchSourceMock(), {}))
|
||||
.toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"columns": Array [
|
||||
"a",
|
||||
"b",
|
||||
],
|
||||
"description": "foo",
|
||||
"grid": Object {},
|
||||
"hideChart": true,
|
||||
"id": "id",
|
||||
"searchSource": SearchSource {
|
||||
"dependencies": Object {
|
||||
"getConfig": [MockFunction],
|
||||
"onResponse": [MockFunction],
|
||||
"search": [MockFunction],
|
||||
},
|
||||
"fields": Object {},
|
||||
"getFieldName": [Function],
|
||||
"history": Array [],
|
||||
"id": "data_source1",
|
||||
"inheritOptions": Object {},
|
||||
"parent": undefined,
|
||||
"requestStartHandlers": Array [],
|
||||
"searchStrategyId": undefined,
|
||||
},
|
||||
"sharingSavedObjectProps": Object {},
|
||||
"sort": Array [],
|
||||
"title": "saved search",
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('throwErrorOnSavedSearchUrlConflict', () => {
|
||||
test('should throw an error on url conflict', async () => {
|
||||
let error = 'no error';
|
||||
|
||||
try {
|
||||
await throwErrorOnSavedSearchUrlConflict({
|
||||
id: 'id',
|
||||
sharingSavedObjectProps: {
|
||||
outcome: 'conflict',
|
||||
errorJSON: '{}',
|
||||
},
|
||||
} as SavedSearch);
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
}
|
||||
|
||||
expect(error).toBe(
|
||||
'This search has the same URL as a legacy alias. Disable the alias to resolve this error : {}'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toSavedSearchAttributes', () => {
|
||||
test('should serialize SavedSearch attributes', () => {
|
||||
const savedSearch: SavedSearch = {
|
||||
id: 'id',
|
||||
searchSource: createSearchSourceMock(),
|
||||
title: 'title',
|
||||
sort: [['a', 'asc']],
|
||||
columns: ['c', 'd'],
|
||||
description: 'description',
|
||||
grid: {},
|
||||
hideChart: true,
|
||||
};
|
||||
|
||||
expect(toSavedSearchAttributes(savedSearch, '{}')).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"columns": Array [
|
||||
"c",
|
||||
"d",
|
||||
],
|
||||
"description": "description",
|
||||
"grid": Object {},
|
||||
"hideChart": true,
|
||||
"kibanaSavedObjectMeta": Object {
|
||||
"searchSourceJSON": "{}",
|
||||
},
|
||||
"sort": Array [
|
||||
Array [
|
||||
"a",
|
||||
"asc",
|
||||
],
|
||||
],
|
||||
"title": "title",
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 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 getSavedSearchUrlConflictMessage = async (savedSearch: SavedSearch) =>
|
||||
i18n.translate('discover.savedSearchEmbeddable.legacyURLConflict.errorMessage', {
|
||||
defaultMessage: `This search has the same URL as a legacy alias. Disable the alias to resolve this error : {json}`,
|
||||
values: {
|
||||
json: savedSearch.sharingSavedObjectProps?.errorJSON,
|
||||
},
|
||||
});
|
||||
|
||||
export const throwErrorOnSavedSearchUrlConflict = async (savedSearch: SavedSearch) => {
|
||||
if (savedSearch.sharingSavedObjectProps?.errorJSON) {
|
||||
throw new Error(await getSavedSearchUrlConflictMessage(savedSearch));
|
||||
}
|
||||
};
|
||||
|
||||
export const fromSavedSearchAttributes = (
|
||||
id: string,
|
||||
attributes: SavedSearchAttributes,
|
||||
searchSource: SavedSearch['searchSource'],
|
||||
sharingSavedObjectProps: SavedSearch['sharingSavedObjectProps']
|
||||
): SavedSearch => ({
|
||||
id,
|
||||
searchSource,
|
||||
sharingSavedObjectProps,
|
||||
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,
|
||||
});
|
|
@ -6,26 +6,39 @@
|
|||
* 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<string>;
|
||||
lastSavedTitle?: string;
|
||||
copyOnSave?: boolean;
|
||||
description: string;
|
||||
grid: {
|
||||
columns?: Record<string, DiscoverGridSettingsColumn>;
|
||||
};
|
||||
hideChart: boolean;
|
||||
kibanaSavedObjectMeta: {
|
||||
searchSourceJSON: string;
|
||||
};
|
||||
}
|
||||
|
||||
/** @public **/
|
||||
export interface SavedSearch {
|
||||
searchSource: ISearchSource;
|
||||
id?: string;
|
||||
title?: string;
|
||||
sort?: Array<[string, string]>;
|
||||
columns?: string[];
|
||||
description?: string;
|
||||
grid?: {
|
||||
columns?: Record<string, DiscoverGridSettingsColumn>;
|
||||
};
|
||||
hideChart?: boolean;
|
||||
}
|
||||
export interface SavedSearchLoader {
|
||||
get: (id: string) => Promise<SavedSearch>;
|
||||
urlFor: (id: string) => string;
|
||||
sharingSavedObjectProps?: {
|
||||
outcome?: 'aliasMatch' | 'exactMatch' | 'conflict';
|
||||
aliasTargetId?: string;
|
||||
errorJSON?: string;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
{ "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": "../data_views/tsconfig.json" },
|
||||
{ "path": "../../../x-pack/plugins/spaces/tsconfig.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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
||||
|
|
|
@ -23,9 +23,9 @@ import {
|
|||
setAggs,
|
||||
setChrome,
|
||||
setOverlays,
|
||||
setSavedSearchLoader,
|
||||
setEmbeddable,
|
||||
setDocLinks,
|
||||
setSpaces,
|
||||
} from './services';
|
||||
import {
|
||||
VISUALIZE_EMBEDDABLE_TYPE,
|
||||
|
@ -51,8 +51,6 @@ import {
|
|||
findListItems,
|
||||
} from './utils/saved_visualize_utils';
|
||||
|
||||
import { createSavedSearchesLoader } from '../../discover/public';
|
||||
|
||||
import type {
|
||||
PluginInitializerContext,
|
||||
CoreSetup,
|
||||
|
@ -191,6 +189,11 @@ export class VisualizationsPlugin
|
|||
setAggs(data.search.aggs);
|
||||
setOverlays(core.overlays);
|
||||
setChrome(core.chrome);
|
||||
|
||||
if (spaces) {
|
||||
setSpaces(spaces);
|
||||
}
|
||||
|
||||
const savedVisualizationsLoader = createSavedVisLoader({
|
||||
savedObjectsClient: core.savedObjects.client,
|
||||
indexPatterns: data.indexPatterns,
|
||||
|
@ -198,11 +201,7 @@ export class VisualizationsPlugin
|
|||
visualizationTypes: types,
|
||||
});
|
||||
setSavedVisualizationsLoader(savedVisualizationsLoader);
|
||||
const savedSearchLoader = createSavedSearchesLoader({
|
||||
savedObjectsClient: core.savedObjects.client,
|
||||
savedObjects,
|
||||
});
|
||||
setSavedSearchLoader(savedSearchLoader);
|
||||
|
||||
return {
|
||||
...types,
|
||||
showNewVisModal,
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
import type { SavedObjectsStart, SavedObject } from '../../../../plugins/saved_objects/public';
|
||||
// @ts-ignore
|
||||
import { updateOldState } from '../legacy/vis_update_state';
|
||||
import { __LEGACY } from '../../../discover/public';
|
||||
import { extractReferences, injectReferences } from '../utils/saved_visualization_references';
|
||||
import { createSavedSearchesLoader } from '../../../discover/public';
|
||||
import type { SavedObjectsClientContract } from '../../../../core/public';
|
||||
import type { IndexPatternsContract } from '../../../../plugins/data/public';
|
||||
import type { ISavedVis } from '../types';
|
||||
|
@ -30,7 +30,7 @@ export interface SavedVisServices {
|
|||
|
||||
/** @deprecated **/
|
||||
export function createSavedVisClass(services: SavedVisServices) {
|
||||
const savedSearch = createSavedSearchesLoader(services);
|
||||
const savedSearch = __LEGACY.createSavedSearchesLoader(services);
|
||||
|
||||
class SavedVis extends services.savedObjects.SavedObjectClass {
|
||||
public static type: string = 'visualization';
|
||||
|
|
|
@ -18,13 +18,14 @@ import type {
|
|||
} from '../../../core/public';
|
||||
import type { TypesStart } from './vis_types';
|
||||
import { createGetterSetter } from '../../../plugins/kibana_utils/public';
|
||||
import { DataPublicPluginStart, TimefilterContract } from '../../../plugins/data/public';
|
||||
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';
|
||||
import type { DataPublicPluginStart, TimefilterContract } from '../../../plugins/data/public';
|
||||
import type { UsageCollectionSetup } from '../../../plugins/usage_collection/public';
|
||||
import type { ExpressionsStart } from '../../../plugins/expressions/public';
|
||||
import type { UiActionsStart } from '../../../plugins/ui_actions/public';
|
||||
import type { SavedVisualizationsLoader } from './saved_visualizations';
|
||||
import type { EmbeddableStart } from '../../embeddable/public';
|
||||
|
||||
import type { SpacesPluginStart } from '../../../../x-pack/plugins/spaces/public';
|
||||
|
||||
export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings');
|
||||
|
||||
|
@ -64,5 +65,4 @@ export const [getOverlays, setOverlays] = createGetterSetter<OverlayStart>('Over
|
|||
|
||||
export const [getChrome, setChrome] = createGetterSetter<ChromeStart>('Chrome');
|
||||
|
||||
export const [getSavedSearchLoader, setSavedSearchLoader] =
|
||||
createGetterSetter<SavedObjectLoader>('savedSearchLoader');
|
||||
export const [getSpaces, setSpaces] = createGetterSetter<SpacesPluginStart>('Spaces', false);
|
||||
|
|
|
@ -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, getSpaces } 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, throwErrorOnSavedSearchUrlConflict } from '../../discover/public';
|
||||
|
||||
export interface SerializedVisData {
|
||||
expression?: string;
|
||||
aggs: AggConfigSerialized[];
|
||||
|
@ -58,14 +60,20 @@ export interface VisData {
|
|||
}
|
||||
|
||||
const getSearchSource = async (inputSearchSource: ISearchSource, savedSearchId?: string) => {
|
||||
const searchSource = inputSearchSource.createCopy();
|
||||
if (savedSearchId) {
|
||||
const savedSearch = await getSavedSearchLoader().get(savedSearchId);
|
||||
const savedSearch = await getSavedSearch(savedSearchId, {
|
||||
search: getSearch(),
|
||||
savedObjectsClient: getSavedObjects().client,
|
||||
spaces: getSpaces(),
|
||||
});
|
||||
|
||||
searchSource.setParent(savedSearch.searchSource);
|
||||
await throwErrorOnSavedSearchUrlConflict(savedSearch);
|
||||
|
||||
if (savedSearch?.searchSource) {
|
||||
inputSearchSource.setParent(savedSearch.searchSource);
|
||||
}
|
||||
}
|
||||
searchSource.setField('size', 0);
|
||||
return searchSource;
|
||||
return inputSearchSource;
|
||||
};
|
||||
|
||||
type PartialVisState = Assign<SerializedVis, { data: Partial<SerializedVisData> }>;
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import type { EventEmitter } from 'events';
|
||||
import type { History } from 'history';
|
||||
|
||||
import type { SerializableRecord } from '@kbn/utility-types';
|
||||
|
||||
import type {
|
||||
|
@ -38,7 +37,7 @@ 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';
|
||||
|
@ -46,6 +45,7 @@ import type { SpacesPluginStart } from '../../../../../x-pack/plugins/spaces/pub
|
|||
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';
|
||||
|
||||
|
@ -108,20 +108,15 @@ export interface VisualizeServices extends CoreStart {
|
|||
spaces?: SpacesPluginStart;
|
||||
}
|
||||
|
||||
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<TVisParams = VisParams> = new (
|
||||
|
@ -142,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
|
||||
|
|
|
@ -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,13 @@ 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: {},
|
||||
}),
|
||||
throwErrorOnSavedSearchUrlConflict: jest.fn(),
|
||||
}));
|
||||
|
||||
let savedVisMock: VisSavedObject;
|
||||
|
@ -116,9 +116,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 () => {
|
||||
|
|
|
@ -14,10 +14,13 @@ 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,
|
||||
throwErrorOnSavedSearchUrlConflict,
|
||||
} from '../../../../discover/public';
|
||||
import { SavedFieldNotFound, SavedFieldTypeInvalidForAgg } from '../../../../kibana_utils/common';
|
||||
import { VisualizeServices } from '../types';
|
||||
|
||||
|
@ -33,8 +36,7 @@ const createVisualizeEmbeddableAndLinkSavedSearch = async (
|
|||
vis: Vis,
|
||||
visualizeServices: VisualizeServices
|
||||
) => {
|
||||
const { data, createVisEmbeddableFromObject, savedObjects, savedObjectsPublic } =
|
||||
visualizeServices;
|
||||
const { data, createVisEmbeddableFromObject, savedObjects, spaces } = visualizeServices;
|
||||
const embeddableHandler = (await createVisEmbeddableFromObject(vis, {
|
||||
id: '',
|
||||
timeRange: data.query.timefilter.timefilter.getTime(),
|
||||
|
@ -50,13 +52,16 @@ 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);
|
||||
spaces,
|
||||
});
|
||||
|
||||
await throwErrorOnSavedSearchUrlConflict(savedSearch);
|
||||
}
|
||||
|
||||
return { savedSearch, embeddableHandler };
|
||||
|
|
|
@ -28,7 +28,6 @@ import {
|
|||
createKbnUrlStateStorage,
|
||||
withNotifyOnErrors,
|
||||
} from '../../kibana_utils/public';
|
||||
import type { SpacesPluginStart } from '../../../../x-pack/plugins/spaces/public';
|
||||
|
||||
import { VisualizeConstants } from './application/visualize_constants';
|
||||
import { DataPublicPluginStart, DataPublicPluginSetup, esFilters } from '../../data/public';
|
||||
|
@ -45,6 +44,7 @@ import type { EmbeddableStart } from '../../embeddable/public';
|
|||
import type { DashboardStart } from '../../dashboard/public';
|
||||
import type { SavedObjectTaggingOssPluginStart } from '../../saved_objects_tagging_oss/public';
|
||||
import type { UsageCollectionStart } from '../../usage_collection/public';
|
||||
import type { SpacesApi } from '../../../../x-pack/plugins/spaces/public';
|
||||
|
||||
import { setVisEditorsRegistry, setUISettings, setUsageCollector } from './services';
|
||||
import { createVisEditorsRegistry, VisEditorsRegistry } from './vis_editors_registry';
|
||||
|
@ -62,7 +62,7 @@ export interface VisualizePluginStartDependencies {
|
|||
savedObjectsTaggingOss?: SavedObjectTaggingOssPluginStart;
|
||||
presentationUtil: PresentationUtilPluginStart;
|
||||
usageCollection?: UsageCollectionStart;
|
||||
spaces: SpacesPluginStart;
|
||||
spaces?: SpacesApi;
|
||||
}
|
||||
|
||||
export interface VisualizePluginSetupDependencies {
|
||||
|
|
|
@ -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',
|
||||
});
|
||||
});
|
||||
|
|
|
@ -122,7 +122,7 @@ export class ReportingCsvPanelAction implements ActionDefinition<ActionContext>
|
|||
const immediateJobParams = this.apiClient.getDecoratedJobParams({
|
||||
searchSource: getSearchSource(true),
|
||||
columns,
|
||||
title: savedSearch.title,
|
||||
title: savedSearch.title || '',
|
||||
objectType: 'downloadCsv', // FIXME: added for typescript, but immediate download job does not need objectType
|
||||
});
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
"optionalPlugins": [
|
||||
"security",
|
||||
"usageCollection",
|
||||
"spaces",
|
||||
"alerting"
|
||||
],
|
||||
"configPath": ["xpack", "transform"],
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -10,6 +10,7 @@ import type { DataPublicPluginStart } from 'src/plugins/data/public';
|
|||
import type { SavedObjectsStart } from 'src/plugins/saved_objects/public';
|
||||
import type { ScopedHistory } from 'kibana/public';
|
||||
import type { SharePluginStart } from 'src/plugins/share/public';
|
||||
import type { SpacesPluginStart } from '../../../spaces/public';
|
||||
|
||||
import { useKibana } from '../../../../../src/plugins/kibana_react/public';
|
||||
import type { Storage } from '../../../../../src/plugins/kibana_utils/public';
|
||||
|
@ -32,6 +33,7 @@ export interface AppDependencies {
|
|||
savedObjectsPlugin: SavedObjectsStart;
|
||||
share: SharePluginStart;
|
||||
ml: GetMlSharedImportsReturnType;
|
||||
spaces?: SpacesPluginStart;
|
||||
}
|
||||
|
||||
export const useAppDependencies = () => {
|
||||
|
|
|
@ -20,12 +20,10 @@ import { isIndexPattern } from '../../../../common/types/index_pattern';
|
|||
export type SavedSearchQuery = object;
|
||||
|
||||
type IndexPatternId = string;
|
||||
type SavedSearchId = string;
|
||||
|
||||
let indexPatternCache: Array<SimpleSavedObject<Record<string, any>>> = [];
|
||||
let fullIndexPatterns;
|
||||
let currentIndexPattern = null;
|
||||
let currentSavedSearch = null;
|
||||
|
||||
export let refreshIndexPatterns: () => Promise<unknown>;
|
||||
|
||||
|
@ -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;
|
||||
|
|
|
@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import { isIndexPattern } from '../../../../common/types/index_pattern';
|
||||
|
||||
import { createSavedSearchesLoader } from '../../../shared_imports';
|
||||
import { getSavedSearch, getSavedSearchUrlConflictMessage } 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<SearchItems | undefined>(undefined);
|
||||
|
||||
|
@ -52,7 +47,16 @@ export const useSearchItems = (defaultSavedObjectId: string | undefined) => {
|
|||
}
|
||||
|
||||
try {
|
||||
fetchedSavedSearch = await loadCurrentSavedSearch(savedSearches, id);
|
||||
fetchedSavedSearch = await getSavedSearch(id, {
|
||||
search: appDeps.data.search,
|
||||
savedObjectsClient: appDeps.savedObjects.client,
|
||||
spaces: appDeps.spaces,
|
||||
});
|
||||
|
||||
if (fetchedSavedSearch?.sharingSavedObjectProps?.errorJSON) {
|
||||
setError(await getSavedSearchUrlConflictMessage(fetchedSavedSearch));
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// Just let fetchedSavedSearch stay undefined in case it doesn't exist.
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ export async function mountManagementSection(
|
|||
const startServices = await getStartServices();
|
||||
const [core, plugins] = startServices;
|
||||
const { application, chrome, docLinks, i18n, overlays, savedObjects, uiSettings } = core;
|
||||
const { data, share } = plugins;
|
||||
const { data, share, spaces } = plugins;
|
||||
const { docTitle } = chrome;
|
||||
|
||||
// Initialize services
|
||||
|
@ -53,6 +53,7 @@ export async function mountManagementSection(
|
|||
history,
|
||||
savedObjectsPlugin: plugins.savedObjects,
|
||||
share,
|
||||
spaces,
|
||||
ml: await getMlSharedImports(),
|
||||
};
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import type { HomePublicPluginSetup } from 'src/plugins/home/public';
|
|||
import type { SavedObjectsStart } from 'src/plugins/saved_objects/public';
|
||||
import type { ManagementSetup } from 'src/plugins/management/public';
|
||||
import type { SharePluginStart } from 'src/plugins/share/public';
|
||||
import type { SpacesApi } from '../../spaces/public';
|
||||
import { registerFeature } from './register_feature';
|
||||
import type { PluginSetupContract as AlertingSetup } from '../../alerting/public';
|
||||
import type { TriggersAndActionsUIPublicPluginSetup } from '../../triggers_actions_ui/public';
|
||||
|
@ -24,6 +25,7 @@ export interface PluginsDependencies {
|
|||
home: HomePublicPluginSetup;
|
||||
savedObjects: SavedObjectsStart;
|
||||
share: SharePluginStart;
|
||||
spaces?: SpacesApi;
|
||||
alerting?: AlertingSetup;
|
||||
triggersActionsUi?: TriggersAndActionsUIPublicPluginSetup;
|
||||
}
|
||||
|
|
|
@ -5,9 +5,12 @@
|
|||
* 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,
|
||||
getSavedSearchUrlConflictMessage,
|
||||
} from '../../../../src/plugins/discover/public';
|
||||
|
||||
export {
|
||||
getMlSharedImports,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue