import { isEqual } from 'lodash' ; import { History } from 'history' ; import { NotificationsStart } from 'opensearch-dashboards/public' ; import { createStateContainer , createOsdUrlStateStorage , syncState , ReduxLikeStateContainer , IOsdUrlStateStorage , withNotifyOnErrors , } from '../../../../opensearch_dashboards_utils/public' ; import { opensearchFilters , Filter , Query } from '../../../../data/public' ; import { migrateLegacyQuery } from '../helpers/migrate_legacy_query' ; export interface AppState { /**
     * Columns displayed in the table
     */ columns ? : string [ ] ; /**
     * Array of applied filters
     */ filters ? : Filter [ ] ; /**
     * id of the used index pattern
     */ index ? : string ; /**
     * Used interval of the histogram
     */ interval ? : string ; /**
     * Lucence or DQL query
     */ query ? : Query ; /**
     * Array of the used sorting [[field,direction],...]
     */ sort ? : string [ ] [ ] ; /**
     * id of the used saved query
     */ savedQuery ? : string ; } interface GetStateParams { /**
     * Default state used for merging with with URL state to get the initial state
     */ defaultAppState ? : AppState ; /**
     * Determins the use of long vs. short/hashed urls
     */ storeInSessionStorage ? : boolean ; /**
     * Browser history
     */ history : History ; /**
     * Core's notifications.toasts service
     * In case it is passed in,
     * osdUrlStateStorage will use it notifying about inner errors
     */ toasts ? : NotificationsStart [ 'toasts' ] ; } export interface GetStateReturn { /**
     * osdUrlStateStorage
     */ osdUrlStateStorage : IOsdUrlStateStorage ; /**
     * App state, the _a part of the URL
     */ appStateContainer : ReduxLikeStateContainer < AppState > ; /**
     * Start sync between state and URL
     */ startSync : ( ) => void ; /**
     * Stop sync between state and URL
     */ stopSync : ( ) => void ; /**
     * Set app state to with a partial new app state
     */ setAppState : ( newState : Partial < AppState > ) => void ; /**
     * Set state in Url using history.replace
     */ replaceUrlAppState : ( newState : Partial < AppState > ) => Promise < void > ; /**
     * Sync state to URL, used for testing
     */ flushToUrl : ( ) => void ; /**
     * Reset initial state to the current app state
     */ resetInitialAppState : ( ) => void ; /**
     * Return the Appstate before the current app state, useful for diffing changes
     */ getPreviousAppState : ( ) => AppState ; /**
     * Returns whether the current app state is different to the initial state
     */ isAppStateDirty : ( ) => void ; } const APP_STATE_URL_KEY = '_a' ; /**
 * Builds and returns appState and globalState containers and helper functions
 * Used to sync URL with UI state
 */ export function getState ( { defaultAppState = { } , storeInSessionStorage = false , history , toasts , } ) { const stateStorage = createOsdUrlStateStorage ( { useHash : storeInSessionStorage , history , ... ( toasts && withNotifyOnErrors ( toasts ) ) , } ) ; const appStateFromUrl = stateStorage . get ( APP_STATE_URL_KEY ) as AppState ; if ( appStateFromUrl && appStateFromUrl . query && ! appStateFromUrl . query . language ) { appStateFromUrl . query = migrateLegacyQuery ( appStateFromUrl . query ) ; } let initialAppState = { ... defaultAppState , ... appStateFromUrl , } ; let previousAppState ; const appStateContainer = createStateContainer < AppState > ( initialAppState ) ; const appStateContainerModified = { ... appStateContainer , set : ( value ) => { if ( value ) { previousAppState = appStateContainer . getState ( ) ; appStateContainer . set ( value ) ; } } , } ; const { start , stop } = syncState ( { storageKey : APP_STATE_URL_KEY , stateContainer : appStateContainerModified , stateStorage , } ) ; return { osdUrlStateStorage : stateStorage , appStateContainer : appStateContainerModified , startSync : start , stopSync : stop , setAppState : ( newPartial ) => setState ( appStateContainerModified , newPartial ) , replaceUrlAppState : async ( newPartial = { } ) => { const state = { ... appStateContainer . getState ( ) , ... newPartial } ; await stateStorage . set ( APP_STATE_URL_KEY , state , { replace : true } ) ; } , resetInitialAppState : ( ) => { initialAppState = appStateContainer . getState ( ) ; } , getPreviousAppState : ( ) => previousAppState , flushToUrl : ( ) => stateStorage . flush ( ) , isAppStateDirty : ( ) => ! isEqualState ( initialAppState , appStateContainer . getState ( ) ) , } ; } /**
 * Helper function to merge a given new state with the existing state and to set the given state
 * container
 */ export function setState ( stateContainer , newState ) { const oldState = stateContainer . getState ( ) ; const mergedState = { ... oldState , ... newState } ; if ( ! isEqualState ( oldState , mergedState ) ) { stateContainer . set ( mergedState ) ; } } /**
 * Helper function to compare 2 different filter states
 */ export function isEqualFilters ( filtersA , filtersB ) { if ( ! filtersA && ! filtersB ) { return true ; } else if ( ! filtersA || ! filtersB ) { return false ; } return opensearchFilters . compareFilters ( filtersA , filtersB , opensearchFilters . COMPARE_ALL_OPTIONS ) ; } /**
 * helper function to extract filters of the given state
 * returns a state object without filters and an array of filters
 */ export function splitState ( state = { } ) { const { filters = [ ] , ... statePartial } = state ; return { filters , state : statePartial } ; } /**
 * Helper function to compare 2 different state, is needed since comparing filters
 * works differently
 */ export function isEqualState ( stateA , stateB ) { if ( ! stateA && ! stateB ) { return true ; } else if ( ! stateA || ! stateB ) { return false ; } const { filters : stateAFilters = [ ] , ... stateAPartial } = stateA ; const { filters : stateBFilters = [ ] , ... stateBPartial } = stateB ; return isEqual ( stateAPartial , stateBPartial ) && isEqualFilters ( stateAFilters , stateBFilters ) ; }