Add 5s minRefreshInterval to timefilter with validation (#188881)

Adds enforcement for the global time filter to limit the min refresh interval.
This commit is contained in:
Nick Partridge 2024-08-23 09:21:52 -07:00 committed by GitHub
parent af4f482f5f
commit 94b657e6c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 239 additions and 48 deletions

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { schema, TypeOf } from '@kbn/config-schema';
import { offeringBasedSchema, schema, TypeOf } from '@kbn/config-schema';
export const searchSessionsConfigSchema = schema.object({
/**
@ -84,7 +84,23 @@ export const searchConfigSchema = schema.object({
sessions: searchSessionsConfigSchema,
});
export const queryConfigSchema = schema.object({
/**
* Config for timefilter
*/
timefilter: schema.object({
/**
* Lower limit of refresh interval (in milliseconds)
*/
minRefreshInterval: offeringBasedSchema<number>({
serverless: schema.number({ min: 1000, defaultValue: 5000 }),
traditional: schema.number({ min: 1000, defaultValue: 1000 }),
}),
}),
});
export const configSchema = schema.object({
query: queryConfigSchema,
search: searchConfigSchema,
/**
* Turns on/off limit validations for the registered uiSettings.
@ -96,4 +112,6 @@ export type ConfigSchema = TypeOf<typeof configSchema>;
export type SearchConfigSchema = TypeOf<typeof searchConfigSchema>;
export type QueryConfigSchema = TypeOf<typeof queryConfigSchema>;
export type SearchSessionsConfigSchema = TypeOf<typeof searchSessionsConfigSchema>;

View file

@ -59,7 +59,9 @@ export class DataPublicPlugin
constructor(initializerContext: PluginInitializerContext<ConfigSchema>) {
this.searchService = new SearchService(initializerContext);
this.queryService = new QueryService();
this.queryService = new QueryService(
initializerContext.config.get().query.timefilter.minRefreshInterval
);
this.storage = new Storage(window.localStorage);
this.nowProvider = new NowProvider();

View file

@ -18,6 +18,8 @@ import { StubBrowserStorage } from '@kbn/test-jest-helpers';
import { TimefilterContract } from './timefilter';
import { createNowProviderMock } from '../now_provider/mocks';
const minRefreshIntervalDefault = 1000;
const setupMock = coreMock.createSetup();
const startMock = coreMock.createStart();
@ -48,6 +50,7 @@ describe('query_service', () => {
uiSettings: setupMock.uiSettings,
storage: new Storage(new StubBrowserStorage()),
nowProvider: createNowProviderMock(),
minRefreshInterval: minRefreshIntervalDefault,
});
queryServiceStart = queryService.start({
uiSettings: setupMock.uiSettings,
@ -82,7 +85,7 @@ describe('query_service', () => {
const filters = [getFilter(FilterStateStore.GLOBAL_STATE, true, true, 'key1', 'value1')];
const query = { language: 'kql', query: 'query' };
const time = { from: new Date().toISOString(), to: new Date().toISOString() };
const refreshInterval = { pause: false, value: 10 };
const refreshInterval = { pause: false, value: 2000 };
filterManager.setFilters(filters);
queryStringManager.setQuery(query);

View file

@ -39,6 +39,7 @@ interface QueryServiceSetupDependencies {
storage: IStorageWrapper;
uiSettings: IUiSettingsClient;
nowProvider: NowProviderInternalContract;
minRefreshInterval?: number;
}
interface QueryServiceStartDependencies {
@ -81,13 +82,21 @@ export class QueryService implements PersistableStateService<QueryState> {
state$!: QueryState$;
public setup({ storage, uiSettings, nowProvider }: QueryServiceSetupDependencies): QuerySetup {
constructor(private minRefreshInterval: number = 5000) {}
public setup({
storage,
uiSettings,
nowProvider,
minRefreshInterval = this.minRefreshInterval,
}: QueryServiceSetupDependencies): QuerySetup {
this.filterManager = new FilterManager(uiSettings);
const timefilterService = new TimefilterService(nowProvider);
this.timefilter = timefilterService.setup({
uiSettings,
storage,
minRefreshInterval,
});
this.queryStringManager = new QueryStringManager(storage, uiSettings);

View file

@ -66,7 +66,7 @@ describe('connect_to_global_state', () => {
let aF2: Filter;
beforeEach(() => {
const queryService = new QueryService();
const queryService = new QueryService(1000);
queryService.setup({
uiSettings: setupMock.uiSettings,
storage: new Storage(new StubBrowserStorage()),
@ -120,11 +120,21 @@ describe('connect_to_global_state', () => {
});
test('when refresh interval changes, state container contains updated refresh interval', () => {
const stop = connectToQueryGlobalState(queryServiceStart, globalState);
timeFilter.setRefreshInterval({ pause: true, value: 5000 });
expect(globalState.get().refreshInterval).toEqual({
pause: true,
value: 5000,
});
stop();
});
test('when refresh interval is set below min, state container contains min refresh interval', () => {
const stop = connectToQueryGlobalState(queryServiceStart, globalState);
timeFilter.setRefreshInterval({ pause: true, value: 100 });
expect(globalState.get().refreshInterval).toEqual({
pause: true,
value: 100,
value: 1000,
});
stop();
});
@ -135,14 +145,14 @@ describe('connect_to_global_state', () => {
globalState.set({
...globalState.get(),
filters: [gF1, gF2],
refreshInterval: { pause: true, value: 100 },
refreshInterval: { pause: true, value: 5000 },
time: { from: 'now-30m', to: 'now' },
});
expect(globalStateChangeTriggered).toBeCalledTimes(1);
expect(filterManager.getGlobalFilters()).toHaveLength(2);
expect(timeFilter.getRefreshInterval()).toEqual({ pause: true, value: 100 });
expect(timeFilter.getRefreshInterval()).toEqual({ pause: true, value: 5000 });
expect(timeFilter.getTime()).toEqual({ from: 'now-30m', to: 'now' });
stop();
});

View file

@ -25,6 +25,8 @@ import { syncQueryStateWithUrl } from './sync_state_with_url';
import { GlobalQueryStateFromUrl } from './types';
import { createNowProviderMock } from '../../now_provider/mocks';
const minRefreshIntervalDefault = 1000;
const setupMock = coreMock.createSetup();
const startMock = coreMock.createStart();
@ -65,6 +67,7 @@ describe('sync_query_state_with_url', () => {
uiSettings: setupMock.uiSettings,
storage: new Storage(new StubBrowserStorage()),
nowProvider: createNowProviderMock(),
minRefreshInterval: minRefreshIntervalDefault,
});
queryServiceStart = queryService.start({
uiSettings: startMock.uiSettings,
@ -93,7 +96,7 @@ describe('sync_query_state_with_url', () => {
filterManager.setFilters([gF, aF]);
kbnUrlStateStorage.kbnUrlControls.flush(); // sync force location change
expect(history.location.hash).toMatchInlineSnapshot(
`"#?_g=(filters:!(('$state':(store:globalState),meta:(alias:!n,disabled:!t,index:'logstash-*',key:query,negate:!t,type:custom,value:'%7B%22match%22:%7B%22key1%22:%22value1%22%7D%7D'),query:(match:(key1:value1)))),refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))"`
`"#?_g=(filters:!(('$state':(store:globalState),meta:(alias:!n,disabled:!t,index:'logstash-*',key:query,negate:!t,type:custom,value:'%7B%22match%22:%7B%22key1%22:%22value1%22%7D%7D'),query:(match:(key1:value1)))),refreshInterval:(pause:!t,value:1000),time:(from:now-15m,to:now))"`
);
stop();
});
@ -116,11 +119,21 @@ describe('sync_query_state_with_url', () => {
});
test('when refresh interval changes, refresh interval is synced to urlStorage', () => {
const { stop } = syncQueryStateWithUrl(queryServiceStart, kbnUrlStateStorage);
timefilter.setRefreshInterval({ pause: true, value: 5000 });
expect(kbnUrlStateStorage.get<GlobalQueryStateFromUrl>('_g')?.refreshInterval).toEqual({
pause: true,
value: 5000,
});
stop();
});
test('when refresh interval is set below min, refresh interval is not synced to urlStorage', () => {
const { stop } = syncQueryStateWithUrl(queryServiceStart, kbnUrlStateStorage);
timefilter.setRefreshInterval({ pause: true, value: 100 });
expect(kbnUrlStateStorage.get<GlobalQueryStateFromUrl>('_g')?.refreshInterval).toEqual({
pause: true,
value: 100,
value: minRefreshIntervalDefault,
});
stop();
});

View file

@ -17,15 +17,23 @@ import { RefreshInterval } from '../../../common';
import { createNowProviderMock } from '../../now_provider/mocks';
import { timefilterServiceMock } from './timefilter_service.mock';
const timefilterSetupMock = timefilterServiceMock.createSetupContract();
import { TimefilterConfig } from './types';
const timefilterConfig = {
const timefilterSetupMock = timefilterServiceMock.createSetupContract();
const minRefreshIntervalDefault = 1000;
const defaultTimefilterConfig: TimefilterConfig = {
timeDefaults: { from: 'now-15m', to: 'now' },
refreshIntervalDefaults: { pause: false, value: 0 },
refreshIntervalDefaults: { pause: false, value: minRefreshIntervalDefault },
minRefreshIntervalDefault,
};
const nowProviderMock = createNowProviderMock();
const timefilter = new Timefilter(timefilterConfig, timefilterSetupMock.history, nowProviderMock);
let timefilter = new Timefilter(
defaultTimefilterConfig,
timefilterSetupMock.history,
nowProviderMock
);
function stubNowTime(nowTime: any) {
nowProviderMock.get.mockImplementation(() => (nowTime ? new Date(nowTime) : new Date()));
@ -118,21 +126,28 @@ describe('setRefreshInterval', () => {
let fetchSub: Subscription;
let refreshSub: Subscription;
let autoRefreshSub: Subscription;
let prevTimefilter = timefilter;
beforeEach(() => {
function setup() {
update = sinon.spy();
fetch = sinon.spy();
autoRefreshFetch = sinon.spy((done) => done());
timefilter.setRefreshInterval({
pause: false,
value: 0,
value: minRefreshIntervalDefault,
});
refreshSub = timefilter.getRefreshIntervalUpdate$().subscribe(update);
fetchSub = timefilter.getFetch$().subscribe(fetch);
autoRefreshSub = timefilter.getAutoRefreshFetch$().subscribe(autoRefreshFetch);
}
beforeEach(() => {
prevTimefilter = timefilter;
setup();
});
afterEach(() => {
timefilter = prevTimefilter;
refreshSub.unsubscribe();
fetchSub.unsubscribe();
autoRefreshSub.unsubscribe();
@ -142,16 +157,50 @@ describe('setRefreshInterval', () => {
expect(timefilter.isRefreshIntervalTouched()).toBe(false);
});
test('should register changes to the initial interval', () => {
timefilter.setRefreshInterval(timefilterConfig.refreshIntervalDefaults);
test('should limit initial default value to minRefreshIntervalDefault', () => {
timefilter = new Timefilter(
{
...defaultTimefilterConfig,
refreshIntervalDefaults: { pause: false, value: minRefreshIntervalDefault - 100 },
},
timefilterSetupMock.history,
nowProviderMock
);
setup();
expect(timefilter.isRefreshIntervalTouched()).toBe(false);
timefilter.setRefreshInterval({ pause: false, value: 1000 });
expect(timefilter.getRefreshInterval()).toEqual({
pause: false,
value: minRefreshIntervalDefault,
});
});
test('should pause if initial value is set to 0 regardless of minRefreshInterval', () => {
timefilter = new Timefilter(
{
...defaultTimefilterConfig,
refreshIntervalDefaults: { pause: false, value: 0 },
},
timefilterSetupMock.history,
nowProviderMock
);
expect(timefilter.getRefreshInterval()).toEqual({
pause: true,
value: minRefreshIntervalDefault,
});
});
test('should register changes to the initial interval', () => {
timefilter.setRefreshInterval(defaultTimefilterConfig.refreshIntervalDefaults);
expect(timefilter.isRefreshIntervalTouched()).toBe(false);
timefilter.setRefreshInterval({ pause: false, value: 5000 });
expect(timefilter.isRefreshIntervalTouched()).toBe(true);
});
test('should update refresh interval', () => {
timefilter.setRefreshInterval({ pause: true, value: 10 });
expect(timefilter.getRefreshInterval()).toEqual({ pause: true, value: 10 });
timefilter.setRefreshInterval({ pause: true, value: 5000 });
expect(timefilter.getRefreshInterval()).toEqual({ pause: true, value: 5000 });
});
test('should not add unexpected object keys to refreshInterval state', () => {
@ -165,23 +214,40 @@ describe('setRefreshInterval', () => {
});
test('should allow partial updates to refresh interval', () => {
timefilter.setRefreshInterval({ value: 10 });
expect(timefilter.getRefreshInterval()).toEqual({ pause: true, value: 10 });
const { pause } = timefilter.getRefreshInterval();
timefilter.setRefreshInterval({ value: 5000 });
expect(timefilter.getRefreshInterval()).toEqual({ pause, value: 5000 });
});
test('should not allow negative intervals', () => {
timefilter.setRefreshInterval({ value: -10 });
expect(timefilter.getRefreshInterval()).toEqual({ pause: true, value: 0 });
expect(timefilter.getRefreshInterval()).toEqual({ pause: false, value: 1000 });
});
test('should set pause to true when interval is changed to zero from non-zero', () => {
timefilter = new Timefilter(
{
...defaultTimefilterConfig,
minRefreshIntervalDefault: 0,
},
timefilterSetupMock.history,
nowProviderMock
);
setup();
timefilter.setRefreshInterval({ value: 1000, pause: false });
timefilter.setRefreshInterval({ value: 0, pause: false });
expect(timefilter.getRefreshInterval()).toEqual({ pause: true, value: 0 });
});
test('should pause when interval is set to zero regardless of minRefreshInterval', () => {
timefilter.setRefreshInterval({ value: 5000, pause: false });
timefilter.setRefreshInterval({ value: 0 });
expect(timefilter.getRefreshInterval()).toEqual({ pause: true, value: 1000 });
});
test('not emit anything if nothing has changed', () => {
timefilter.setRefreshInterval({ pause: false, value: 0 });
timefilter.setRefreshInterval(timefilter.getRefreshInterval());
expect(update.called).toBe(false);
expect(fetch.called).toBe(false);
});
@ -192,7 +258,25 @@ describe('setRefreshInterval', () => {
expect(fetch.called).toBe(false);
});
test('should not emit update, nor fetch, when setting interval below min', () => {
const prevInterval = timefilter.getRefreshInterval();
timefilter.setRefreshInterval({ value: minRefreshIntervalDefault - 100 });
expect(update.called).toBe(false);
expect(fetch.called).toBe(false);
expect(timefilter.getRefreshInterval()).toEqual(prevInterval);
});
test('emit update, not fetch, when switching to value: 0', () => {
timefilter = new Timefilter(
{
...defaultTimefilterConfig,
minRefreshIntervalDefault: 0,
},
timefilterSetupMock.history,
nowProviderMock
);
setup();
timefilter.setRefreshInterval({ pause: false, value: 5000 });
expect(update.calledOnce).toBe(true);
expect(fetch.calledOnce).toBe(true);

View file

@ -27,7 +27,6 @@ import { createAutoRefreshLoop, AutoRefreshDoneFn } from './lib/auto_refresh_loo
export type { AutoRefreshDoneFn };
// TODO: remove!
export class Timefilter {
// Fired when isTimeRangeSelectorEnabled \ isAutoRefreshSelectorEnabled are toggled
private enabledUpdated$ = new BehaviorSubject(false);
@ -41,6 +40,7 @@ export class Timefilter {
// Denotes whether setTime has been called, can be used to determine if the constructor defaults are being used.
private _isTimeTouched: boolean = false;
private _refreshInterval!: RefreshInterval;
private _minRefreshInterval: number;
// Denotes whether the refresh interval defaults were overriden.
private _isRefreshIntervalTouched: boolean = false;
private _history: TimeHistoryContract;
@ -61,7 +61,15 @@ export class Timefilter {
) {
this._history = timeHistory;
this.timeDefaults = config.timeDefaults;
this.refreshIntervalDefaults = config.refreshIntervalDefaults;
// Initialize 0ms intervals with pause set to true and min value
this.refreshIntervalDefaults = {
pause:
config.refreshIntervalDefaults.value === 0 ? true : config.refreshIntervalDefaults.pause,
value: Math.max(config.refreshIntervalDefaults.value, config.minRefreshIntervalDefault),
};
this._minRefreshInterval = config.minRefreshIntervalDefault;
this._time = config.timeDefaults;
this.setRefreshInterval(config.refreshIntervalDefaults);
}
@ -148,6 +156,10 @@ export class Timefilter {
return _.clone(this._refreshInterval);
};
public getMinRefreshInterval = () => {
return this._minRefreshInterval;
};
/**
* Set timefilter refresh interval.
* @param {Object} refreshInterval
@ -157,6 +169,16 @@ export class Timefilter {
public setRefreshInterval = (refreshInterval: Partial<RefreshInterval>) => {
const prevRefreshInterval = this.getRefreshInterval();
const newRefreshInterval = { ...prevRefreshInterval, ...refreshInterval };
if (newRefreshInterval.value === 0) {
// override only when explicitly set to 0
newRefreshInterval.pause = true;
}
if (newRefreshInterval.value < this._minRefreshInterval) {
newRefreshInterval.value = this._minRefreshInterval;
}
let shouldUnpauseRefreshLoop =
newRefreshInterval.pause === false && prevRefreshInterval != null;
if (prevRefreshInterval?.value > 0 && newRefreshInterval.value <= 0) {

View file

@ -28,6 +28,7 @@ const createSetupContractMock = () => {
setTime: jest.fn(),
setRefreshInterval: jest.fn(),
getRefreshInterval: jest.fn(),
getMinRefreshInterval: jest.fn().mockReturnValue(1000),
getActiveBounds: jest.fn(),
disableAutoRefreshSelector: jest.fn(),
disableTimeRangeSelector: jest.fn(),

View file

@ -8,27 +8,38 @@
import { IUiSettingsClient } from '@kbn/core/public';
import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public';
import { TimeHistory, Timefilter, TimeHistoryContract, TimefilterContract } from '.';
import {
TimeHistory,
Timefilter,
TimeHistoryContract,
TimefilterContract,
TimefilterConfig,
} from '.';
import { UI_SETTINGS } from '../../../common';
import { NowProviderInternalContract } from '../../now_provider';
export interface TimeFilterServiceDependencies {
uiSettings: IUiSettingsClient;
storage: IStorageWrapper;
minRefreshInterval: number;
}
/**
* Filter Service
* @internal
*/
export interface TimeFilterServiceDependencies {
uiSettings: IUiSettingsClient;
storage: IStorageWrapper;
}
export class TimefilterService {
constructor(private readonly nowProvider: NowProviderInternalContract) {}
public setup({ uiSettings, storage }: TimeFilterServiceDependencies): TimefilterSetup {
const timefilterConfig = {
public setup({
uiSettings,
storage,
minRefreshInterval,
}: TimeFilterServiceDependencies): TimefilterSetup {
const timefilterConfig: TimefilterConfig = {
timeDefaults: uiSettings.get(UI_SETTINGS.TIMEPICKER_TIME_DEFAULTS),
refreshIntervalDefaults: uiSettings.get(UI_SETTINGS.TIMEPICKER_REFRESH_INTERVAL_DEFAULTS),
minRefreshIntervalDefault: minRefreshInterval,
};
const history = new TimeHistory(storage);
const timefilter = new Timefilter(timefilterConfig, history, this.nowProvider);

View file

@ -14,6 +14,7 @@ import { RefreshInterval } from '../../../common';
export interface TimefilterConfig {
timeDefaults: TimeRange;
refreshIntervalDefaults: RefreshInterval;
minRefreshIntervalDefault: number;
}
// Timefilter accepts moment input but always returns string output

View file

@ -107,6 +107,7 @@ export const config: PluginConfigDescriptor<ConfigSchema> = {
deprecations: configDeprecationProvider,
exposeToBrowser: {
search: true,
query: true,
},
schema: configSchema,
};

View file

@ -185,7 +185,7 @@ export function getDataStateContainer({
totalHits$: new BehaviorSubject<DataTotalHitsMsg>(initialState),
};
let autoRefreshDone: AutoRefreshDoneFn | undefined;
let autoRefreshDone: AutoRefreshDoneFn | undefined | null = null;
/**
* handler emitted by `timefilter.getAutoRefreshFetch$()`
* to notify when data completed loading and to start a new autorefresh loop
@ -304,8 +304,8 @@ export function getDataStateContainer({
// If the autoRefreshCallback is still the same as when we started i.e. there was no newer call
// replacing this current one, call it to make sure we tell that the auto refresh is done
// and a new one can be scheduled.
if (autoRefreshDone === prevAutoRefreshDone) {
// and a new one can be scheduled. null is checked to always start initial looping.
if (autoRefreshDone === prevAutoRefreshDone || prevAutoRefreshDone === null) {
// if this function was set and is executed, another refresh fetch can be triggered
autoRefreshDone?.();
autoRefreshDone = undefined;

View file

@ -141,7 +141,7 @@ export const getSavedObjects = (): SavedObject[] => [
description: 'Analyze mock eCommerce orders and revenue',
refreshInterval: {
pause: true,
value: 0,
value: 60000,
},
timeRestore: true,
optionsJSON:

View file

@ -141,7 +141,7 @@ export const getSavedObjects = (): SavedObject[] => [
'Analyze mock flight data for ES-Air, Logstash Airways, Kibana Airlines and JetBeats',
refreshInterval: {
pause: true,
value: 0,
value: 60000,
},
timeRestore: true,
optionsJSON:

View file

@ -305,8 +305,8 @@ export const getSavedObjects = (): SavedObject[] => [
},
description: "Analyze mock web traffic log data for Elastic's website",
refreshInterval: {
pause: false,
value: 900000,
pause: true,
value: 60000,
},
timeRestore: true,
optionsJSON:

View file

@ -79,7 +79,7 @@ export function TopNavMenu<QT extends AggregateQuery | Query = Query>(
}
function renderSearchBar(): ReactElement | null {
// Validate presense of all required fields
// Validate presence of all required fields
if (!showSearchBar || !props.unifiedSearch) return null;
const { AggregateQuerySearchBar } = props.unifiedSearch.ui;
return <AggregateQuerySearchBar<QT> {...searchBarProps} />;

View file

@ -152,6 +152,7 @@ export interface QueryBarTopRowProps<QT extends Query | AggregateQuery = Query>
prepend?: React.ComponentProps<typeof EuiFieldText>['prepend'];
query?: Query | QT;
refreshInterval?: number;
minRefreshInterval?: number;
screenTitle?: string;
showQueryInput?: boolean;
showAddFilter?: boolean;
@ -503,6 +504,7 @@ export const QueryBarTopRow = React.memo(
end={props.dateRangeTo}
isPaused={props.isRefreshPaused}
refreshInterval={props.refreshInterval}
refreshMinInterval={props.minRefreshInterval}
onTimeChange={onTimeChange}
onRefresh={onRefresh}
onRefreshChange={props.onRefreshChange}

View file

@ -171,7 +171,7 @@ export function createSearchBar({
query: props.query,
queryStringManager: data.query.queryString,
}) as { query: QT };
const { timeRange, refreshInterval } = useTimefilter({
const { timeRange, refreshInterval, minRefreshInterval } = useTimefilter({
dateRangeFrom: props.dateRangeFrom,
dateRangeTo: props.dateRangeTo,
refreshInterval: props.refreshInterval,
@ -232,6 +232,7 @@ export function createSearchBar({
timeHistory={data.query.timefilter.history}
dateRangeFrom={timeRange.from}
dateRangeTo={timeRange.to}
minRefreshInterval={minRefreshInterval}
refreshInterval={refreshInterval.value}
isRefreshPaused={refreshInterval.pause}
isLoading={props.isLoading}

View file

@ -59,5 +59,6 @@ export const useTimefilter = (props: UseTimefilterProps) => {
return {
refreshInterval,
timeRange,
minRefreshInterval: props.timefilter.getMinRefreshInterval(),
};
};

View file

@ -79,6 +79,7 @@ export interface SearchBarOwnProps<QT extends AggregateQuery | Query = Query> {
// Date picker
isRefreshPaused?: boolean;
refreshInterval?: number;
minRefreshInterval?: number;
dateRangeFrom?: string;
dateRangeTo?: string;
// Query bar - should be in SearchBarInjectedDeps
@ -619,6 +620,7 @@ class SearchBarUI<QT extends (Query | AggregateQuery) | Query = Query> extends C
dateRangeTo={this.state.dateRangeTo}
isRefreshPaused={this.props.isRefreshPaused}
refreshInterval={this.props.refreshInterval}
minRefreshInterval={this.props.minRefreshInterval}
showAutoRefreshOnly={this.props.showAutoRefreshOnly}
showQueryInput={this.props.showQueryInput}
showAddFilter={this.props.showFilterBar}

View file

@ -42,6 +42,7 @@ export default function () {
`--elasticsearch.password=${kibanaServerTestUser.password}`,
// Needed for async search functional tests to introduce a delay
`--data.search.aggs.shardDelay.enabled=true`,
`--data.query.timefilter.minRefreshInterval=1000`,
`--security.showInsecureClusterWarning=false`,
'--telemetry.banner=false',
'--telemetry.optIn=false',

View file

@ -123,6 +123,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
'data.search.sessions.management.refreshTimeout (duration?)',
'data.search.sessions.maxUpdateRetries (number?)',
'data.search.sessions.notTouchedTimeout (duration?)',
'data.query.timefilter.minRefreshInterval (number?)',
'data_views.scriptedFieldsEnabled (boolean?|never)',
'data_visualizer.resultLinks.fileBeat.enabled (boolean)',
'dev_tools.deeplinks.navLinkStatus (string?)',

View file

@ -8,6 +8,7 @@
import { FilterQueryContextProvider, useFilterQueryUpdates } from './use_filters_query';
import { act, renderHook } from '@testing-library/react-hooks';
import { dataPluginMock as mockDataPlugin } from '@kbn/data-plugin/public/mocks';
import type { TimefilterConfig } from '@kbn/data-plugin/public/query';
import { Timefilter } from '@kbn/data-plugin/public/query';
import { useAiopsAppContext } from './use_aiops_app_context';
import { useReload } from './use_reload';
@ -43,9 +44,10 @@ describe('useFilterQueryUpdates', () => {
test('provides correct search bounds for relative time range on each reload', async () => {
const mockDataContract = mockDataPlugin.createStartContract();
const mockTimefilterConfig = {
const mockTimefilterConfig: TimefilterConfig = {
timeDefaults: { from: 'now-15m', to: 'now' },
refreshIntervalDefaults: { pause: false, value: 0 },
minRefreshIntervalDefault: 1000,
};
useAiopsAppContext().data.query.timefilter.timefilter = new Timefilter(

View file

@ -61,7 +61,7 @@ import { alertingPluginMock } from '@kbn/alerting-plugin/public/mocks';
const mockUiSettings: Record<string, unknown> = {
[DEFAULT_TIME_RANGE]: { from: 'now-15m', to: 'now', mode: 'quick' },
[DEFAULT_REFRESH_RATE_INTERVAL]: { pause: false, value: 0 },
[DEFAULT_REFRESH_RATE_INTERVAL]: { pause: true, value: 5000 },
[DEFAULT_APP_TIME_RANGE]: {
from: DEFAULT_FROM,
to: DEFAULT_TO,

View file

@ -70,16 +70,21 @@ export class PluginServices {
{ prebuiltRulesPackageVersion: this.prebuiltRulesPackageVersion }
);
const minRefreshInterval =
pluginsSetup.data.query.timefilter.timefilter.getMinRefreshInterval();
this.queryService.setup({
uiSettings: coreSetup.uiSettings,
storage: this.storage,
nowProvider: new NowProvider(),
minRefreshInterval,
});
this.timelineQueryService.setup({
uiSettings: coreSetup.uiSettings,
storage: this.storage,
nowProvider: new NowProvider(),
minRefreshInterval,
});
}

View file

@ -9,7 +9,7 @@ import type { Observable } from 'rxjs';
import type { CoreStart, AppMountParameters, AppLeaveHandler } from '@kbn/core/public';
import type { HomePublicPluginSetup } from '@kbn/home-plugin/public';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { DataPublicPluginStart, DataPublicPluginSetup } from '@kbn/data-plugin/public';
import type { FieldFormatsStartCommon } from '@kbn/field-formats-plugin/common';
import type { EmbeddableStart } from '@kbn/embeddable-plugin/public';
import type { LensPublicStart } from '@kbn/lens-plugin/public';
@ -103,6 +103,7 @@ export interface SetupPlugins {
usageCollection?: UsageCollectionSetup;
ml?: MlPluginSetup;
cases?: CasesPublicSetup;
data: DataPublicPluginSetup;
}
/**