diff --git a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts
index 0c5329d8b259f..b497f73f3df2a 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts
@@ -28,7 +28,7 @@ export { npSetup, npStart } from 'ui/new_platform';
export { KbnUrl } from 'ui/url/kbn_url';
// @ts-ignore
-export { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url/index';
+export { KbnUrlProvider } from 'ui/url/index';
export { IInjector } from 'ui/chrome';
export { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url';
export {
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts
index fe0e7a1d3e6d0..9447b5384d172 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts
@@ -35,7 +35,6 @@ import {
KbnUrlProvider,
PrivateProvider,
PromiseServiceCreator,
- RedirectWhenMissingProvider,
} from '../legacy_imports';
// @ts-ignore
import { initDashboardApp } from './legacy_app';
@@ -146,8 +145,7 @@ function createLocalIconModule() {
function createLocalKbnUrlModule() {
angular
.module('app/dashboard/KbnUrl', ['app/dashboard/Private', 'ngRoute'])
- .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider))
- .service('redirectWhenMissing', (Private: IPrivate) => Private(RedirectWhenMissingProvider));
+ .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider));
}
function createLocalConfigModule(core: AppMountContext['core']) {
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js
index 35b510894179d..f7baba663da75 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js
@@ -28,6 +28,7 @@ import { initDashboardAppDirective } from './dashboard_app';
import { createDashboardEditUrl, DashboardConstants } from './dashboard_constants';
import {
createKbnUrlStateStorage,
+ redirectWhenMissing,
InvalidJSONProperty,
SavedObjectNotFound,
} from '../../../../../../plugins/kibana_utils/public';
@@ -136,7 +137,7 @@ export function initDashboardApp(app, deps) {
});
},
resolve: {
- dash: function($rootScope, $route, redirectWhenMissing, kbnUrl, history) {
+ dash: function($rootScope, $route, kbnUrl, history) {
return ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl).then(() => {
const savedObjectsClient = deps.savedObjectsClient;
const title = $route.current.params.title;
@@ -171,14 +172,18 @@ export function initDashboardApp(app, deps) {
controller: createNewDashboardCtrl,
requireUICapability: 'dashboard.createNew',
resolve: {
- dash: function(redirectWhenMissing, $rootScope, kbnUrl) {
+ dash: function($rootScope, kbnUrl, history) {
return ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl)
.then(() => {
return deps.savedDashboards.get();
})
.catch(
redirectWhenMissing({
- dashboard: DashboardConstants.LANDING_PAGE_PATH,
+ history,
+ mapping: {
+ dashboard: DashboardConstants.LANDING_PAGE_PATH,
+ },
+ toastNotifications: deps.core.notifications.toasts,
})
);
},
@@ -189,7 +194,7 @@ export function initDashboardApp(app, deps) {
template: dashboardTemplate,
controller: createNewDashboardCtrl,
resolve: {
- dash: function($rootScope, $route, redirectWhenMissing, kbnUrl, history) {
+ dash: function($rootScope, $route, kbnUrl, history) {
const id = $route.current.params.id;
return ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl)
@@ -207,7 +212,7 @@ export function initDashboardApp(app, deps) {
.catch(error => {
// A corrupt dashboard was detected (e.g. with invalid JSON properties)
if (error instanceof InvalidJSONProperty) {
- deps.toastNotifications.addDanger(error.message);
+ deps.core.notifications.toasts.addDanger(error.message);
kbnUrl.redirect(DashboardConstants.LANDING_PAGE_PATH);
return;
}
@@ -221,7 +226,7 @@ export function initDashboardApp(app, deps) {
pathname: DashboardConstants.CREATE_NEW_DASHBOARD_URL,
});
- deps.toastNotifications.addWarning(
+ deps.core.notifications.toasts.addWarning(
i18n.translate('kbn.dashboard.urlWasRemovedInSixZeroWarningMessage', {
defaultMessage:
'The url "dashboard/create" was removed in 6.0. Please update your bookmarks.',
@@ -234,7 +239,11 @@ export function initDashboardApp(app, deps) {
})
.catch(
redirectWhenMissing({
- dashboard: DashboardConstants.LANDING_PAGE_PATH,
+ history,
+ mapping: {
+ dashboard: DashboardConstants.LANDING_PAGE_PATH,
+ },
+ toastNotifications: deps.core.notifications.toasts,
})
);
},
diff --git a/src/legacy/core_plugins/kibana/public/discover/build_services.ts b/src/legacy/core_plugins/kibana/public/discover/build_services.ts
index c58307adaf38c..282eef0c983eb 100644
--- a/src/legacy/core_plugins/kibana/public/discover/build_services.ts
+++ b/src/legacy/core_plugins/kibana/public/discover/build_services.ts
@@ -16,6 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { createHashHistory, History } from 'history';
+
import {
Capabilities,
ChromeStart,
@@ -46,6 +48,7 @@ export interface DiscoverServices {
data: DataPublicPluginStart;
docLinks: DocLinksStart;
docViewsRegistry: DocViewsRegistry;
+ history: History;
theme: ChartsPluginStart['theme'];
filterManager: FilterManager;
indexPatterns: IndexPatternsContract;
@@ -79,6 +82,7 @@ export async function buildServices(
data: plugins.data,
docLinks: core.docLinks,
docViewsRegistry,
+ history: createHashHistory(),
theme: plugins.charts.theme,
filterManager: plugins.data.query.filterManager,
getSavedSearchById: async (id: string) => savedObjectService.get(id),
diff --git a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts
index 76d475c4f7f96..4d871bcb7a858 100644
--- a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts
+++ b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts
@@ -27,7 +27,7 @@ import { CoreStart, LegacyCoreStart, IUiSettingsClient } from 'kibana/public';
// @ts-ignore
import { StateManagementConfigProvider } from 'ui/state_management/config_provider';
// @ts-ignore
-import { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url';
+import { KbnUrlProvider } from 'ui/url';
import { DataPublicPluginStart } from '../../../../../plugins/data/public';
import { Storage } from '../../../../../plugins/kibana_utils/public';
import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public';
@@ -173,8 +173,7 @@ export function initializeInnerAngularModule(
function createLocalKbnUrlModule() {
angular
.module('discoverKbnUrl', ['discoverPrivate', 'ngRoute'])
- .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider))
- .service('redirectWhenMissing', (Private: IPrivate) => Private(RedirectWhenMissingProvider));
+ .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider));
}
function createLocalConfigModule(uiSettings: IUiSettingsClient) {
diff --git a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts
index 57a9e4966d6d6..8202ba13b30cc 100644
--- a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts
+++ b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts
@@ -59,7 +59,7 @@ export { intervalOptions } from 'ui/agg_types';
export { subscribeWithScope } from '../../../../../plugins/kibana_legacy/public';
// @ts-ignore
export { timezoneProvider } from 'ui/vis/lib/timezone';
-export { unhashUrl } from '../../../../../plugins/kibana_utils/public';
+export { unhashUrl, redirectWhenMissing } from '../../../../../plugins/kibana_utils/public';
export {
ensureDefaultIndexPattern,
formatMsg,
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js
index f3334c9211e4b..6978781fe6696 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js
@@ -50,6 +50,7 @@ import {
tabifyAggResponse,
getAngularModule,
ensureDefaultIndexPattern,
+ redirectWhenMissing,
} from '../../kibana_services';
const {
@@ -57,6 +58,7 @@ const {
chrome,
data,
docTitle,
+ history,
indexPatterns,
filterManager,
share,
@@ -113,10 +115,10 @@ app.config($routeProvider => {
template: indexTemplate,
reloadOnSearch: false,
resolve: {
- savedObjects: function(redirectWhenMissing, $route, kbnUrl, Promise, $rootScope) {
+ savedObjects: function($route, kbnUrl, Promise, $rootScope) {
const savedSearchId = $route.current.params.id;
return ensureDefaultIndexPattern(core, data, $rootScope, kbnUrl).then(() => {
- const { appStateContainer } = getState({});
+ const { appStateContainer } = getState({ history });
const { index } = appStateContainer.getState();
return Promise.props({
ip: indexPatterns.getCache().then(indexPatternList => {
@@ -151,9 +153,13 @@ app.config($routeProvider => {
})
.catch(
redirectWhenMissing({
- search: '/discover',
- 'index-pattern':
- '/management/kibana/objects/savedSearches/' + $route.current.params.id,
+ history,
+ mapping: {
+ search: '/discover',
+ 'index-pattern':
+ '/management/kibana/objects/savedSearches/' + $route.current.params.id,
+ },
+ toastNotifications,
})
),
});
@@ -207,6 +213,7 @@ function discoverController(
} = getState({
defaultAppState: getStateDefaults(),
storeInSessionStorage: config.get('state:storeInSessionStorage'),
+ history,
});
if (appStateContainer.getState().index !== $scope.indexPattern.id) {
//used index pattern is different than the given by url/state which is invalid
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.test.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.test.ts
index af772cb5c76f1..3840fd0c2e3be 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.test.ts
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.test.ts
@@ -30,7 +30,7 @@ describe('Test discover state', () => {
history.push('/');
state = getState({
defaultAppState: { index: 'test' },
- hashHistory: history,
+ history,
});
await state.replaceUrlAppState({});
await state.startSync();
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.ts
index 10e7cd1d0c49d..981855d1ee774 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.ts
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.ts
@@ -17,7 +17,7 @@
* under the License.
*/
import { isEqual } from 'lodash';
-import { createHashHistory, History } from 'history';
+import { History } from 'history';
import {
createStateContainer,
createKbnUrlStateStorage,
@@ -65,9 +65,9 @@ interface GetStateParams {
*/
storeInSessionStorage?: boolean;
/**
- * Browser history used for testing
+ * Browser history
*/
- hashHistory?: History;
+ history: History;
}
export interface GetStateReturn {
@@ -121,11 +121,11 @@ const APP_STATE_URL_KEY = '_a';
export function getState({
defaultAppState = {},
storeInSessionStorage = false,
- hashHistory,
+ history,
}: GetStateParams): GetStateReturn {
const stateStorage = createKbnUrlStateStorage({
useHash: storeInSessionStorage,
- history: hashHistory ? hashHistory : createHashHistory(),
+ history,
});
const appStateFromUrl = stateStorage.get(APP_STATE_URL_KEY) as AppState;
diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts
index 0ddf3ee67aa94..69af466a03729 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts
+++ b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts
@@ -25,7 +25,7 @@
*/
// @ts-ignore
-export { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url';
+export { KbnUrlProvider } from 'ui/url';
export { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url';
export { KibanaParsedUrl } from 'ui/url/kibana_parsed_url';
export { wrapInI18nContext } from 'ui/i18n';
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts
index 8ef63ec5778e2..c7c3286bb5c71 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts
@@ -24,7 +24,6 @@ import { AppMountContext } from 'kibana/public';
import {
configureAppAngularModule,
KbnUrlProvider,
- RedirectWhenMissingProvider,
IPrivate,
PrivateProvider,
PromiseServiceCreator,
@@ -102,8 +101,7 @@ function createLocalAngularModule(core: AppMountContext['core'], navigation: Nav
function createLocalKbnUrlModule() {
angular
.module('app/visualize/KbnUrl', ['app/visualize/Private', 'ngRoute'])
- .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider))
- .service('redirectWhenMissing', (Private: IPrivate) => Private(RedirectWhenMissingProvider));
+ .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider));
}
function createLocalPromiseModule() {
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js
index c023c402f5fea..1fab38027f65b 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js
@@ -31,6 +31,7 @@ import { getEditBreadcrumbs } from '../breadcrumbs';
import { addHelpMenuToAppChrome } from '../help_menu/help_menu_util';
import { unhashUrl } from '../../../../../../../plugins/kibana_utils/public';
+import { MarkdownSimple, toMountPoint } from '../../../../../../../plugins/kibana_react/public';
import { addFatalError, kbnBaseUrl } from '../../../../../../../plugins/kibana_legacy/public';
import {
SavedObjectSaveModal,
@@ -75,7 +76,6 @@ function VisualizeAppController(
$injector,
$timeout,
kbnUrl,
- redirectWhenMissing,
kbnUrlStateStorage,
history
) {
@@ -313,16 +313,33 @@ function VisualizeAppController(
}
);
+ const stopAllSyncing = () => {
+ stopStateSync();
+ stopSyncingQueryServiceStateWithUrl();
+ stopSyncingAppFilters();
+ };
+
// The savedVis is pulled from elasticsearch, but the appState is pulled from the url, with the
// defaults applied. If the url was from a previous session which included modifications to the
// appState then they won't be equal.
if (!_.isEqual(stateContainer.getState().vis, stateDefaults.vis)) {
try {
vis.setState(stateContainer.getState().vis);
- } catch {
- redirectWhenMissing({
- 'index-pattern-field': '/visualize',
+ } catch (error) {
+ // stop syncing url updtes with the state to prevent extra syncing
+ stopAllSyncing();
+
+ toastNotifications.addWarning({
+ title: i18n.translate('kbn.visualize.visualizationTypeInvalidNotificationMessage', {
+ defaultMessage: 'Invalid visualization type',
+ }),
+ text: toMountPoint({error.message}),
});
+
+ history.replace(`${VisualizeConstants.LANDING_PAGE_PATH}?notFound=visualization`);
+
+ // prevent further controller execution
+ return;
}
}
@@ -529,9 +546,8 @@ function VisualizeAppController(
unsubscribePersisted();
unsubscribeStateUpdates();
- stopStateSync();
- stopSyncingQueryServiceStateWithUrl();
- stopSyncingAppFilters();
+
+ stopAllSyncing();
});
$timeout(() => {
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js
index 6acdb0abdd0b5..c8acea168444f 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js
@@ -59,7 +59,9 @@ export function initVisualizationDirective(app, deps) {
});
$scope.$on('$destroy', () => {
- $scope._handler.destroy();
+ if ($scope._handler) {
+ $scope._handler.destroy();
+ }
});
},
};
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js
index b9409445166bc..1002f401706cd 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js
@@ -21,7 +21,10 @@ import { find } from 'lodash';
import { i18n } from '@kbn/i18n';
import { createHashHistory } from 'history';
-import { createKbnUrlStateStorage } from '../../../../../../plugins/kibana_utils/public';
+import {
+ createKbnUrlStateStorage,
+ redirectWhenMissing,
+} from '../../../../../../plugins/kibana_utils/public';
import editorTemplate from './editor/editor.html';
import visualizeListingTemplate from './listing/visualize_listing.html';
@@ -100,8 +103,8 @@ export function initVisualizeApp(app, deps) {
template: editorTemplate,
k7Breadcrumbs: getCreateBreadcrumbs,
resolve: {
- savedVis: function(redirectWhenMissing, $route, $rootScope, kbnUrl) {
- const { core, data, savedVisualizations, visualizations } = deps;
+ savedVis: function($route, $rootScope, kbnUrl, history) {
+ const { core, data, savedVisualizations, visualizations, toastNotifications } = deps;
const visTypes = visualizations.all();
const visType = find(visTypes, { name: $route.current.params.type });
const shouldHaveIndex = visType.requiresSearch && visType.options.showIndexSelection;
@@ -128,7 +131,9 @@ export function initVisualizeApp(app, deps) {
})
.catch(
redirectWhenMissing({
- '*': '/visualize',
+ history,
+ mapping: VisualizeConstants.LANDING_PAGE_PATH,
+ toastNotifications,
})
);
},
@@ -139,8 +144,8 @@ export function initVisualizeApp(app, deps) {
template: editorTemplate,
k7Breadcrumbs: getEditBreadcrumbs,
resolve: {
- savedVis: function(redirectWhenMissing, $route, $rootScope, kbnUrl) {
- const { chrome, core, data, savedVisualizations } = deps;
+ savedVis: function($route, $rootScope, kbnUrl, history) {
+ const { chrome, core, data, savedVisualizations, toastNotifications } = deps;
return ensureDefaultIndexPattern(core, data, $rootScope, kbnUrl)
.then(() => savedVisualizations.get($route.current.params.id))
.then(savedVis => {
@@ -155,13 +160,17 @@ export function initVisualizeApp(app, deps) {
})
.catch(
redirectWhenMissing({
- visualization: '/visualize',
- search:
- '/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
- 'index-pattern':
- '/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
- 'index-pattern-field':
- '/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
+ history,
+ mapping: {
+ visualization: VisualizeConstants.LANDING_PAGE_PATH,
+ search:
+ '/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
+ 'index-pattern':
+ '/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
+ 'index-pattern-field':
+ '/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
+ },
+ toastNotifications,
})
);
},
diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts
index b6ca91169a933..305aa8575e4d7 100644
--- a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts
+++ b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts
@@ -18,6 +18,8 @@
*/
import { defaults, pluck, last, get } from 'lodash';
+
+jest.mock('../../../../kibana_utils/public/history');
import { IndexPattern } from './index_pattern';
import { DuplicateField } from '../../../../kibana_utils/public';
diff --git a/src/plugins/kibana_utils/public/history/index.ts b/src/plugins/kibana_utils/public/history/index.ts
index b4b5658c1c886..bb13ea09f928a 100644
--- a/src/plugins/kibana_utils/public/history/index.ts
+++ b/src/plugins/kibana_utils/public/history/index.ts
@@ -18,3 +18,4 @@
*/
export { removeQueryParam } from './remove_query_param';
+export { redirectWhenMissing } from './redirect_when_missing';
diff --git a/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx b/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx
new file mode 100644
index 0000000000000..cbdeef6fbe96c
--- /dev/null
+++ b/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx
@@ -0,0 +1,80 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import { History } from 'history';
+import { i18n } from '@kbn/i18n';
+
+import { ToastsSetup } from 'kibana/public';
+import { MarkdownSimple, toMountPoint } from '../../../kibana_react/public';
+import { SavedObjectNotFound } from '../errors';
+
+interface Mapping {
+ [key: string]: string;
+}
+
+/**
+ * Creates an error handler that will redirect to a url when a SavedObjectNotFound
+ * error is thrown
+ */
+export function redirectWhenMissing({
+ history,
+ mapping,
+ toastNotifications,
+}: {
+ history: History;
+ /**
+ * a mapping of url's to redirect to based on the saved object that
+ * couldn't be found, or just a string that will be used for all types
+ */
+ mapping: string | Mapping;
+ /**
+ * Toast notifications service to show toasts in error cases.
+ */
+ toastNotifications: ToastsSetup;
+}) {
+ let localMappingObject: Mapping;
+
+ if (typeof mapping === 'string') {
+ localMappingObject = { '*': mapping };
+ } else {
+ localMappingObject = mapping;
+ }
+
+ return (error: SavedObjectNotFound) => {
+ // if this error is not "404", rethrow
+ // we can't check "error instanceof SavedObjectNotFound" since this class can live in a separate bundle
+ // and the error will be an instance of other class with the same interface (actually the copy of SavedObjectNotFound class)
+ if (!error.savedObjectType) {
+ throw error;
+ }
+
+ let url = localMappingObject[error.savedObjectType] || localMappingObject['*'] || '/';
+ url += (url.indexOf('?') >= 0 ? '&' : '?') + `notFound=${error.savedObjectType}`;
+
+ toastNotifications.addWarning({
+ title: i18n.translate('kibana_utils.history.savedObjectIsMissingNotificationMessage', {
+ defaultMessage: 'Saved object is missing',
+ }),
+ text: toMountPoint({error.message}),
+ });
+
+ history.replace(url);
+ };
+}
diff --git a/src/plugins/kibana_utils/public/index.ts b/src/plugins/kibana_utils/public/index.ts
index ee38d5e8111c9..47f90cbe2a627 100644
--- a/src/plugins/kibana_utils/public/index.ts
+++ b/src/plugins/kibana_utils/public/index.ts
@@ -73,5 +73,5 @@ export {
StartSyncStateFnType,
StopSyncStateFnType,
} from './state_sync';
-export { removeQueryParam } from './history';
+export { removeQueryParam, redirectWhenMissing } from './history';
export { applyDiff } from './state_management/utils/diff_object';
diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts b/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts
index c14b64a32fb5c..b506784bf15ee 100644
--- a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts
+++ b/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts
@@ -19,7 +19,6 @@ import {
StateManagementConfigProvider,
AppStateProvider,
KbnUrlProvider,
- RedirectWhenMissingProvider,
npStart,
} from '../legacy_imports';
@@ -79,8 +78,7 @@ function createLocalStateModule() {
function createLocalKbnUrlModule() {
angular
.module('monitoring/KbnUrl', ['monitoring/Private', 'ngRoute'])
- .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider))
- .service('redirectWhenMissing', (Private: IPrivate) => Private(RedirectWhenMissingProvider));
+ .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider));
}
function createLocalConfigModule(core: AppMountContext['core']) {
diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/legacy_imports.ts b/x-pack/legacy/plugins/monitoring/public/np_imports/legacy_imports.ts
index a2ebe8231456f..208b7e2acdb0f 100644
--- a/x-pack/legacy/plugins/monitoring/public/np_imports/legacy_imports.ts
+++ b/x-pack/legacy/plugins/monitoring/public/np_imports/legacy_imports.ts
@@ -18,5 +18,5 @@ export { AppStateProvider } from 'ui/state_management/app_state';
// @ts-ignore
export { EventsProvider } from 'ui/events';
// @ts-ignore
-export { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url';
+export { KbnUrlProvider } from 'ui/url';
export { registerTimefilterWithGlobalStateFactory } from 'ui/timefilter/setup_router';