From e146ae09985c834f73995629c4cf09757903f1f3 Mon Sep 17 00:00:00 2001 From: Daniil Suleiman <31325372+sulemanof@users.noreply.github.com> Date: Mon, 30 Mar 2020 13:30:36 +0300 Subject: [PATCH] [NP] Remove absoluteToParsedUrl & KibanaParsedUrl ref in kibana app (#61105) (#61762) * Remove absoluteToParsedUrl reference in dashboard * Remove KibanaParsedUrl from visualize * Fix tests * Add tests * Fix saved dashboard * Fix empty line after resolving conflicts Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- .../kibana/public/dashboard/legacy_imports.ts | 1 - .../dashboard/np_ready/url_helper.test.ts | 118 ------------------ .../public/dashboard/np_ready/url_helper.ts | 100 --------------- .../public/visualize/kibana_services.ts | 1 - .../kibana/public/visualize/legacy_imports.ts | 2 - .../visualize/np_ready/editor/editor.js | 41 ++---- .../visualize/np_ready/editor/lib/index.ts | 1 + .../np_ready/editor/lib/url_helper.test.ts | 48 +++++++ .../np_ready/editor/lib/url_helper.ts | 39 ++++++ .../kibana/public/visualize/plugin.ts | 1 - .../plugins/lens/public/helpers/index.ts | 7 ++ .../lens/public/helpers/url_helper.test.ts | 49 ++++++++ .../plugins/lens/public/helpers/url_helper.ts | 45 +++++++ .../plugins/lens/public/legacy_imports.ts | 1 + x-pack/legacy/plugins/lens/public/plugin.tsx | 54 +++----- 15 files changed, 217 insertions(+), 291 deletions(-) delete mode 100644 src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.test.ts delete mode 100644 src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.ts create mode 100644 src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.test.ts create mode 100644 src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.ts create mode 100644 x-pack/legacy/plugins/lens/public/helpers/index.ts create mode 100644 x-pack/legacy/plugins/lens/public/helpers/url_helper.test.ts create mode 100644 x-pack/legacy/plugins/lens/public/helpers/url_helper.ts 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 55e1475fcb03a2..86bce5997cdd2c 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts @@ -25,7 +25,6 @@ */ export { npSetup, npStart } from 'ui/new_platform'; -export { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; export { configureAppAngularModule, migrateLegacyQuery, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.test.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.test.ts deleted file mode 100644 index 60ca1b39d29d63..00000000000000 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -jest.mock('../', () => ({ - DashboardConstants: { - ADD_EMBEDDABLE_ID: 'addEmbeddableId', - ADD_EMBEDDABLE_TYPE: 'addEmbeddableType', - }, -})); - -jest.mock('../legacy_imports', () => { - return { - absoluteToParsedUrl: jest.fn(() => { - return { - basePath: '/pep', - appId: 'kibana', - appPath: '/dashboard?addEmbeddableType=lens&addEmbeddableId=123eb456cd&x=1&y=2&z=3', - hostname: 'localhost', - port: 5601, - protocol: 'http:', - addQueryParameter: () => {}, - getAbsoluteUrl: () => { - return 'http://localhost:5601/pep/app/kibana#/dashboard?addEmbeddableType=lens&addEmbeddableId=123eb456cd&x=1&y=2&z=3'; - }, - }; - }), - }; -}); - -import { - addEmbeddableToDashboardUrl, - getLensUrlFromDashboardAbsoluteUrl, - getUrlVars, -} from './url_helper'; - -describe('Dashboard URL Helper', () => { - beforeEach(() => { - jest.resetModules(); - }); - - it('addEmbeddableToDashboardUrl', () => { - const id = '123eb456cd'; - const type = 'lens'; - const urlVars = { - x: '1', - y: '2', - z: '3', - }; - const basePath = '/pep'; - const url = - "http://localhost:5601/pep/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!()"; - expect(addEmbeddableToDashboardUrl(url, basePath, id, urlVars, type)).toEqual( - `http://localhost:5601/pep/app/kibana#/dashboard?addEmbeddableType=${type}&addEmbeddableId=${id}&x=1&y=2&z=3` - ); - }); - - it('getUrlVars', () => { - let url = - "http://localhost:5601/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!()"; - expect(getUrlVars(url)).toEqual({ - _g: '(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))', - _a: "(description:'',filters:!()", - }); - url = 'http://mybusiness.mydomain.com/app/kibana#/dashboard?x=y&y=z'; - expect(getUrlVars(url)).toEqual({ - x: 'y', - y: 'z', - }); - url = 'http://localhost:5601/app/kibana#/dashboard/777182'; - expect(getUrlVars(url)).toEqual({}); - url = - 'http://localhost:5601/app/kibana#/dashboard/777182?title=Some%20Dashboard%20With%20Spaces'; - expect(getUrlVars(url)).toEqual({ title: 'Some Dashboard With Spaces' }); - }); - - it('getLensUrlFromDashboardAbsoluteUrl', () => { - const id = '1244'; - const basePath = '/wev'; - let url = - "http://localhost:5601/wev/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!()"; - expect(getLensUrlFromDashboardAbsoluteUrl(url, basePath, id)).toEqual( - 'http://localhost:5601/wev/app/kibana#/lens/edit/1244' - ); - - url = - "http://localhost:5601/wev/app/kibana#/dashboard/625357282?_a=(description:'',filters:!()&_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))"; - expect(getLensUrlFromDashboardAbsoluteUrl(url, basePath, id)).toEqual( - 'http://localhost:5601/wev/app/kibana#/lens/edit/1244' - ); - - url = 'http://myserver.mydomain.com:5601/wev/app/kibana#/dashboard/777182'; - expect(getLensUrlFromDashboardAbsoluteUrl(url, basePath, id)).toEqual( - 'http://myserver.mydomain.com:5601/wev/app/kibana#/lens/edit/1244' - ); - - url = - "http://localhost:5601/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!()"; - expect(getLensUrlFromDashboardAbsoluteUrl(url, '', id)).toEqual( - 'http://localhost:5601/app/kibana#/lens/edit/1244' - ); - }); -}); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.ts deleted file mode 100644 index 73383f2ff3f68b..00000000000000 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.ts +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { parse } from 'url'; -import { absoluteToParsedUrl } from '../legacy_imports'; -import { DashboardConstants } from './dashboard_constants'; -/** - * Return query params from URL - * @param url given url - */ -export function getUrlVars(url: string): Record { - const vars: Record = {}; - for (const [, key, value] of url.matchAll(/[?&]+([^=&]+)=([^&]*)/gi)) { - vars[key] = decodeURIComponent(value); - } - return vars; -} - -/** * - * Returns dashboard URL with added embeddableType and embeddableId query params - * eg. - * input: url: http://localhost:5601/lib/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now)), embeddableId: 12345, embeddableType: 'lens' - * output: http://localhost:5601/lib/app/kibana#dashboard?addEmbeddableType=lens&addEmbeddableId=12345&_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now)) - * @param url dasbhoard absolute url - * @param embeddableId id of the saved visualization - * @param basePath current base path - * @param urlVars url query params (optional) - * @param embeddableType 'lens' or 'visualization' (optional, default is 'lens') - */ -export function addEmbeddableToDashboardUrl( - url: string | undefined, - basePath: string, - embeddableId: string, - urlVars?: Record, - embeddableType?: string -): string | null { - if (!url) { - return null; - } - const dashboardUrl = getUrlWithoutQueryParams(url); - const dashboardParsedUrl = absoluteToParsedUrl(dashboardUrl, basePath); - if (urlVars) { - const keys = Object.keys(urlVars).sort(); - keys.forEach(key => { - dashboardParsedUrl.addQueryParameter(key, urlVars[key]); - }); - } - dashboardParsedUrl.addQueryParameter( - DashboardConstants.ADD_EMBEDDABLE_TYPE, - embeddableType || 'lens' - ); - dashboardParsedUrl.addQueryParameter(DashboardConstants.ADD_EMBEDDABLE_ID, embeddableId); - return dashboardParsedUrl.getAbsoluteUrl(); -} - -/** - * Return Lens URL from dashboard absolute URL - * @param dashboardAbsoluteUrl - * @param basePath current base path - * @param id Lens id - */ -export function getLensUrlFromDashboardAbsoluteUrl( - dashboardAbsoluteUrl: string | undefined | null, - basePath: string | null | undefined, - id: string -): string | null { - if (!dashboardAbsoluteUrl || basePath === null || basePath === undefined) { - return null; - } - const { host, protocol } = parse(dashboardAbsoluteUrl); - return `${protocol}//${host}${basePath}/app/kibana#/lens/edit/${id}`; -} - -/** - * Returns the portion of the URL without query params - * eg. - * input: http://localhost:5601/lib/app/kibana#/dashboard?param1=x¶m2=y¶m3=z - * output:http://localhost:5601/lib/app/kibana#/dashboard - * input: http://localhost:5601/lib/app/kibana#/dashboard/39292992?param1=x¶m2=y¶m3=z - * output: http://localhost:5601/lib/app/kibana#/dashboard/39292992 - * @param url url to parse - */ -function getUrlWithoutQueryParams(url: string): string { - return url.split('?')[0]; -} diff --git a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts index f29f07ba4b20bb..2ed7e3d43168c8 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts @@ -45,7 +45,6 @@ export interface VisualizeKibanaServices { core: CoreStart; data: DataPublicPluginStart; embeddable: EmbeddableStart; - getBasePath: () => string; indexPatterns: IndexPatternsContract; localStorage: Storage; navigation: NavigationStart; 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 a6774e2dd47e8e..f6557ed4af1551 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts @@ -24,8 +24,6 @@ * directly where they are needed. */ -export { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; -export { KibanaParsedUrl } from 'ui/url/kibana_parsed_url'; export { wrapInI18nContext } from 'ui/i18n'; export { DashboardConstants } from '../dashboard/np_ready/dashboard_constants'; export { 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 c5325ca3108b42..9ccd45dfc1b458 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 @@ -26,14 +26,14 @@ import { EventEmitter } from 'events'; import React from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { makeStateful, useVisualizeAppState } from './lib'; +import { makeStateful, useVisualizeAppState, addEmbeddableToDashboardUrl } from './lib'; import { VisualizeConstants } from '../visualize_constants'; import { getEditBreadcrumbs } from '../breadcrumbs'; import { addHelpMenuToAppChrome } from '../help_menu/help_menu_util'; import { unhashUrl, removeQueryParam } from '../../../../../../../plugins/kibana_utils/public'; import { MarkdownSimple, toMountPoint } from '../../../../../../../plugins/kibana_react/public'; -import { addFatalError, kbnBaseUrl } from '../../../../../../../plugins/kibana_legacy/public'; +import { addFatalError } from '../../../../../../../plugins/kibana_legacy/public'; import { SavedObjectSaveModal, showSaveModal, @@ -46,14 +46,7 @@ import { import { initVisEditorDirective } from './visualization_editor'; import { initVisualizationDirective } from './visualization'; -import { - VISUALIZE_EMBEDDABLE_TYPE, - subscribeWithScope, - absoluteToParsedUrl, - KibanaParsedUrl, - migrateLegacyQuery, - DashboardConstants, -} from '../../legacy_imports'; +import { subscribeWithScope, migrateLegacyQuery, DashboardConstants } from '../../legacy_imports'; import { getServices } from '../../kibana_services'; @@ -79,7 +72,6 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState data: { query: queryService }, toastNotifications, chrome, - getBasePath, core: { docLinks, fatalErrors }, savedQueryService, uiSettings, @@ -653,29 +645,14 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState }); if ($scope.isAddToDashMode()) { - const savedVisualizationParsedUrl = new KibanaParsedUrl({ - basePath: getBasePath(), - appId: kbnBaseUrl.slice('/app/'.length), - appPath: `${VisualizeConstants.EDIT_PATH}/${encodeURIComponent(savedVis.id)}`, - }); + const appPath = `${VisualizeConstants.EDIT_PATH}/${encodeURIComponent(savedVis.id)}`; // Manually insert a new url so the back button will open the saved visualization. - history.replace(savedVisualizationParsedUrl.appPath); - setActiveUrl(savedVisualizationParsedUrl.appPath); + history.replace(appPath); + setActiveUrl(appPath); - const lastDashboardAbsoluteUrl = chrome.navLinks.get('kibana:dashboard').url; - const dashboardParsedUrl = absoluteToParsedUrl( - lastDashboardAbsoluteUrl, - getBasePath() - ); - dashboardParsedUrl.addQueryParameter( - DashboardConstants.ADD_EMBEDDABLE_TYPE, - VISUALIZE_EMBEDDABLE_TYPE - ); - dashboardParsedUrl.addQueryParameter( - DashboardConstants.ADD_EMBEDDABLE_ID, - savedVis.id - ); - history.push(dashboardParsedUrl.appPath); + const lastDashboardUrl = chrome.navLinks.get('kibana:dashboard').url; + const dashboardUrl = addEmbeddableToDashboardUrl(lastDashboardUrl, savedVis.id); + history.push(dashboardUrl); } else if (savedVis.id === $route.current.params.id) { chrome.docTitle.change(savedVis.lastSavedTitle); chrome.setBreadcrumbs($injector.invoke(getEditBreadcrumbs)); diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.ts index fa5b91b00edaf1..6e2f759c73f2f2 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.ts @@ -19,3 +19,4 @@ export { useVisualizeAppState } from './visualize_app_state'; export { makeStateful } from './make_stateful'; +export { addEmbeddableToDashboardUrl } from './url_helper'; diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.test.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.test.ts new file mode 100644 index 00000000000000..e6974af9af8322 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.test.ts @@ -0,0 +1,48 @@ +/* + * 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 { addEmbeddableToDashboardUrl } from './url_helper'; + +jest.mock('../../../legacy_imports', () => ({ + DashboardConstants: { + ADD_EMBEDDABLE_ID: 'addEmbeddableId', + ADD_EMBEDDABLE_TYPE: 'addEmbeddableType', + CREATE_NEW_DASHBOARD_URL: '/dashboard', + }, + VISUALIZE_EMBEDDABLE_TYPE: 'visualization', +})); + +describe('', () => { + it('addEmbeddableToDashboardUrl when dashboard is not saved', () => { + const id = '123eb456cd'; + const url = + "/pep/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!())"; + expect(addEmbeddableToDashboardUrl(url, id)).toEqual( + `/dashboard?_a=%28description%3A%27%27%2Cfilters%3A%21%28%29%29&_g=%28refreshInterval%3A%28pause%3A%21t%2Cvalue%3A0%29%2Ctime%3A%28from%3Anow-15m%2Cto%3Anow%29%29&addEmbeddableId=${id}&addEmbeddableType=visualization` + ); + }); + it('addEmbeddableToDashboardUrl when dashboard is saved', () => { + const id = '123eb456cd'; + const url = + "/pep/app/kibana#/dashboard/9b780cd0-3dd3-11e8-b2b9-5d5dc1715159?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!())"; + expect(addEmbeddableToDashboardUrl(url, id)).toEqual( + `/dashboard/9b780cd0-3dd3-11e8-b2b9-5d5dc1715159?_a=%28description%3A%27%27%2Cfilters%3A%21%28%29%29&_g=%28refreshInterval%3A%28pause%3A%21t%2Cvalue%3A0%29%2Ctime%3A%28from%3Anow-15m%2Cto%3Anow%29%29&addEmbeddableId=${id}&addEmbeddableType=visualization` + ); + }); +}); diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.ts new file mode 100644 index 00000000000000..c7937c856184a8 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.ts @@ -0,0 +1,39 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { parseUrl, stringify } from 'query-string'; +import { DashboardConstants, VISUALIZE_EMBEDDABLE_TYPE } from '../../../legacy_imports'; + +/** * + * Returns relative dashboard URL with added embeddableType and embeddableId query params + * eg. + * input: url: lol/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now)), embeddableId: 12345 + * output: /dashboard?addEmbeddableType=visualization&addEmbeddableId=12345&_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now)) + * @param url dasbhoard absolute url + * @param embeddableId id of the saved visualization + */ +export function addEmbeddableToDashboardUrl(dashboardUrl: string, embeddableId: string) { + const { url, query } = parseUrl(dashboardUrl); + const [, dashboardId] = url.split(DashboardConstants.CREATE_NEW_DASHBOARD_URL); + + query[DashboardConstants.ADD_EMBEDDABLE_TYPE] = VISUALIZE_EMBEDDABLE_TYPE; + query[DashboardConstants.ADD_EMBEDDABLE_ID] = embeddableId; + + return `${DashboardConstants.CREATE_NEW_DASHBOARD_URL}${dashboardId}?${stringify(query)}`; +} diff --git a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts index 59b814c98dd082..6d32579f5c5412 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts @@ -140,7 +140,6 @@ export class VisualizePlugin implements Plugin { chrome: coreStart.chrome, data: dataStart, embeddable, - getBasePath: core.http.basePath.get, indexPatterns: dataStart.indexPatterns, localStorage: new Storage(localStorage), navigation, diff --git a/x-pack/legacy/plugins/lens/public/helpers/index.ts b/x-pack/legacy/plugins/lens/public/helpers/index.ts new file mode 100644 index 00000000000000..f464b5dcc97a32 --- /dev/null +++ b/x-pack/legacy/plugins/lens/public/helpers/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { addEmbeddableToDashboardUrl, getUrlVars } from './url_helper'; diff --git a/x-pack/legacy/plugins/lens/public/helpers/url_helper.test.ts b/x-pack/legacy/plugins/lens/public/helpers/url_helper.test.ts new file mode 100644 index 00000000000000..9c59c9a96d00ff --- /dev/null +++ b/x-pack/legacy/plugins/lens/public/helpers/url_helper.test.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +jest.mock('../legacy_imports', () => ({ + DashboardConstants: { + ADD_EMBEDDABLE_ID: 'addEmbeddableId', + ADD_EMBEDDABLE_TYPE: 'addEmbeddableType', + }, +})); + +import { addEmbeddableToDashboardUrl, getUrlVars } from './url_helper'; + +describe('Dashboard URL Helper', () => { + it('addEmbeddableToDashboardUrl', () => { + const id = '123eb456cd'; + const urlVars = { + x: '1', + y: '2', + z: '3', + }; + const url = + "/pep/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!())"; + expect(addEmbeddableToDashboardUrl(url, id, urlVars)).toEqual( + `/pep/app/kibana#/dashboard?_a=%28description%3A%27%27%2Cfilters%3A%21%28%29%29&_g=%28refreshInterval%3A%28pause%3A%21t%2Cvalue%3A0%29%2Ctime%3A%28from%3Anow-15m%2Cto%3Anow%29%29&addEmbeddableId=${id}&addEmbeddableType=lens&x=1&y=2&z=3` + ); + }); + + it('getUrlVars', () => { + let url = + "http://localhost:5601/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!()"; + expect(getUrlVars(url)).toEqual({ + _g: '(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))', + _a: "(description:'',filters:!()", + }); + url = 'http://mybusiness.mydomain.com/app/kibana#/dashboard?x=y&y=z'; + expect(getUrlVars(url)).toEqual({ + x: 'y', + y: 'z', + }); + url = 'http://localhost:5601/app/kibana#/dashboard/777182'; + expect(getUrlVars(url)).toEqual({}); + url = + 'http://localhost:5601/app/kibana#/dashboard/777182?title=Some%20Dashboard%20With%20Spaces'; + expect(getUrlVars(url)).toEqual({ title: 'Some Dashboard With Spaces' }); + }); +}); diff --git a/x-pack/legacy/plugins/lens/public/helpers/url_helper.ts b/x-pack/legacy/plugins/lens/public/helpers/url_helper.ts new file mode 100644 index 00000000000000..fca44195b98c42 --- /dev/null +++ b/x-pack/legacy/plugins/lens/public/helpers/url_helper.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { parseUrl, stringify } from 'query-string'; +import { DashboardConstants } from '../legacy_imports'; + +type UrlVars = Record; + +/** + * Return query params from URL + * @param url given url + */ +export function getUrlVars(url: string): Record { + const vars: UrlVars = {}; + for (const [, key, value] of url.matchAll(/[?&]+([^=&]+)=([^&]*)/gi)) { + vars[key] = decodeURIComponent(value); + } + return vars; +} + +/** * + * Returns dashboard URL with added embeddableType and embeddableId query params + * eg. + * input: url: /lol/app/kibana#/dashboard?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now)), embeddableId: 12345 + * output: /lol/app/kibana#/dashboard?addEmbeddableType=lens&addEmbeddableId=12345&_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now)) + * @param url dasbhoard absolute url + * @param embeddableId id of the saved visualization + * @param urlVars url query params + */ +export function addEmbeddableToDashboardUrl(url: string, embeddableId: string, urlVars: UrlVars) { + const dashboardParsedUrl = parseUrl(url); + const keys = Object.keys(urlVars).sort(); + + keys.forEach(key => { + dashboardParsedUrl.query[key] = urlVars[key]; + }); + dashboardParsedUrl.query[DashboardConstants.ADD_EMBEDDABLE_TYPE] = 'lens'; + dashboardParsedUrl.query[DashboardConstants.ADD_EMBEDDABLE_ID] = embeddableId; + const query = stringify(dashboardParsedUrl.query); + + return `${dashboardParsedUrl.url}?${query}`; +} diff --git a/x-pack/legacy/plugins/lens/public/legacy_imports.ts b/x-pack/legacy/plugins/lens/public/legacy_imports.ts index 5c5afc1a87df00..857443ae0fa1c5 100644 --- a/x-pack/legacy/plugins/lens/public/legacy_imports.ts +++ b/x-pack/legacy/plugins/lens/public/legacy_imports.ts @@ -7,3 +7,4 @@ import { npSetup } from 'ui/new_platform'; export const { visualizations } = npSetup.plugins; export { VisualizationsSetup } from '../../../../../src/plugins/visualizations/public'; +export { DashboardConstants } from '../../../../../src/legacy/core_plugins/kibana/public/dashboard'; diff --git a/x-pack/legacy/plugins/lens/public/plugin.tsx b/x-pack/legacy/plugins/lens/public/plugin.tsx index fad1371199e6a1..45817fdc3c05f2 100644 --- a/x-pack/legacy/plugins/lens/public/plugin.tsx +++ b/x-pack/legacy/plugins/lens/public/plugin.tsx @@ -8,10 +8,14 @@ import React from 'react'; import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; import { HashRouter, Route, RouteComponentProps, Switch } from 'react-router-dom'; import { render, unmountComponentAtNode } from 'react-dom'; -import { AppMountParameters, CoreSetup, CoreStart } from 'src/core/public'; -import { DataPublicPluginSetup, DataPublicPluginStart } from 'src/plugins/data/public'; import rison, { RisonObject, RisonValue } from 'rison-node'; import { isObject } from 'lodash'; + +import { AppMountParameters, CoreSetup, CoreStart } from 'src/core/public'; +import { DataPublicPluginSetup, DataPublicPluginStart } from 'src/plugins/data/public'; +import { EmbeddableSetup, EmbeddableStart } from 'src/plugins/embeddable/public'; +import { ExpressionsSetup, ExpressionsStart } from 'src/plugins/expressions/public'; +import { KibanaLegacySetup } from 'src/plugins/kibana_legacy/public'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { EditorFrameService } from './editor_frame_service'; import { IndexPatternDatasource } from './indexpattern_datasource'; @@ -19,7 +23,6 @@ import { addHelpMenuToAppChrome } from './help_menu_util'; import { SavedObjectIndexStore } from './persistence'; import { XyVisualization } from './xy_visualization'; import { MetricVisualization } from './metric_visualization'; -import { ExpressionsSetup, ExpressionsStart } from '../../../../../src/plugins/expressions/public'; import { DatatableVisualization } from './datatable_visualization'; import { App } from './app_plugin'; import { @@ -30,17 +33,12 @@ import { } from './lens_ui_telemetry'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; -import { KibanaLegacySetup } from '../../../../../src/plugins/kibana_legacy/public'; import { NOT_INTERNATIONALIZED_PRODUCT_NAME } from '../../../../plugins/lens/common'; -import { - addEmbeddableToDashboardUrl, - getUrlVars, - getLensUrlFromDashboardAbsoluteUrl, -} from '../../../../../src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper'; -import { EmbeddableSetup, EmbeddableStart } from '../../../../../src/plugins/embeddable/public'; +import { addEmbeddableToDashboardUrl, getUrlVars } from './helpers'; import { EditorFrameStart } from './types'; import { getLensAliasConfig } from './vis_type_alias'; -import { VisualizationsSetup } from './legacy_imports'; +import { VisualizationsSetup, DashboardConstants } from './legacy_imports'; + export interface LensPluginSetupDependencies { kibanaLegacy: KibanaLegacySetup; expressions: ExpressionsSetup; @@ -144,40 +142,24 @@ export class LensPlugin { routeProps.history.push(`/lens/edit/${id}`); } else if (addToDashboardMode && id) { routeProps.history.push(`/lens/edit/${id}`); - const url = coreStart.chrome.navLinks.get('kibana:dashboard'); - if (!url) { + const lastDashboardLink = coreStart.chrome.navLinks.get('kibana:dashboard'); + if (!lastDashboardLink || !lastDashboardLink.url) { throw new Error('Cannot get last dashboard url'); } - const lastDashboardAbsoluteUrl = url.url; - const basePath = coreStart.http.basePath.get(); - const lensUrl = getLensUrlFromDashboardAbsoluteUrl( - lastDashboardAbsoluteUrl, - basePath, - id - ); - if (!lastDashboardAbsoluteUrl || !lensUrl) { - throw new Error('Cannot get last dashboard url'); - } - window.history.pushState({}, '', lensUrl); - const urlVars = getUrlVars(lastDashboardAbsoluteUrl); + const urlVars = getUrlVars(lastDashboardLink.url); updateUrlTime(urlVars); // we need to pass in timerange in query params directly - const dashboardParsedUrl = addEmbeddableToDashboardUrl( - lastDashboardAbsoluteUrl, - basePath, - id, - urlVars - ); - if (!dashboardParsedUrl) { - throw new Error('Problem parsing dashboard url'); - } - window.history.pushState({}, '', dashboardParsedUrl); + const dashboardUrl = addEmbeddableToDashboardUrl(lastDashboardLink.url, id, urlVars); + window.history.pushState({}, '', dashboardUrl); } }; const renderEditor = (routeProps: RouteComponentProps<{ id?: string }>) => { trackUiEvent('loaded'); const addToDashboardMode = - !!routeProps.location.search && routeProps.location.search.includes('addToDashboard'); + !!routeProps.location.search && + routeProps.location.search.includes( + DashboardConstants.ADD_VISUALIZATION_TO_DASHBOARD_MODE_PARAM + ); return (