From ab8d873301ffff576a53d1e3ec9303f49f9785b5 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Fri, 5 Jun 2020 19:21:58 +0100 Subject: [PATCH 01/23] Plugging in DashboardStart dependency --- .../dashboard/public/application/application.ts | 2 ++ .../dashboard/public/application/dashboard_app.tsx | 2 ++ .../public/application/dashboard_app_controller.tsx | 2 ++ src/plugins/dashboard/public/plugin.tsx | 13 ++++++++++++- .../visualize/public/application/editor/editor.js | 4 ++++ 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/plugins/dashboard/public/application/application.ts b/src/plugins/dashboard/public/application/application.ts index 543450916c5056..abbc243bc440a4 100644 --- a/src/plugins/dashboard/public/application/application.ts +++ b/src/plugins/dashboard/public/application/application.ts @@ -33,6 +33,7 @@ import { ScopedHistory, } from 'kibana/public'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; +import { DashboardStart } from 'src/plugins/dashboard/public'; import { Storage } from '../../../kibana_utils/public'; // @ts-ignore import { initDashboardApp } from './legacy_app'; @@ -73,6 +74,7 @@ export interface RenderDeps { navigateToDefaultApp: KibanaLegacyStart['navigateToDefaultApp']; scopedHistory: () => ScopedHistory; savedObjects: SavedObjectsStart; + dashboard: DashboardStart; } let angularModuleInstance: IModule | null = null; diff --git a/src/plugins/dashboard/public/application/dashboard_app.tsx b/src/plugins/dashboard/public/application/dashboard_app.tsx index f101935b9288d1..a6ef237e842752 100644 --- a/src/plugins/dashboard/public/application/dashboard_app.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app.tsx @@ -29,6 +29,7 @@ import { DashboardAppState, SavedDashboardPanel } from '../types'; import { DashboardAppController } from './dashboard_app_controller'; import { RenderDeps } from './application'; import { SavedObjectDashboard } from '../saved_dashboards'; +import { DashboardStart } from '../plugin'; export interface DashboardAppScope extends ng.IScope { dash: SavedObjectDashboard; @@ -60,6 +61,7 @@ export interface DashboardAppScope extends ng.IScope { enterEditMode: () => void; timefilterSubscriptions$: Subscription; isVisible: boolean; + dashboard: DashboardStart; } export function initDashboardAppDirective(app: any, deps: RenderDeps) { diff --git a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx index 206ef4f3d4313a..1cafa788984eee 100644 --- a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx @@ -149,6 +149,7 @@ export class DashboardAppController { kbnUrlStateStorage, usageCollection, navigation, + dashboard, }: DashboardAppControllerDependencies) { const filterManager = queryService.filterManager; const queryFilter = filterManager; @@ -1112,6 +1113,7 @@ export class DashboardAppController { outputSubscription.unsubscribe(); } if (dashboardContainer) { + dashboard.setLastLoadedDashboardAppDashboardInput(dashboardContainer.getInput()); dashboardContainer.destroy(); } }); diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index e99b8ad616cb92..c6f317c6d13832 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -33,6 +33,7 @@ import { SavedObjectsClientContract, ScopedHistory, } from 'src/core/public'; +import { DashboardContainerInput } from './index'; import { UsageCollectionSetup } from '../../usage_collection/public'; import { CONTEXT_MENU_TRIGGER, EmbeddableSetup, EmbeddableStart } from '../../embeddable/public'; import { DataPublicPluginSetup, DataPublicPluginStart, esFilters } from '../../data/public'; @@ -101,6 +102,7 @@ interface SetupDependencies { share?: SharePluginSetup; uiActions: UiActionsSetup; usageCollection?: UsageCollectionSetup; + dashboard: DashboardStart; } interface StartDependencies { @@ -113,6 +115,7 @@ interface StartDependencies { share?: SharePluginStart; uiActions: UiActionsStart; savedObjects: SavedObjectsStart; + dashboard: DashboardStart; } export type Setup = void; @@ -125,6 +128,8 @@ export interface DashboardStart { }) => void | undefined; dashboardUrlGenerator?: DashboardUrlGenerator; DashboardContainerByValueRenderer: ReturnType; + getLastLoadedDashboardAppDashboardInput: () => DashboardContainerInput | undefined; + setLastLoadedDashboardAppDashboardInput: (dashboard: DashboardContainerInput | undefined) => void; } declare module '../../../plugins/ui_actions/public' { @@ -137,6 +142,7 @@ declare module '../../../plugins/ui_actions/public' { export class DashboardPlugin implements Plugin { + private currentDashboardInput: DashboardContainerInput | undefined; constructor(private initializerContext: PluginInitializerContext) {} private appStateUpdater = new BehaviorSubject(() => ({})); @@ -169,7 +175,7 @@ export class DashboardPlugin } const getStartServices = async () => { - const [coreStart, deps] = await core.getStartServices(); + const [coreStart, deps, dashboardStart] = await core.getStartServices(); const useHideChrome = ({ toggleChrome } = { toggleChrome: true }) => { React.useEffect(() => { @@ -203,6 +209,7 @@ export class DashboardPlugin SavedObjectFinder: getSavedObjectFinder(coreStart.savedObjects, coreStart.uiSettings), ExitFullScreenButton, uiActions: deps.uiActions, + dashboard: dashboardStart, }; }; @@ -286,6 +293,7 @@ export class DashboardPlugin usageCollection, scopedHistory: () => this.currentHistory!, savedObjects, + dashboard: dashboardStart, }; // make sure the index pattern list is up to date await dataStart.indexPatterns.clearCache(); @@ -403,6 +411,9 @@ export class DashboardPlugin DashboardContainerByValueRenderer: createDashboardContainerByValueRenderer({ factory: dashboardContainerFactory, }), + getLastLoadedDashboardAppDashboardInput: () => this.currentDashboardInput, + setLastLoadedDashboardAppDashboardInput: (dashboardInput) => + (this.currentDashboardInput = dashboardInput), }; } diff --git a/src/plugins/visualize/public/application/editor/editor.js b/src/plugins/visualize/public/application/editor/editor.js index 0e341ebf46e3a6..7d52ad73b039d3 100644 --- a/src/plugins/visualize/public/application/editor/editor.js +++ b/src/plugins/visualize/public/application/editor/editor.js @@ -244,6 +244,7 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState onTitleDuplicate, returnToOrigin, }; + //const currentDashboardInput = dashboard.getLastLoadedDashboardAppDashboardInput(); return doSave(saveOptions).then((response) => { // If the save wasn't successful, put the original values back. if (!response.id || response.error) { @@ -657,6 +658,9 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState savedVis.uiStateJSON = angular.toJson($scope.uiState.toJSON()); $appStatus.dirty = false; + const currentDashboardInput = dashboard.getLastLoadedDashboardAppDashboardInput(); + console.dir(currentDashboardInput); + return savedVis.save(saveOptions).then( function (id) { $scope.$evalAsync(() => { From 1922ee2987fc841d46c2176b609ab8dffc0f77bf Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Wed, 10 Jun 2020 14:43:41 +0100 Subject: [PATCH 02/23] Create embeddable by reference and navigate back to dashboard --- src/plugins/dashboard/public/plugin.tsx | 35 ++++++++++-- .../visualize_embeddable_factory.tsx | 19 ++++--- src/plugins/visualize/kibana.json | 3 +- .../public/application/editor/editor.js | 53 +++++++++++++------ .../visualize/public/kibana_services.ts | 2 + src/plugins/visualize/public/plugin.ts | 3 ++ 6 files changed, 88 insertions(+), 27 deletions(-) diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index c6f317c6d13832..53c53b74043a79 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -33,10 +33,16 @@ import { SavedObjectsClientContract, ScopedHistory, } from 'src/core/public'; +import { parseUrl, stringify } from 'query-string'; import { DashboardContainerInput } from './index'; import { UsageCollectionSetup } from '../../usage_collection/public'; -import { CONTEXT_MENU_TRIGGER, EmbeddableSetup, EmbeddableStart } from '../../embeddable/public'; -import { DataPublicPluginSetup, DataPublicPluginStart, esFilters } from '../../data/public'; +import { + CONTEXT_MENU_TRIGGER, + EmbeddableInput, + EmbeddableSetup, + EmbeddableStart, +} from '../../embeddable/public'; +import { DataPublicPluginStart, DataPublicPluginSetup, esFilters } from '../../data/public'; import { SharePluginSetup, SharePluginStart, UrlGeneratorContract } from '../../share/public'; import { UiActionsSetup, UiActionsStart } from '../../ui_actions/public'; @@ -51,7 +57,7 @@ import { ExitFullScreenButton as ExitFullScreenButtonUi, ExitFullScreenButtonProps, } from '../../kibana_react/public'; -import { createKbnUrlTracker, Storage } from '../../kibana_utils/public'; +import { createKbnUrlTracker, setStateToKbnUrl, Storage } from '../../kibana_utils/public'; import { initAngularBootstrap, KibanaLegacySetup, @@ -85,6 +91,7 @@ import { DashboardConstants } from './dashboard_constants'; import { addEmbeddableToDashboardUrl } from './url_utils/url_helper'; import { PlaceholderEmbeddableFactory } from './application/embeddable/placeholder'; import { createDashboardContainerByValueRenderer } from './application'; +import { convertPanelStateToSavedDashboardPanel } from './application/lib/embeddable_saved_object_converters'; declare module '../../share/public' { export interface UrlGeneratorStateMapping { @@ -126,6 +133,7 @@ export interface DashboardStart { embeddableId: string; embeddableType: string; }) => void | undefined; + navigateToDashboard: () => void; dashboardUrlGenerator?: DashboardUrlGenerator; DashboardContainerByValueRenderer: ReturnType; getLastLoadedDashboardAppDashboardInput: () => DashboardContainerInput | undefined; @@ -354,6 +362,26 @@ export class DashboardPlugin } } + private navigateToDashboard(core: CoreStart, dashInput: EmbeddableInput) { + if (!this.getActiveUrl) { + return; + } + const lastDashboardUrl = this.getActiveUrl(); + const { query, panels, filters } = dashInput; + const { url } = parseUrl(lastDashboardUrl); + const dashUrl = setStateToKbnUrl( + '_a', + { + query, + filters, + panels: Object.values(panels).map((panel) => convertPanelStateToSavedDashboardPanel(panel)), + }, + { useHash: false }, + url + ); + core.application.navigateToApp('dashboards', { path: dashUrl }); + } + private addEmbeddableToDashboard( core: CoreStart, { embeddableId, embeddableType }: { embeddableId: string; embeddableType: string } @@ -414,6 +442,7 @@ export class DashboardPlugin getLastLoadedDashboardAppDashboardInput: () => this.currentDashboardInput, setLastLoadedDashboardAppDashboardInput: (dashboardInput) => (this.currentDashboardInput = dashboardInput), + navigateToDashboard: this.navigateToDashboard.bind(this, core), }; } diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx index c4267c9a36f784..1b2bad9602839b 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx @@ -133,14 +133,21 @@ export class VisualizeEmbeddableFactory } } - public async create() { + public async create(input: VisualizeInput & { hideVisModal: boolean }, parent?: IContainer) { // TODO: This is a bit of a hack to preserve the original functionality. Ideally we will clean this up // to allow for in place creation of visualizations without having to navigate away to a new URL. const originatingAppParam = await this.getCurrentAppId(); - showNewVisModal({ - editorParams: [`${EMBEDDABLE_ORIGINATING_APP_PARAM}=${originatingAppParam}`], - outsideVisualizeApp: true, - }); - return undefined; + if (input.hideVisModal) { + const visState = convertToSerializedVis(input.savedVis); + const vis = new Vis(visState.type, visState); + await vis.setState(visState); + return createVisEmbeddableFromObject(this.deps)(vis, input, parent); + } else { + showNewVisModal({ + editorParams: [`${EMBEDDABLE_ORIGINATING_APP_PARAM}=${originatingAppParam}`], + outsideVisualizeApp: true, + }); + return undefined; + } } } diff --git a/src/plugins/visualize/kibana.json b/src/plugins/visualize/kibana.json index 817a8262f91988..cda45f3acc102d 100644 --- a/src/plugins/visualize/kibana.json +++ b/src/plugins/visualize/kibana.json @@ -9,7 +9,8 @@ "navigation", "savedObjects", "visualizations", - "dashboard" + "dashboard", + "embeddable" ], "optionalPlugins": ["home", "share"] } diff --git a/src/plugins/visualize/public/application/editor/editor.js b/src/plugins/visualize/public/application/editor/editor.js index 7d52ad73b039d3..9202b7ba0bfd75 100644 --- a/src/plugins/visualize/public/application/editor/editor.js +++ b/src/plugins/visualize/public/application/editor/editor.js @@ -30,7 +30,6 @@ import { VisualizeConstants } from '../visualize_constants'; import { getEditBreadcrumbs } from '../breadcrumbs'; import { EMBEDDABLE_ORIGINATING_APP_PARAM } from '../../../../embeddable/public'; - import { addHelpMenuToAppChrome } from '../help_menu/help_menu_util'; import { unhashUrl, removeQueryParam } from '../../../../kibana_utils/public'; import { MarkdownSimple, toMountPoint } from '../../../../kibana_react/public'; @@ -79,8 +78,8 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState setActiveUrl, visualizations, dashboard, + embeddable, } = getServices(); - const { filterManager, timefilter: { timefilter }, @@ -244,14 +243,31 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState onTitleDuplicate, returnToOrigin, }; - //const currentDashboardInput = dashboard.getLastLoadedDashboardAppDashboardInput(); - return doSave(saveOptions).then((response) => { - // If the save wasn't successful, put the original values back. - if (!response.id || response.error) { - savedVis.title = currentTitle; - } - return response; - }); + const currentDashboardInput = dashboard.getLastLoadedDashboardAppDashboardInput(); + embeddable + .getEmbeddableFactory('dashboard') + .create(currentDashboardInput) + .then((currentDashboard) => { + if (currentDashboard) { + const input = { + savedVis: { ...savedVis }, + hideVisModal: true, + }; + currentDashboard.addNewEmbeddable('visualization', input).then(() => { + const dashInput = currentDashboard.getInput(); + dashboard.navigateToDashboard(dashInput); + }); + } else { + return doSave(saveOptions).then((response) => { + // If the save wasn't successful, put the original values back. + if (!response.id || response.error) { + savedVis.title = currentTitle; + } + return response; + }); + } + }); + return { id: '123' }; }; const saveModal = ( @@ -413,6 +429,7 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState }; function init() { + console.dir(embeddable); if (vis.data.indexPattern) { $scope.indexPattern = vis.data.indexPattern; } else { @@ -483,13 +500,15 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState state.vis ) ) { - const { aggs, ...visState } = state.vis; - vis.setState({ - ...visState, - data: { - aggs, - }, - }); + if (state.vis) { + const { aggs, ...visState } = state.vis; + vis.setState({ + ...visState, + data: { + aggs, + }, + }); + } embeddableHandler.reload(); $scope.eventEmitter.emit('updateEditor'); } diff --git a/src/plugins/visualize/public/kibana_services.ts b/src/plugins/visualize/public/kibana_services.ts index 8d714f18443455..89e1055e1df1e0 100644 --- a/src/plugins/visualize/public/kibana_services.ts +++ b/src/plugins/visualize/public/kibana_services.ts @@ -36,6 +36,7 @@ import { SavedVisualizations } from './application/types'; import { KibanaLegacyStart } from '../../kibana_legacy/public'; import { DashboardStart } from '../../dashboard/public'; import { SavedObjectsStart } from '../../saved_objects/public'; +import { EmbeddableStart } from '../../embeddable/public'; export interface VisualizeKibanaServices { pluginInitializerContext: PluginInitializerContext; @@ -58,6 +59,7 @@ export interface VisualizeKibanaServices { createVisEmbeddableFromObject: VisualizationsStart['__LEGACY']['createVisEmbeddableFromObject']; scopedHistory: () => ScopedHistory; savedObjects: SavedObjectsStart; + embeddable: EmbeddableStart; } let services: VisualizeKibanaServices | null = null; diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index 8a05adc18964a0..9ca76aff1cab65 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -43,6 +43,7 @@ import { FeatureCatalogueCategory, HomePublicPluginSetup } from '../../home/publ import { DashboardStart } from '../../dashboard/public'; import { DEFAULT_APP_CATEGORIES } from '../../../core/public'; import { SavedObjectsStart } from '../../saved_objects/public'; +import { EmbeddableStart } from '../../embeddable/public'; export interface VisualizePluginStartDependencies { data: DataPublicPluginStart; @@ -52,6 +53,7 @@ export interface VisualizePluginStartDependencies { dashboard: DashboardStart; kibanaLegacy: KibanaLegacyStart; savedObjects: SavedObjectsStart; + embeddable: EmbeddableStart; } export interface VisualizePluginSetupDependencies { @@ -136,6 +138,7 @@ export class VisualizePlugin dashboard: pluginsStart.dashboard, scopedHistory: () => this.currentHistory!, savedObjects: pluginsStart.savedObjects, + embeddable: pluginsStart.embeddable, }; setServices(deps); From 53719ab5122d59e569e62e42dd2349dd7fccb29e Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Thu, 11 Jun 2020 13:49:57 +0100 Subject: [PATCH 03/23] Trying to feature flag the new flow --- src/plugins/visualize/config.ts | 26 +++++++++++++++++ .../public/application/editor/editor.js | 29 ++++++++++--------- .../visualize/public/kibana_services.ts | 2 ++ src/plugins/visualize/public/plugin.ts | 7 ++++- src/plugins/visualize/server/index.ts | 11 ++++++- 5 files changed, 60 insertions(+), 15 deletions(-) create mode 100644 src/plugins/visualize/config.ts diff --git a/src/plugins/visualize/config.ts b/src/plugins/visualize/config.ts new file mode 100644 index 00000000000000..ee79a37717f266 --- /dev/null +++ b/src/plugins/visualize/config.ts @@ -0,0 +1,26 @@ +/* + * 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 { schema, TypeOf } from '@kbn/config-schema'; + +export const configSchema = schema.object({ + showNewVisualizeFlow: schema.boolean({ defaultValue: false }), +}); + +export type ConfigSchema = TypeOf; diff --git a/src/plugins/visualize/public/application/editor/editor.js b/src/plugins/visualize/public/application/editor/editor.js index 9202b7ba0bfd75..a3d1f4b8719d4e 100644 --- a/src/plugins/visualize/public/application/editor/editor.js +++ b/src/plugins/visualize/public/application/editor/editor.js @@ -243,6 +243,16 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState onTitleDuplicate, returnToOrigin, }; + return doSave(saveOptions).then((response) => { + // If the save wasn't successful, put the original values back. + if (!response.id || response.error) { + savedVis.title = currentTitle; + } + return response; + }); + }; + + const createVisReference = () => { const currentDashboardInput = dashboard.getLastLoadedDashboardAppDashboardInput(); embeddable .getEmbeddableFactory('dashboard') @@ -257,17 +267,8 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState const dashInput = currentDashboard.getInput(); dashboard.navigateToDashboard(dashInput); }); - } else { - return doSave(saveOptions).then((response) => { - // If the save wasn't successful, put the original values back. - if (!response.id || response.error) { - savedVis.title = currentTitle; - } - return response; - }); } }); - return { id: '123' }; }; const saveModal = ( @@ -279,7 +280,12 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState originatingApp={$scope.getOriginatingApp()} /> ); - showSaveModal(saveModal, I18nContext); + const lastAppType = $scope.getOriginatingApp(); + if (lastAppType !== 'dashboards') { + showSaveModal(saveModal, I18nContext); + } else { + createVisReference(); + } }, }, ] @@ -677,9 +683,6 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState savedVis.uiStateJSON = angular.toJson($scope.uiState.toJSON()); $appStatus.dirty = false; - const currentDashboardInput = dashboard.getLastLoadedDashboardAppDashboardInput(); - console.dir(currentDashboardInput); - return savedVis.save(saveOptions).then( function (id) { $scope.$evalAsync(() => { diff --git a/src/plugins/visualize/public/kibana_services.ts b/src/plugins/visualize/public/kibana_services.ts index 89e1055e1df1e0..44be5e09acc37c 100644 --- a/src/plugins/visualize/public/kibana_services.ts +++ b/src/plugins/visualize/public/kibana_services.ts @@ -37,6 +37,7 @@ import { KibanaLegacyStart } from '../../kibana_legacy/public'; import { DashboardStart } from '../../dashboard/public'; import { SavedObjectsStart } from '../../saved_objects/public'; import { EmbeddableStart } from '../../embeddable/public'; +import { ConfigSchema } from '../config'; export interface VisualizeKibanaServices { pluginInitializerContext: PluginInitializerContext; @@ -60,6 +61,7 @@ export interface VisualizeKibanaServices { scopedHistory: () => ScopedHistory; savedObjects: SavedObjectsStart; embeddable: EmbeddableStart; + featureFlagConfig: ConfigSchema; } let services: VisualizeKibanaServices | null = null; diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index 9ca76aff1cab65..5103d3160250f6 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -62,6 +62,10 @@ export interface VisualizePluginSetupDependencies { data: DataPublicPluginSetup; } +export interface FeatureFlagConfig { + showNewVisualizeFlow: boolean; +} + export class VisualizePlugin implements Plugin { @@ -75,6 +79,7 @@ export class VisualizePlugin core: CoreSetup, { home, kibanaLegacy, data }: VisualizePluginSetupDependencies ) { + const featureFlagConfig = this.initializerContext.config.get(); const { appMounted, appUnMounted, stop: stopUrlTracker, setActiveUrl } = createKbnUrlTracker({ baseUrl: core.http.basePath.prepend('/app/visualize'), defaultSubUrl: '#/', @@ -115,7 +120,6 @@ export class VisualizePlugin this.currentHistory = params.history; appMounted(); - const deps: VisualizeKibanaServices = { pluginInitializerContext: this.initializerContext, addBasePath: coreStart.http.basePath.prepend, @@ -139,6 +143,7 @@ export class VisualizePlugin scopedHistory: () => this.currentHistory!, savedObjects: pluginsStart.savedObjects, embeddable: pluginsStart.embeddable, + featureFlagConfig, }; setServices(deps); diff --git a/src/plugins/visualize/server/index.ts b/src/plugins/visualize/server/index.ts index 5cebef71d8d22e..01c1fde65f21b1 100644 --- a/src/plugins/visualize/server/index.ts +++ b/src/plugins/visualize/server/index.ts @@ -17,8 +17,17 @@ * under the License. */ -import { PluginInitializerContext } from 'kibana/server'; +import { PluginInitializerContext, PluginConfigDescriptor } from 'kibana/server'; import { VisualizeServerPlugin } from './plugin'; +import { ConfigSchema, configSchema } from '../config'; + +export const config: PluginConfigDescriptor = { + exposeToBrowser: { + showNewVisualizeFlow: false, + }, + schema: configSchema, +}; + export const plugin = (initContext: PluginInitializerContext) => new VisualizeServerPlugin(initContext); From 6c6b1a18aafa2dbdf54d699d9f508b8c942e558e Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Thu, 11 Jun 2020 15:21:16 +0100 Subject: [PATCH 04/23] Feature flagging new visualize flow --- src/plugins/visualize/public/application/editor/editor.js | 7 ++++--- src/plugins/visualize/public/plugin.ts | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/visualize/public/application/editor/editor.js b/src/plugins/visualize/public/application/editor/editor.js index a3d1f4b8719d4e..f5ffaebd39efe1 100644 --- a/src/plugins/visualize/public/application/editor/editor.js +++ b/src/plugins/visualize/public/application/editor/editor.js @@ -79,6 +79,7 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState visualizations, dashboard, embeddable, + featureFlagConfig, } = getServices(); const { filterManager, @@ -281,10 +282,10 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState /> ); const lastAppType = $scope.getOriginatingApp(); - if (lastAppType !== 'dashboards') { - showSaveModal(saveModal, I18nContext); - } else { + if (lastAppType === 'dashboards' && featureFlagConfig.showNewVisualizeFlow) { createVisReference(); + } else { + showSaveModal(saveModal, I18nContext); } }, }, diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index 5103d3160250f6..21f69dca1167cd 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -79,7 +79,6 @@ export class VisualizePlugin core: CoreSetup, { home, kibanaLegacy, data }: VisualizePluginSetupDependencies ) { - const featureFlagConfig = this.initializerContext.config.get(); const { appMounted, appUnMounted, stop: stopUrlTracker, setActiveUrl } = createKbnUrlTracker({ baseUrl: core.http.basePath.prepend('/app/visualize'), defaultSubUrl: '#/', @@ -143,7 +142,7 @@ export class VisualizePlugin scopedHistory: () => this.currentHistory!, savedObjects: pluginsStart.savedObjects, embeddable: pluginsStart.embeddable, - featureFlagConfig, + featureFlagConfig: this.initializerContext.config.get(), }; setServices(deps); From eb4e150ad27c309f4fc8c4315b9ee95322f39941 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Thu, 11 Jun 2020 15:37:40 +0100 Subject: [PATCH 05/23] Removing unnecessary console statement --- src/plugins/visualize/public/application/editor/editor.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/visualize/public/application/editor/editor.js b/src/plugins/visualize/public/application/editor/editor.js index f5ffaebd39efe1..62a8e4c7c4fcf8 100644 --- a/src/plugins/visualize/public/application/editor/editor.js +++ b/src/plugins/visualize/public/application/editor/editor.js @@ -436,7 +436,6 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState }; function init() { - console.dir(embeddable); if (vis.data.indexPattern) { $scope.indexPattern = vis.data.indexPattern; } else { From 0df45e24a669a9d72601d69af5e8b8533b9124ce Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Fri, 12 Jun 2020 10:45:02 +0100 Subject: [PATCH 06/23] Fixing typescript errors --- .../public/application/embeddable/types.ts | 4 ++++ src/plugins/dashboard/public/plugin.tsx | 15 +++++++++++---- .../embeddable/visualize_embeddable_factory.tsx | 3 ++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/plugins/dashboard/public/application/embeddable/types.ts b/src/plugins/dashboard/public/application/embeddable/types.ts index 66cdd22ed6bd43..0e127e58d3bc06 100644 --- a/src/plugins/dashboard/public/application/embeddable/types.ts +++ b/src/plugins/dashboard/public/application/embeddable/types.ts @@ -27,3 +27,7 @@ export interface DashboardPanelState< > extends PanelState { readonly gridData: GridData; } + +export interface DashboardPanels { + [panelId: string]: DashboardPanelState; +} diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 53c53b74043a79..7c400c0f0deea7 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -33,7 +33,7 @@ import { SavedObjectsClientContract, ScopedHistory, } from 'src/core/public'; -import { parseUrl, stringify } from 'query-string'; +import { parseUrl } from 'query-string'; import { DashboardContainerInput } from './index'; import { UsageCollectionSetup } from '../../usage_collection/public'; import { @@ -92,6 +92,7 @@ import { addEmbeddableToDashboardUrl } from './url_utils/url_helper'; import { PlaceholderEmbeddableFactory } from './application/embeddable/placeholder'; import { createDashboardContainerByValueRenderer } from './application'; import { convertPanelStateToSavedDashboardPanel } from './application/lib/embeddable_saved_object_converters'; +import { DashboardPanels } from './application/embeddable/types'; declare module '../../share/public' { export interface UrlGeneratorStateMapping { @@ -133,7 +134,7 @@ export interface DashboardStart { embeddableId: string; embeddableType: string; }) => void | undefined; - navigateToDashboard: () => void; + navigateToDashboard: (input: DashboardContainerInput) => void; dashboardUrlGenerator?: DashboardUrlGenerator; DashboardContainerByValueRenderer: ReturnType; getLastLoadedDashboardAppDashboardInput: () => DashboardContainerInput | undefined; @@ -367,14 +368,20 @@ export class DashboardPlugin return; } const lastDashboardUrl = this.getActiveUrl(); - const { query, panels, filters } = dashInput; + const { query, filters } = dashInput; + const panels = dashInput.panels as DashboardPanels; const { url } = parseUrl(lastDashboardUrl); const dashUrl = setStateToKbnUrl( '_a', { query, filters, - panels: Object.values(panels).map((panel) => convertPanelStateToSavedDashboardPanel(panel)), + panels: Object.values(panels).map((panel) => { + return convertPanelStateToSavedDashboardPanel( + panel, + this.initializerContext.env.packageInfo.version + ); + }), }, { useHash: false }, url diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx index 1b2bad9602839b..28906800085bd6 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx @@ -44,6 +44,7 @@ import { createVisEmbeddableFromObject } from './create_vis_embeddable_from_obje import { StartServicesGetter } from '../../../kibana_utils/public'; import { VisualizationsStartDeps } from '../plugin'; import { VISUALIZE_ENABLE_LABS_SETTING } from '../../common/constants'; +import { ISavedVis } from '../types'; interface VisualizationAttributes extends SavedObjectAttributes { visState: string; @@ -138,7 +139,7 @@ export class VisualizeEmbeddableFactory // to allow for in place creation of visualizations without having to navigate away to a new URL. const originatingAppParam = await this.getCurrentAppId(); if (input.hideVisModal) { - const visState = convertToSerializedVis(input.savedVis); + const visState = convertToSerializedVis(input.savedVis as ISavedVis); const vis = new Vis(visState.type, visState); await vis.setState(visState); return createVisEmbeddableFromObject(this.deps)(vis, input, parent); From 857bd18227e18415a6cb740464260fabdbc54239 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Mon, 15 Jun 2020 11:54:08 +0100 Subject: [PATCH 07/23] Adding a functional test for new functionality --- src/plugins/visualize/server/index.ts | 2 +- .../dashboard/new_visualize_flow/config.js | 260 ++++++++++ .../new_visualize_flow/dashboard_embedding.js | 68 +++ .../fixtures/es_archiver/kibana/data.json.gz | Bin 0 -> 20860 bytes .../fixtures/es_archiver/kibana/mappings.json | 490 ++++++++++++++++++ .../services/dashboard/visualizations.ts | 25 + 6 files changed, 844 insertions(+), 1 deletion(-) create mode 100644 test/functional/apps/dashboard/new_visualize_flow/config.js create mode 100644 test/functional/apps/dashboard/new_visualize_flow/dashboard_embedding.js create mode 100644 test/functional/apps/dashboard/new_visualize_flow/fixtures/es_archiver/kibana/data.json.gz create mode 100644 test/functional/apps/dashboard/new_visualize_flow/fixtures/es_archiver/kibana/mappings.json diff --git a/src/plugins/visualize/server/index.ts b/src/plugins/visualize/server/index.ts index 01c1fde65f21b1..6da0a513b1475d 100644 --- a/src/plugins/visualize/server/index.ts +++ b/src/plugins/visualize/server/index.ts @@ -24,7 +24,7 @@ import { ConfigSchema, configSchema } from '../config'; export const config: PluginConfigDescriptor = { exposeToBrowser: { - showNewVisualizeFlow: false, + showNewVisualizeFlow: true, }, schema: configSchema, }; diff --git a/test/functional/apps/dashboard/new_visualize_flow/config.js b/test/functional/apps/dashboard/new_visualize_flow/config.js new file mode 100644 index 00000000000000..752b43083d161b --- /dev/null +++ b/test/functional/apps/dashboard/new_visualize_flow/config.js @@ -0,0 +1,260 @@ +/* + * 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 { pageObjects } from '../../../page_objects'; +import { services } from '../../../services'; + +export default async function ({ readConfigFile }) { + const commonConfig = await readConfigFile(require.resolve('../../../config.js')); + + return { + testFiles: [require.resolve('./add_to_dashboard')], + pageObjects, + services, + servers: commonConfig.get('servers'), + + esTestCluster: commonConfig.get('esTestCluster'), + + kbnTestServer: { + ...commonConfig.get('kbnTestServer'), + serverArgs: [ + ...commonConfig.get('kbnTestServer.serverArgs'), + '--oss', + '--telemetry.optIn=false', + '--visualize.showNewVisualizeFlow=true', + ], + }, + + uiSettings: { + defaults: { + 'accessibility:disableAnimations': true, + 'dateFormat:tz': 'UTC', + }, + }, + + apps: { + kibana: { + pathname: '/app/kibana', + }, + status_page: { + pathname: '/status', + }, + discover: { + pathname: '/app/discover', + hash: '/', + }, + context: { + pathname: '/app/discover', + hash: '/context', + }, + visualize: { + pathname: '/app/visualize', + hash: '/', + }, + dashboard: { + pathname: '/app/dashboards', + hash: '/list', + }, + management: { + pathname: '/app/management', + }, + console: { + pathname: '/app/dev_tools', + hash: '/console', + }, + home: { + pathname: '/app/home', + hash: '/', + }, + }, + junit: { + reportName: 'Chrome UI Functional Tests', + }, + browser: { + type: 'chrome', + }, + + security: { + roles: { + test_logstash_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['logstash*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + test_shakespeare_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['shakes*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + test_testhuge_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['testhuge*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + test_alias_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['alias*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + //for sample data - can remove but not add sample data.( not ml)- for ml use built in role. + kibana_sample_admin: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['kibana_sample*'], + privileges: ['read', 'view_index_metadata', 'manage', 'create_index', 'index'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_date_nanos: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['date-nanos'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_date_nanos_custom: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['date_nanos_custom_timestamp'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_date_nanos_mixed: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['date_nanos_mixed', 'timestamp-*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_large_strings: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['testlargestring'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + long_window_logstash: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['long-window-logstash-*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + animals: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['animals-*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + }, + defaultRoles: ['kibana_admin'], + }, + }; +} diff --git a/test/functional/apps/dashboard/new_visualize_flow/dashboard_embedding.js b/test/functional/apps/dashboard/new_visualize_flow/dashboard_embedding.js new file mode 100644 index 00000000000000..91381c4e3ef1b9 --- /dev/null +++ b/test/functional/apps/dashboard/new_visualize_flow/dashboard_embedding.js @@ -0,0 +1,68 @@ +/* + * 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 expect from '@kbn/expect'; + +/** + * This tests both that one of each visualization can be added to a dashboard (as opposed to opening an existing + * dashboard with the visualizations already on it), as well as conducts a rough type of snapshot testing by checking + * for various ui components. The downside is these tests are a bit fragile to css changes (though not as fragile as + * actual screenshot snapshot regression testing), and can be difficult to diagnose failures (which visualization + * broke?). The upside is that this offers very good coverage with a minimal time investment. + */ + +export default function ({ getService, getPageObjects }) { + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const dashboardExpect = getService('dashboardExpect'); + const testSubjects = getService('testSubjects'); + const dashboardVisualizations = getService('dashboardVisualizations'); + const PageObjects = getPageObjects([ + 'common', + 'dashboard', + 'header', + 'visualize', + 'discover', + 'timePicker', + ]); + + describe('Dashboard Embedding', function describeIndexTests() { + before(async () => { + await esArchiver.load('kibana'); + await kibanaServer.uiSettings.replace({ + defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', + }); + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.preserveCrossAppState(); + await PageObjects.dashboard.clickNewDashboard(); + }); + + it('adding a metric visualization', async function () { + const originalPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(originalPanelCount).to.eql(0); + await testSubjects.exists('addVisualizationButton'); + await testSubjects.click('addVisualizationButton'); + await dashboardVisualizations.createAndEmbedMetric('Embedding Vis Test'); + await PageObjects.dashboard.waitForRenderComplete(); + await dashboardExpect.metricValuesExist(['0']); + const panelCount = await PageObjects.dashboard.getPanelCount(); + expect(panelCount).to.eql(1); + }); + }); +} diff --git a/test/functional/apps/dashboard/new_visualize_flow/fixtures/es_archiver/kibana/data.json.gz b/test/functional/apps/dashboard/new_visualize_flow/fixtures/es_archiver/kibana/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..ae78761fef0d3415c8ec05ea4bfa9dcca070981a GIT binary patch literal 20860 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 literal 0 HcmV?d00001 diff --git a/test/functional/apps/dashboard/new_visualize_flow/fixtures/es_archiver/kibana/mappings.json b/test/functional/apps/dashboard/new_visualize_flow/fixtures/es_archiver/kibana/mappings.json new file mode 100644 index 00000000000000..9f5edaad0fe763 --- /dev/null +++ b/test/functional/apps/dashboard/new_visualize_flow/fixtures/es_archiver/kibana/mappings.json @@ -0,0 +1,490 @@ +{ + "type": "index", + "value": { + "aliases": { + ".kibana": { + } + }, + "index": ".kibana_1", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "application_usage_totals": "c897e4310c5f24b07caaff3db53ae2c1", + "application_usage_transactional": "965839e75f809fefe04f92dc4d99722a", + "config": "ae24d22d5986d04124cc6568f771066f", + "dashboard": "d00f614b29a80360e1190193fd333bab", + "index-pattern": "66eccb05066c5a89924f48a9e9736499", + "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "namespace": "2f4316de49999235636386fe51dc06c1", + "namespaces": "2f4316de49999235636386fe51dc06c1", + "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", + "search": "181661168bbadd1eff5902361e2a0d5c", + "telemetry": "36a616f7026dfa617d6655df850fe16d", + "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", + "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", + "type": "2f4316de49999235636386fe51dc06c1", + "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "url": "b675c3be8d76ecf029294d51dc7ec65d", + "visualization": "52d7a13ad68a150c4525b292d23e12cc" + } + }, + "dynamic": "strict", + "properties": { + "application_usage_totals": { + "properties": { + "appId": { + "type": "keyword" + }, + "minutesOnScreen": { + "type": "float" + }, + "numberOfClicks": { + "type": "long" + } + } + }, + "application_usage_transactional": { + "properties": { + "appId": { + "type": "keyword" + }, + "minutesOnScreen": { + "type": "float" + }, + "numberOfClicks": { + "type": "long" + }, + "timestamp": { + "type": "date" + } + } + }, + "config": { + "dynamic": "true", + "properties": { + "accessibility:disableAnimations": { + "type": "boolean" + }, + "buildNum": { + "type": "keyword" + }, + "dateFormat:tz": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "defaultIndex": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "notifications:lifetime:banner": { + "type": "long" + }, + "notifications:lifetime:error": { + "type": "long" + }, + "notifications:lifetime:info": { + "type": "long" + }, + "notifications:lifetime:warning": { + "type": "long" + }, + "xPackMonitoring:showBanner": { + "type": "boolean" + } + } + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "optionsJSON": { + "type": "text" + }, + "panelsJSON": { + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "type": "keyword" + }, + "pause": { + "type": "boolean" + }, + "section": { + "type": "integer" + }, + "value": { + "type": "integer" + } + } + }, + "timeFrom": { + "type": "keyword" + }, + "timeRestore": { + "type": "boolean" + }, + "timeTo": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "index-pattern": { + "properties": { + "fieldFormatMap": { + "type": "text" + }, + "fields": { + "type": "text" + }, + "intervalName": { + "type": "keyword" + }, + "notExpandable": { + "type": "boolean" + }, + "sourceFilters": { + "type": "text" + }, + "timeFieldName": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "typeMeta": { + "type": "keyword" + } + } + }, + "kql-telemetry": { + "properties": { + "optInCount": { + "type": "long" + }, + "optOutCount": { + "type": "long" + } + } + }, + "migrationVersion": { + "dynamic": "true", + "properties": { + "dashboard": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "index-pattern": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "search": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "visualization": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "namespace": { + "type": "keyword" + }, + "namespaces": { + "type": "keyword" + }, + "query": { + "properties": { + "description": { + "type": "text" + }, + "filters": { + "enabled": false, + "type": "object" + }, + "query": { + "properties": { + "language": { + "type": "keyword" + }, + "query": { + "index": false, + "type": "keyword" + } + } + }, + "timefilter": { + "enabled": false, + "type": "object" + }, + "title": { + "type": "text" + } + } + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "sample-data-telemetry": { + "properties": { + "installCount": { + "type": "long" + }, + "unInstallCount": { + "type": "long" + } + } + }, + "search": { + "properties": { + "columns": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "sort": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "server": { + "properties": { + "uuid": { + "type": "keyword" + } + } + }, + "telemetry": { + "properties": { + "allowChangingOptInStatus": { + "type": "boolean" + }, + "enabled": { + "type": "boolean" + }, + "lastReported": { + "type": "date" + }, + "lastVersionChecked": { + "type": "keyword" + }, + "reportFailureCount": { + "type": "integer" + }, + "reportFailureVersion": { + "type": "keyword" + }, + "sendUsageFrom": { + "type": "keyword" + }, + "userHasSeenNotice": { + "type": "boolean" + } + } + }, + "timelion-sheet": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "tsvb-validation-telemetry": { + "properties": { + "failedRequests": { + "type": "long" + } + } + }, + "type": { + "type": "keyword" + }, + "ui-metric": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "updated_at": { + "type": "date" + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "savedSearchRefName": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "type": "text" + } + } + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} \ No newline at end of file diff --git a/test/functional/services/dashboard/visualizations.ts b/test/functional/services/dashboard/visualizations.ts index 10747658d8c9b7..5eee28ea950dec 100644 --- a/test/functional/services/dashboard/visualizations.ts +++ b/test/functional/services/dashboard/visualizations.ts @@ -139,5 +139,30 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }: F redirectToOrigin: true, }); } + + async createAndEmbedMetric(name: string) { + log.debug(`createAndEmbedMarkdown(${name})`); + const inViewMode = await PageObjects.dashboard.getIsInViewMode(); + if (inViewMode) { + await PageObjects.dashboard.switchToEditMode(); + } + await this.ensureNewVisualizationDialogIsShowing(); + await PageObjects.visualize.clickMetric(); + await find.clickByCssSelector('li.euiListGroupItem:nth-of-type(2)'); + await testSubjects.click('visualizeSaveButton'); + } + + async createAndEmbedMarkdown({ name, markdown }: { name: string; markdown: string }) { + log.debug(`createAndEmbedMarkdown(${markdown})`); + const inViewMode = await PageObjects.dashboard.getIsInViewMode(); + if (inViewMode) { + await PageObjects.dashboard.switchToEditMode(); + } + await this.ensureNewVisualizationDialogIsShowing(); + await PageObjects.visualize.clickMarkdownWidget(); + await PageObjects.visEditor.setMarkdownTxt(markdown); + await PageObjects.visEditor.clickGo(); + await testSubjects.click('visualizeSaveButton'); + } })(); } From 786431c63b07c06a47ee2d20cfbeb89554891b0e Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Mon, 15 Jun 2020 14:03:07 +0100 Subject: [PATCH 08/23] Adding a functional test for new functionality --- test/functional/apps/dashboard/index.js | 1 + test/functional/services/dashboard/visualizations.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/dashboard/index.js b/test/functional/apps/dashboard/index.js index a8d0e03c9421e8..ed281daa8609e2 100644 --- a/test/functional/apps/dashboard/index.js +++ b/test/functional/apps/dashboard/index.js @@ -50,6 +50,7 @@ export default function ({ getService, loadTestFile }) { loadTestFile(require.resolve('./empty_dashboard')); loadTestFile(require.resolve('./embeddable_rendering')); + loadTestFile(require.resolve('./new_visualize_flow/embeddable_rendering')); loadTestFile(require.resolve('./create_and_add_embeddables')); loadTestFile(require.resolve('./edit_embeddable_redirects')); loadTestFile(require.resolve('./time_zones')); diff --git a/test/functional/services/dashboard/visualizations.ts b/test/functional/services/dashboard/visualizations.ts index 5eee28ea950dec..7cf8e76c5cc767 100644 --- a/test/functional/services/dashboard/visualizations.ts +++ b/test/functional/services/dashboard/visualizations.ts @@ -141,7 +141,7 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }: F } async createAndEmbedMetric(name: string) { - log.debug(`createAndEmbedMarkdown(${name})`); + log.debug(`createAndEmbedMetric(${name})`); const inViewMode = await PageObjects.dashboard.getIsInViewMode(); if (inViewMode) { await PageObjects.dashboard.switchToEditMode(); From cb8f3cb6744ab15470a7756cb1fab142b6b19cc5 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Mon, 15 Jun 2020 16:28:48 +0100 Subject: [PATCH 09/23] Fixing test name --- test/functional/apps/dashboard/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/apps/dashboard/index.js b/test/functional/apps/dashboard/index.js index ed281daa8609e2..35ecec2ce20102 100644 --- a/test/functional/apps/dashboard/index.js +++ b/test/functional/apps/dashboard/index.js @@ -50,7 +50,7 @@ export default function ({ getService, loadTestFile }) { loadTestFile(require.resolve('./empty_dashboard')); loadTestFile(require.resolve('./embeddable_rendering')); - loadTestFile(require.resolve('./new_visualize_flow/embeddable_rendering')); + loadTestFile(require.resolve('./new_visualize_flow/dashboard_embedding')); loadTestFile(require.resolve('./create_and_add_embeddables')); loadTestFile(require.resolve('./edit_embeddable_redirects')); loadTestFile(require.resolve('./time_zones')); From afee45afc519eed84aabd4ffed2071483012eb10 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Mon, 15 Jun 2020 16:52:38 +0100 Subject: [PATCH 10/23] Changing test name --- test/functional/apps/dashboard/new_visualize_flow/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/apps/dashboard/new_visualize_flow/config.js b/test/functional/apps/dashboard/new_visualize_flow/config.js index 752b43083d161b..1e31e5d483a0ad 100644 --- a/test/functional/apps/dashboard/new_visualize_flow/config.js +++ b/test/functional/apps/dashboard/new_visualize_flow/config.js @@ -24,7 +24,7 @@ export default async function ({ readConfigFile }) { const commonConfig = await readConfigFile(require.resolve('../../../config.js')); return { - testFiles: [require.resolve('./add_to_dashboard')], + testFiles: [require.resolve('./dashboard_embedding')], pageObjects, services, servers: commonConfig.get('servers'), From cdfb5773892be2f242021779616337cdaa784561 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Tue, 16 Jun 2020 09:27:31 +0100 Subject: [PATCH 11/23] Moving functional test to a separate folder --- scripts/functional_tests.js | 1 + .../dashboard => }/new_visualize_flow/config.js | 4 ++-- .../new_visualize_flow/dashboard_embedding.js | 0 .../fixtures/es_archiver/kibana/data.json.gz | Bin .../fixtures/es_archiver/kibana/mappings.json | 0 5 files changed, 3 insertions(+), 2 deletions(-) rename test/{functional/apps/dashboard => }/new_visualize_flow/config.js (98%) rename test/{functional/apps/dashboard => }/new_visualize_flow/dashboard_embedding.js (100%) rename test/{functional/apps/dashboard => }/new_visualize_flow/fixtures/es_archiver/kibana/data.json.gz (100%) rename test/{functional/apps/dashboard => }/new_visualize_flow/fixtures/es_archiver/kibana/mappings.json (100%) diff --git a/scripts/functional_tests.js b/scripts/functional_tests.js index fc88f2657018f0..3fdab481dc7500 100644 --- a/scripts/functional_tests.js +++ b/scripts/functional_tests.js @@ -22,6 +22,7 @@ const alwaysImportedTests = [ require.resolve('../test/functional/config.js'), require.resolve('../test/plugin_functional/config.js'), require.resolve('../test/ui_capabilities/newsfeed_err/config.ts'), + require.resolve('../test/new_visualize_flow/config.js'), ]; // eslint-disable-next-line no-restricted-syntax const onlyNotInCoverageTests = [ diff --git a/test/functional/apps/dashboard/new_visualize_flow/config.js b/test/new_visualize_flow/config.js similarity index 98% rename from test/functional/apps/dashboard/new_visualize_flow/config.js rename to test/new_visualize_flow/config.js index 1e31e5d483a0ad..b8af39117c7a0e 100644 --- a/test/functional/apps/dashboard/new_visualize_flow/config.js +++ b/test/new_visualize_flow/config.js @@ -17,8 +17,8 @@ * under the License. */ -import { pageObjects } from '../../../page_objects'; -import { services } from '../../../services'; +import { pageObjects } from '../functional/page_objects'; +import { services } from '../functional/services'; export default async function ({ readConfigFile }) { const commonConfig = await readConfigFile(require.resolve('../../../config.js')); diff --git a/test/functional/apps/dashboard/new_visualize_flow/dashboard_embedding.js b/test/new_visualize_flow/dashboard_embedding.js similarity index 100% rename from test/functional/apps/dashboard/new_visualize_flow/dashboard_embedding.js rename to test/new_visualize_flow/dashboard_embedding.js diff --git a/test/functional/apps/dashboard/new_visualize_flow/fixtures/es_archiver/kibana/data.json.gz b/test/new_visualize_flow/fixtures/es_archiver/kibana/data.json.gz similarity index 100% rename from test/functional/apps/dashboard/new_visualize_flow/fixtures/es_archiver/kibana/data.json.gz rename to test/new_visualize_flow/fixtures/es_archiver/kibana/data.json.gz diff --git a/test/functional/apps/dashboard/new_visualize_flow/fixtures/es_archiver/kibana/mappings.json b/test/new_visualize_flow/fixtures/es_archiver/kibana/mappings.json similarity index 100% rename from test/functional/apps/dashboard/new_visualize_flow/fixtures/es_archiver/kibana/mappings.json rename to test/new_visualize_flow/fixtures/es_archiver/kibana/mappings.json From d7bb297c8341503419c4ca4b36a0cb0f07f91588 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Tue, 16 Jun 2020 11:13:57 +0100 Subject: [PATCH 12/23] Trying to fix the config file --- test/new_visualize_flow/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/new_visualize_flow/config.js b/test/new_visualize_flow/config.js index b8af39117c7a0e..9913c0120b5862 100644 --- a/test/new_visualize_flow/config.js +++ b/test/new_visualize_flow/config.js @@ -21,7 +21,7 @@ import { pageObjects } from '../functional/page_objects'; import { services } from '../functional/services'; export default async function ({ readConfigFile }) { - const commonConfig = await readConfigFile(require.resolve('../../../config.js')); + const commonConfig = await readConfigFile(require.resolve('../functional/config.js')); return { testFiles: [require.resolve('./dashboard_embedding')], From 74839ec36cd2d687cd4ac8965cc9fa542c8a6a6e Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Tue, 16 Jun 2020 15:34:09 +0100 Subject: [PATCH 13/23] Adding an index file --- .../services/dashboard/visualizations.ts | 1 + test/new_visualize_flow/config.js | 105 +----------------- test/new_visualize_flow/index.ts | 27 +++++ 3 files changed, 29 insertions(+), 104 deletions(-) create mode 100644 test/new_visualize_flow/index.ts diff --git a/test/functional/services/dashboard/visualizations.ts b/test/functional/services/dashboard/visualizations.ts index 7cf8e76c5cc767..a5c16010d3ebaa 100644 --- a/test/functional/services/dashboard/visualizations.ts +++ b/test/functional/services/dashboard/visualizations.ts @@ -149,6 +149,7 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }: F await this.ensureNewVisualizationDialogIsShowing(); await PageObjects.visualize.clickMetric(); await find.clickByCssSelector('li.euiListGroupItem:nth-of-type(2)'); + await testSubjects.exists('visualizeSaveButton'); await testSubjects.click('visualizeSaveButton'); } diff --git a/test/new_visualize_flow/config.js b/test/new_visualize_flow/config.js index 9913c0120b5862..a6440d16481d59 100644 --- a/test/new_visualize_flow/config.js +++ b/test/new_visualize_flow/config.js @@ -106,49 +106,7 @@ export default async function ({ readConfigFile }) { }, kibana: [], }, - test_shakespeare_reader: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['shakes*'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - test_testhuge_reader: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['testhuge*'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - test_alias_reader: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['alias*'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - //for sample data - can remove but not add sample data.( not ml)- for ml use built in role. + //for sample data - can remove but not add sample data kibana_sample_admin: { elasticsearch: { cluster: [], @@ -163,67 +121,6 @@ export default async function ({ readConfigFile }) { }, kibana: [], }, - - kibana_date_nanos: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['date-nanos'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - kibana_date_nanos_custom: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['date_nanos_custom_timestamp'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - kibana_date_nanos_mixed: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['date_nanos_mixed', 'timestamp-*'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - kibana_large_strings: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['testlargestring'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - long_window_logstash: { elasticsearch: { cluster: [], diff --git a/test/new_visualize_flow/index.ts b/test/new_visualize_flow/index.ts new file mode 100644 index 00000000000000..e915525155990e --- /dev/null +++ b/test/new_visualize_flow/index.ts @@ -0,0 +1,27 @@ +/* + * 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 { FtrProviderContext } from '../functional/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function ({ loadTestFile }: FtrProviderContext) { + describe('New Visualize Flow', function () { + this.tags('ciGroup2'); + loadTestFile(require.resolve('./dashboard_embedding')); + }); +} From defaa395c335667a2fac76f758d058ab89ffbff2 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Tue, 16 Jun 2020 16:35:14 +0100 Subject: [PATCH 14/23] Remove falsly included file --- test/functional/apps/dashboard/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/functional/apps/dashboard/index.js b/test/functional/apps/dashboard/index.js index 35ecec2ce20102..a8d0e03c9421e8 100644 --- a/test/functional/apps/dashboard/index.js +++ b/test/functional/apps/dashboard/index.js @@ -50,7 +50,6 @@ export default function ({ getService, loadTestFile }) { loadTestFile(require.resolve('./empty_dashboard')); loadTestFile(require.resolve('./embeddable_rendering')); - loadTestFile(require.resolve('./new_visualize_flow/dashboard_embedding')); loadTestFile(require.resolve('./create_and_add_embeddables')); loadTestFile(require.resolve('./edit_embeddable_redirects')); loadTestFile(require.resolve('./time_zones')); From 0bb50b932942a861cc6b85b9c0bb1fd220df6e79 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Tue, 16 Jun 2020 18:07:46 +0100 Subject: [PATCH 15/23] Adding aggs and params to vis input --- .../public/application/editor/editor.js | 26 ++++++++++++------- .../new_visualize_flow/dashboard_embedding.js | 16 ++++++++++++ 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/plugins/visualize/public/application/editor/editor.js b/src/plugins/visualize/public/application/editor/editor.js index 62a8e4c7c4fcf8..50c086aec16f19 100644 --- a/src/plugins/visualize/public/application/editor/editor.js +++ b/src/plugins/visualize/public/application/editor/editor.js @@ -264,6 +264,16 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState savedVis: { ...savedVis }, hideVisModal: true, }; + if (stateContainer.getState() && stateContainer.getState().vis) { + const { vis } = stateContainer.getState(); + const { type, aggs, params } = vis; + const visState = { + type, + aggs, + params, + }; + input.savedVis.visState = visState; + } currentDashboard.addNewEmbeddable('visualization', input).then(() => { const dashInput = currentDashboard.getInput(); dashboard.navigateToDashboard(dashInput); @@ -506,15 +516,13 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState state.vis ) ) { - if (state.vis) { - const { aggs, ...visState } = state.vis; - vis.setState({ - ...visState, - data: { - aggs, - }, - }); - } + const { aggs, ...visState } = state.vis; + vis.setState({ + ...visState, + data: { + aggs, + }, + }); embeddableHandler.reload(); $scope.eventEmitter.emit('updateEditor'); } diff --git a/test/new_visualize_flow/dashboard_embedding.js b/test/new_visualize_flow/dashboard_embedding.js index 91381c4e3ef1b9..4fd7c43578994d 100644 --- a/test/new_visualize_flow/dashboard_embedding.js +++ b/test/new_visualize_flow/dashboard_embedding.js @@ -44,6 +44,7 @@ export default function ({ getService, getPageObjects }) { describe('Dashboard Embedding', function describeIndexTests() { before(async () => { + this.tags('ciGroup2'); await esArchiver.load('kibana'); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', @@ -64,5 +65,20 @@ export default function ({ getService, getPageObjects }) { const panelCount = await PageObjects.dashboard.getPanelCount(); expect(panelCount).to.eql(1); }); + + it('adding a markdown', async function () { + const originalPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(originalPanelCount).to.eql(1); + await testSubjects.exists('dashboardAddNewPanelButton'); + await testSubjects.click('dashboardAddNewPanelButton'); + await dashboardVisualizations.createAndEmbedMarkdown({ + name: 'Embedding Markdown Test', + markdown: 'Nice to meet you, markdown is my name', + }); + await PageObjects.dashboard.waitForRenderComplete(); + await dashboardExpect.markdownWithValuesExists(['Nice to meet you, markdown is my name']); + const panelCount = await PageObjects.dashboard.getPanelCount(); + expect(panelCount).to.eql(2); + }); }); } From 811cba177ae4049d2c6ab02d24c89801452dc477 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Wed, 17 Jun 2020 15:06:37 +0100 Subject: [PATCH 16/23] Serializing vis before passing it as an input --- .../embeddable/visualize_embeddable_factory.tsx | 9 ++++----- .../visualize/public/application/editor/editor.js | 13 +------------ 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx index 28906800085bd6..6a5b6976a570ec 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx @@ -31,7 +31,7 @@ import { import { DisabledLabEmbeddable } from './disabled_lab_embeddable'; import { VisualizeEmbeddable, VisualizeInput, VisualizeOutput } from './visualize_embeddable'; import { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; -import { Vis } from '../vis'; +import { SerializedVis, Vis } from '../vis'; import { getCapabilities, getTypes, @@ -44,7 +44,6 @@ import { createVisEmbeddableFromObject } from './create_vis_embeddable_from_obje import { StartServicesGetter } from '../../../kibana_utils/public'; import { VisualizationsStartDeps } from '../plugin'; import { VISUALIZE_ENABLE_LABS_SETTING } from '../../common/constants'; -import { ISavedVis } from '../types'; interface VisualizationAttributes extends SavedObjectAttributes { visState: string; @@ -134,12 +133,12 @@ export class VisualizeEmbeddableFactory } } - public async create(input: VisualizeInput & { hideVisModal: boolean }, parent?: IContainer) { + public async create(input: VisualizeInput & { savedVis?: SerializedVis }, parent?: IContainer) { // TODO: This is a bit of a hack to preserve the original functionality. Ideally we will clean this up // to allow for in place creation of visualizations without having to navigate away to a new URL. const originatingAppParam = await this.getCurrentAppId(); - if (input.hideVisModal) { - const visState = convertToSerializedVis(input.savedVis as ISavedVis); + if (input.savedVis) { + const visState = input.savedVis; const vis = new Vis(visState.type, visState); await vis.setState(visState); return createVisEmbeddableFromObject(this.deps)(vis, input, parent); diff --git a/src/plugins/visualize/public/application/editor/editor.js b/src/plugins/visualize/public/application/editor/editor.js index 50c086aec16f19..4cc5692ae51026 100644 --- a/src/plugins/visualize/public/application/editor/editor.js +++ b/src/plugins/visualize/public/application/editor/editor.js @@ -261,19 +261,8 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState .then((currentDashboard) => { if (currentDashboard) { const input = { - savedVis: { ...savedVis }, - hideVisModal: true, + savedVis: { ...vis.serialize() }, }; - if (stateContainer.getState() && stateContainer.getState().vis) { - const { vis } = stateContainer.getState(); - const { type, aggs, params } = vis; - const visState = { - type, - aggs, - params, - }; - input.savedVis.visState = visState; - } currentDashboard.addNewEmbeddable('visualization', input).then(() => { const dashInput = currentDashboard.getInput(); dashboard.navigateToDashboard(dashInput); From 5aeb2b25c2e50752f8be8e5f625fddc01b838df0 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Thu, 25 Jun 2020 10:19:08 +0100 Subject: [PATCH 17/23] Incorporating new state transfer logic --- .../application/dashboard_app_controller.tsx | 15 +++++++++----- .../public/lib/state_transfer/types.ts | 19 +++++++++++++----- .../visualize_embeddable_factory.tsx | 1 - .../public/application/editor/editor.js | 20 ++++++------------- .../visualize/public/kibana_services.ts | 1 - src/plugins/visualize/public/plugin.ts | 2 -- 6 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx index a7aeb6c776d00a..36c6651dc581d4 100644 --- a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx @@ -150,7 +150,6 @@ export class DashboardAppController { kbnUrlStateStorage, usageCollection, navigation, - dashboard, }: DashboardAppControllerDependencies) { const filterManager = queryService.filterManager; const queryFilter = filterManager; @@ -431,9 +430,16 @@ export class DashboardAppController { .getStateTransfer(scopedHistory()) .getIncomingEmbeddablePackage(); if (incomingState) { - container.addNewEmbeddable(incomingState.type, { - savedObjectId: incomingState.id, - }); + if ('id' in incomingState) { + container.addNewEmbeddable(incomingState.type, { + savedObjectId: incomingState.id, + }); + } else if ('input' in incomingState) { + container.addNewEmbeddable( + incomingState.type, + incomingState.input + ); + } } } @@ -1112,7 +1118,6 @@ export class DashboardAppController { outputSubscription.unsubscribe(); } if (dashboardContainer) { - dashboard.setLastLoadedDashboardAppDashboardInput(dashboardContainer.getInput()); dashboardContainer.destroy(); } }); diff --git a/src/plugins/embeddable/public/lib/state_transfer/types.ts b/src/plugins/embeddable/public/lib/state_transfer/types.ts index 8eae441d1be23c..71be826de22845 100644 --- a/src/plugins/embeddable/public/lib/state_transfer/types.ts +++ b/src/plugins/embeddable/public/lib/state_transfer/types.ts @@ -17,6 +17,8 @@ * under the License. */ +import { EmbeddableInput } from '../embeddables'; + /** * Represents a state package that contains the last active app id. * @public @@ -31,19 +33,26 @@ export function isEmbeddableOriginatingAppState( return ensureFieldOfTypeExists('originatingApp', state, 'string'); } +export interface EmbeddableByReference { + type: string; + id: string; +} + +export interface EmbeddableByValue { + type: string; + input: EmbeddableInput; +} + /** * Represents a state package that contains all fields necessary to create an embeddable in a container. * @public */ -export interface EmbeddablePackageState { - type: string; - id: string; -} +export type EmbeddablePackageState = EmbeddableByValue | EmbeddableByReference; export function isEmbeddablePackageState(state: unknown): state is EmbeddablePackageState { return ( ensureFieldOfTypeExists('type', state, 'string') && - ensureFieldOfTypeExists('id', state, 'string') + (ensureFieldOfTypeExists('id', state, 'string') || ensureFieldOfTypeExists('input', state)) ); } diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx index 9e8623ecf2999a..b81ff5c1661831 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx @@ -127,7 +127,6 @@ export class VisualizeEmbeddableFactory public async create(input: VisualizeInput & { savedVis?: SerializedVis }, parent?: IContainer) { // TODO: This is a bit of a hack to preserve the original functionality. Ideally we will clean this up // to allow for in place creation of visualizations without having to navigate away to a new URL. - const originatingAppParam = await this.getCurrentAppId(); if (input.savedVis) { const visState = input.savedVis; const vis = new Vis(visState.type, visState); diff --git a/src/plugins/visualize/public/application/editor/editor.js b/src/plugins/visualize/public/application/editor/editor.js index 906c287c45ddee..ab64396f4e995a 100644 --- a/src/plugins/visualize/public/application/editor/editor.js +++ b/src/plugins/visualize/public/application/editor/editor.js @@ -76,7 +76,6 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState I18nContext, setActiveUrl, visualizations, - dashboard, embeddable, featureFlagConfig, scopedHistory, @@ -254,20 +253,13 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState }; const createVisReference = () => { - const currentDashboardInput = dashboard.getLastLoadedDashboardAppDashboardInput(); + const input = { + savedVis: { ...vis.serialize() }, + }; embeddable - .getEmbeddableFactory('dashboard') - .create(currentDashboardInput) - .then((currentDashboard) => { - if (currentDashboard) { - const input = { - savedVis: { ...vis.serialize() }, - }; - currentDashboard.addNewEmbeddable('visualization', input).then(() => { - const dashInput = currentDashboard.getInput(); - dashboard.navigateToDashboard(dashInput); - }); - } + .getStateTransfer() + .navigateToWithEmbeddablePackage($scope.getOriginatingApp(), { + state: { input, type: VISUALIZE_EMBEDDABLE_TYPE }, }); }; diff --git a/src/plugins/visualize/public/kibana_services.ts b/src/plugins/visualize/public/kibana_services.ts index ebe963041b8ff3..c49d071dd15eaa 100644 --- a/src/plugins/visualize/public/kibana_services.ts +++ b/src/plugins/visualize/public/kibana_services.ts @@ -53,7 +53,6 @@ export interface VisualizeKibanaServices { kibanaLegacy: KibanaLegacyStart; visualizeCapabilities: any; visualizations: VisualizationsStart; - embeddable: EmbeddableStart; I18nContext: I18nStart['Context']; setActiveUrl: (newUrl: string) => void; restorePreviousUrl: () => void; diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index 4db0234b25b99f..c3a6de829ed16e 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -52,7 +52,6 @@ export interface VisualizePluginStartDependencies { embeddable: EmbeddableStart; kibanaLegacy: KibanaLegacyStart; savedObjects: SavedObjectsStart; - embeddable: EmbeddableStart; } export interface VisualizePluginSetupDependencies { @@ -146,7 +145,6 @@ export class VisualizePlugin pluginsStart.visualizations.__LEGACY.createVisEmbeddableFromObject, scopedHistory: () => this.currentHistory!, savedObjects: pluginsStart.savedObjects, - embeddable: pluginsStart.embeddable, featureFlagConfig: this.initializerContext.config.get(), restorePreviousUrl, }; From d5bce1f5e3352f82c91daae0af7dfbf83306e3a6 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Thu, 25 Jun 2020 10:21:31 +0100 Subject: [PATCH 18/23] Remove dashboardStart as a dependency --- .../public/application/application.ts | 2 - .../public/application/dashboard_app.tsx | 2 - .../public/application/embeddable/types.ts | 4 -- src/plugins/dashboard/public/plugin.tsx | 55 ++----------------- 4 files changed, 4 insertions(+), 59 deletions(-) diff --git a/src/plugins/dashboard/public/application/application.ts b/src/plugins/dashboard/public/application/application.ts index 4aadcdb3b4f986..08eeb19dcda930 100644 --- a/src/plugins/dashboard/public/application/application.ts +++ b/src/plugins/dashboard/public/application/application.ts @@ -33,7 +33,6 @@ import { ScopedHistory, } from 'kibana/public'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; -import { DashboardStart } from 'src/plugins/dashboard/public'; import { Storage } from '../../../kibana_utils/public'; // @ts-ignore import { initDashboardApp } from './legacy_app'; @@ -74,7 +73,6 @@ export interface RenderDeps { navigateToLegacyKibanaUrl: KibanaLegacyStart['navigateToLegacyKibanaUrl']; scopedHistory: () => ScopedHistory; savedObjects: SavedObjectsStart; - dashboard: DashboardStart; restorePreviousUrl: () => void; } diff --git a/src/plugins/dashboard/public/application/dashboard_app.tsx b/src/plugins/dashboard/public/application/dashboard_app.tsx index a6ef237e842752..f101935b9288d1 100644 --- a/src/plugins/dashboard/public/application/dashboard_app.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app.tsx @@ -29,7 +29,6 @@ import { DashboardAppState, SavedDashboardPanel } from '../types'; import { DashboardAppController } from './dashboard_app_controller'; import { RenderDeps } from './application'; import { SavedObjectDashboard } from '../saved_dashboards'; -import { DashboardStart } from '../plugin'; export interface DashboardAppScope extends ng.IScope { dash: SavedObjectDashboard; @@ -61,7 +60,6 @@ export interface DashboardAppScope extends ng.IScope { enterEditMode: () => void; timefilterSubscriptions$: Subscription; isVisible: boolean; - dashboard: DashboardStart; } export function initDashboardAppDirective(app: any, deps: RenderDeps) { diff --git a/src/plugins/dashboard/public/application/embeddable/types.ts b/src/plugins/dashboard/public/application/embeddable/types.ts index 0e127e58d3bc06..66cdd22ed6bd43 100644 --- a/src/plugins/dashboard/public/application/embeddable/types.ts +++ b/src/plugins/dashboard/public/application/embeddable/types.ts @@ -27,7 +27,3 @@ export interface DashboardPanelState< > extends PanelState { readonly gridData: GridData; } - -export interface DashboardPanels { - [panelId: string]: DashboardPanelState; -} diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 8806dcb0054143..041a02a251e8ab 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -33,16 +33,9 @@ import { SavedObjectsClientContract, ScopedHistory, } from 'src/core/public'; -import { parseUrl } from 'query-string'; -import { DashboardContainerInput } from './index'; import { UsageCollectionSetup } from '../../usage_collection/public'; -import { - CONTEXT_MENU_TRIGGER, - EmbeddableInput, - EmbeddableSetup, - EmbeddableStart, -} from '../../embeddable/public'; -import { DataPublicPluginStart, DataPublicPluginSetup, esFilters } from '../../data/public'; +import { CONTEXT_MENU_TRIGGER, EmbeddableSetup, EmbeddableStart } from '../../embeddable/public'; +import { DataPublicPluginSetup, DataPublicPluginStart, esFilters } from '../../data/public'; import { SharePluginSetup, SharePluginStart, UrlGeneratorContract } from '../../share/public'; import { UiActionsSetup, UiActionsStart } from '../../ui_actions/public'; @@ -57,7 +50,7 @@ import { ExitFullScreenButton as ExitFullScreenButtonUi, ExitFullScreenButtonProps, } from '../../kibana_react/public'; -import { createKbnUrlTracker, setStateToKbnUrl, Storage } from '../../kibana_utils/public'; +import { createKbnUrlTracker, Storage } from '../../kibana_utils/public'; import { initAngularBootstrap, KibanaLegacySetup, @@ -91,8 +84,6 @@ import { DashboardConstants } from './dashboard_constants'; import { addEmbeddableToDashboardUrl } from './url_utils/url_helper'; import { PlaceholderEmbeddableFactory } from './application/embeddable/placeholder'; import { createDashboardContainerByValueRenderer } from './application'; -import { convertPanelStateToSavedDashboardPanel } from './application/lib/embeddable_saved_object_converters'; -import { DashboardPanels } from './application/embeddable/types'; declare module '../../share/public' { export interface UrlGeneratorStateMapping { @@ -110,7 +101,6 @@ interface SetupDependencies { share?: SharePluginSetup; uiActions: UiActionsSetup; usageCollection?: UsageCollectionSetup; - dashboard: DashboardStart; } interface StartDependencies { @@ -123,7 +113,6 @@ interface StartDependencies { share?: SharePluginStart; uiActions: UiActionsStart; savedObjects: SavedObjectsStart; - dashboard: DashboardStart; } export type Setup = void; @@ -134,11 +123,8 @@ export interface DashboardStart { embeddableId: string; embeddableType: string; }) => void | undefined; - navigateToDashboard: (input: DashboardContainerInput) => void; dashboardUrlGenerator?: DashboardUrlGenerator; DashboardContainerByValueRenderer: ReturnType; - getLastLoadedDashboardAppDashboardInput: () => DashboardContainerInput | undefined; - setLastLoadedDashboardAppDashboardInput: (dashboard: DashboardContainerInput | undefined) => void; } declare module '../../../plugins/ui_actions/public' { @@ -151,7 +137,6 @@ declare module '../../../plugins/ui_actions/public' { export class DashboardPlugin implements Plugin { - private currentDashboardInput: DashboardContainerInput | undefined; constructor(private initializerContext: PluginInitializerContext) {} private appStateUpdater = new BehaviorSubject(() => ({})); @@ -184,7 +169,7 @@ export class DashboardPlugin } const getStartServices = async () => { - const [coreStart, deps, dashboardStart] = await core.getStartServices(); + const [coreStart, deps] = await core.getStartServices(); const useHideChrome = ({ toggleChrome } = { toggleChrome: true }) => { React.useEffect(() => { @@ -218,7 +203,6 @@ export class DashboardPlugin SavedObjectFinder: getSavedObjectFinder(coreStart.savedObjects, coreStart.uiSettings), ExitFullScreenButton, uiActions: deps.uiActions, - dashboard: dashboardStart, }; }; @@ -311,7 +295,6 @@ export class DashboardPlugin usageCollection, scopedHistory: () => this.currentHistory!, savedObjects, - dashboard: dashboardStart, restorePreviousUrl, }; // make sure the index pattern list is up to date @@ -373,32 +356,6 @@ export class DashboardPlugin } } - private navigateToDashboard(core: CoreStart, dashInput: EmbeddableInput) { - if (!this.getActiveUrl) { - return; - } - const lastDashboardUrl = this.getActiveUrl(); - const { query, filters } = dashInput; - const panels = dashInput.panels as DashboardPanels; - const { url } = parseUrl(lastDashboardUrl); - const dashUrl = setStateToKbnUrl( - '_a', - { - query, - filters, - panels: Object.values(panels).map((panel) => { - return convertPanelStateToSavedDashboardPanel( - panel, - this.initializerContext.env.packageInfo.version - ); - }), - }, - { useHash: false }, - url - ); - core.application.navigateToApp('dashboards', { path: dashUrl }); - } - private addEmbeddableToDashboard( core: CoreStart, { embeddableId, embeddableType }: { embeddableId: string; embeddableType: string } @@ -456,10 +413,6 @@ export class DashboardPlugin DashboardContainerByValueRenderer: createDashboardContainerByValueRenderer({ factory: dashboardContainerFactory, }), - getLastLoadedDashboardAppDashboardInput: () => this.currentDashboardInput, - setLastLoadedDashboardAppDashboardInput: (dashboardInput) => - (this.currentDashboardInput = dashboardInput), - navigateToDashboard: this.navigateToDashboard.bind(this, core), }; } From 2d4f0e3a66b94e34d1c517b12ee69df92476a100 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Thu, 25 Jun 2020 14:19:53 +0100 Subject: [PATCH 19/23] Trying to get the test to run --- tasks/function_test_groups.js | 2 ++ test/new_visualize_flow/dashboard_embedding.js | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tasks/function_test_groups.js b/tasks/function_test_groups.js index 799b9e9eb81947..d60f3ae53eecc3 100644 --- a/tasks/function_test_groups.js +++ b/tasks/function_test_groups.js @@ -41,6 +41,8 @@ const getDefaultArgs = (tag) => { // '--config', 'test/functional/config.firefox.js', '--bail', '--debug', + '--config', + 'test/new_visualize_flow/config.js', ]; }; diff --git a/test/new_visualize_flow/dashboard_embedding.js b/test/new_visualize_flow/dashboard_embedding.js index 4fd7c43578994d..b1a6bd14547fbd 100644 --- a/test/new_visualize_flow/dashboard_embedding.js +++ b/test/new_visualize_flow/dashboard_embedding.js @@ -44,7 +44,6 @@ export default function ({ getService, getPageObjects }) { describe('Dashboard Embedding', function describeIndexTests() { before(async () => { - this.tags('ciGroup2'); await esArchiver.load('kibana'); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', From 4862bbf8450c211bd32c84ac34f8c22cb2e69e14 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Fri, 26 Jun 2020 13:56:22 +0100 Subject: [PATCH 20/23] Remove unused import --- .../dashboard/public/application/dashboard_app_controller.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx index 578e6a97389612..a4810338135f8d 100644 --- a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx @@ -58,7 +58,6 @@ import { isErrorEmbeddable, openAddPanelFlyout, ViewMode, - SavedObjectEmbeddableInput, ContainerOutput, EmbeddableInput, } from '../../../embeddable/public'; From 0e14f42f9899ebebdf86f483d58b1f3f60e9db49 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Fri, 26 Jun 2020 14:02:50 +0100 Subject: [PATCH 21/23] Readding spaces --- src/plugins/embeddable/public/lib/state_transfer/types.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/embeddable/public/lib/state_transfer/types.ts b/src/plugins/embeddable/public/lib/state_transfer/types.ts index 3e9f6ec7dad07a..a6721784302ac7 100644 --- a/src/plugins/embeddable/public/lib/state_transfer/types.ts +++ b/src/plugins/embeddable/public/lib/state_transfer/types.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ + import { EmbeddableInput } from '..'; /** @@ -31,6 +32,7 @@ export interface EmbeddableEditorState { export function isEmbeddableEditorState(state: unknown): state is EmbeddableEditorState { return ensureFieldOfTypeExists('originatingApp', state, 'string'); } + /** * Represents a state package that contains all fields necessary to create an embeddable by reference in a container. * @public From fc4d5cbaecca42e0b589304013d00d947ac2706d Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Tue, 30 Jun 2020 09:19:25 +0100 Subject: [PATCH 22/23] Fixing type errors --- .../visualize/public/application/types.ts | 2 +- .../application/utils/get_top_nav_config.tsx | 5 +- .../visualize/public/kibana_services.ts | 82 ------------------- 3 files changed, 4 insertions(+), 85 deletions(-) delete mode 100644 src/plugins/visualize/public/kibana_services.ts diff --git a/src/plugins/visualize/public/application/types.ts b/src/plugins/visualize/public/application/types.ts index 8e5a768b3ff35b..a6adaf1f3c62b0 100644 --- a/src/plugins/visualize/public/application/types.ts +++ b/src/plugins/visualize/public/application/types.ts @@ -44,7 +44,7 @@ import { SharePluginStart } from 'src/plugins/share/public'; import { SavedObjectsStart, SavedObject } from 'src/plugins/saved_objects/public'; import { EmbeddableStart } from 'src/plugins/embeddable/public'; import { KibanaLegacyStart } from 'src/plugins/kibana_legacy/public'; -import { ConfigSchema } from '../config'; +import { ConfigSchema } from '../../config'; export type PureVisState = SavedVisState; diff --git a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx index 6dc21b9d64b8a7..96f64c6478fa97 100644 --- a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx +++ b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx @@ -21,6 +21,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { TopNavMenuData } from 'src/plugins/navigation/public'; +import uuid from 'uuid'; import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../visualizations/public'; import { showSaveModal, @@ -33,7 +34,6 @@ import { unhashUrl } from '../../../../kibana_utils/public'; import { SavedVisInstance, VisualizeServices, VisualizeAppStateContainer } from '../types'; import { VisualizeConstants } from '../visualize_constants'; import { getEditBreadcrumbs } from './breadcrumbs'; - interface TopNavConfigParams { hasUnsavedChanges: boolean; setHasUnsavedChanges: (value: boolean) => void; @@ -240,7 +240,8 @@ export const getTopNavConfig = ( return; } const input = { - savedVis: { ...vis.serialize() }, + ...vis.serialize(), + id: uuid.v4(), }; embeddable.getStateTransfer().navigateToWithEmbeddablePackage(originatingApp, { state: { input, type: VISUALIZE_EMBEDDABLE_TYPE }, diff --git a/src/plugins/visualize/public/kibana_services.ts b/src/plugins/visualize/public/kibana_services.ts deleted file mode 100644 index c49d071dd15eaa..00000000000000 --- a/src/plugins/visualize/public/kibana_services.ts +++ /dev/null @@ -1,82 +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 { - ChromeStart, - CoreStart, - SavedObjectsClientContract, - ToastsStart, - PluginInitializerContext, - I18nStart, - ScopedHistory, -} from 'kibana/public'; - -import { NavigationPublicPluginStart as NavigationStart } from '../../navigation/public'; -import { Storage } from '../../kibana_utils/public'; -import { SharePluginStart } from '../../share/public'; -import { DataPublicPluginStart } from '../../data/public'; -import { VisualizationsStart } from '../../visualizations/public'; -import { SavedVisualizations } from './application/types'; -import { KibanaLegacyStart } from '../../kibana_legacy/public'; -import { SavedObjectsStart } from '../../saved_objects/public'; -import { EmbeddableStart } from '../../embeddable/public'; -import { ConfigSchema } from '../config'; - -export interface VisualizeKibanaServices { - pluginInitializerContext: PluginInitializerContext; - addBasePath: (url: string) => string; - chrome: ChromeStart; - core: CoreStart; - data: DataPublicPluginStart; - localStorage: Storage; - navigation: NavigationStart; - toastNotifications: ToastsStart; - savedObjectsClient: SavedObjectsClientContract; - savedVisualizations: SavedVisualizations; - share?: SharePluginStart; - kibanaLegacy: KibanaLegacyStart; - visualizeCapabilities: any; - visualizations: VisualizationsStart; - I18nContext: I18nStart['Context']; - setActiveUrl: (newUrl: string) => void; - restorePreviousUrl: () => void; - createVisEmbeddableFromObject: VisualizationsStart['__LEGACY']['createVisEmbeddableFromObject']; - scopedHistory: () => ScopedHistory; - savedObjects: SavedObjectsStart; - embeddable: EmbeddableStart; - featureFlagConfig: ConfigSchema; -} - -let services: VisualizeKibanaServices | null = null; -export function setServices(newServices: VisualizeKibanaServices) { - services = newServices; -} - -export function getServices() { - if (!services) { - throw new Error( - 'Kibana services not set - are you trying to import this module from outside of the visualize app?' - ); - } - return services; -} - -export function clearServices() { - services = null; -} From 5b166194b8ffdae87801311423abe788cc052c54 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Tue, 30 Jun 2020 10:35:10 +0100 Subject: [PATCH 23/23] Incorporating new changes --- .../public/application/dashboard_app_controller.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx index a4810338135f8d..58477d28f9081e 100644 --- a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx @@ -435,10 +435,12 @@ export class DashboardAppController { savedObjectId: incomingState.id, }); } else if ('input' in incomingState) { - container.addNewEmbeddable( - incomingState.type, - incomingState.input - ); + const input = incomingState.input; + delete input.id; + const explicitInput = { + savedVis: input, + }; + container.addNewEmbeddable(incomingState.type, explicitInput); } } }