diff --git a/x-pack/plugins/apm/public/components/shared/date_picker/index.tsx b/x-pack/plugins/apm/public/components/shared/date_picker/index.tsx index 146cc87737c652..2bcc1c403ec85a 100644 --- a/x-pack/plugins/apm/public/components/shared/date_picker/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/date_picker/index.tsx @@ -25,7 +25,7 @@ export function DatePicker({ rangeTo?: string; refreshPaused?: boolean; refreshInterval?: number; - onTimeRangeRefresh: (range: { start: string; end: string }) => void; + onTimeRangeRefresh: () => void; }) { const history = useHistory(); const location = useLocation(); @@ -93,9 +93,9 @@ export function DatePicker({ isPaused={refreshPaused} refreshInterval={refreshInterval} onTimeChange={onTimeChange} - onRefresh={({ start, end }) => { + onRefresh={() => { clearCache(); - onTimeRangeRefresh({ start, end }); + onTimeRangeRefresh(); }} onRefreshChange={({ isPaused: nextRefreshPaused, diff --git a/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx b/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx index d9efbe0403b419..c47b45604f0f31 100644 --- a/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx @@ -49,6 +49,8 @@ function setup({ timefilter: { timefilter: { setTime: () => {}, + getRefreshInterval: () => {}, + setRefreshInterval: () => {}, }, }, }, diff --git a/x-pack/plugins/apm/public/components/shared/unified_search_bar/index.tsx b/x-pack/plugins/apm/public/components/shared/unified_search_bar/index.tsx index 83e080c8c0df33..9379351045eac1 100644 --- a/x-pack/plugins/apm/public/components/shared/unified_search_bar/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/unified_search_bar/index.tsx @@ -20,6 +20,7 @@ import { EuiSkeletonRectangle } from '@elastic/eui'; import qs from 'query-string'; import { DataView, UI_SETTINGS } from '@kbn/data-plugin/common'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { OnRefreshChangeProps } from '@elastic/eui/src/components/date_picker/types'; import { UIProcessorEvent } from '../../../../common/processor_event'; import { TimePickerTimeDefaults } from '../date_picker/typings'; import { ApmPluginStartDeps } from '../../../plugin'; @@ -30,6 +31,14 @@ import { fromQuery, toQuery } from '../links/url_helpers'; import { useApmParams } from '../../../hooks/use_apm_params'; import { getBoolFilter } from '../get_bool_filter'; import { useLegacyUrlParams } from '../../../context/url_params_context/use_url_params'; +import { clearCache } from '../../../services/rest/call_api'; +import { useTimeRangeId } from '../../../context/time_range_id/use_time_range_id'; +import { + toBoolean, + toNumber, +} from '../../../context/url_params_context/helpers'; + +export const DEFAULT_REFRESH_INTERVAL = 60000; function useSearchBarParams(defaultKuery?: string) { const { path, query } = useApmParams('/*'); @@ -37,6 +46,12 @@ function useSearchBarParams(defaultKuery?: string) { const serviceName = 'serviceName' in path ? path.serviceName : undefined; const groupId = 'groupId' in path ? path.groupId : undefined; const environment = 'environment' in query ? query.environment : undefined; + const refreshIntervalFromUrl = + 'refreshInterval' in query + ? toNumber(query.refreshInterval) + : DEFAULT_REFRESH_INTERVAL; + const refreshPausedFromUrl = + 'refreshPaused' in query ? query.refreshPaused : 'true'; return { kuery: urlKuery @@ -48,6 +63,8 @@ function useSearchBarParams(defaultKuery?: string) { serviceName, groupId, environment, + refreshPausedFromUrl, + refreshIntervalFromUrl, }; } @@ -129,8 +146,14 @@ export function UnifiedSearchBar({ }, } = services; - const { kuery, serviceName, environment, groupId } = - useSearchBarParams(value); + const { + kuery, + serviceName, + environment, + groupId, + refreshPausedFromUrl, + refreshIntervalFromUrl, + } = useSearchBarParams(value); const timePickerTimeDefaults = core.uiSettings.get( UI_SETTINGS.TIMEPICKER_TIME_DEFAULTS ); @@ -148,7 +171,21 @@ export function UnifiedSearchBar({ } // Sync Time Range with Search Bar timeFilterService.timefilter.setTime(urlTimeRange as TimeRange); - }, [kuery, queryStringService, timeFilterService, urlTimeRange]); + + // Sync Auto refresh interval with Search Bar + const refreshInterval = { + pause: toBoolean(refreshPausedFromUrl), + value: refreshIntervalFromUrl, + }; + timeFilterService.timefilter.setRefreshInterval(refreshInterval); + }, [ + kuery, + queryStringService, + refreshIntervalFromUrl, + refreshPausedFromUrl, + timeFilterService.timefilter, + urlTimeRange, + ]); useEffect(() => { syncSearchBarWithUrl(); @@ -159,6 +196,7 @@ export function UnifiedSearchBar({ const { dataView } = useApmDataView(); const { urlParams } = useLegacyUrlParams(); const processorEvent = useProcessorEvent(); + const { incrementTimeRangeId } = useTimeRangeId(); const searchbarPlaceholder = getSearchBarPlaceholder( placeholder, processorEvent @@ -183,7 +221,32 @@ export function UnifiedSearchBar({ query: filter, } as Filter; }); - const handleSubmit = (payload: { dateRange: TimeRange; query?: Query }) => { + + const onRefresh = () => { + clearCache(); + incrementTimeRangeId(); + }; + + const onRefreshChange = ({ + isPaused, + refreshInterval, + }: OnRefreshChangeProps) => { + const existingQueryParams = toQuery(location.search); + const updatedQueryParams = { + ...existingQueryParams, + refreshPaused: isPaused, + refreshInterval, + }; + + history.push({ + ...location, + search: fromQuery(updatedQueryParams), + }); + }; + const handleSubmit = ( + payload: { dateRange: TimeRange; query?: Query }, + isUpdate?: boolean + ) => { if (dataView == null) { return; } @@ -206,13 +269,19 @@ export function UnifiedSearchBar({ rangeFrom, rangeTo, }; - history.push({ - ...location, - search: fromQuery({ - ...updatedQueryWithTime, - kuery: query?.query, - }), - }); + const newSearchParams = { + ...updatedQueryWithTime, + kuery: query?.query, + }; + + if (isUpdate) { + history.push({ + ...location, + search: fromQuery(newSearchParams), + }); + } else { + onRefresh(); + } } catch (e) { console.log('Invalid kuery syntax'); // eslint-disable-line no-console } @@ -239,6 +308,8 @@ export function UnifiedSearchBar({ showSubmitButton={showSubmitButton} displayStyle="inPage" onQuerySubmit={handleSubmit} + onRefresh={onRefresh} + onRefreshChange={onRefreshChange} isClearable={isClearable} dataTestSubj="apmUnifiedSearchBar" filtersForSuggestions={filtersForSearchBarSuggestions} diff --git a/x-pack/plugins/apm/public/components/shared/unified_search_bar/unified_search_bar.test.tsx b/x-pack/plugins/apm/public/components/shared/unified_search_bar/unified_search_bar.test.tsx index 25d3ecbc41b0f4..75a7b8c5ad79bb 100644 --- a/x-pack/plugins/apm/public/components/shared/unified_search_bar/unified_search_bar.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/unified_search_bar/unified_search_bar.test.tsx @@ -41,6 +41,7 @@ function setup({ const getQuerySpy = jest.fn(); const clearQuerySpy = jest.fn(); const setTimeSpy = jest.fn(); + const setRefreshIntervalSpy = jest.fn(); const KibanaReactContext = createKibanaReactContext({ usageCollection: { @@ -59,6 +60,7 @@ function setup({ timefilter: { timefilter: { setTime: setTimeSpy, + setRefreshInterval: setRefreshIntervalSpy, }, }, }, @@ -82,7 +84,14 @@ function setup({ ); - return { wrapper, setQuerySpy, getQuerySpy, clearQuerySpy, setTimeSpy }; + return { + wrapper, + setQuerySpy, + getQuerySpy, + clearQuerySpy, + setTimeSpy, + setRefreshIntervalSpy, + }; } describe('when kuery is already present in the url, the search bar must reflect the same', () => { @@ -114,6 +123,11 @@ describe('when kuery is already present in the url, the search bar must reflect to: 'now', }; + const refreshInterval = { + pause: false, + value: 5000, + }; + const urlParams = { kuery: expectedQuery.query, rangeFrom: expectedTimeRange.from, @@ -122,17 +136,20 @@ describe('when kuery is already present in the url, the search bar must reflect comparisonEnabled: true, serviceGroup: '', offset: '1d', + refreshPaused: refreshInterval.pause, + refreshInterval: refreshInterval.value, }; jest .spyOn(useApmParamsHook, 'useApmParams') .mockReturnValue({ query: urlParams, path: {} }); - const { setQuerySpy, setTimeSpy } = setup({ + const { setQuerySpy, setTimeSpy, setRefreshIntervalSpy } = setup({ history, urlParams, }); expect(setQuerySpy).toBeCalledWith(expectedQuery); expect(setTimeSpy).toBeCalledWith(expectedTimeRange); + expect(setRefreshIntervalSpy).toBeCalledWith(refreshInterval); }); });