[7.x] Timefilter - TypeScript (#43770) (#44283)

* Timefilter - TypeScript (#43770)

* Typescriptifying

* More TS

* define InputTimeRange type

* fix import path

* change import path to stabilize tests
This commit is contained in:
Liza Katz 2019-08-29 16:04:13 +03:00 committed by GitHub
parent 909dd21e00
commit bcf570dc22
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 226 additions and 259 deletions

View file

@ -23,26 +23,27 @@ import { DashboardStateManager } from './dashboard_state_manager';
import { getAppStateMock, getSavedDashboardMock } from './__tests__';
import { AppStateClass } from 'ui/state_management/app_state';
import { DashboardAppState } from './types';
import { Timefilter } from 'ui/timefilter';
import { Timefilter, TimeRange } from 'ui/timefilter';
import { ViewMode } from '../../../embeddable_api/public/np_ready/public';
describe('DashboardState', function() {
let dashboardState: DashboardStateManager;
const savedDashboard = getSavedDashboardMock();
const mockTimefilter: Timefilter = {
time: { to: 'now', from: 'now-15m' },
let mockTime: TimeRange = { to: 'now', from: 'now-15m' };
const mockTimefilter: Partial<Timefilter> = {
setTime(time) {
this.time = time;
mockTime = time as TimeRange;
},
getTime() {
return this.time;
return mockTime;
},
disableAutoRefreshSelector: jest.fn(),
setRefreshInterval: jest.fn(),
getRefreshInterval: jest.fn(),
disableTimeRangeSelector: jest.fn(),
enableAutoRefreshSelector: jest.fn(),
getActiveBounds: () => {},
getActiveBounds: jest.fn(),
enableTimeRangeSelector: () => {},
isAutoRefreshSelectorEnabled: true,
isTimeRangeSelectorEnabled: true,
@ -67,14 +68,14 @@ describe('DashboardState', function() {
savedDashboard.timeFrom = 'now/w';
savedDashboard.timeTo = 'now/w';
mockTimefilter.time.from = '2015-09-19 06:31:44.000';
mockTimefilter.time.to = '2015-09-29 06:31:44.000';
mockTime.from = '2015-09-19 06:31:44.000';
mockTime.to = '2015-09-29 06:31:44.000';
initDashboardState();
dashboardState.syncTimefilterWithDashboard(mockTimefilter);
dashboardState.syncTimefilterWithDashboard(mockTimefilter as Timefilter);
expect(mockTimefilter.time.to).toBe('now/w');
expect(mockTimefilter.time.from).toBe('now/w');
expect(mockTime.to).toBe('now/w');
expect(mockTime.from).toBe('now/w');
});
test('syncs relative time', function() {
@ -82,14 +83,14 @@ describe('DashboardState', function() {
savedDashboard.timeFrom = 'now-13d';
savedDashboard.timeTo = 'now';
mockTimefilter.time.from = '2015-09-19 06:31:44.000';
mockTimefilter.time.to = '2015-09-29 06:31:44.000';
mockTime.from = '2015-09-19 06:31:44.000';
mockTime.to = '2015-09-29 06:31:44.000';
initDashboardState();
dashboardState.syncTimefilterWithDashboard(mockTimefilter);
dashboardState.syncTimefilterWithDashboard(mockTimefilter as Timefilter);
expect(mockTimefilter.time.to).toBe('now');
expect(mockTimefilter.time.from).toBe('now-13d');
expect(mockTime.to).toBe('now');
expect(mockTime.from).toBe('now-13d');
});
test('syncs absolute time', function() {
@ -97,14 +98,14 @@ describe('DashboardState', function() {
savedDashboard.timeFrom = '2015-09-19 06:31:44.000';
savedDashboard.timeTo = '2015-09-29 06:31:44.000';
mockTimefilter.time.from = 'now/w';
mockTimefilter.time.to = 'now/w';
mockTime.from = 'now/w';
mockTime.to = 'now/w';
initDashboardState();
dashboardState.syncTimefilterWithDashboard(mockTimefilter);
dashboardState.syncTimefilterWithDashboard(mockTimefilter as Timefilter);
expect(mockTimefilter.time.to).toBe(savedDashboard.timeTo);
expect(mockTimefilter.time.from).toBe(savedDashboard.timeFrom);
expect(mockTime.to).toBe(savedDashboard.timeTo);
expect(mockTime.from).toBe(savedDashboard.timeFrom);
});
});

View file

@ -19,7 +19,7 @@
import dateMath from '@elastic/datemath';
import { Field, IndexPattern } from 'ui/index_patterns';
import { TimeRange } from './time_history';
import { TimeRange } from 'src/plugins/data/public';
interface CalculateBoundsOptions {
forceNow?: Date;

View file

@ -17,9 +17,8 @@
* under the License.
*/
// @ts-ignore
export { registerTimefilterWithGlobalState } from './timefilter';
export { timefilter, Timefilter, RefreshInterval } from './timefilter';
export { timeHistory, TimeRange, TimeHistory } from './time_history';
export { TimeRange, RefreshInterval } from '../../../../plugins/data/public';
export { timefilter, Timefilter, registerTimefilterWithGlobalState } from './timefilter';
export { timeHistory, TimeHistory } from './time_history';
export { getTime } from './get_time';

View file

@ -17,42 +17,35 @@
* under the License.
*/
import moment from 'moment';
import expect from '@kbn/expect';
import { areTimePickerValsDifferent } from './diff_time_picker_vals';
import { areTimeRangesDifferent } from './diff_time_picker_vals';
describe('Diff Time Picker Values', () => {
test('accepts two undefined values', () => {
const diff = areTimePickerValsDifferent(undefined, undefined);
expect(diff).to.be(false);
});
describe('dateMath ranges', () => {
test('knows a match', () => {
const diff = areTimePickerValsDifferent(
const diff = areTimeRangesDifferent(
{
to: 'now',
from: 'now-7d'
from: 'now-7d',
},
{
to: 'now',
from: 'now-7d'
from: 'now-7d',
}
);
expect(diff).to.be(false);
});
test('knows a difference', () => {
const diff = areTimePickerValsDifferent(
const diff = areTimeRangesDifferent(
{
to: 'now',
from: 'now-7d'
from: 'now-7d',
},
{
to: 'now',
from: 'now-1h'
from: 'now-1h',
}
);
@ -62,14 +55,14 @@ describe('Diff Time Picker Values', () => {
describe('a dateMath range, and a moment range', () => {
test('is always different', () => {
const diff = areTimePickerValsDifferent(
const diff = areTimeRangesDifferent(
{
to: moment(),
from: moment()
from: moment(),
},
{
to: 'now',
from: 'now-1h'
from: 'now-1h',
}
);
@ -82,14 +75,14 @@ describe('Diff Time Picker Values', () => {
const to = moment();
const from = moment().add(1, 'day');
const diff = areTimePickerValsDifferent(
const diff = areTimeRangesDifferent(
{
to: to.clone(),
from: from.clone()
from: from.clone(),
},
{
to: to.clone(),
from: from.clone()
from: from.clone(),
}
);
@ -101,23 +94,18 @@ describe('Diff Time Picker Values', () => {
const from = moment().add(1, 'day');
const from2 = moment().add(2, 'day');
const diff = areTimePickerValsDifferent(
const diff = areTimeRangesDifferent(
{
to: to.clone(),
from: from.clone()
from: from.clone(),
},
{
to: to.clone(),
from: from2.clone()
from: from2.clone(),
}
);
expect(diff).to.be(true);
});
});
test('does not fall apart with unusual values', () => {
const diff = areTimePickerValsDifferent({}, {});
expect(diff).to.be(false);
});
});

View file

@ -19,17 +19,33 @@
import _ from 'lodash';
const valueOf = function (o) {
import { RefreshInterval } from 'src/plugins/data/public';
import { InputTimeRange } from '../timefilter';
const valueOf = function(o: any) {
if (o) return o.valueOf();
};
export function areTimePickerValsDifferent(rangeA, rangeB) {
export function areRefreshIntervalsDifferent(rangeA: RefreshInterval, rangeB: RefreshInterval) {
if (_.isObject(rangeA) && _.isObject(rangeB)) {
if (
valueOf(rangeA.to) !== valueOf(rangeB.to)
|| valueOf(rangeA.from) !== valueOf(rangeB.from)
|| valueOf(rangeA.value) !== valueOf(rangeB.value)
|| valueOf(rangeA.pause) !== valueOf(rangeB.pause)
valueOf(rangeA.value) !== valueOf(rangeB.value) ||
valueOf(rangeA.pause) !== valueOf(rangeB.pause)
) {
return true;
}
} else {
return !_.isEqual(rangeA, rangeB);
}
return false;
}
export function areTimeRangesDifferent(rangeA: InputTimeRange, rangeB: InputTimeRange) {
if (rangeA && rangeB && _.isObject(rangeA) && _.isObject(rangeB)) {
if (
valueOf(rangeA.to) !== valueOf(rangeB.to) ||
valueOf(rangeA.from) !== valueOf(rangeB.from)
) {
return true;
}

View file

@ -1,29 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { TimeRange } from '../../../../plugins/data/public';
export { TimeRange };
export interface TimeHistory {
add: (options: TimeRange) => void;
get: () => TimeRange[];
}
export const timeHistory: TimeHistory;

View file

@ -18,21 +18,24 @@
*/
import moment from 'moment';
import { TimeRange } from 'src/plugins/data/public';
import { PersistedLog } from '../persisted_log';
class TimeHistory {
export class TimeHistory {
private history: PersistedLog;
constructor() {
const historyOptions = {
maxLength: 10,
filterDuplicates: true,
isDuplicate: (oldItem, newItem) => {
isDuplicate: (oldItem: TimeRange, newItem: TimeRange) => {
return oldItem.from === newItem.from && oldItem.to === newItem.to;
}
},
};
this.history = new PersistedLog('kibana.timepicker.timeHistory', historyOptions);
}
add(time) {
add(time: TimeRange) {
if (!time) {
return;
}
@ -40,7 +43,7 @@ class TimeHistory {
// time from/to can be strings or moment objects - convert to strings so always dealing with same types
const justStringsTime = {
from: moment.isMoment(time.from) ? time.from.toISOString() : time.from,
to: moment.isMoment(time.to) ? time.to.toISOString() : time.to
to: moment.isMoment(time.to) ? time.to.toISOString() : time.to,
};
this.history.add(justStringsTime);
}

View file

@ -1,49 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { Moment } from 'moment';
import { Observable } from 'rxjs';
import { TimeRange } from './time_history';
import { RefreshInterval } from '../../../../plugins/data/public';
// NOTE: These types are somewhat guessed, they may be incorrect.
export { RefreshInterval, TimeRange };
export interface Timefilter {
time: TimeRange;
getEnabledUpdated$: () => Observable<any>;
getTimeUpdate$: () => Observable<any>;
getRefreshIntervalUpdate$: () => Observable<any>;
getAutoRefreshFetch$: () => Observable<any>;
getFetch$: () => Observable<any>;
getTime: () => TimeRange;
setTime: (timeRange: TimeRange) => void;
setRefreshInterval: (refreshInterval: RefreshInterval) => void;
getRefreshInterval: () => RefreshInterval;
getActiveBounds: () => void;
disableAutoRefreshSelector: () => void;
disableTimeRangeSelector: () => void;
enableAutoRefreshSelector: () => void;
enableTimeRangeSelector: () => void;
isAutoRefreshSelectorEnabled: boolean;
isTimeRangeSelectorEnabled: boolean;
}
export const timefilter: Timefilter;

View file

@ -19,12 +19,13 @@
import './timefilter.test.mocks';
jest.mock('ui/chrome',
jest.mock(
'ui/chrome',
() => ({
getBasePath: () => `/some/base/path`,
getUiSettingsClient: () => {
return {
get: (key) => {
get: (key: string) => {
switch (key) {
case 'timepicker:timeDefaults':
return { from: 'now-15m', to: 'now' };
@ -33,46 +34,56 @@ jest.mock('ui/chrome',
default:
throw new Error(`Unexpected config key: ${key}`);
}
}
},
};
},
}), { virtual: true });
}),
{ virtual: true }
);
jest.mock('ui/timefilter/lib/parse_querystring',
jest.mock(
'ui/timefilter/lib/parse_querystring',
() => ({
parseQueryString: () => {
return {
// Can not access local variable from within a mock
forceNow: global.nowTime
// @ts-ignore
forceNow: global.nowTime,
};
},
}), { virtual: true });
}),
{ virtual: true }
);
import sinon from 'sinon';
import expect from '@kbn/expect';
import moment from 'moment';
import { timefilter } from './timefilter';
import { Subscription } from 'rxjs';
import { TimeRange, RefreshInterval } from 'src/plugins/data/public';
function stubNowTime(nowTime) {
function stubNowTime(nowTime: any) {
// @ts-ignore
global.nowTime = nowTime;
}
function clearNowTimeStub() {
// @ts-ignore
delete global.nowTime;
}
describe('setTime', () => {
let update;
let fetch;
let updateSub;
let fetchSub;
let update: sinon.SinonSpy;
let fetch: sinon.SinonSpy;
let updateSub: Subscription;
let fetchSub: Subscription;
beforeEach(() => {
update = sinon.spy();
fetch = sinon.spy();
timefilter.setTime({
from: 0,
to: 1,
from: '0',
to: '1',
});
updateSub = timefilter.getTimeUpdate$().subscribe(update);
fetchSub = timefilter.getFetch$().subscribe(fetch);
@ -84,29 +95,36 @@ describe('setTime', () => {
});
test('should update time', () => {
timefilter.setTime({ from: 5, to: 10 });
expect(timefilter.getTime()).to.eql({ from: 5, to: 10 });
timefilter.setTime({ from: '5', to: '10' });
expect(timefilter.getTime()).to.eql({
from: '5',
to: '10',
});
});
test('should not add unexpected object keys to time state', () => {
const unexpectedKey = 'unexpectedKey';
timefilter.setTime({ from: 5, to: 10, [unexpectedKey]: 'I should not be added to time state' });
timefilter.setTime({
from: '5',
to: '10',
[unexpectedKey]: 'I should not be added to time state',
} as TimeRange);
expect(timefilter.getTime()).not.to.have.property(unexpectedKey);
});
test('should allow partial updates to time', () => {
timefilter.setTime({ from: 5, to: 10 });
expect(timefilter.getTime()).to.eql({ from: 5, to: 10 });
timefilter.setTime({ from: '5', to: '10' });
expect(timefilter.getTime()).to.eql({ from: '5', to: '10' });
});
test('not emit anything if the time has not changed', () => {
timefilter.setTime({ from: 0, to: 1 });
timefilter.setTime({ from: '0', to: '1' });
expect(update.called).to.be(false);
expect(fetch.called).to.be(false);
});
test('emit update and fetch if the time has changed', () => {
timefilter.setTime({ from: 5, to: 10 });
timefilter.setTime({ from: '5', to: '10' });
expect(update.called).to.be(true);
expect(fetch.called).to.be(true);
});
@ -123,17 +141,17 @@ describe('setTime', () => {
});
describe('setRefreshInterval', () => {
let update;
let fetch;
let fetchSub;
let refreshSub;
let update: sinon.SinonSpy;
let fetch: sinon.SinonSpy;
let fetchSub: Subscription;
let refreshSub: Subscription;
beforeEach(() => {
update = sinon.spy();
fetch = sinon.spy();
timefilter.setRefreshInterval({
pause: false,
value: 0
value: 0,
});
refreshSub = timefilter.getRefreshIntervalUpdate$().subscribe(update);
fetchSub = timefilter.getFetch$().subscribe(fetch);
@ -151,7 +169,11 @@ describe('setRefreshInterval', () => {
test('should not add unexpected object keys to refreshInterval state', () => {
const unexpectedKey = 'unexpectedKey';
timefilter.setRefreshInterval({ pause: true, value: 10, [unexpectedKey]: 'I should not be added to refreshInterval state' });
timefilter.setRefreshInterval({
pause: true,
value: 10,
[unexpectedKey]: 'I should not be added to refreshInterval state',
} as RefreshInterval);
expect(timefilter.getRefreshInterval()).not.to.have.property(unexpectedKey);
});
@ -211,12 +233,11 @@ describe('setRefreshInterval', () => {
expect(update.calledTwice).to.be(true);
expect(fetch.calledOnce).to.be(true);
});
});
describe('isTimeRangeSelectorEnabled', () => {
let update;
let updateSub;
let update: sinon.SinonSpy;
let updateSub: Subscription;
beforeEach(() => {
update = sinon.spy();
@ -241,8 +262,8 @@ describe('isTimeRangeSelectorEnabled', () => {
});
describe('isAutoRefreshSelectorEnabled', () => {
let update;
let updateSub;
let update: sinon.SinonSpy;
let updateSub: Subscription;
beforeEach(() => {
update = sinon.spy();
@ -267,11 +288,10 @@ describe('isAutoRefreshSelectorEnabled', () => {
});
describe('calculateBounds', () => {
const fifteenMinutesInMilliseconds = 15 * 60 * 1000;
const clockNowTicks = new Date(2000, 1, 1, 0, 0, 0, 0).valueOf();
let clock;
let clock: sinon.SinonFakeTimers;
beforeEach(() => {
clock = sinon.useFakeTimers(clockNowTicks);
@ -285,19 +305,19 @@ describe('calculateBounds', () => {
test('uses clock time by default', () => {
const timeRange = {
from: 'now-15m',
to: 'now'
to: 'now',
};
stubNowTime(undefined);
const result = timefilter.calculateBounds(timeRange);
expect(result.min.valueOf()).to.eql(clockNowTicks - fifteenMinutesInMilliseconds);
expect(result.max.valueOf()).to.eql(clockNowTicks);
expect(result.min && result.min.valueOf()).to.eql(clockNowTicks - fifteenMinutesInMilliseconds);
expect(result.max && result.max.valueOf()).to.eql(clockNowTicks);
});
test('uses forceNow string', () => {
const timeRange = {
from: 'now-15m',
to: 'now'
to: 'now',
};
const forceNowString = '1999-01-01T00:00:00.000Z';
@ -305,18 +325,17 @@ describe('calculateBounds', () => {
const result = timefilter.calculateBounds(timeRange);
const forceNowTicks = Date.parse(forceNowString);
expect(result.min.valueOf()).to.eql(forceNowTicks - fifteenMinutesInMilliseconds);
expect(result.max.valueOf()).to.eql(forceNowTicks);
expect(result.min && result.min.valueOf()).to.eql(forceNowTicks - fifteenMinutesInMilliseconds);
expect(result.max && result.max.valueOf()).to.eql(forceNowTicks);
});
test(`throws Error if forceNow can't be parsed`, () => {
const timeRange = {
from: 'now-15m',
to: 'now'
to: 'now',
};
stubNowTime('not_a_parsable_date');
expect(() => timefilter.calculateBounds(timeRange)).to.throwError();
});
});

View file

@ -19,67 +19,77 @@
import _ from 'lodash';
import { Subject, BehaviorSubject } from 'rxjs';
import moment from 'moment';
import { calculateBounds, getTime } from './get_time';
import { parseQueryString } from './lib/parse_querystring';
import moment, { Moment } from 'moment';
import { subscribeWithScope } from 'ui/utils/subscribe_with_scope';
import uiRoutes from '../routes';
import chrome from 'ui/chrome';
import { areTimePickerValsDifferent } from './lib/diff_time_picker_vals';
import { UiSettingsClientContract } from 'src/core/public';
import { RefreshInterval, TimeRange } from 'src/plugins/data/public';
import { IndexPattern } from 'src/legacy/core_plugins/data/public';
import { IScope } from 'angular';
import { timeHistory } from './time_history';
import { areRefreshIntervalsDifferent, areTimeRangesDifferent } from './lib/diff_time_picker_vals';
import uiRoutes from '../routes';
import { parseQueryString } from './lib/parse_querystring';
import { calculateBounds, getTime } from './get_time';
class Timefilter {
constructor() {
// Timefilter accepts moment input but always returns string output
export type InputTimeRange =
| TimeRange
| {
from: Moment;
to: Moment;
};
// Fired when isTimeRangeSelectorEnabled \ isAutoRefreshSelectorEnabled are toggled
this.enabledUpdated$ = new BehaviorSubject();
export class Timefilter {
// Fired when isTimeRangeSelectorEnabled \ isAutoRefreshSelectorEnabled are toggled
private enabledUpdated$ = new BehaviorSubject(false);
// Fired when a user changes the timerange
private timeUpdate$ = new Subject();
// Fired when a user changes the the autorefresh settings
private refreshIntervalUpdate$ = new Subject();
// Used when search poll triggers an auto refresh
private autoRefreshFetch$ = new Subject();
private fetch$ = new Subject();
// Fired when a user changes the timerange
this.timeUpdate$ = new Subject();
private _time: TimeRange;
private _refreshInterval!: RefreshInterval;
// Fired when a user changes the the autorefresh settings
this.refreshIntervalUpdate$ = new Subject();
public isTimeRangeSelectorEnabled: boolean = false;
public isAutoRefreshSelectorEnabled: boolean = false;
// Used when search poll triggers an auto refresh
this.autoRefreshFetch$ = new Subject();
this.fetch$ = new Subject();
this.isTimeRangeSelectorEnabled = false;
this.isAutoRefreshSelectorEnabled = false;
this._time = chrome.getUiSettingsClient().get('timepicker:timeDefaults');
this.setRefreshInterval(chrome.getUiSettingsClient().get('timepicker:refreshIntervalDefaults'));
constructor(uiSettings: UiSettingsClientContract) {
this._time = uiSettings.get('timepicker:timeDefaults');
this.setRefreshInterval(uiSettings.get('timepicker:refreshIntervalDefaults'));
}
getEnabledUpdated$ = () => {
return this.enabledUpdated$.asObservable();
}
};
getTimeUpdate$ = () => {
return this.timeUpdate$.asObservable();
}
};
getRefreshIntervalUpdate$ = () => {
return this.refreshIntervalUpdate$.asObservable();
}
};
getAutoRefreshFetch$ = () => {
return this.autoRefreshFetch$.asObservable();
}
};
getFetch$ = () => {
return this.fetch$.asObservable();
}
};
getTime = () => {
getTime = (): TimeRange => {
const { from, to } = this._time;
return {
...this._time,
from: moment.isMoment(from) ? from.toISOString() : from,
to: moment.isMoment(to) ? to.toISOString() : to
to: moment.isMoment(to) ? to.toISOString() : to,
};
}
};
/**
* Updates timefilter time.
@ -88,10 +98,10 @@ class Timefilter {
* @property {string|moment} time.from
* @property {string|moment} time.to
*/
setTime = (time) => {
setTime = (time: InputTimeRange) => {
// Object.assign used for partially composed updates
const newTime = Object.assign(this.getTime(), time);
if (areTimePickerValsDifferent(this.getTime(), newTime)) {
if (areTimeRangesDifferent(this.getTime(), newTime)) {
this._time = {
from: newTime.from,
to: newTime.to,
@ -100,11 +110,11 @@ class Timefilter {
this.timeUpdate$.next();
this.fetch$.next();
}
}
};
getRefreshInterval = () => {
return _.clone(this._refreshInterval);
}
};
/**
* Set timefilter refresh interval.
@ -112,7 +122,7 @@ class Timefilter {
* @property {number} time.value Refresh interval in milliseconds. Positive integer
* @property {boolean} time.pause
*/
setRefreshInterval = (refreshInterval) => {
setRefreshInterval = (refreshInterval: Partial<RefreshInterval>) => {
const prevRefreshInterval = this.getRefreshInterval();
const newRefreshInterval = { ...prevRefreshInterval, ...refreshInterval };
// If the refresh interval is <= 0 handle that as a paused refresh
@ -122,32 +132,38 @@ class Timefilter {
}
this._refreshInterval = {
value: newRefreshInterval.value,
pause: newRefreshInterval.pause
pause: newRefreshInterval.pause,
};
// Only send out an event if we already had a previous refresh interval (not for the initial set)
// and the old and new refresh interval are actually different.
if (prevRefreshInterval && areTimePickerValsDifferent(prevRefreshInterval, newRefreshInterval)) {
if (
prevRefreshInterval &&
areRefreshIntervalsDifferent(prevRefreshInterval, newRefreshInterval)
) {
this.refreshIntervalUpdate$.next();
if (!newRefreshInterval.pause && newRefreshInterval.value !== 0) {
this.fetch$.next();
}
}
}
};
toggleRefresh = () => {
this.setRefreshInterval({ pause: !this._refreshInterval.pause });
}
this.setRefreshInterval({
pause: !this._refreshInterval.pause,
value: this._refreshInterval.value,
});
};
createFilter = (indexPattern, timeRange) => {
createFilter = (indexPattern: IndexPattern, timeRange: TimeRange) => {
return getTime(indexPattern, timeRange ? timeRange : this._time, this.getForceNow());
}
};
getBounds = () => {
return this.calculateBounds(this._time);
}
};
getForceNow = () => {
const forceNow = parseQueryString().forceNow;
const forceNow = parseQueryString().forceNow as string;
if (!forceNow) {
return;
}
@ -157,82 +173,87 @@ class Timefilter {
throw new Error(`forceNow query parameter, ${forceNow}, can't be parsed by Date.parse`);
}
return new Date(ticks);
}
};
calculateBounds = (timeRange) => {
calculateBounds = (timeRange: TimeRange) => {
return calculateBounds(timeRange, { forceNow: this.getForceNow() });
}
};
getActiveBounds = () => {
if (this.isTimeRangeSelectorEnabled) {
return this.getBounds();
}
}
};
/**
* Show the time bounds selector part of the time filter
*/
enableTimeRangeSelector = () => {
this.isTimeRangeSelectorEnabled = true;
this.enabledUpdated$.next();
}
this.enabledUpdated$.next(true);
};
/**
* Hide the time bounds selector part of the time filter
*/
disableTimeRangeSelector = () => {
this.isTimeRangeSelectorEnabled = false;
this.enabledUpdated$.next();
}
this.enabledUpdated$.next(false);
};
/**
* Show the auto refresh part of the time filter
*/
enableAutoRefreshSelector = () => {
this.isAutoRefreshSelectorEnabled = true;
this.enabledUpdated$.next();
}
this.enabledUpdated$.next(true);
};
/**
* Hide the auto refresh part of the time filter
*/
disableAutoRefreshSelector = () => {
this.isAutoRefreshSelectorEnabled = false;
this.enabledUpdated$.next();
}
this.enabledUpdated$.next(false);
};
notifyShouldFetch = () => {
this.autoRefreshFetch$.next();
}
};
}
export const timefilter = new Timefilter();
export const timefilter = new Timefilter(chrome.getUiSettingsClient());
// TODO
// remove everything underneath once globalState is no longer an angular service
// and listener can be registered without angular.
function convertISO8601(stringTime) {
function convertISO8601(stringTime: string): string {
const obj = moment(stringTime, 'YYYY-MM-DDTHH:mm:ss.SSSZ', true);
return obj.isValid() ? obj : stringTime;
return obj.isValid() ? obj.toString() : stringTime;
}
// Currently some parts of Kibana (index patterns, timefilter) rely on addSetupWork in the uiRouter
// and require it to be executed to properly function.
// This function is exposed for applications that do not use uiRoutes like APM
// Kibana issue https://github.com/elastic/kibana/issues/19110 tracks the removal of this dependency on uiRouter
export const registerTimefilterWithGlobalState = _.once((globalState, $rootScope) => {
export const registerTimefilterWithGlobalState = _.once((globalState: any, $rootScope: IScope) => {
const uiSettings = chrome.getUiSettingsClient();
const timeDefaults = uiSettings.get('timepicker:timeDefaults');
const refreshIntervalDefaults = uiSettings.get('timepicker:refreshIntervalDefaults');
timefilter.setTime(_.defaults(globalState.time || {}, timeDefaults));
timefilter.setRefreshInterval(_.defaults(globalState.refreshInterval || {}, refreshIntervalDefaults));
timefilter.setRefreshInterval(
_.defaults(globalState.refreshInterval || {}, refreshIntervalDefaults)
);
globalState.on('fetch_with_changes', () => {
// clone and default to {} in one
const newTime = _.defaults({}, globalState.time, timeDefaults);
const newRefreshInterval = _.defaults({}, globalState.refreshInterval, refreshIntervalDefaults);
// clone and default to {} in one
const newTime: TimeRange = _.defaults({}, globalState.time, timeDefaults);
const newRefreshInterval: RefreshInterval = _.defaults(
{},
globalState.refreshInterval,
refreshIntervalDefaults
);
if (newTime) {
if (newTime.to) newTime.to = convertISO8601(newTime.to);
@ -250,16 +271,14 @@ export const registerTimefilterWithGlobalState = _.once((globalState, $rootScope
};
subscribeWithScope($rootScope, timefilter.getRefreshIntervalUpdate$(), {
next: updateGlobalStateWithTime
next: updateGlobalStateWithTime,
});
subscribeWithScope($rootScope, timefilter.getTimeUpdate$(), {
next: updateGlobalStateWithTime
next: updateGlobalStateWithTime,
});
});
uiRoutes
.addSetupWork((globalState, $rootScope) => {
return registerTimefilterWithGlobalState(globalState, $rootScope);
});
uiRoutes.addSetupWork((globalState, $rootScope) => {
return registerTimefilterWithGlobalState(globalState, $rootScope);
});