mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Discover] Fix duplicate request in Discover when adding a filter (#161992)
This PR fixes a bug in Discover that can cause a duplicate request to be sent when adding a filter under certain circumstances. It also reenables the flaky tests that were skipped in #161157 which were failing due to this bug. Flaky test runs: - x50: https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2645 🔴 - x100: https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2646 🔴 - x100: https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2647 🟢 Resolves #161157.
This commit is contained in:
parent
8960f61242
commit
9191bd9939
11 changed files with 116 additions and 61 deletions
|
@ -47,6 +47,7 @@ import { getRawRecordType } from '../../utils/get_raw_record_type';
|
|||
import { SavedSearchURLConflictCallout } from '../../../../components/saved_search_url_conflict_callout/saved_search_url_conflict_callout';
|
||||
import { DiscoverHistogramLayout } from './discover_histogram_layout';
|
||||
import { ErrorCallout } from '../../../../components/common/error_callout';
|
||||
import { addLog } from '../../../../utils/add_log';
|
||||
|
||||
/**
|
||||
* Local storage key for sidebar persistence state
|
||||
|
@ -209,6 +210,7 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
|
|||
}
|
||||
|
||||
if (resultState === 'uninitialized') {
|
||||
addLog('[DiscoverLayout] uninitialized triggers data fetching');
|
||||
return <DiscoverUninitialized onRefresh={() => stateContainer.dataState.fetch()} />;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import { getHeaderActionMenuMounter } from '../../../../kibana_services';
|
|||
import { DiscoverStateContainer } from '../../services/discover_state';
|
||||
import { onSaveSearch } from './on_save_search';
|
||||
import { useDiscoverCustomization } from '../../../../customizations';
|
||||
import { addLog } from '../../../../utils/add_log';
|
||||
|
||||
export interface DiscoverTopNavProps {
|
||||
onOpenInspector: () => void;
|
||||
|
@ -143,6 +144,7 @@ export const DiscoverTopNav = ({
|
|||
await stateContainer.actions.updateAdHocDataViewId();
|
||||
}
|
||||
stateContainer.actions.loadDataViewList();
|
||||
addLog('[DiscoverTopNav] onEditDataView triggers data fetching');
|
||||
stateContainer.dataState.fetch();
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import { useSavedSearchAliasMatchRedirect } from '../../hooks/saved_search_alias
|
|||
import { useSavedSearchInitial } from './services/discover_state_provider';
|
||||
import { useAdHocDataViews } from './hooks/use_adhoc_data_views';
|
||||
import { useTextBasedQueryLanguage } from './hooks/use_text_based_query_language';
|
||||
import { addLog } from '../../utils/add_log';
|
||||
|
||||
const DiscoverLayoutMemoized = React.memo(DiscoverLayout);
|
||||
|
||||
|
@ -53,6 +54,7 @@ export function DiscoverMainApp(props: DiscoverMainProps) {
|
|||
*/
|
||||
useEffect(() => {
|
||||
const unsubscribe = stateContainer.actions.initializeAndSync();
|
||||
addLog('[DiscoverMainApp] state container initialization triggers data fetching');
|
||||
stateContainer.actions.fetchData(true);
|
||||
return () => unsubscribe();
|
||||
}, [stateContainer]);
|
||||
|
|
|
@ -139,16 +139,13 @@ export function DiscoverMainRoute({ customizationCallbacks, isDev }: MainRoutePr
|
|||
}
|
||||
try {
|
||||
await stateContainer.actions.loadDataViewList();
|
||||
// reset appState in case a saved search with id is loaded and the url is empty
|
||||
// so the saved search is loaded in a clean state
|
||||
// else it might be updated by the previous app state
|
||||
const useAppState = !stateContainer.appState.isEmptyURL();
|
||||
|
||||
const currentSavedSearch = await stateContainer.actions.loadSavedSearch({
|
||||
savedSearchId,
|
||||
dataView: nextDataView,
|
||||
dataViewSpec: historyLocationState?.dataViewSpec,
|
||||
useAppState,
|
||||
});
|
||||
|
||||
if (currentSavedSearch?.id) {
|
||||
chrome.recentlyAccessed.add(
|
||||
getSavedSearchFullPathUrl(currentSavedSearch.id),
|
||||
|
|
|
@ -45,7 +45,7 @@ describe('buildStateSubscribe', () => {
|
|||
it('should not call refetch$ if nothing changes', async () => {
|
||||
await getSubscribeFn()(stateContainer.appState.getState());
|
||||
|
||||
expect(stateContainer.dataState.refetch$.next).toHaveBeenCalled();
|
||||
expect(stateContainer.dataState.refetch$.next).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call refetch$ if the chart is hidden', async () => {
|
||||
|
|
|
@ -105,7 +105,30 @@ export const buildStateSubscribe =
|
|||
dataViewChanged ||
|
||||
queryChanged
|
||||
) {
|
||||
addLog('[appstate] subscribe triggers data fetching');
|
||||
const logData = {
|
||||
chartDisplayChanged: logEntry(chartDisplayChanged, hideChart, nextState.hideChart),
|
||||
chartIntervalChanged: logEntry(chartIntervalChanged, interval, nextState.interval),
|
||||
breakdownFieldChanged: logEntry(
|
||||
breakdownFieldChanged,
|
||||
breakdownField,
|
||||
nextState.breakdownField
|
||||
),
|
||||
docTableSortChanged: logEntry(docTableSortChanged, sort, nextState.sort),
|
||||
dataViewChanged: logEntry(dataViewChanged, index, nextState.index),
|
||||
queryChanged: logEntry(queryChanged, prevQuery, nextQuery),
|
||||
};
|
||||
|
||||
addLog(
|
||||
'[buildStateSubscribe] state changes triggers data fetching',
|
||||
JSON.stringify(logData, null, 2)
|
||||
);
|
||||
|
||||
dataState.fetch();
|
||||
}
|
||||
};
|
||||
|
||||
const logEntry = <T>(changed: boolean, prevState: T, nextState: T) => ({
|
||||
changed,
|
||||
prevState,
|
||||
nextState,
|
||||
});
|
||||
|
|
|
@ -23,12 +23,12 @@ import { SavedSearch, VIEW_MODE } from '@kbn/saved-search-plugin/public';
|
|||
import { IKbnUrlStateStorage, ISyncStateRef, syncState } from '@kbn/kibana-utils-plugin/public';
|
||||
import { isEqual } from 'lodash';
|
||||
import { connectToQueryState, syncGlobalQueryStateWithUrl } from '@kbn/data-plugin/public';
|
||||
import { DiscoverServices } from '../../../build_services';
|
||||
import type { DiscoverServices } from '../../../build_services';
|
||||
import { addLog } from '../../../utils/add_log';
|
||||
import { cleanupUrlState } from '../utils/cleanup_url_state';
|
||||
import { getStateDefaults } from '../utils/get_state_defaults';
|
||||
import { handleSourceColumnState } from '../../../utils/state_helpers';
|
||||
import { DiscoverGridSettings } from '../../../components/discover_grid/types';
|
||||
import type { DiscoverGridSettings } from '../../../components/discover_grid/types';
|
||||
|
||||
export const APP_STATE_URL_KEY = '_a';
|
||||
export interface DiscoverAppStateContainer extends ReduxLikeStateContainer<DiscoverAppState> {
|
||||
|
@ -55,6 +55,10 @@ export interface DiscoverAppStateContainer extends ReduxLikeStateContainer<Disco
|
|||
* @param merge if true, the given state is merged with the current state
|
||||
*/
|
||||
replaceUrlState: (newPartial: DiscoverAppState, merge?: boolean) => Promise<void>;
|
||||
/**
|
||||
* Resets the state container to a given state, clearing the previous state
|
||||
*/
|
||||
resetToState: (state: DiscoverAppState) => void;
|
||||
/**
|
||||
* Resets the current state to the initial state
|
||||
*/
|
||||
|
@ -148,8 +152,8 @@ export const getDiscoverAppStateContainer = ({
|
|||
savedSearch: SavedSearch;
|
||||
services: DiscoverServices;
|
||||
}): DiscoverAppStateContainer => {
|
||||
let previousState: DiscoverAppState = {};
|
||||
let initialState = getInitialState(stateStorage, savedSearch, services);
|
||||
let previousState = initialState;
|
||||
const appStateContainer = createStateContainer<DiscoverAppState>(initialState);
|
||||
|
||||
const enhancedAppContainer = {
|
||||
|
@ -166,6 +170,12 @@ export const getDiscoverAppStateContainer = ({
|
|||
return !isEqualState(initialState, appStateContainer.getState());
|
||||
};
|
||||
|
||||
const resetToState = (state: DiscoverAppState) => {
|
||||
addLog('[appState] reset state to', state);
|
||||
previousState = state;
|
||||
appStateContainer.set(state);
|
||||
};
|
||||
|
||||
const resetInitialState = () => {
|
||||
addLog('[appState] reset initial state to the current state');
|
||||
initialState = appStateContainer.getState();
|
||||
|
@ -245,6 +255,7 @@ export const getDiscoverAppStateContainer = ({
|
|||
getPrevious,
|
||||
hasChanged,
|
||||
initAndSync: initializeAndSync,
|
||||
resetToState,
|
||||
resetInitialState,
|
||||
replaceUrlState,
|
||||
syncState: startAppStateUrlSync,
|
||||
|
|
|
@ -34,7 +34,10 @@ const startSync = (appState: DiscoverAppStateContainer) => {
|
|||
return stop;
|
||||
};
|
||||
|
||||
async function getState(url: string = '/', savedSearch?: SavedSearch) {
|
||||
async function getState(
|
||||
url: string = '/',
|
||||
{ savedSearch, isEmptyUrl }: { savedSearch?: SavedSearch; isEmptyUrl?: boolean } = {}
|
||||
) {
|
||||
const nextHistory = createBrowserHistory();
|
||||
nextHistory.push(url);
|
||||
|
||||
|
@ -49,6 +52,7 @@ async function getState(url: string = '/', savedSearch?: SavedSearch) {
|
|||
services: discoverServiceMock,
|
||||
history: nextHistory,
|
||||
});
|
||||
nextState.appState.isEmptyURL = jest.fn(() => isEmptyUrl ?? true);
|
||||
jest.spyOn(nextState.dataState, 'fetch');
|
||||
await nextState.actions.loadDataViewList();
|
||||
if (savedSearch) {
|
||||
|
@ -144,14 +148,14 @@ describe('Test discover initial state sort handling', () => {
|
|||
...{ sort: [['bytes', 'desc']] },
|
||||
} as SavedSearch;
|
||||
|
||||
const { state } = await getState('/#?_a=(sort:!(!(timestamp,desc)))', savedSearch);
|
||||
const { state } = await getState('/#?_a=(sort:!(!(timestamp,desc)))', { savedSearch });
|
||||
const unsubscribe = state.actions.initializeAndSync();
|
||||
expect(state.appState.getState().sort).toEqual([['timestamp', 'desc']]);
|
||||
unsubscribe();
|
||||
});
|
||||
test('Empty URL should use saved search sort for state', async () => {
|
||||
const nextSavedSearch = { ...savedSearchMock, ...{ sort: [['bytes', 'desc']] as SortOrder[] } };
|
||||
const { state } = await getState('/', nextSavedSearch);
|
||||
const { state } = await getState('/', { savedSearch: nextSavedSearch });
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id });
|
||||
const unsubscribe = state.actions.initializeAndSync();
|
||||
expect(state.appState.getState().sort).toEqual([['bytes', 'desc']]);
|
||||
|
@ -163,7 +167,7 @@ describe('Test discover state with legacy migration', () => {
|
|||
test('migration of legacy query ', async () => {
|
||||
const { state } = await getState(
|
||||
"/#?_a=(query:(query_string:(analyze_wildcard:!t,query:'type:nice%20name:%22yeah%22')))",
|
||||
savedSearchMockWithTimeFieldNew
|
||||
{ savedSearch: savedSearchMockWithTimeFieldNew }
|
||||
);
|
||||
expect(state.appState.getState().query).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
|
@ -384,9 +388,10 @@ describe('Test discover state actions', () => {
|
|||
});
|
||||
test('loadNewSavedSearch with URL changing interval state', async () => {
|
||||
const { state, getCurrentUrl } = await getState(
|
||||
'/#?_a=(interval:month,columns:!(bytes))&_g=()'
|
||||
'/#?_a=(interval:month,columns:!(bytes))&_g=()',
|
||||
{ isEmptyUrl: false }
|
||||
);
|
||||
const newSavedSearch = await state.actions.loadSavedSearch({ useAppState: true });
|
||||
const newSavedSearch = await state.actions.loadSavedSearch();
|
||||
expect(newSavedSearch?.id).toBeUndefined();
|
||||
const unsubscribe = state.actions.initializeAndSync();
|
||||
await new Promise(process.nextTick);
|
||||
|
@ -398,9 +403,10 @@ describe('Test discover state actions', () => {
|
|||
});
|
||||
test('loadSavedSearch with no id, given URL changes state', async () => {
|
||||
const { state, getCurrentUrl } = await getState(
|
||||
'/#?_a=(interval:month,columns:!(bytes))&_g=()'
|
||||
'/#?_a=(interval:month,columns:!(bytes))&_g=()',
|
||||
{ isEmptyUrl: false }
|
||||
);
|
||||
const newSavedSearch = await state.actions.loadSavedSearch({ useAppState: true });
|
||||
const newSavedSearch = await state.actions.loadSavedSearch();
|
||||
expect(newSavedSearch?.id).toBeUndefined();
|
||||
const unsubscribe = state.actions.initializeAndSync();
|
||||
await new Promise(process.nextTick);
|
||||
|
@ -411,7 +417,7 @@ describe('Test discover state actions', () => {
|
|||
unsubscribe();
|
||||
});
|
||||
test('loadSavedSearch given an empty URL, no state changes', async () => {
|
||||
const { state, getCurrentUrl } = await getState('/', savedSearchMock);
|
||||
const { state, getCurrentUrl } = await getState('/', { savedSearch: savedSearchMock });
|
||||
const newSavedSearch = await state.actions.loadSavedSearch({
|
||||
savedSearchId: 'the-saved-search-id',
|
||||
});
|
||||
|
@ -426,8 +432,11 @@ describe('Test discover state actions', () => {
|
|||
});
|
||||
test('loadSavedSearch given a URL with different interval and columns modifying the state', async () => {
|
||||
const url = '/#?_a=(interval:month,columns:!(message))&_g=()';
|
||||
const { state, getCurrentUrl } = await getState(url, savedSearchMock);
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id, useAppState: true });
|
||||
const { state, getCurrentUrl } = await getState(url, {
|
||||
savedSearch: savedSearchMock,
|
||||
isEmptyUrl: false,
|
||||
});
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id });
|
||||
const unsubscribe = state.actions.initializeAndSync();
|
||||
await new Promise(process.nextTick);
|
||||
expect(getCurrentUrl()).toMatchInlineSnapshot(
|
||||
|
@ -439,7 +448,7 @@ describe('Test discover state actions', () => {
|
|||
|
||||
test('loadSavedSearch ignoring hideChart in URL', async () => {
|
||||
const url = '/#?_a=(hideChart:true,columns:!(message))&_g=()';
|
||||
const { state } = await getState(url, savedSearchMock);
|
||||
const { state } = await getState(url, { savedSearch: savedSearchMock });
|
||||
await state.actions.loadSavedSearch();
|
||||
expect(state.savedSearchState.getState().hideChart).toBe(undefined);
|
||||
expect(state.appState.getState().hideChart).toBe(undefined);
|
||||
|
@ -447,8 +456,8 @@ describe('Test discover state actions', () => {
|
|||
|
||||
test('loadSavedSearch without id ignoring invalid index in URL, adding a warning toast', async () => {
|
||||
const url = '/#?_a=(index:abc)&_g=()';
|
||||
const { state } = await getState(url, savedSearchMock);
|
||||
await state.actions.loadSavedSearch({ useAppState: true });
|
||||
const { state } = await getState(url, { savedSearch: savedSearchMock, isEmptyUrl: false });
|
||||
await state.actions.loadSavedSearch();
|
||||
expect(state.savedSearchState.getState().searchSource.getField('index')?.id).toBe(
|
||||
'the-data-view-id'
|
||||
);
|
||||
|
@ -461,15 +470,15 @@ describe('Test discover state actions', () => {
|
|||
|
||||
test('loadSavedSearch without id containing sql, adding no warning toast with an invalid index', async () => {
|
||||
const url = "/#?_a=(index:abcde,query:(sql:'Select * from test'))&_g=()";
|
||||
const { state } = await getState(url, savedSearchMock);
|
||||
await state.actions.loadSavedSearch({ useAppState: true });
|
||||
const { state } = await getState(url, { savedSearch: savedSearchMock, isEmptyUrl: false });
|
||||
await state.actions.loadSavedSearch();
|
||||
expect(discoverServiceMock.toastNotifications.addWarning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('loadSavedSearch with id ignoring invalid index in URL, adding a warning toast', async () => {
|
||||
const url = '/#?_a=(index:abc)&_g=()';
|
||||
const { state } = await getState(url, savedSearchMock);
|
||||
await state.actions.loadSavedSearch({ useAppState: true, savedSearchId: savedSearchMock.id });
|
||||
const { state } = await getState(url, { savedSearch: savedSearchMock, isEmptyUrl: false });
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id });
|
||||
expect(state.savedSearchState.getState().searchSource.getField('index')?.id).toBe(
|
||||
'the-data-view-id'
|
||||
);
|
||||
|
@ -481,7 +490,7 @@ describe('Test discover state actions', () => {
|
|||
});
|
||||
|
||||
test('loadSavedSearch data view handling', async () => {
|
||||
const { state } = await getState('/', savedSearchMock);
|
||||
const { state } = await getState('/', { savedSearch: savedSearchMock });
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id });
|
||||
expect(state.savedSearchState.getState().searchSource.getField('index')?.id).toBe(
|
||||
'the-data-view-id'
|
||||
|
@ -498,7 +507,8 @@ describe('Test discover state actions', () => {
|
|||
expect(state.savedSearchState.getHasChanged$().getValue()).toBe(false);
|
||||
|
||||
// switch back to the previous savedSearch, but not cleaning up appState index, so it's considered as update to the persisted saved search
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id, useAppState: true });
|
||||
state.appState.isEmptyURL = jest.fn().mockReturnValue(false);
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id });
|
||||
expect(state.savedSearchState.getState().searchSource.getField('index')?.id).toBe(
|
||||
'index-pattern-with-timefield-id'
|
||||
);
|
||||
|
@ -531,7 +541,7 @@ describe('Test discover state actions', () => {
|
|||
});
|
||||
|
||||
test('loadSavedSearch resetting query & filters of data service', async () => {
|
||||
const { state } = await getState('/', savedSearchMock);
|
||||
const { state } = await getState('/', { savedSearch: savedSearchMock });
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id });
|
||||
expect(discoverServiceMock.data.query.queryString.clearQuery).toHaveBeenCalled();
|
||||
expect(discoverServiceMock.data.query.filterManager.setAppFilters).toHaveBeenCalledWith([]);
|
||||
|
@ -543,7 +553,7 @@ describe('Test discover state actions', () => {
|
|||
const filters = [{ meta: { index: 'the-data-view-id' }, query: { match_all: {} } }];
|
||||
savedSearchWithQueryAndFilters.searchSource.setField('query', query);
|
||||
savedSearchWithQueryAndFilters.searchSource.setField('filter', filters);
|
||||
const { state } = await getState('/', savedSearchWithQueryAndFilters);
|
||||
const { state } = await getState('/', { savedSearch: savedSearchWithQueryAndFilters });
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id });
|
||||
expect(discoverServiceMock.data.query.queryString.setQuery).toHaveBeenCalledWith(query);
|
||||
expect(discoverServiceMock.data.query.filterManager.setAppFilters).toHaveBeenCalledWith(
|
||||
|
@ -554,14 +564,14 @@ describe('Test discover state actions', () => {
|
|||
test('loadSavedSearch with ad-hoc data view being added to internal state adHocDataViews', async () => {
|
||||
const savedSearchAdHocCopy = copySavedSearch(savedSearchAdHoc);
|
||||
const adHocDataViewId = savedSearchAdHoc.searchSource.getField('index')!.id;
|
||||
const { state } = await getState('/', savedSearchAdHocCopy);
|
||||
const { state } = await getState('/', { savedSearch: savedSearchAdHocCopy });
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchAdHoc.id });
|
||||
expect(state.appState.getState().index).toBe(adHocDataViewId);
|
||||
expect(state.internalState.getState().adHocDataViews[0].id).toBe(adHocDataViewId);
|
||||
});
|
||||
|
||||
test('onChangeDataView', async () => {
|
||||
const { state, getCurrentUrl } = await getState('/', savedSearchMock);
|
||||
const { state, getCurrentUrl } = await getState('/', { savedSearch: savedSearchMock });
|
||||
const { actions, savedSearchState, dataState, appState } = state;
|
||||
|
||||
await actions.loadSavedSearch({ savedSearchId: savedSearchMock.id });
|
||||
|
@ -587,7 +597,7 @@ describe('Test discover state actions', () => {
|
|||
unsubscribe();
|
||||
});
|
||||
test('onDataViewCreated - persisted data view', async () => {
|
||||
const { state } = await getState('/', savedSearchMock);
|
||||
const { state } = await getState('/', { savedSearch: savedSearchMock });
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id });
|
||||
const unsubscribe = state.actions.initializeAndSync();
|
||||
await state.actions.onDataViewCreated(dataViewComplexMock);
|
||||
|
@ -601,7 +611,7 @@ describe('Test discover state actions', () => {
|
|||
unsubscribe();
|
||||
});
|
||||
test('onDataViewCreated - ad-hoc data view', async () => {
|
||||
const { state } = await getState('/', savedSearchMock);
|
||||
const { state } = await getState('/', { savedSearch: savedSearchMock });
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id });
|
||||
const unsubscribe = state.actions.initializeAndSync();
|
||||
await state.actions.onDataViewCreated(dataViewAdHoc);
|
||||
|
@ -615,7 +625,7 @@ describe('Test discover state actions', () => {
|
|||
unsubscribe();
|
||||
});
|
||||
test('onDataViewEdited - persisted data view', async () => {
|
||||
const { state } = await getState('/', savedSearchMock);
|
||||
const { state } = await getState('/', { savedSearch: savedSearchMock });
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id });
|
||||
const selectedDataView = state.internalState.getState().dataView;
|
||||
await waitFor(() => {
|
||||
|
@ -630,7 +640,7 @@ describe('Test discover state actions', () => {
|
|||
unsubscribe();
|
||||
});
|
||||
test('onDataViewEdited - ad-hoc data view', async () => {
|
||||
const { state } = await getState('/', savedSearchMock);
|
||||
const { state } = await getState('/', { savedSearch: savedSearchMock });
|
||||
const unsubscribe = state.actions.initializeAndSync();
|
||||
await state.actions.onDataViewCreated(dataViewAdHoc);
|
||||
const previousId = dataViewAdHoc.id;
|
||||
|
@ -642,7 +652,7 @@ describe('Test discover state actions', () => {
|
|||
});
|
||||
|
||||
test('onOpenSavedSearch - same target id', async () => {
|
||||
const { state } = await getState('/', savedSearchMock);
|
||||
const { state } = await getState('/', { savedSearch: savedSearchMock });
|
||||
const unsubscribe = state.actions.initializeAndSync();
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id });
|
||||
await state.savedSearchState.update({ nextState: { hideChart: true } });
|
||||
|
@ -655,16 +665,17 @@ describe('Test discover state actions', () => {
|
|||
test('onOpenSavedSearch - cleanup of previous filter', async () => {
|
||||
const { state } = await getState(
|
||||
"/#?_g=(filters:!(),refreshInterval:(pause:!t,value:60000),time:(from:now-15m,to:now))&_a=(columns:!(customer_first_name),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:ff959d40-b880-11e8-a6d9-e546fe2bba5f,key:customer_first_name,negate:!f,params:(query:Mary),type:phrase),query:(match_phrase:(customer_first_name:Mary)))),hideChart:!f,index:ff959d40-b880-11e8-a6d9-e546fe2bba5f,interval:auto,query:(language:kuery,query:''),sort:!())",
|
||||
savedSearchMock
|
||||
{ savedSearch: savedSearchMock, isEmptyUrl: false }
|
||||
);
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id, useAppState: true });
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id });
|
||||
expect(state.appState.get().filters).toHaveLength(1);
|
||||
await state.actions.loadSavedSearch({ useAppState: false });
|
||||
state.appState.isEmptyURL = jest.fn().mockReturnValue(true);
|
||||
await state.actions.loadSavedSearch();
|
||||
expect(state.appState.get().filters).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('onCreateDefaultAdHocDataView', async () => {
|
||||
const { state } = await getState('/', savedSearchMock);
|
||||
const { state } = await getState('/', { savedSearch: savedSearchMock });
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id });
|
||||
const unsubscribe = state.actions.initializeAndSync();
|
||||
await state.actions.onCreateDefaultAdHocDataView({ title: 'ad-hoc-test' });
|
||||
|
@ -673,7 +684,7 @@ describe('Test discover state actions', () => {
|
|||
unsubscribe();
|
||||
});
|
||||
test('undoSavedSearchChanges - when changing data views', async () => {
|
||||
const { state, getCurrentUrl } = await getState('/', savedSearchMock);
|
||||
const { state, getCurrentUrl } = await getState('/', { savedSearch: savedSearchMock });
|
||||
// Load a given persisted saved search
|
||||
await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id });
|
||||
const unsubscribe = state.actions.initializeAndSync();
|
||||
|
@ -708,10 +719,12 @@ describe('Test discover state actions', () => {
|
|||
|
||||
test('undoSavedSearchChanges with timeRestore', async () => {
|
||||
const { state } = await getState('/', {
|
||||
...savedSearchMockWithTimeField,
|
||||
timeRestore: true,
|
||||
refreshInterval: { pause: false, value: 1000 },
|
||||
timeRange: { from: 'now-15d', to: 'now-10d' },
|
||||
savedSearch: {
|
||||
...savedSearchMockWithTimeField,
|
||||
timeRestore: true,
|
||||
refreshInterval: { pause: false, value: 1000 },
|
||||
timeRange: { from: 'now-15d', to: 'now-10d' },
|
||||
},
|
||||
});
|
||||
const setTime = jest.fn();
|
||||
const setRefreshInterval = jest.fn();
|
||||
|
|
|
@ -78,11 +78,6 @@ export interface LoadParams {
|
|||
* the data view spec to use, if undefined, the saved search's data view will be used
|
||||
*/
|
||||
dataViewSpec?: DataViewSpec;
|
||||
/**
|
||||
* determines if AppState should be used to update the saved search
|
||||
* URL is overwriting savedSearch params in this case
|
||||
*/
|
||||
useAppState?: boolean;
|
||||
}
|
||||
|
||||
export interface DiscoverStateContainer {
|
||||
|
@ -321,6 +316,7 @@ export function getDiscoverStateContainer({
|
|||
await updateAdHocDataViewId();
|
||||
}
|
||||
loadDataViewList();
|
||||
addLog('[getDiscoverStateContainer] onDataViewEdited triggers data fetching');
|
||||
fetchData();
|
||||
};
|
||||
|
||||
|
@ -364,6 +360,7 @@ export function getDiscoverStateContainer({
|
|||
nextState: appStateContainer.getState(),
|
||||
useFilterAndQueryServices: true,
|
||||
});
|
||||
addLog('[getDiscoverStateContainer] filter changes triggers data fetching');
|
||||
fetchData();
|
||||
});
|
||||
|
||||
|
@ -411,6 +408,7 @@ export function getDiscoverStateContainer({
|
|||
if (isUpdate === false) {
|
||||
// remove the search session if the given query is not just updated
|
||||
searchSessionManager.removeSearchSessionIdFromURL({ replace: false });
|
||||
addLog('[getDiscoverStateContainer] onUpdateQuery triggers data fetching');
|
||||
dataStateContainer.fetch();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -43,9 +43,10 @@ export const loadSavedSearch = async (
|
|||
deps: LoadSavedSearchDeps
|
||||
): Promise<SavedSearch> => {
|
||||
addLog('[discoverState] loadSavedSearch');
|
||||
const { savedSearchId, useAppState } = params ?? {};
|
||||
const { savedSearchId } = params ?? {};
|
||||
const { appStateContainer, internalStateContainer, savedSearchContainer, services } = deps;
|
||||
const appState = useAppState ? appStateContainer.getState() : undefined;
|
||||
const appStateExists = !appStateContainer.isEmptyURL();
|
||||
const appState = appStateExists ? appStateContainer.getState() : undefined;
|
||||
|
||||
// Loading the saved search or creating a new one
|
||||
let nextSavedSearch = savedSearchId
|
||||
|
@ -57,7 +58,11 @@ export const loadSavedSearch = async (
|
|||
// Cleaning up the previous state
|
||||
services.filterManager.setAppFilters([]);
|
||||
services.data.query.queryString.clearQuery();
|
||||
if (!useAppState) {
|
||||
|
||||
// reset appState in case a saved search with id is loaded and
|
||||
// the url is empty so the saved search is loaded in a clean
|
||||
// state else it might be updated by the previous app state
|
||||
if (!appStateExists) {
|
||||
appStateContainer.set({});
|
||||
}
|
||||
|
||||
|
@ -85,12 +90,15 @@ export const loadSavedSearch = async (
|
|||
|
||||
// Update app state container with the next state derived from the next saved search
|
||||
const nextAppState = getInitialState(undefined, nextSavedSearch, services);
|
||||
appStateContainer.set(
|
||||
appState ? { ...nextAppState, ...cleanupUrlState({ ...appState }) } : nextAppState
|
||||
);
|
||||
const mergedAppState = appState
|
||||
? { ...nextAppState, ...cleanupUrlState({ ...appState }) }
|
||||
: nextAppState;
|
||||
|
||||
appStateContainer.resetToState(mergedAppState);
|
||||
|
||||
// Update all other services and state containers by the next saved search
|
||||
updateBySavedSearch(nextSavedSearch, deps);
|
||||
|
||||
return nextSavedSearch;
|
||||
};
|
||||
|
||||
|
|
|
@ -27,8 +27,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const queryBar = getService('queryBar');
|
||||
const elasticChart = getService('elasticChart');
|
||||
|
||||
// Failing: See https://github.com/elastic/kibana/issues/161157
|
||||
describe.skip('discover request counts', function describeIndexTests() {
|
||||
describe('discover request counts', function describeIndexTests() {
|
||||
before(async function () {
|
||||
await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional');
|
||||
await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/long_window_logstash');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue