"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.initializeDashboard = exports.createDashboard = void 0; var _rxjs = require("rxjs"); var _lodash = require("lodash"); var _common = require("@kbn/controls-plugin/common"); var _public = require("@kbn/embeddable-plugin/public"); var _public2 = require("@kbn/presentation-util-plugin/public"); var _public3 = require("@kbn/data-plugin/public"); var _dashboard_container = require("../dashboard_container"); var _plugin_services = require("../../../services/plugin_services"); var _sync_dashboard_data_views = require("./data_views/sync_dashboard_data_views"); var _sync_dashboard_unified_search_state = require("./unified_search/sync_dashboard_unified_search_state"); var _dashboard_constants = require("../../../dashboard_constants"); var _dashboard_control_group_integration = require("./controls/dashboard_control_group_integration"); var _start_dashboard_search_session_integration = require("./search_sessions/start_dashboard_search_session_integration"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ /** * Builds a new Dashboard from scratch. */ const createDashboard = async (creationOptions, dashboardCreationStartTime, savedObjectId) => { const { data: { dataViews }, dashboardContentManagement: { loadDashboardState } } = _plugin_services.pluginServices.getServices(); // -------------------------------------------------------------------------------------- // Create method which allows work to be done on the dashboard container when it's ready. // -------------------------------------------------------------------------------------- const dashboardContainerReady$ = new _rxjs.Subject(); const untilDashboardReady = () => new Promise(resolve => { const subscription = dashboardContainerReady$.subscribe(container => { subscription.unsubscribe(); resolve(container); }); }); // -------------------------------------------------------------------------------------- // Lazy load required systems and Dashboard saved object. // -------------------------------------------------------------------------------------- const reduxEmbeddablePackagePromise = (0, _public2.lazyLoadReduxToolsPackage)(); const defaultDataViewAssignmentPromise = dataViews.getDefaultDataView(); const dashboardSavedObjectPromise = loadDashboardState({ id: savedObjectId }); const [reduxEmbeddablePackage, savedObjectResult, defaultDataView] = await Promise.all([reduxEmbeddablePackagePromise, dashboardSavedObjectPromise, defaultDataViewAssignmentPromise]); if (!defaultDataView) { throw new Error('Dashboard requires at least one data view before it can be initialized.'); } // -------------------------------------------------------------------------------------- // Initialize Dashboard integrations // -------------------------------------------------------------------------------------- const initializeResult = await initializeDashboard({ loadDashboardReturn: savedObjectResult, untilDashboardReady, creationOptions }); if (!initializeResult) return; const { input, searchSessionId } = initializeResult; // -------------------------------------------------------------------------------------- // Build and return the dashboard container. // -------------------------------------------------------------------------------------- const dashboardContainer = new _dashboard_container.DashboardContainer(input, reduxEmbeddablePackage, searchSessionId, savedObjectResult === null || savedObjectResult === void 0 ? void 0 : savedObjectResult.dashboardInput, dashboardCreationStartTime, undefined, creationOptions, savedObjectId); dashboardContainerReady$.next(dashboardContainer); return dashboardContainer; }; /** * Initializes a Dashboard and starts all of its integrations */ exports.createDashboard = createDashboard; const initializeDashboard = async ({ loadDashboardReturn, untilDashboardReady, creationOptions, controlGroup }) => { var _loadDashboardReturn$, _creationOptions$getI; const { dashboardSessionStorage, embeddable: { getEmbeddableFactory }, data: { query: queryService, search: { session } } } = _plugin_services.pluginServices.getServices(); const { queryString, filterManager, timefilter: { timefilter: timefilterService } } = queryService; const { getInitialInput, searchSessionSettings, unifiedSearchSettings, validateLoadedSavedObject, useControlGroupIntegration, useUnifiedSearchIntegration, useSessionStorageIntegration } = creationOptions !== null && creationOptions !== void 0 ? creationOptions : {}; // -------------------------------------------------------------------------------------- // Run validation. // -------------------------------------------------------------------------------------- const validationResult = loadDashboardReturn && (validateLoadedSavedObject === null || validateLoadedSavedObject === void 0 ? void 0 : validateLoadedSavedObject(loadDashboardReturn)); if (validationResult === 'invalid') { // throw error to stop the rest of Dashboard loading and make the factory return an ErrorEmbeddable. throw new Error('Dashboard failed saved object result validation'); } else if (validationResult === 'redirected') { return; } // -------------------------------------------------------------------------------------- // Gather input from session storage if integration is used. // -------------------------------------------------------------------------------------- const sessionStorageInput = (() => { if (!useSessionStorageIntegration) return; return dashboardSessionStorage.getState(loadDashboardReturn.dashboardId); })(); // -------------------------------------------------------------------------------------- // Combine input from saved object, session storage, & passed input to create initial input. // -------------------------------------------------------------------------------------- const overrideInput = getInitialInput === null || getInitialInput === void 0 ? void 0 : getInitialInput(); const initialInput = (0, _lodash.cloneDeep)({ ..._dashboard_constants.DEFAULT_DASHBOARD_INPUT, ...((_loadDashboardReturn$ = loadDashboardReturn === null || loadDashboardReturn === void 0 ? void 0 : loadDashboardReturn.dashboardInput) !== null && _loadDashboardReturn$ !== void 0 ? _loadDashboardReturn$ : {}), ...sessionStorageInput, ...overrideInput }); initialInput.executionContext = { type: 'dashboard', description: initialInput.title }; // -------------------------------------------------------------------------------------- // Set up unified search integration. // -------------------------------------------------------------------------------------- if (useUnifiedSearchIntegration && unifiedSearchSettings !== null && unifiedSearchSettings !== void 0 && unifiedSearchSettings.kbnUrlStateStorage) { const { query, filters, timeRestore, timeRange: savedTimeRange, refreshInterval: savedRefreshInterval } = initialInput; const { kbnUrlStateStorage } = unifiedSearchSettings; // apply filters and query to the query service filterManager.setAppFilters((0, _lodash.cloneDeep)(filters !== null && filters !== void 0 ? filters : [])); queryString.setQuery(query !== null && query !== void 0 ? query : queryString.getDefaultQuery()); /** * Get initial time range, and set up dashboard time restore if applicable */ const initialTimeRange = (() => { var _kbnUrlStateStorage$g; // if there is an explicit time range in the URL it always takes precedence. const urlOverrideTimeRange = (_kbnUrlStateStorage$g = kbnUrlStateStorage.get(_dashboard_constants.GLOBAL_STATE_STORAGE_KEY)) === null || _kbnUrlStateStorage$g === void 0 ? void 0 : _kbnUrlStateStorage$g.time; if (urlOverrideTimeRange) return urlOverrideTimeRange; // if this Dashboard has timeRestore return the time range that was saved with the dashboard. if (timeRestore && savedTimeRange) return savedTimeRange; // otherwise fall back to the time range from the timefilterService. return timefilterService.getTime(); })(); initialInput.timeRange = initialTimeRange; if (timeRestore) { if (savedTimeRange) timefilterService.setTime(savedTimeRange); if (savedRefreshInterval) timefilterService.setRefreshInterval(savedRefreshInterval); } // start syncing global query state with the URL. const { stop: stopSyncingQueryServiceStateWithUrl } = (0, _public3.syncGlobalQueryStateWithUrl)(queryService, kbnUrlStateStorage); untilDashboardReady().then(dashboardContainer => { const stopSyncingUnifiedSearchState = _sync_dashboard_unified_search_state.syncUnifiedSearchState.bind(dashboardContainer)(kbnUrlStateStorage); dashboardContainer.stopSyncingWithUnifiedSearch = () => { stopSyncingUnifiedSearchState(); stopSyncingQueryServiceStateWithUrl(); }; }); } // -------------------------------------------------------------------------------------- // Place the incoming embeddable if there is one // -------------------------------------------------------------------------------------- const incomingEmbeddable = creationOptions === null || creationOptions === void 0 ? void 0 : (_creationOptions$getI = creationOptions.getIncomingEmbeddable) === null || _creationOptions$getI === void 0 ? void 0 : _creationOptions$getI.call(creationOptions); if (incomingEmbeddable) { const scrolltoIncomingEmbeddable = (container, id) => { container.setScrollToPanelId(id); container.setHighlightPanelId(id); }; initialInput.viewMode = _public.ViewMode.EDIT; // view mode must always be edit to recieve an embeddable. if (incomingEmbeddable.embeddableId && Boolean(initialInput.panels[incomingEmbeddable.embeddableId])) { // this embeddable already exists, we will update the explicit input. const panelToUpdate = initialInput.panels[incomingEmbeddable.embeddableId]; const sameType = panelToUpdate.type === incomingEmbeddable.type; panelToUpdate.type = incomingEmbeddable.type; panelToUpdate.explicitInput = { // if the incoming panel is the same type as what was there before we can safely spread the old panel's explicit input ...(sameType ? panelToUpdate.explicitInput : {}), ...incomingEmbeddable.input, id: incomingEmbeddable.embeddableId, // maintain hide panel titles setting. hidePanelTitles: panelToUpdate.explicitInput.hidePanelTitles }; untilDashboardReady().then(container => scrolltoIncomingEmbeddable(container, incomingEmbeddable.embeddableId)); } else { // otherwise this incoming embeddable is brand new and can be added via the default method after the dashboard container is created. untilDashboardReady().then(async container => { const embeddable = await container.addNewEmbeddable(incomingEmbeddable.type, incomingEmbeddable.input); scrolltoIncomingEmbeddable(container, embeddable.id); }); } } // -------------------------------------------------------------------------------------- // Set up search sessions integration. // -------------------------------------------------------------------------------------- let initialSearchSessionId; if (searchSessionSettings) { const { sessionIdToRestore } = searchSessionSettings; // if this incoming embeddable has a session, continue it. if (incomingEmbeddable !== null && incomingEmbeddable !== void 0 && incomingEmbeddable.searchSessionId) { session.continue(incomingEmbeddable.searchSessionId); } if (sessionIdToRestore) { session.restore(sessionIdToRestore); } const existingSession = session.getSessionId(); initialSearchSessionId = sessionIdToRestore !== null && sessionIdToRestore !== void 0 ? sessionIdToRestore : existingSession && incomingEmbeddable ? existingSession : session.start(); untilDashboardReady().then(container => { _start_dashboard_search_session_integration.startDashboardSearchSessionIntegration.bind(container)(creationOptions === null || creationOptions === void 0 ? void 0 : creationOptions.searchSessionSettings); }); } // -------------------------------------------------------------------------------------- // Start the control group integration. // -------------------------------------------------------------------------------------- if (useControlGroupIntegration) { const controlsGroupFactory = getEmbeddableFactory(_common.CONTROL_GROUP_TYPE); const { filters, query, timeRange, viewMode, controlGroupInput, id } = initialInput; const fullControlGroupInput = { id: `control_group_${id !== null && id !== void 0 ? id : 'new_dashboard'}`, ...(0, _common.getDefaultControlGroupInput)(), ...(0, _lodash.pickBy)(controlGroupInput, _lodash.identity), // undefined keys in initialInput should not overwrite defaults timeRange, viewMode, filters, query }; if (controlGroup) { controlGroup.updateInputAndReinitialize(fullControlGroupInput); } else { const newControlGroup = await (controlsGroupFactory === null || controlsGroupFactory === void 0 ? void 0 : controlsGroupFactory.create(fullControlGroupInput)); if (!newControlGroup || (0, _public.isErrorEmbeddable)(newControlGroup)) { throw new Error('Error in control group startup'); } controlGroup = newControlGroup; } untilDashboardReady().then(dashboardContainer => { dashboardContainer.controlGroup = controlGroup; _dashboard_control_group_integration.startSyncingDashboardControlGroup.bind(dashboardContainer)(); }); await controlGroup.untilInitialized(); } // -------------------------------------------------------------------------------------- // Start the data views integration. // -------------------------------------------------------------------------------------- untilDashboardReady().then(dashboardContainer => { dashboardContainer.integrationSubscriptions.add(_sync_dashboard_data_views.startSyncingDashboardDataViews.bind(dashboardContainer)()); }); // -------------------------------------------------------------------------------------- // Start animating panel transforms 500 ms after dashboard is created. // -------------------------------------------------------------------------------------- untilDashboardReady().then(dashboard => setTimeout(() => dashboard.dispatch.setAnimatePanelTransforms(true), 500)); return { input: initialInput, searchSessionId: initialSearchSessionId }; }; exports.initializeDashboard = initializeDashboard;