"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createSearchSessionRestorationDataProvider = createSearchSessionRestorationDataProvider; exports.getDiscoverStateContainer = getDiscoverStateContainer; var _i18n = require("@kbn/i18n"); var _public = require("@kbn/kibana-utils-plugin/public"); var _public2 = require("@kbn/data-plugin/public"); var _public3 = require("@kbn/data-views-plugin/public"); var _uuid = require("uuid"); var _rxjs = require("rxjs"); var _load_saved_search = require("./load_saved_search"); var _restore_from_saved_search = require("../../../services/saved_searches/restore_from_saved_search"); var _types = require("../../types"); var _change_data_view = require("../hooks/utils/change_data_view"); var _build_state_subscribe = require("../hooks/utils/build_state_subscribe"); var _add_log = require("../../../utils/add_log"); var _kibana_services = require("../../../kibana_services"); var _discover_data_state_container = require("./discover_data_state_container"); var _discover_search_session = require("./discover_search_session"); var _common = require("../../../../common"); var _discover_app_state_container = require("./discover_app_state_container"); var _discover_internal_state_container = require("./discover_internal_state_container"); var _discover_saved_search_container = require("./discover_saved_search_container"); var _update_filter_references = require("../utils/update_filter_references"); var _discover_global_state_container = require("./discover_global_state_container"); /* * 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 and returns appState and globalState containers and helper functions * Used to sync URL with UI state */ function getDiscoverStateContainer({ history, services }) { const storeInSessionStorage = services.uiSettings.get('state:storeInSessionStorage'); const toasts = services.core.notifications.toasts; /** * state storage for state in the URL */ const stateStorage = (0, _public.createKbnUrlStateStorage)({ useHash: storeInSessionStorage, history, ...(toasts && (0, _public.withNotifyOnErrors)(toasts)) }); /** * Search session logic */ const searchSessionManager = new _discover_search_session.DiscoverSearchSessionManager({ history, session: services.data.search.session }); /** * Global State Container, synced with the _g part URL */ const globalStateContainer = (0, _discover_global_state_container.getDiscoverGlobalStateContainer)(stateStorage); /** * Saved Search State Container, the persisted saved object of Discover */ const savedSearchContainer = (0, _discover_saved_search_container.getSavedSearchContainer)({ services, globalStateContainer }); /** * App State Container, synced with the _a part URL */ const appStateContainer = (0, _discover_app_state_container.getDiscoverAppStateContainer)({ stateStorage, savedSearch: savedSearchContainer.getState(), services }); /** * Internal State Container, state that's not persisted and not part of the URL */ const internalStateContainer = (0, _discover_internal_state_container.getInternalStateContainer)(); const pauseAutoRefreshInterval = async dataView => { if (dataView && (!dataView.isTimeBased() || dataView.type === _public3.DataViewType.ROLLUP)) { const state = globalStateContainer.get(); if (state !== null && state !== void 0 && state.refreshInterval && !state.refreshInterval.pause) { await globalStateContainer.set({ ...state, refreshInterval: { ...(state === null || state === void 0 ? void 0 : state.refreshInterval), pause: true } }); } } }; const setDataView = dataView => { internalStateContainer.transitions.setDataView(dataView); pauseAutoRefreshInterval(dataView); savedSearchContainer.getState().searchSource.setField('index', dataView); }; const dataStateContainer = (0, _discover_data_state_container.getDataStateContainer)({ services, searchSessionManager, getAppState: appStateContainer.getState, getInternalState: internalStateContainer.getState, getSavedSearch: savedSearchContainer.getState, setDataView }); const loadDataViewList = async () => { const dataViewList = await services.dataViews.getIdsWithTitle(true); internalStateContainer.transitions.setSavedDataViews(dataViewList); }; /** * When saving a saved search with an ad hoc data view, a new id needs to be generated for the data view * This is to prevent duplicate ids messing with our system */ const updateAdHocDataViewId = async () => { const prevDataView = internalStateContainer.getState().dataView; if (!prevDataView || prevDataView.isPersisted()) return; const newDataView = await services.dataViews.create({ ...prevDataView.toSpec(), id: (0, _uuid.v4)() }); services.dataViews.clearInstanceCache(prevDataView.id); (0, _update_filter_references.updateFiltersReferences)(prevDataView, newDataView); internalStateContainer.transitions.replaceAdHocDataViewWithId(prevDataView.id, newDataView); await appStateContainer.replaceUrlState({ index: newDataView.id }); const trackingEnabled = Boolean(newDataView.isPersisted() || savedSearchContainer.getId()); (0, _kibana_services.getUrlTracker)().setTrackingEnabled(trackingEnabled); return newDataView; }; const onOpenSavedSearch = async newSavedSearchId => { (0, _add_log.addLog)('[discoverState] onOpenSavedSearch', newSavedSearchId); const currentSavedSearch = savedSearchContainer.getState(); if (currentSavedSearch.id && currentSavedSearch.id === newSavedSearchId) { (0, _add_log.addLog)('[discoverState] undo changes since saved search did not change'); await undoSavedSearchChanges(); } else { (0, _add_log.addLog)('[discoverState] onOpenSavedSearch open view URL'); services.locator.navigate({ savedSearchId: newSavedSearchId }); } }; const onDataViewCreated = async nextDataView => { if (!nextDataView.isPersisted()) { internalStateContainer.transitions.appendAdHocDataViews(nextDataView); } else { await loadDataViewList(); } if (nextDataView.id) { await onChangeDataView(nextDataView); } }; const onDataViewEdited = async editedDataView => { if (editedDataView.isPersisted()) { // Clear the current data view from the cache and create a new instance // of it, ensuring we have a new object reference to trigger a re-render services.dataViews.clearInstanceCache(editedDataView.id); setDataView(await services.dataViews.create(editedDataView.toSpec(), true)); } else { await updateAdHocDataViewId(); } loadDataViewList(); (0, _add_log.addLog)('[getDiscoverStateContainer] onDataViewEdited triggers data fetching'); fetchData(); }; const loadSavedSearch = async params => { return (0, _load_saved_search.loadSavedSearch)(params !== null && params !== void 0 ? params : {}, { appStateContainer, dataStateContainer, internalStateContainer, savedSearchContainer, services, setDataView }); }; /** * state containers initializing and subscribing to changes triggering e.g. data fetching */ const initializeAndSync = () => { // initialize app state container, syncing with _g and _a part of the URL const appStateInitAndSyncUnsubscribe = appStateContainer.initAndSync(savedSearchContainer.getState()); // subscribing to state changes of appStateContainer, triggering data fetching const appStateUnsubscribe = appStateContainer.subscribe((0, _build_state_subscribe.buildStateSubscribe)({ appState: appStateContainer, savedSearchState: savedSearchContainer, dataState: dataStateContainer, internalState: internalStateContainer, services, setDataView })); // start subscribing to dataStateContainer, triggering data fetching const unsubscribeData = dataStateContainer.subscribe(); // updates saved search when query or filters change, triggers data fetching const filterUnsubscribe = (0, _rxjs.merge)(services.filterManager.getFetches$()).subscribe(() => { savedSearchContainer.update({ nextDataView: internalStateContainer.getState().dataView, nextState: appStateContainer.getState(), useFilterAndQueryServices: true }); (0, _add_log.addLog)('[getDiscoverStateContainer] filter changes triggers data fetching'); fetchData(); }); services.data.search.session.enableStorage(createSearchSessionRestorationDataProvider({ appStateContainer, data: services.data, getSavedSearch: () => savedSearchContainer.getState() }), { isDisabled: () => services.capabilities.discover.storeSearchSession ? { disabled: false } : { disabled: true, reasonText: _public2.noSearchSessionStorageCapabilityMessage } }); return () => { unsubscribeData(); appStateUnsubscribe(); appStateInitAndSyncUnsubscribe(); filterUnsubscribe.unsubscribe(); }; }; const createAndAppendAdHocDataView = async dataViewSpec => { var _newDataView$fields$g; const newDataView = await services.dataViews.create(dataViewSpec); if (((_newDataView$fields$g = newDataView.fields.getByName('@timestamp')) === null || _newDataView$fields$g === void 0 ? void 0 : _newDataView$fields$g.type) === 'date') { newDataView.timeFieldName = '@timestamp'; } internalStateContainer.transitions.appendAdHocDataViews(newDataView); await onChangeDataView(newDataView); return newDataView; }; /** * Triggered when a user submits a query in the search bar */ const onUpdateQuery = (payload, isUpdate) => { if (isUpdate === false) { // remove the search session if the given query is not just updated searchSessionManager.removeSearchSessionIdFromURL({ replace: false }); (0, _add_log.addLog)('[getDiscoverStateContainer] onUpdateQuery triggers data fetching'); dataStateContainer.fetch(); } }; /** * Function e.g. triggered when user changes data view in the sidebar */ const onChangeDataView = async id => { await (0, _change_data_view.changeDataView)(id, { services, internalState: internalStateContainer, appState: appStateContainer }); }; /** * Undo all changes to the current saved search */ const undoSavedSearchChanges = async () => { (0, _add_log.addLog)('undoSavedSearchChanges'); const nextSavedSearch = savedSearchContainer.getInitial$().getValue(); savedSearchContainer.set(nextSavedSearch); (0, _restore_from_saved_search.restoreStateFromSavedSearch)({ savedSearch: nextSavedSearch, timefilter: services.timefilter }); const newAppState = (0, _discover_saved_search_container.getDefaultAppState)(nextSavedSearch, services); await appStateContainer.replaceUrlState(newAppState); return nextSavedSearch; }; const fetchData = (initial = false) => { (0, _add_log.addLog)('fetchData', { initial }); if (!initial || dataStateContainer.getInitialFetchStatus() === _types.FetchStatus.LOADING) { dataStateContainer.fetch(); } }; return { appState: appStateContainer, internalState: internalStateContainer, dataState: dataStateContainer, savedSearchState: savedSearchContainer, stateStorage, searchSessionManager, actions: { initializeAndSync, fetchData, loadDataViewList, loadSavedSearch, onChangeDataView, createAndAppendAdHocDataView, onDataViewCreated, onDataViewEdited, onOpenSavedSearch, onUpdateQuery, setDataView, undoSavedSearchChanges, updateAdHocDataViewId } }; } function createSearchSessionRestorationDataProvider(deps) { const getSavedSearch = () => deps.getSavedSearch(); return { getName: async () => { const savedSearch = deps.getSavedSearch(); return savedSearch.id && savedSearch.title || _i18n.i18n.translate('discover.discoverDefaultSearchSessionName', { defaultMessage: 'Discover' }); }, getLocatorData: async () => { return { id: _common.DISCOVER_APP_LOCATOR, initialState: createUrlGeneratorState({ ...deps, getSavedSearch, shouldRestoreSearchSession: false }), restoreState: createUrlGeneratorState({ ...deps, getSavedSearch, shouldRestoreSearchSession: true }) }; } }; } function createUrlGeneratorState({ appStateContainer, data, getSavedSearch, shouldRestoreSearchSession }) { const appState = appStateContainer.get(); const dataView = getSavedSearch().searchSource.getField('index'); return { filters: data.query.filterManager.getFilters(), dataViewId: appState.index, query: appState.query, savedSearchId: getSavedSearch().id, timeRange: shouldRestoreSearchSession ? data.query.timefilter.timefilter.getAbsoluteTime() : data.query.timefilter.timefilter.getTime(), searchSessionId: shouldRestoreSearchSession ? data.search.session.getSessionId() : undefined, columns: appState.columns, sort: appState.sort, savedQuery: appState.savedQuery, interval: appState.interval, refreshInterval: shouldRestoreSearchSession ? { pause: true, // force pause refresh interval when restoring a session value: 0 } : undefined, useHash: false, viewMode: appState.viewMode, hideAggregatedPreview: appState.hideAggregatedPreview, breakdownField: appState.breakdownField, dataViewSpec: !(dataView !== null && dataView !== void 0 && dataView.isPersisted()) ? dataView === null || dataView === void 0 ? void 0 : dataView.toMinimalSpec() : undefined }; }