diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx index 13365af53e33be5..f6299e4596427c8 100644 --- a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx @@ -747,11 +747,7 @@ export class SavedSearchEmbeddable } } - public getSavedSearch(): SavedSearch { - if (!this.savedSearch) { - throw new Error('Saved search not defined'); - } - + public getSavedSearch(): SavedSearch | undefined { return this.savedSearch; } diff --git a/src/plugins/discover/public/embeddable/types.ts b/src/plugins/discover/public/embeddable/types.ts index 7fe8a32665783fa..8ca9611cb1703ef 100644 --- a/src/plugins/discover/public/embeddable/types.ts +++ b/src/plugins/discover/public/embeddable/types.ts @@ -22,7 +22,7 @@ export interface SearchOutput extends EmbeddableOutput { } export interface ISearchEmbeddable extends IEmbeddable { - getSavedSearch(): SavedSearch; + getSavedSearch(): SavedSearch | undefined; hasTimeRange(): boolean; } diff --git a/src/plugins/discover/public/embeddable/view_saved_search_action.test.ts b/src/plugins/discover/public/embeddable/view_saved_search_action.test.ts index 0f9b1698c54e9ba..667ded0ba28e80c 100644 --- a/src/plugins/discover/public/embeddable/view_saved_search_action.test.ts +++ b/src/plugins/discover/public/embeddable/view_saved_search_action.test.ts @@ -82,7 +82,7 @@ describe('view saved search action', () => { expect(discoverServiceMock.locator.navigate).toHaveBeenCalledWith( getDiscoverLocatorParams({ input: embeddable.getInput(), - savedSearch: embeddable.getSavedSearch(), + savedSearch: embeddable.getSavedSearch()!, }) ); }); diff --git a/src/plugins/discover/public/embeddable/view_saved_search_action.ts b/src/plugins/discover/public/embeddable/view_saved_search_action.ts index 75cf0971c1481d6..1e5044707ce1d7e 100644 --- a/src/plugins/discover/public/embeddable/view_saved_search_action.ts +++ b/src/plugins/discover/public/embeddable/view_saved_search_action.ts @@ -33,9 +33,13 @@ export class ViewSavedSearchAction implements Action { async execute(context: ActionExecutionContext): Promise { const embeddable = context.embeddable as SavedSearchEmbeddable; + const savedSearch = embeddable.getSavedSearch(); + if (!savedSearch) { + return; + } const locatorParams = getDiscoverLocatorParams({ input: embeddable.getInput(), - savedSearch: embeddable.getSavedSearch(), + savedSearch, }); await this.locator.navigate(locatorParams); } diff --git a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx index 900fbf0994a3e8a..497d52b94444009 100644 --- a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx +++ b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx @@ -104,7 +104,7 @@ export class ReportingCsvPanelAction implements ActionDefinition } const savedSearch = embeddable.getSavedSearch(); - const query = savedSearch.searchSource.getField('query'); + const query = savedSearch?.searchSource.getField('query'); // using isOfAggregateQueryType(query) added increased the bundle size over the configured limit of 55.7KB if (query && Boolean(query && 'sql' in query)) { @@ -121,11 +121,12 @@ export class ReportingCsvPanelAction implements ActionDefinition throw new IncompatibleActionError(); } - if (this.isDownloading) { + const savedSearch = embeddable.getSavedSearch(); + + if (!savedSearch || this.isDownloading) { return; } - const savedSearch = embeddable.getSavedSearch(); const { columns, getSearchSource } = await this.getSharingData(savedSearch); const immediateJobParams = this.apiClient.getDecoratedJobParams({ diff --git a/x-pack/test/functional/apps/discover/index.ts b/x-pack/test/functional/apps/discover/index.ts index 9f277a85551cd30..a07eb9c66323991 100644 --- a/x-pack/test/functional/apps/discover/index.ts +++ b/x-pack/test/functional/apps/discover/index.ts @@ -19,5 +19,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./visualize_field')); loadTestFile(require.resolve('./value_suggestions')); loadTestFile(require.resolve('./value_suggestions_non_timebased')); + loadTestFile(require.resolve('./saved_search_embeddable')); }); } diff --git a/x-pack/test/functional/apps/discover/saved_search_embeddable.ts b/x-pack/test/functional/apps/discover/saved_search_embeddable.ts new file mode 100644 index 000000000000000..fd546315a1fd947 --- /dev/null +++ b/x-pack/test/functional/apps/discover/saved_search_embeddable.ts @@ -0,0 +1,109 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const browser = getService('browser'); + const dataGrid = getService('dataGrid'); + const dashboardAddPanel = getService('dashboardAddPanel'); + const dashboardPanelActions = getService('dashboardPanelActions'); + const filterBar = getService('filterBar'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'timePicker', 'discover']); + + describe('discover saved search embeddable', () => { + before(async () => { + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/dashboard/current/data'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); + await kibanaServer.uiSettings.replace({ + defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', + }); + await PageObjects.common.setTime({ + from: 'Sep 22, 2015 @ 00:00:00.000', + to: 'Sep 23, 2015 @ 00:00:00.000', + }); + }); + + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + await PageObjects.common.unsetTime(); + }); + + beforeEach(async () => { + await PageObjects.dashboard.navigateToApp(); + await filterBar.ensureFieldEditorModalIsClosed(); + await PageObjects.dashboard.gotoDashboardLandingPage(); + await PageObjects.dashboard.clickNewDashboard(); + }); + + const addSearchEmbeddableToDashboard = async (title = 'Rendering-Test:-saved-search') => { + await dashboardAddPanel.addSavedSearch(title); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.waitForRenderComplete(); + const rows = await dataGrid.getDocTableRows(); + expect(rows.length).to.be.above(0); + }; + + const refreshDashboardPage = async (requireRenderComplete = false) => { + await browser.refresh(); + await PageObjects.header.waitUntilLoadingHasFinished(); + if (requireRenderComplete) { + await PageObjects.dashboard.waitForRenderComplete(); + } + }; + + it('should allow removing the dashboard panel after the underlying saved search has been deleted', async () => { + const searchTitle = 'TempSearch'; + const searchId = '90943e30-9a47-11e8-b64d-95841ca0b247'; + await kibanaServer.savedObjects.create({ + type: 'search', + id: searchId, + overwrite: false, + attributes: { + title: searchTitle, + description: '', + columns: ['agent', 'bytes', 'clientip'], + sort: [['@timestamp', 'desc']], + kibanaSavedObjectMeta: { + searchSourceJSON: + '{"highlightAll":true,"version":true,"query":{"language":"lucene","query":""},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}', + }, + }, + references: [ + { + id: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + type: 'index-pattern', + }, + ], + }); + await addSearchEmbeddableToDashboard(searchTitle); + await PageObjects.dashboard.saveDashboard('Dashboard with deleted saved search', { + waitDialogIsClosed: true, + exitFromEditMode: false, + }); + await kibanaServer.savedObjects.delete({ + type: 'search', + id: searchId, + }); + await refreshDashboardPage(); + await testSubjects.existOrFail('embeddableError'); + const panels = await PageObjects.dashboard.getDashboardPanels(); + await dashboardPanelActions.removePanel(panels[0]); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.missingOrFail('embeddableError'); + }); + }); +}