[Discover] Extract fetch observable initialization to separate function (#108831)
* Don't trigger autorefresh when there's no time picker - because there's no UI for that * Refactor and add test * Add doc and test * Refactor * Remove index pattern without timefield filtering Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
163133827b
commit
c868cd5c81
|
@ -6,15 +6,14 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { BehaviorSubject, merge, Subject } from 'rxjs';
|
||||
import { debounceTime, filter, tap } from 'rxjs/operators';
|
||||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
import { DiscoverServices } from '../../../../build_services';
|
||||
import { DiscoverSearchSessionManager } from './discover_search_session';
|
||||
import { SearchSource } from '../../../../../../data/common';
|
||||
import { GetStateReturn } from './discover_state';
|
||||
import { ElasticSearchHit } from '../../../doc_views/doc_views_types';
|
||||
import { RequestAdapter } from '../../../../../../inspector/public';
|
||||
import { AutoRefreshDoneFn } from '../../../../../../data/public';
|
||||
import type { AutoRefreshDoneFn } from '../../../../../../data/public';
|
||||
import { validateTimeRange } from '../utils/validate_time_range';
|
||||
import { Chart } from '../components/chart/point_series';
|
||||
import { useSingleton } from '../utils/use_singleton';
|
||||
|
@ -23,6 +22,7 @@ import { FetchStatus } from '../../../types';
|
|||
import { fetchAll } from '../utils/fetch_all';
|
||||
import { useBehaviorSubject } from '../utils/use_behavior_subject';
|
||||
import { sendResetMsg } from './use_saved_search_messages';
|
||||
import { getFetch$ } from '../utils/get_fetch_observable';
|
||||
|
||||
export interface SavedSearchData {
|
||||
main$: DataMain$;
|
||||
|
@ -134,6 +134,7 @@ export const useSavedSearch = ({
|
|||
*/
|
||||
const refs = useRef<{
|
||||
abortController?: AbortController;
|
||||
autoRefreshDone?: AutoRefreshDoneFn;
|
||||
}>({});
|
||||
|
||||
/**
|
||||
|
@ -145,29 +146,17 @@ export const useSavedSearch = ({
|
|||
* handler emitted by `timefilter.getAutoRefreshFetch$()`
|
||||
* to notify when data completed loading and to start a new autorefresh loop
|
||||
*/
|
||||
let autoRefreshDoneCb: AutoRefreshDoneFn | undefined;
|
||||
const fetch$ = merge(
|
||||
const setAutoRefreshDone = (fn: AutoRefreshDoneFn | undefined) => {
|
||||
refs.current.autoRefreshDone = fn;
|
||||
};
|
||||
const fetch$ = getFetch$({
|
||||
setAutoRefreshDone,
|
||||
data,
|
||||
main$,
|
||||
refetch$,
|
||||
filterManager.getFetches$(),
|
||||
timefilter.getFetch$(),
|
||||
timefilter.getAutoRefreshFetch$().pipe(
|
||||
tap((done) => {
|
||||
autoRefreshDoneCb = done;
|
||||
}),
|
||||
filter(() => {
|
||||
/**
|
||||
* filter to prevent auto-refresh triggered fetch when
|
||||
* loading is still ongoing
|
||||
*/
|
||||
const currentFetchStatus = main$.getValue().fetchStatus;
|
||||
return (
|
||||
currentFetchStatus !== FetchStatus.LOADING && currentFetchStatus !== FetchStatus.PARTIAL
|
||||
);
|
||||
})
|
||||
),
|
||||
data.query.queryString.getUpdates$(),
|
||||
searchSessionManager.newSearchSessionIdFromURL$.pipe(filter((sessionId) => !!sessionId))
|
||||
).pipe(debounceTime(100));
|
||||
searchSessionManager,
|
||||
searchSource,
|
||||
});
|
||||
|
||||
const subscription = fetch$.subscribe((val) => {
|
||||
if (!validateTimeRange(timefilter.getTime(), services.toastNotifications)) {
|
||||
|
@ -190,8 +179,8 @@ export const useSavedSearch = ({
|
|||
}).subscribe({
|
||||
complete: () => {
|
||||
// if this function was set and is executed, another refresh fetch can be triggered
|
||||
autoRefreshDoneCb?.();
|
||||
autoRefreshDoneCb = undefined;
|
||||
refs.current.autoRefreshDone?.();
|
||||
refs.current.autoRefreshDone = undefined;
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
import { merge } from 'rxjs';
|
||||
import { debounceTime, filter, tap } from 'rxjs/operators';
|
||||
|
||||
import { FetchStatus } from '../../../types';
|
||||
import type {
|
||||
AutoRefreshDoneFn,
|
||||
DataPublicPluginStart,
|
||||
SearchSource,
|
||||
} from '../../../../../../data/public';
|
||||
import { DataMain$, DataRefetch$ } from '../services/use_saved_search';
|
||||
import { DiscoverSearchSessionManager } from '../services/discover_search_session';
|
||||
|
||||
/**
|
||||
* This function returns an observable that's used to trigger data fetching
|
||||
*/
|
||||
export function getFetch$({
|
||||
setAutoRefreshDone,
|
||||
data,
|
||||
main$,
|
||||
refetch$,
|
||||
searchSessionManager,
|
||||
}: {
|
||||
setAutoRefreshDone: (val: AutoRefreshDoneFn | undefined) => void;
|
||||
data: DataPublicPluginStart;
|
||||
main$: DataMain$;
|
||||
refetch$: DataRefetch$;
|
||||
searchSessionManager: DiscoverSearchSessionManager;
|
||||
searchSource: SearchSource;
|
||||
}) {
|
||||
const { timefilter } = data.query.timefilter;
|
||||
const { filterManager } = data.query;
|
||||
return merge(
|
||||
refetch$,
|
||||
filterManager.getFetches$(),
|
||||
timefilter.getFetch$(),
|
||||
timefilter.getAutoRefreshFetch$().pipe(
|
||||
tap((done) => {
|
||||
setAutoRefreshDone(done);
|
||||
}),
|
||||
filter(() => {
|
||||
const currentFetchStatus = main$.getValue().fetchStatus;
|
||||
return (
|
||||
/**
|
||||
* filter to prevent auto-refresh triggered fetch when
|
||||
* loading is still ongoing
|
||||
*/
|
||||
currentFetchStatus !== FetchStatus.LOADING && currentFetchStatus !== FetchStatus.PARTIAL
|
||||
);
|
||||
})
|
||||
),
|
||||
data.query.queryString.getUpdates$(),
|
||||
searchSessionManager.newSearchSessionIdFromURL$.pipe(filter((sessionId) => !!sessionId))
|
||||
).pipe(debounceTime(100));
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
import { getFetch$ } from './get_fetch_observable';
|
||||
import { FetchStatus } from '../../../types';
|
||||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
import { DataPublicPluginStart } from '../../../../../../data/public';
|
||||
import { createSearchSessionMock } from '../../../../__mocks__/search_session';
|
||||
import { DataRefetch$ } from '../services/use_saved_search';
|
||||
import { savedSearchMock, savedSearchMockWithTimeField } from '../../../../__mocks__/saved_search';
|
||||
|
||||
function createDataMock(
|
||||
queryString$: Subject<unknown>,
|
||||
filterManager$: Subject<unknown>,
|
||||
timefilterFetch$: Subject<unknown>,
|
||||
autoRefreshFetch$: Subject<unknown>
|
||||
) {
|
||||
return {
|
||||
query: {
|
||||
queryString: {
|
||||
getUpdates$: () => {
|
||||
return queryString$;
|
||||
},
|
||||
},
|
||||
filterManager: {
|
||||
getFetches$: () => {
|
||||
return filterManager$;
|
||||
},
|
||||
},
|
||||
timefilter: {
|
||||
timefilter: {
|
||||
getFetch$: () => {
|
||||
return timefilterFetch$;
|
||||
},
|
||||
getAutoRefreshFetch$: () => {
|
||||
return autoRefreshFetch$;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as DataPublicPluginStart;
|
||||
}
|
||||
|
||||
describe('getFetchObservable', () => {
|
||||
test('refetch$.next should trigger fetch$.next', async (done) => {
|
||||
const searchSessionManagerMock = createSearchSessionMock();
|
||||
|
||||
const main$ = new BehaviorSubject({ fetchStatus: FetchStatus.UNINITIALIZED });
|
||||
const refetch$: DataRefetch$ = new Subject();
|
||||
const fetch$ = getFetch$({
|
||||
setAutoRefreshDone: jest.fn(),
|
||||
main$,
|
||||
refetch$,
|
||||
data: createDataMock(new Subject(), new Subject(), new Subject(), new Subject()),
|
||||
searchSessionManager: searchSessionManagerMock.searchSessionManager,
|
||||
searchSource: savedSearchMock.searchSource,
|
||||
});
|
||||
|
||||
fetch$.subscribe(() => {
|
||||
done();
|
||||
});
|
||||
refetch$.next();
|
||||
});
|
||||
test('getAutoRefreshFetch$ should trigger fetch$.next', async () => {
|
||||
jest.useFakeTimers();
|
||||
const searchSessionManagerMock = createSearchSessionMock();
|
||||
const autoRefreshFetch$ = new Subject();
|
||||
|
||||
const main$ = new BehaviorSubject({ fetchStatus: FetchStatus.UNINITIALIZED });
|
||||
const refetch$: DataRefetch$ = new Subject();
|
||||
const dataMock = createDataMock(new Subject(), new Subject(), new Subject(), autoRefreshFetch$);
|
||||
const setAutoRefreshDone = jest.fn();
|
||||
const fetch$ = getFetch$({
|
||||
setAutoRefreshDone,
|
||||
main$,
|
||||
refetch$,
|
||||
data: dataMock,
|
||||
searchSessionManager: searchSessionManagerMock.searchSessionManager,
|
||||
searchSource: savedSearchMockWithTimeField.searchSource,
|
||||
});
|
||||
|
||||
const fetchfnMock = jest.fn();
|
||||
fetch$.subscribe(() => {
|
||||
fetchfnMock();
|
||||
});
|
||||
autoRefreshFetch$.next(jest.fn());
|
||||
jest.runAllTimers();
|
||||
expect(fetchfnMock).toHaveBeenCalledTimes(1);
|
||||
expect(setAutoRefreshDone).toHaveBeenCalled();
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue