[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:
Matthias Wilhelm 2021-10-04 16:00:08 +02:00 committed by GitHub
parent 163133827b
commit c868cd5c81
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 172 additions and 27 deletions

View file

@ -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) {

View file

@ -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));
}

View file

@ -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();
});
});