mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Replace timefilter angular service with singleton (#19852)
* use uiSettingsClient instead of angular config service * replace timefilter service with singleton * remove all other uses of timefilter service, get timefilter singleton working * remove Private from brush event * globalState * fix timefilter jest test * clean up rebase artifacts * lint errors * another lint error * fix broken functional test _shared_links * fix broken mocha test doc.js * fix service load order breaking status page * remove Provider from change_time_filter * another round of mocha test updates * convert create_brush_handler test to jest * fix kbnGlobalTimepicker toogle mocha test * remove _root_search_source - removed in another PR and somehow I added it back during rebase * better comments around Object.assign and proper js-docs format * use so listerners get cleaned up when scope is destroyed * listen to correct timefilter update event * update APM to use timefilter singleton * update angular scope when timefilter changes * fix APM jest test * add listenAndDigestAsync to listen.js * fix apm breadcrumbs jest test * move diffTime and diffInterval into timefilter * pass mode to updateFilter to resolve functional test failure * call registerTimefilterWithGlobalState from APM application * remove rootScope from kbn_global_timepicker * use to subscribe to timefilter in cases where results need to happen in digest async * spalger review changes * ensure monitoring timefilter callbacks are executed inside evalAsync * remove unneeded evalAsync calls around setTime
This commit is contained in:
parent
2acb0265d8
commit
2341661cd8
118 changed files with 1374 additions and 1329 deletions
|
@ -25,7 +25,7 @@ export function createSearchSource(kbnApi, initialState, indexPattern, aggs, use
|
|||
searchSource.filter(() => {
|
||||
const activeFilters = [...filters];
|
||||
if (useTimeFilter) {
|
||||
activeFilters.push(kbnApi.timeFilter.get(indexPattern));
|
||||
activeFilters.push(kbnApi.timeFilter.createFilter(indexPattern));
|
||||
}
|
||||
return activeFilters;
|
||||
});
|
||||
|
|
|
@ -36,6 +36,7 @@ import {
|
|||
LOADING_STATUS,
|
||||
QueryActionsProvider,
|
||||
} from './query';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
const module = uiModules.get('apps/context', [
|
||||
'elasticsearch',
|
||||
|
@ -66,7 +67,7 @@ module.directive('contextApp', function ContextApp() {
|
|||
};
|
||||
});
|
||||
|
||||
function ContextAppController($scope, config, Private, timefilter) {
|
||||
function ContextAppController($scope, config, Private) {
|
||||
const queryParameterActions = Private(QueryParameterActionsProvider);
|
||||
const queryActions = Private(QueryActionsProvider);
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ import { EmbeddableFactoriesRegistryProvider } from 'ui/embeddable/embeddable_fa
|
|||
import { DashboardPanelActionsRegistryProvider } from 'ui/dashboard_panel_actions/dashboard_panel_actions_registry';
|
||||
import { SavedObjectsClientProvider } from 'ui/saved_objects';
|
||||
import { VisTypesRegistryProvider } from 'ui/registry/vis_types';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
import { DashboardViewportProvider } from './viewport/dashboard_viewport_provider';
|
||||
|
||||
|
@ -68,7 +69,6 @@ app.directive('dashboardViewportProvider', function (reactDirective) {
|
|||
app.directive('dashboardApp', function ($injector) {
|
||||
const courier = $injector.get('courier');
|
||||
const AppState = $injector.get('AppState');
|
||||
const timefilter = $injector.get('timefilter');
|
||||
const kbnUrl = $injector.get('kbnUrl');
|
||||
const confirmModal = $injector.get('confirmModal');
|
||||
const config = $injector.get('config');
|
||||
|
@ -164,8 +164,7 @@ app.directive('dashboardApp', function ($injector) {
|
|||
$rootScope.$broadcast('fetch');
|
||||
courier.fetch(...args);
|
||||
};
|
||||
$scope.timefilter = timefilter;
|
||||
dashboardStateManager.handleTimeChange($scope.timefilter.time);
|
||||
dashboardStateManager.handleTimeChange(timefilter.getTime());
|
||||
|
||||
$scope.expandedPanel = null;
|
||||
$scope.dashboardViewMode = dashboardStateManager.getViewMode();
|
||||
|
@ -223,8 +222,8 @@ app.directive('dashboardApp', function ($injector) {
|
|||
|
||||
$scope.$watch('model.query', $scope.updateQueryAndFetch);
|
||||
|
||||
$scope.$listen(timefilter, 'fetch', () => {
|
||||
dashboardStateManager.handleTimeChange($scope.timefilter.time);
|
||||
$scope.$listenAndDigestAsync(timefilter, 'fetch', () => {
|
||||
dashboardStateManager.handleTimeChange(timefilter.getTime());
|
||||
// Currently discover relies on this logic to re-fetch. We need to refactor it to rely instead on the
|
||||
// directly passed down time filter. Then we can get rid of this reliance on scope broadcasts.
|
||||
$scope.refresh();
|
||||
|
|
|
@ -28,7 +28,10 @@ jest.mock('ui/chrome', () => ({ getKibanaVersion: () => '6.0.0' }), { virtual: t
|
|||
describe('DashboardState', function () {
|
||||
let dashboardState;
|
||||
const savedDashboard = getSavedDashboardMock();
|
||||
const timefilter = { time: {} };
|
||||
const mockTimefilter = {
|
||||
time: {},
|
||||
setTime: function (time) { this.time = time; },
|
||||
};
|
||||
const mockQuickTimeRanges = [{ from: 'now/w', to: 'now/w', display: 'This week', section: 0 }];
|
||||
const mockIndexPattern = { id: 'index1' };
|
||||
|
||||
|
@ -46,16 +49,16 @@ describe('DashboardState', function () {
|
|||
savedDashboard.timeFrom = 'now/w';
|
||||
savedDashboard.timeTo = 'now/w';
|
||||
|
||||
timefilter.time.from = '2015-09-19 06:31:44.000';
|
||||
timefilter.time.to = '2015-09-29 06:31:44.000';
|
||||
timefilter.time.mode = 'absolute';
|
||||
mockTimefilter.time.from = '2015-09-19 06:31:44.000';
|
||||
mockTimefilter.time.to = '2015-09-29 06:31:44.000';
|
||||
mockTimefilter.time.mode = 'absolute';
|
||||
|
||||
initDashboardState();
|
||||
dashboardState.syncTimefilterWithDashboard(timefilter, mockQuickTimeRanges);
|
||||
dashboardState.syncTimefilterWithDashboard(mockTimefilter, mockQuickTimeRanges);
|
||||
|
||||
expect(timefilter.time.mode).toBe('quick');
|
||||
expect(timefilter.time.to).toBe('now/w');
|
||||
expect(timefilter.time.from).toBe('now/w');
|
||||
expect(mockTimefilter.time.mode).toBe('quick');
|
||||
expect(mockTimefilter.time.to).toBe('now/w');
|
||||
expect(mockTimefilter.time.from).toBe('now/w');
|
||||
});
|
||||
|
||||
test('syncs relative time', function () {
|
||||
|
@ -63,16 +66,16 @@ describe('DashboardState', function () {
|
|||
savedDashboard.timeFrom = 'now-13d';
|
||||
savedDashboard.timeTo = 'now';
|
||||
|
||||
timefilter.time.from = '2015-09-19 06:31:44.000';
|
||||
timefilter.time.to = '2015-09-29 06:31:44.000';
|
||||
timefilter.time.mode = 'absolute';
|
||||
mockTimefilter.time.from = '2015-09-19 06:31:44.000';
|
||||
mockTimefilter.time.to = '2015-09-29 06:31:44.000';
|
||||
mockTimefilter.time.mode = 'absolute';
|
||||
|
||||
initDashboardState();
|
||||
dashboardState.syncTimefilterWithDashboard(timefilter, mockQuickTimeRanges);
|
||||
dashboardState.syncTimefilterWithDashboard(mockTimefilter, mockQuickTimeRanges);
|
||||
|
||||
expect(timefilter.time.mode).toBe('relative');
|
||||
expect(timefilter.time.to).toBe('now');
|
||||
expect(timefilter.time.from).toBe('now-13d');
|
||||
expect(mockTimefilter.time.mode).toBe('relative');
|
||||
expect(mockTimefilter.time.to).toBe('now');
|
||||
expect(mockTimefilter.time.from).toBe('now-13d');
|
||||
});
|
||||
|
||||
test('syncs absolute time', function () {
|
||||
|
@ -80,16 +83,16 @@ describe('DashboardState', function () {
|
|||
savedDashboard.timeFrom = '2015-09-19 06:31:44.000';
|
||||
savedDashboard.timeTo = '2015-09-29 06:31:44.000';
|
||||
|
||||
timefilter.time.from = 'now/w';
|
||||
timefilter.time.to = 'now/w';
|
||||
timefilter.time.mode = 'quick';
|
||||
mockTimefilter.time.from = 'now/w';
|
||||
mockTimefilter.time.to = 'now/w';
|
||||
mockTimefilter.time.mode = 'quick';
|
||||
|
||||
initDashboardState();
|
||||
dashboardState.syncTimefilterWithDashboard(timefilter, mockQuickTimeRanges);
|
||||
dashboardState.syncTimefilterWithDashboard(mockTimefilter, mockQuickTimeRanges);
|
||||
|
||||
expect(timefilter.time.mode).toBe('absolute');
|
||||
expect(timefilter.time.to).toBe(savedDashboard.timeTo);
|
||||
expect(timefilter.time.from).toBe(savedDashboard.timeFrom);
|
||||
expect(mockTimefilter.time.mode).toBe('absolute');
|
||||
expect(mockTimefilter.time.to).toBe(savedDashboard.timeTo);
|
||||
expect(mockTimefilter.time.from).toBe(savedDashboard.timeFrom);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -142,12 +142,11 @@ export class DashboardStateManager {
|
|||
* @param {String} newTimeFilter.mode
|
||||
*/
|
||||
handleTimeChange(newTimeFilter) {
|
||||
const timeFilter = {
|
||||
store.dispatch(updateTimeRange({
|
||||
from: FilterUtils.convertTimeToUTCString(newTimeFilter.from),
|
||||
to: FilterUtils.convertTimeToUTCString(newTimeFilter.to),
|
||||
mode: newTimeFilter.mode,
|
||||
};
|
||||
store.dispatch(updateTimeRange(timeFilter));
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -433,8 +432,8 @@ export class DashboardStateManager {
|
|||
*/
|
||||
getTimeChanged(timeFilter) {
|
||||
return (
|
||||
!FilterUtils.areTimesEqual(this.lastSavedDashboardFilters.timeFrom, timeFilter.time.from) ||
|
||||
!FilterUtils.areTimesEqual(this.lastSavedDashboardFilters.timeTo, timeFilter.time.to)
|
||||
!FilterUtils.areTimesEqual(this.lastSavedDashboardFilters.timeFrom, timeFilter.getTime().from) ||
|
||||
!FilterUtils.areTimesEqual(this.lastSavedDashboardFilters.timeTo, timeFilter.getTime().to)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -536,7 +535,8 @@ export class DashboardStateManager {
|
|||
/**
|
||||
* Updates timeFilter to match the time saved with the dashboard.
|
||||
* @param {Object} timeFilter
|
||||
* @param {Object} timeFilter.time
|
||||
* @param {func} timeFilter.setTime
|
||||
* @param {func} timeFilter.setRefreshInterval
|
||||
* @param quickTimeRanges
|
||||
*/
|
||||
syncTimefilterWithDashboard(timeFilter, quickTimeRanges) {
|
||||
|
@ -544,20 +544,25 @@ export class DashboardStateManager {
|
|||
throw new Error('The time is not saved with this dashboard so should not be synced.');
|
||||
}
|
||||
|
||||
timeFilter.time.to = this.savedDashboard.timeTo;
|
||||
timeFilter.time.from = this.savedDashboard.timeFrom;
|
||||
let mode;
|
||||
const isMoment = moment(this.savedDashboard.timeTo).isValid();
|
||||
if (isMoment) {
|
||||
timeFilter.time.mode = 'absolute';
|
||||
mode = 'absolute';
|
||||
} else {
|
||||
const quickTime = _.find(
|
||||
quickTimeRanges,
|
||||
(timeRange) => timeRange.from === this.savedDashboard.timeFrom && timeRange.to === this.savedDashboard.timeTo);
|
||||
|
||||
timeFilter.time.mode = quickTime ? 'quick' : 'relative';
|
||||
mode = quickTime ? 'quick' : 'relative';
|
||||
}
|
||||
timeFilter.setTime({
|
||||
from: this.savedDashboard.timeFrom,
|
||||
to: this.savedDashboard.timeTo,
|
||||
mode
|
||||
});
|
||||
|
||||
if (this.savedDashboard.refreshInterval) {
|
||||
timeFilter.refreshInterval = this.savedDashboard.refreshInterval;
|
||||
timeFilter.setRefreshInterval(this.savedDashboard.refreshInterval);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,11 +28,11 @@ export function updateSavedDashboard(savedDashboard, appState, timeFilter, toJso
|
|||
savedDashboard.optionsJSON = toJson(appState.options);
|
||||
|
||||
savedDashboard.timeFrom = savedDashboard.timeRestore ?
|
||||
FilterUtils.convertTimeToUTCString(timeFilter.time.from)
|
||||
FilterUtils.convertTimeToUTCString(timeFilter.getTime().from)
|
||||
: undefined;
|
||||
savedDashboard.timeTo = savedDashboard.timeRestore ?
|
||||
FilterUtils.convertTimeToUTCString(timeFilter.time.to)
|
||||
FilterUtils.convertTimeToUTCString(timeFilter.getTime().to)
|
||||
: undefined;
|
||||
const timeRestoreObj = _.pick(timeFilter.refreshInterval, ['display', 'pause', 'section', 'value']);
|
||||
const timeRestoreObj = _.pick(timeFilter.getRefreshInterval(), ['display', 'pause', 'section', 'value']);
|
||||
savedDashboard.refreshInterval = savedDashboard.timeRestore ? timeRestoreObj : undefined;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ import 'ui/filters/moment';
|
|||
import 'ui/courier';
|
||||
import 'ui/index_patterns';
|
||||
import 'ui/state_management/app_state';
|
||||
import 'ui/timefilter';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import 'ui/share';
|
||||
import 'ui/query_bar';
|
||||
import { toastNotifications, getPainlessError } from 'ui/notify';
|
||||
|
@ -142,7 +142,6 @@ function discoverController(
|
|||
config,
|
||||
courier,
|
||||
kbnUrl,
|
||||
timefilter,
|
||||
localStorage,
|
||||
) {
|
||||
|
||||
|
@ -186,8 +185,6 @@ function discoverController(
|
|||
template: require('plugins/kibana/discover/partials/share_search.html'),
|
||||
testId: 'discoverShareButton',
|
||||
}];
|
||||
$scope.timefilter = timefilter;
|
||||
|
||||
|
||||
// the saved savedSearch
|
||||
const savedSearch = $route.current.locals.savedSearch;
|
||||
|
@ -204,7 +201,7 @@ function discoverController(
|
|||
// searchSource which applies time range
|
||||
const timeRangeSearchSource = savedSearch.searchSource.new();
|
||||
timeRangeSearchSource.set('filter', () => {
|
||||
return timefilter.get($scope.indexPattern);
|
||||
return timefilter.createFilter($scope.indexPattern);
|
||||
});
|
||||
|
||||
$scope.searchSource.inherits(timeRangeSearchSource);
|
||||
|
@ -326,7 +323,6 @@ function discoverController(
|
|||
timefield: $scope.indexPattern.timeFieldName,
|
||||
savedSearch: savedSearch,
|
||||
indexPatternList: $route.current.locals.ip.list,
|
||||
timefilter: $scope.timefilter
|
||||
};
|
||||
|
||||
const init = _.once(function () {
|
||||
|
@ -633,8 +629,8 @@ function discoverController(
|
|||
|
||||
$scope.updateTime = function () {
|
||||
$scope.timeRange = {
|
||||
from: dateMath.parse(timefilter.time.from),
|
||||
to: dateMath.parse(timefilter.time.to, { roundUp: true })
|
||||
from: dateMath.parse(timefilter.getTime().from),
|
||||
to: dateMath.parse(timefilter.getTime().to, { roundUp: true })
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -739,7 +735,7 @@ function discoverController(
|
|||
}
|
||||
|
||||
$scope.vis.filters = {
|
||||
timeRange: timefilter.time
|
||||
timeRange: timefilter.getTime()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -22,10 +22,10 @@ import ngMock from 'ng_mock';
|
|||
import expect from 'expect.js';
|
||||
import '..';
|
||||
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
let $scope;
|
||||
let createController;
|
||||
let timefilter;
|
||||
|
||||
const init = function (index, type, id) {
|
||||
|
||||
|
@ -85,9 +85,8 @@ const init = function (index, type, id) {
|
|||
});
|
||||
|
||||
// Create the scope
|
||||
ngMock.inject(function ($rootScope, $controller, _timefilter_) {
|
||||
ngMock.inject(function ($rootScope, $controller) {
|
||||
$scope = $rootScope.$new();
|
||||
timefilter = _timefilter_;
|
||||
|
||||
createController = function () {
|
||||
return $controller('doc', {
|
||||
|
|
|
@ -24,6 +24,7 @@ import 'ui/index_patterns';
|
|||
import html from '../index.html';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
|
||||
const app = uiModules.get('apps/doc', [
|
||||
|
@ -49,7 +50,7 @@ uiRoutes
|
|||
resolve: resolveIndexPattern
|
||||
});
|
||||
|
||||
app.controller('doc', function ($scope, $route, es, timefilter) {
|
||||
app.controller('doc', function ($scope, $route, es) {
|
||||
|
||||
timefilter.disableAutoRefreshSelector();
|
||||
timefilter.disableTimeRangeSelector();
|
||||
|
|
|
@ -27,6 +27,7 @@ import appTemplate from './app.html';
|
|||
import landingTemplate from './landing.html';
|
||||
import { management } from 'ui/management';
|
||||
import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import 'ui/kbn_top_nav';
|
||||
|
||||
uiRoutes
|
||||
|
@ -45,7 +46,7 @@ require('ui/index_patterns/route_setup/load_default')({
|
|||
|
||||
uiModules
|
||||
.get('apps/management')
|
||||
.directive('kbnManagementApp', function (Private, $location, timefilter) {
|
||||
.directive('kbnManagementApp', function (Private, $location) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: appTemplate,
|
||||
|
|
|
@ -40,6 +40,7 @@ import { KibanaParsedUrl } from 'ui/url/kibana_parsed_url';
|
|||
import { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url';
|
||||
import { migrateLegacyQuery } from 'ui/utils/migrateLegacyQuery';
|
||||
import { recentlyAccessed } from 'ui/persisted_log';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
uiRoutes
|
||||
.when(VisualizeConstants.CREATE_PATH, {
|
||||
|
@ -96,7 +97,7 @@ uiModules
|
|||
};
|
||||
});
|
||||
|
||||
function VisEditor($scope, $route, timefilter, AppState, $window, kbnUrl, courier, Private, Promise, config, kbnBaseUrl, localStorage) {
|
||||
function VisEditor($scope, $route, AppState, $window, kbnUrl, courier, Private, Promise, config, kbnBaseUrl, localStorage) {
|
||||
const docTitle = Private(DocTitleProvider);
|
||||
const queryFilter = Private(FilterBarQueryFilterProvider);
|
||||
|
||||
|
@ -221,9 +222,8 @@ function VisEditor($scope, $route, timefilter, AppState, $window, kbnUrl, courie
|
|||
|
||||
$scope.isAddToDashMode = () => addToDashMode;
|
||||
|
||||
$scope.timefilter = timefilter;
|
||||
$scope.timeRange = timefilter.time;
|
||||
$scope.opts = _.pick($scope, 'doSave', 'savedVis', 'shareData', 'timefilter', 'isAddToDashMode');
|
||||
$scope.timeRange = timefilter.getTime();
|
||||
$scope.opts = _.pick($scope, 'doSave', 'savedVis', 'shareData', 'isAddToDashMode');
|
||||
|
||||
stateMonitor = stateMonitorFactory.create($state, stateDefaults);
|
||||
stateMonitor.ignoreProps([ 'vis.listeners' ]).onChange((status) => {
|
||||
|
@ -252,11 +252,11 @@ function VisEditor($scope, $route, timefilter, AppState, $window, kbnUrl, courie
|
|||
});
|
||||
|
||||
const updateTimeRange = () => {
|
||||
$scope.timeRange = timefilter.time;
|
||||
$scope.timeRange = timefilter.getTime();
|
||||
};
|
||||
|
||||
timefilter.enableAutoRefreshSelector();
|
||||
timefilter.on('update', updateTimeRange);
|
||||
$scope.$listenAndDigestAsync(timefilter, 'timeUpdate', updateTimeRange);
|
||||
|
||||
// update the searchSource when filters update
|
||||
$scope.$listen(queryFilter, 'update', function () {
|
||||
|
@ -274,7 +274,6 @@ function VisEditor($scope, $route, timefilter, AppState, $window, kbnUrl, courie
|
|||
$scope.$on('$destroy', function () {
|
||||
savedVis.destroy();
|
||||
stateMonitor.destroy();
|
||||
timefilter.off('update', updateTimeRange);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_regis
|
|||
import 'ui/pager_control';
|
||||
import 'ui/pager';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
import { VisualizeListingTable } from './visualize_listing_table';
|
||||
|
||||
|
@ -32,7 +33,6 @@ app.directive('visualizeListingTable', function (reactDirective) {
|
|||
export function VisualizeListingController($injector) {
|
||||
const Notifier = $injector.get('Notifier');
|
||||
const Private = $injector.get('Private');
|
||||
const timefilter = $injector.get('timefilter');
|
||||
const config = $injector.get('config');
|
||||
|
||||
timefilter.disableAutoRefreshSelector();
|
||||
|
|
|
@ -33,6 +33,7 @@ import { uiModules } from 'ui/modules';
|
|||
import visualizeWizardStep1Template from './step_1.html';
|
||||
import visualizeWizardStep2Template from './step_2.html';
|
||||
import { SavedObjectsClientProvider } from 'ui/saved_objects';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
const module = uiModules.get('app/visualize', ['kibana/courier']);
|
||||
|
||||
|
@ -50,7 +51,7 @@ routes.when(VisualizeConstants.WIZARD_STEP_1_PAGE_PATH, {
|
|||
controller: 'VisualizeWizardStep1',
|
||||
});
|
||||
|
||||
module.controller('VisualizeWizardStep1', function ($scope, $route, kbnUrl, timefilter, Private, config) {
|
||||
module.controller('VisualizeWizardStep1', function ($scope, $route, kbnUrl, Private, config) {
|
||||
timefilter.disableAutoRefreshSelector();
|
||||
timefilter.disableTimeRangeSelector();
|
||||
|
||||
|
@ -220,7 +221,7 @@ routes.when(VisualizeConstants.WIZARD_STEP_2_PAGE_PATH, {
|
|||
}
|
||||
});
|
||||
|
||||
module.controller('VisualizeWizardStep2', function ($route, $scope, timefilter, kbnUrl) {
|
||||
module.controller('VisualizeWizardStep2', function ($route, $scope, kbnUrl) {
|
||||
const type = $route.current.params.type;
|
||||
const addToDashMode = $route.current.params[DashboardConstants.ADD_VISUALIZATION_TO_DASHBOARD_MODE_PARAM];
|
||||
kbnUrl.removeParam(DashboardConstants.ADD_VISUALIZATION_TO_DASHBOARD_MODE_PARAM);
|
||||
|
|
|
@ -20,8 +20,9 @@
|
|||
import { validateInterval } from '../lib/validate_interval';
|
||||
import { dashboardContextProvider } from 'plugins/kibana/dashboard/dashboard_context';
|
||||
import { timezoneProvider } from 'ui/vis/lib/timezone';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
const MetricsRequestHandlerProvider = function (Private, Notifier, config, timefilter, $http) {
|
||||
const MetricsRequestHandlerProvider = function (Private, Notifier, config, $http) {
|
||||
const dashboardContext = Private(dashboardContextProvider);
|
||||
const notify = new Notifier({ location: 'Metrics' });
|
||||
|
||||
|
|
|
@ -22,21 +22,24 @@ import moment from 'moment';
|
|||
import { expect } from 'chai';
|
||||
|
||||
describe('createBrushHandler', () => {
|
||||
let timefilter;
|
||||
let fn;
|
||||
let mockTimefilter;
|
||||
let onBrush;
|
||||
let range;
|
||||
|
||||
beforeEach(() => {
|
||||
timefilter = { time: {}, update: () => {} };
|
||||
fn = createBrushHandler(timefilter);
|
||||
range = { xaxis: { from: '2017-01-01T00:00:00Z', to: '2017-01-01T00:10:00Z' } };
|
||||
fn(range);
|
||||
mockTimefilter = {
|
||||
time: {},
|
||||
setTime: function (time) { this.time = time; }
|
||||
};
|
||||
onBrush = createBrushHandler(mockTimefilter);
|
||||
});
|
||||
|
||||
it('returns brushHandler() that updates timefilter', () => {
|
||||
expect(timefilter.time.from).to.equal(moment(range.xaxis.from).toISOString());
|
||||
expect(timefilter.time.to).to.equal(moment(range.xaxis.to).toISOString());
|
||||
expect(timefilter.time.mode).to.equal('absolute');
|
||||
test('returns brushHandler() that updates timefilter', () => {
|
||||
range = { xaxis: { from: '2017-01-01T00:00:00Z', to: '2017-01-01T00:10:00Z' } };
|
||||
onBrush(range);
|
||||
expect(mockTimefilter.time.from).to.equal(moment(range.xaxis.from).toISOString());
|
||||
expect(mockTimefilter.time.to).to.equal(moment(range.xaxis.to).toISOString());
|
||||
expect(mockTimefilter.time.mode).to.equal('absolute');
|
||||
});
|
||||
|
||||
});
|
|
@ -19,10 +19,9 @@
|
|||
|
||||
import moment from 'moment';
|
||||
export default (timefilter) => ranges => {
|
||||
//$scope.$evalAsync(() => {
|
||||
timefilter.time.from = moment(ranges.xaxis.from).toISOString();
|
||||
timefilter.time.to = moment(ranges.xaxis.to).toISOString();
|
||||
timefilter.time.mode = 'absolute';
|
||||
timefilter.update();
|
||||
//});
|
||||
timefilter.setTime({
|
||||
from: moment(ranges.xaxis.from).toISOString(),
|
||||
to: moment(ranges.xaxis.to).toISOString(),
|
||||
mode: 'absolute',
|
||||
});
|
||||
};
|
||||
|
|
|
@ -25,6 +25,7 @@ import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_regis
|
|||
import { notify, fatalError, toastNotifications } from 'ui/notify';
|
||||
import { timezoneProvider } from 'ui/vis/lib/timezone';
|
||||
import { recentlyAccessed } from 'ui/persisted_log';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
// import the uiExports that we want to "use"
|
||||
import 'uiExports/fieldFormats';
|
||||
|
@ -93,7 +94,6 @@ app.controller('timelion', function (
|
|||
kbnUrl,
|
||||
Notifier,
|
||||
Private,
|
||||
timefilter
|
||||
) {
|
||||
|
||||
// Keeping this at app scope allows us to keep the current page when the user
|
||||
|
@ -213,8 +213,9 @@ app.controller('timelion', function (
|
|||
};
|
||||
|
||||
let refresher;
|
||||
$scope.$watchCollection('timefilter.refreshInterval', function (interval) {
|
||||
$scope.$listen(timefilter, 'refreshIntervalUpdate', function () {
|
||||
if (refresher) $timeout.cancel(refresher);
|
||||
const interval = timefilter.getRefreshInterval();
|
||||
if (interval.value > 0 && !interval.pause) {
|
||||
function startRefresh() {
|
||||
refresher = $timeout(function () {
|
||||
|
@ -254,7 +255,7 @@ app.controller('timelion', function (
|
|||
|
||||
const httpResult = $http.post('../api/timelion/run', {
|
||||
sheet: $scope.state.sheet,
|
||||
time: _.extend(timefilter.time, {
|
||||
time: _.extend(timefilter.getTime(), {
|
||||
interval: $scope.state.interval,
|
||||
timezone: timezone
|
||||
}),
|
||||
|
|
|
@ -24,10 +24,11 @@ import $ from 'jquery';
|
|||
import moment from 'moment-timezone';
|
||||
import observeResize from '../../lib/observe_resize';
|
||||
import { calculateInterval, DEFAULT_TIME_FORMAT } from '../../../common/lib';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
const DEBOUNCE_DELAY = 50;
|
||||
|
||||
export default function timechartFn(Private, config, $rootScope, timefilter, $compile) {
|
||||
export default function timechartFn(Private, config, $rootScope, $compile) {
|
||||
return function () {
|
||||
return {
|
||||
help: 'Draw a timeseries chart',
|
||||
|
@ -173,10 +174,11 @@ export default function timechartFn(Private, config, $rootScope, timefilter, $co
|
|||
});
|
||||
|
||||
$elem.on('plotselected', function (event, ranges) {
|
||||
timefilter.time.from = moment(ranges.xaxis.from);
|
||||
timefilter.time.to = moment(ranges.xaxis.to);
|
||||
timefilter.time.mode = 'absolute';
|
||||
$scope.$apply();
|
||||
timefilter.setTime({
|
||||
from: moment(ranges.xaxis.from),
|
||||
to: moment(ranges.xaxis.to),
|
||||
mode: 'absolute',
|
||||
});
|
||||
});
|
||||
|
||||
$elem.on('mouseleave', function () {
|
||||
|
|
|
@ -21,8 +21,9 @@ import { VegaParser } from './data_model/vega_parser';
|
|||
import { dashboardContextProvider } from 'plugins/kibana/dashboard/dashboard_context';
|
||||
import { SearchCache } from './data_model/search_cache';
|
||||
import { TimeCache } from './data_model/time_cache';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
export function VegaRequestHandlerProvider(Private, es, timefilter, serviceSettings) {
|
||||
export function VegaRequestHandlerProvider(Private, es, serviceSettings) {
|
||||
|
||||
const dashboardContext = Private(dashboardContextProvider);
|
||||
const searchCache = new SearchCache(es, { max: 10, maxAge: 4 * 1000 });
|
||||
|
|
|
@ -25,6 +25,7 @@ import AggParamWriterProvider from '../../agg_param_writer';
|
|||
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
|
||||
import { aggTypes } from '../../..';
|
||||
import { AggConfig } from '../../../../vis/agg_config';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
describe('params', function () {
|
||||
|
||||
|
@ -35,10 +36,9 @@ describe('params', function () {
|
|||
let timeField;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function (Private, $injector) {
|
||||
beforeEach(ngMock.inject(function (Private) {
|
||||
const AggParamWriter = Private(AggParamWriterProvider);
|
||||
const indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
|
||||
const timefilter = $injector.get('timefilter');
|
||||
|
||||
timeField = indexPattern.timeFieldName;
|
||||
|
||||
|
|
|
@ -24,7 +24,8 @@ import { metadata } from '../metadata';
|
|||
import 'babel-polyfill';
|
||||
import 'whatwg-fetch';
|
||||
import 'custom-event-polyfill';
|
||||
import '../timefilter';
|
||||
import '../state_management/global_state';
|
||||
import '../config';
|
||||
import '../notify';
|
||||
import '../private';
|
||||
import '../promises';
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<kbn-timepicker
|
||||
from="timefilter.time.from"
|
||||
to="timefilter.time.to"
|
||||
mode="timefilter.time.mode"
|
||||
from="timefilterValues.time.from"
|
||||
to="timefilterValues.time.to"
|
||||
mode="timefilterValues.time.mode"
|
||||
active-tab="'filter'"
|
||||
interval="timefilter.refreshInterval"
|
||||
on-filter-select="updateFilter(from, to)"
|
||||
interval="timefilterValues.refreshInterval"
|
||||
on-filter-select="updateFilter(from, to, mode)"
|
||||
on-interval-select="updateInterval(interval)">
|
||||
</kbn-timepicker>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<kbn-timepicker
|
||||
from="timefilter.time.from"
|
||||
to="timefilter.time.to"
|
||||
mode="timefilter.time.mode"
|
||||
from="timefilterValues.time.from"
|
||||
to="timefilterValues.time.to"
|
||||
mode="timefilterValues.time.mode"
|
||||
active-tab="'interval'"
|
||||
interval="timefilter.refreshInterval"
|
||||
on-filter-select="updateFilter(from, to)"
|
||||
interval="timefilterValues.refreshInterval"
|
||||
on-filter-select="updateFilter(from, to, mode)"
|
||||
on-interval-select="updateInterval(interval)">
|
||||
</kbn-timepicker>
|
||||
|
|
|
@ -31,6 +31,7 @@ import { FetchSoonProvider } from './fetch';
|
|||
import { SearchLooperProvider } from './looper/search';
|
||||
import { SavedObjectProvider } from './saved_object';
|
||||
import { RedirectWhenMissingProvider } from './_redirect_when_missing';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
|
||||
uiModules.get('kibana/courier')
|
||||
|
@ -114,10 +115,9 @@ uiModules.get('kibana/courier')
|
|||
}
|
||||
};
|
||||
|
||||
// Listen for refreshInterval changes
|
||||
$rootScope.$watchCollection('timefilter.refreshInterval', function () {
|
||||
const refreshValue = _.get($rootScope, 'timefilter.refreshInterval.value');
|
||||
const refreshPause = _.get($rootScope, 'timefilter.refreshInterval.pause');
|
||||
$rootScope.$listen(timefilter, 'refreshIntervalUpdate', function () {
|
||||
const refreshValue = _.get(timefilter.getRefreshInterval(), 'value');
|
||||
const refreshPause = _.get(timefilter.getRefreshInterval(), 'pause');
|
||||
if (_.isNumber(refreshValue) && !refreshPause) {
|
||||
self.fetchInterval(refreshValue);
|
||||
} else {
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
*/
|
||||
|
||||
import { requestFetchParamsToBody } from './request_fetch_params_to_body';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
export function RequestFetchParamsToBodyProvider(Promise, timefilter, kbnIndex, sessionId, config, esShardTimeout) {
|
||||
export function RequestFetchParamsToBodyProvider(Promise, kbnIndex, sessionId, config, esShardTimeout) {
|
||||
return (requestsFetchParams) => (
|
||||
requestFetchParamsToBody(
|
||||
requestsFetchParams,
|
||||
|
|
|
@ -22,8 +22,9 @@ import { Notifier } from '../../../notify';
|
|||
import { SearchRequestProvider } from './search_request';
|
||||
import { SegmentedHandleProvider } from './segmented_handle';
|
||||
import { pushAll } from '../../../utils/collection';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
export function SegmentedRequestProvider(Private, timefilter, config) {
|
||||
export function SegmentedRequestProvider(Private, config) {
|
||||
const SearchRequest = Private(SearchRequestProvider);
|
||||
const SegmentedHandle = Private(SegmentedHandleProvider);
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ import { FilterBarLibMapAndFlattenFiltersProvider } from './lib/map_and_flatten_
|
|||
import { FilterBarLibMapFlattenAndWrapFiltersProvider } from './lib/map_flatten_and_wrap_filters';
|
||||
import { FilterBarLibExtractTimeFilterProvider } from './lib/extract_time_filter';
|
||||
import { FilterBarLibFilterOutTimeBasedFilterProvider } from './lib/filter_out_time_based_filter';
|
||||
import { FilterBarLibChangeTimeFilterProvider } from './lib/change_time_filter';
|
||||
import { changeTimeFilter } from './lib/change_time_filter';
|
||||
import { FilterBarQueryFilterProvider } from './query_filter';
|
||||
import { compareFilters } from './lib/compare_filters';
|
||||
import { uiModules } from '../modules';
|
||||
|
@ -42,7 +42,6 @@ module.directive('filterBar', function (Private, Promise, getAppState) {
|
|||
const mapFlattenAndWrapFilters = Private(FilterBarLibMapFlattenAndWrapFiltersProvider);
|
||||
const extractTimeFilter = Private(FilterBarLibExtractTimeFilterProvider);
|
||||
const filterOutTimeBasedFilter = Private(FilterBarLibFilterOutTimeBasedFilterProvider);
|
||||
const changeTimeFilter = Private(FilterBarLibChangeTimeFilterProvider);
|
||||
const queryFilter = Private(FilterBarQueryFilterProvider);
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,61 +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 ngMock from 'ng_mock';
|
||||
import expect from 'expect.js';
|
||||
import { FilterBarLibChangeTimeFilterProvider } from '../change_time_filter';
|
||||
|
||||
describe('Filter Bar Directive', function () {
|
||||
describe('changeTimeFilter()', function () {
|
||||
|
||||
let changeTimeFilter;
|
||||
|
||||
let timefilter;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
|
||||
beforeEach(ngMock.inject(function (Private, _timefilter_) {
|
||||
changeTimeFilter = Private(FilterBarLibChangeTimeFilterProvider);
|
||||
timefilter = _timefilter_;
|
||||
}));
|
||||
|
||||
it('should change the timefilter to match the range gt/lt', function () {
|
||||
const filter = { range: { '@timestamp': { gt: 1388559600000, lt: 1388646000000 } } };
|
||||
changeTimeFilter(filter);
|
||||
expect(timefilter.time.mode).to.be('absolute');
|
||||
expect(moment.isMoment(timefilter.time.to)).to.be(true);
|
||||
expect(timefilter.time.to.isSame('2014-01-02'));
|
||||
expect(moment.isMoment(timefilter.time.from)).to.be(true);
|
||||
expect(timefilter.time.from.isSame('2014-01-01'));
|
||||
});
|
||||
|
||||
it('should change the timefilter to match the range gte/lte', function () {
|
||||
const filter = { range: { '@timestamp': { gte: 1388559600000, lte: 1388646000000 } } };
|
||||
changeTimeFilter(filter);
|
||||
expect(timefilter.time.mode).to.be('absolute');
|
||||
expect(moment.isMoment(timefilter.time.to)).to.be(true);
|
||||
expect(timefilter.time.to.isSame('2014-01-02'));
|
||||
expect(moment.isMoment(timefilter.time.from)).to.be(true);
|
||||
expect(timefilter.time.from.isSame('2014-01-01'));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
jest.mock('ui/chrome',
|
||||
() => ({
|
||||
getUiSettingsClient: () => {
|
||||
return {
|
||||
get: (key) => {
|
||||
switch(key) {
|
||||
case 'timepicker:timeDefaults':
|
||||
return { from: 'now-15m', to: 'now', mode: 'quick' };
|
||||
case 'timepicker:refreshIntervalDefaults':
|
||||
return { display: 'Off', pause: false, value: 0 };
|
||||
default:
|
||||
throw new Error(`Unexpected config key: ${key}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
}), { virtual: true });
|
||||
|
||||
import moment from 'moment';
|
||||
import expect from 'expect.js';
|
||||
import { changeTimeFilter } from '../change_time_filter';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
describe('changeTimeFilter()', function () {
|
||||
|
||||
test('should change the timefilter to match the range gt/lt', function () {
|
||||
const filter = { range: { '@timestamp': { gt: 1388559600000, lt: 1388646000000 } } };
|
||||
changeTimeFilter(filter);
|
||||
expect(timefilter.getTime().mode).to.be('absolute');
|
||||
expect(moment.isMoment(timefilter.getTime().to)).to.be(true);
|
||||
expect(timefilter.getTime().to.isSame('2014-01-02'));
|
||||
expect(moment.isMoment(timefilter.getTime().from)).to.be(true);
|
||||
expect(timefilter.getTime().from.isSame('2014-01-01'));
|
||||
});
|
||||
|
||||
test('should change the timefilter to match the range gte/lte', function () {
|
||||
const filter = { range: { '@timestamp': { gte: 1388559600000, lte: 1388646000000 } } };
|
||||
changeTimeFilter(filter);
|
||||
expect(timefilter.getTime().mode).to.be('absolute');
|
||||
expect(moment.isMoment(timefilter.getTime().to)).to.be(true);
|
||||
expect(timefilter.getTime().to.isSame('2014-01-02'));
|
||||
expect(moment.isMoment(timefilter.getTime().from)).to.be(true);
|
||||
expect(timefilter.getTime().from.isSame('2014-01-01'));
|
||||
});
|
||||
|
||||
});
|
|
@ -19,13 +19,14 @@
|
|||
|
||||
import moment from 'moment';
|
||||
import _ from 'lodash';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
export function FilterBarLibChangeTimeFilterProvider(timefilter) {
|
||||
return function (filter) {
|
||||
const key = _.keys(filter.range)[0];
|
||||
const values = filter.range[key];
|
||||
timefilter.time.from = moment(values.gt || values.gte);
|
||||
timefilter.time.to = moment(values.lt || values.lte);
|
||||
timefilter.time.mode = 'absolute';
|
||||
};
|
||||
export function changeTimeFilter(filter) {
|
||||
const key = _.keys(filter.range)[0];
|
||||
const values = filter.range[key];
|
||||
timefilter.setTime({
|
||||
from: moment(values.gt || values.gte),
|
||||
to: moment(values.lt || values.lte),
|
||||
mode: 'absolute',
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,8 +21,9 @@ import _ from 'lodash';
|
|||
import moment from 'moment';
|
||||
import { IndexedArray } from '../indexed_array';
|
||||
import { isNumeric } from '../utils/numeric';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
export function IndexPatternsIntervalsProvider(timefilter) {
|
||||
export function IndexPatternsIntervalsProvider() {
|
||||
|
||||
const intervals = new IndexedArray({
|
||||
index: ['name'],
|
||||
|
|
|
@ -22,15 +22,15 @@ import { uiModules } from '../modules';
|
|||
uiModules.get('kibana')
|
||||
.run(function ($rootScope) {
|
||||
|
||||
/**
|
||||
* Helper that registers an event listener, and removes that listener when
|
||||
* the $scope is destroyed.
|
||||
*
|
||||
* @param {EventEmitter} emitter - the event emitter to listen to
|
||||
* @param {string} eventName - the event name
|
||||
* @param {Function} handler - the event handler
|
||||
* @return {undefined}
|
||||
*/
|
||||
/**
|
||||
* Helper that registers an event listener, and removes that listener when
|
||||
* the $scope is destroyed.
|
||||
*
|
||||
* @param {SimpleEmitter} emitter - the event emitter to listen to
|
||||
* @param {string} eventName - the event name
|
||||
* @param {Function} handler - the event handler
|
||||
* @return {undefined}
|
||||
*/
|
||||
$rootScope.constructor.prototype.$listen = function (emitter, eventName, handler) {
|
||||
emitter.on(eventName, handler);
|
||||
this.$on('$destroy', function () {
|
||||
|
@ -38,4 +38,20 @@ uiModules.get('kibana')
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper that registers an event listener, and removes that listener when
|
||||
* the $scope is destroyed. Handler is executed inside $evalAsync, ensuring digest cycle is run after the handler
|
||||
*
|
||||
* @param {SimpleEmitter} emitter - the event emitter to listen to
|
||||
* @param {string} eventName - the event name
|
||||
* @param {Function} handler - the event handler
|
||||
* @return {undefined}
|
||||
*/
|
||||
$rootScope.constructor.prototype.$listenAndDigestAsync = function (emitter, eventName, handler) {
|
||||
const evalAsyncWrappedHandler = (...args) => {
|
||||
this.$evalAsync(() => handler(args));
|
||||
};
|
||||
this.$listen(emitter, eventName, evalAsyncWrappedHandler);
|
||||
};
|
||||
|
||||
});
|
||||
|
|
|
@ -1,84 +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 sinon from 'sinon';
|
||||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
|
||||
describe('Timefilter service', function () {
|
||||
|
||||
describe('calculateBounds', function () {
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
|
||||
const fifteenMinutesInMilliseconds = 15 * 60 * 1000;
|
||||
const clockNowTicks = new Date(2000, 1, 1, 0, 0, 0, 0).valueOf();
|
||||
|
||||
let timefilter;
|
||||
let $location;
|
||||
let clock;
|
||||
|
||||
beforeEach(ngMock.inject(function (_timefilter_, _$location_) {
|
||||
timefilter = _timefilter_;
|
||||
$location = _$location_;
|
||||
|
||||
clock = sinon.useFakeTimers(clockNowTicks);
|
||||
}));
|
||||
|
||||
afterEach(function () {
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('uses clock time by default', function () {
|
||||
const timeRange = {
|
||||
from: 'now-15m',
|
||||
to: 'now'
|
||||
};
|
||||
|
||||
const result = timefilter.calculateBounds(timeRange);
|
||||
expect(result.min.valueOf()).to.eql(clockNowTicks - fifteenMinutesInMilliseconds);
|
||||
expect(result.max.valueOf()).to.eql(clockNowTicks);
|
||||
});
|
||||
|
||||
it('uses forceNow string', function () {
|
||||
const timeRange = {
|
||||
from: 'now-15m',
|
||||
to: 'now'
|
||||
};
|
||||
|
||||
const forceNowString = '1999-01-01T00:00:00.000Z';
|
||||
$location.search('forceNow', forceNowString);
|
||||
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);
|
||||
});
|
||||
|
||||
it(`throws Error if forceNow can't be parsed`, function () {
|
||||
const timeRange = {
|
||||
from: 'now-15m',
|
||||
to: 'now'
|
||||
};
|
||||
|
||||
$location.search('forceNow', 'malformed%20string');
|
||||
expect(() => timefilter.calculateBounds(timeRange)).to.throwError();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -17,4 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import './timefilter';
|
||||
export { timefilter, registerTimefilterWithGlobalState } from './timefilter';
|
||||
|
|
|
@ -1,36 +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 _ from 'lodash';
|
||||
import { areTimePickerValsDifferent } from './diff_time_picker_vals';
|
||||
|
||||
export function diffIntervalFactory(self) {
|
||||
let oldRefreshInterval = _.clone(self.refreshInterval);
|
||||
|
||||
return function () {
|
||||
if (areTimePickerValsDifferent(self.refreshInterval, oldRefreshInterval)) {
|
||||
self.emit('update');
|
||||
if (!self.refreshInterval.pause && self.refreshInterval.value !== 0) {
|
||||
self.emit('fetch');
|
||||
}
|
||||
}
|
||||
|
||||
oldRefreshInterval = _.clone(self.refreshInterval);
|
||||
};
|
||||
}
|
|
@ -1,95 +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 sinon from 'sinon';
|
||||
import expect from 'expect.js';
|
||||
import { diffIntervalFactory } from '../lib/diff_interval';
|
||||
|
||||
describe('Refresh interval diff watcher', () => {
|
||||
|
||||
let diffInterval;
|
||||
let update;
|
||||
let fetch;
|
||||
let timefilter;
|
||||
|
||||
beforeEach(() => {
|
||||
update = sinon.spy();
|
||||
fetch = sinon.spy();
|
||||
timefilter = {
|
||||
refreshInterval: {
|
||||
pause: false,
|
||||
value: 0
|
||||
},
|
||||
emit: (eventType) => {
|
||||
if (eventType === 'update') update();
|
||||
if (eventType === 'fetch') fetch();
|
||||
}
|
||||
};
|
||||
|
||||
diffInterval = diffIntervalFactory(timefilter);
|
||||
});
|
||||
|
||||
test('not emit anything if nothing has changed', () => {
|
||||
timefilter.refreshInterval = { pause: false, value: 0 };
|
||||
diffInterval();
|
||||
expect(update.called).to.be(false);
|
||||
expect(fetch.called).to.be(false);
|
||||
});
|
||||
|
||||
test('emit only an update when paused', () => {
|
||||
timefilter.refreshInterval = { pause: true, value: 5000 };
|
||||
diffInterval();
|
||||
expect(update.called).to.be(true);
|
||||
expect(fetch.called).to.be(false);
|
||||
});
|
||||
|
||||
test('emit update, not fetch, when switching to value: 0', () => {
|
||||
timefilter.refreshInterval = { pause: false, value: 5000 };
|
||||
diffInterval();
|
||||
expect(update.calledOnce).to.be(true);
|
||||
expect(fetch.calledOnce).to.be(true);
|
||||
timefilter.refreshInterval = { pause: false, value: 0 };
|
||||
diffInterval();
|
||||
expect(update.calledTwice).to.be(true);
|
||||
expect(fetch.calledTwice).to.be(false);
|
||||
});
|
||||
|
||||
test('should emit update, not fetch, when moving from unpaused to paused', () => {
|
||||
timefilter.refreshInterval = { pause: false, value: 5000 };
|
||||
diffInterval();
|
||||
expect(update.calledOnce).to.be(true);
|
||||
expect(fetch.calledOnce).to.be(true);
|
||||
timefilter.refreshInterval = { pause: true, value: 5000 };
|
||||
diffInterval();
|
||||
expect(update.calledTwice).to.be(true);
|
||||
expect(fetch.calledTwice).to.be(false);
|
||||
});
|
||||
|
||||
test('should emit update and fetch when unpaused', () => {
|
||||
timefilter.refreshInterval = { pause: true, value: 5000 };
|
||||
diffInterval();
|
||||
expect(update.calledOnce).to.be(true);
|
||||
expect(fetch.calledOnce).to.be(false);
|
||||
timefilter.refreshInterval = { pause: false, value: 5000 };
|
||||
diffInterval();
|
||||
expect(update.calledTwice).to.be(true);
|
||||
expect(fetch.calledOnce).to.be(true);
|
||||
});
|
||||
|
||||
});
|
|
@ -1,34 +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 _ from 'lodash';
|
||||
import { areTimePickerValsDifferent } from './diff_time_picker_vals';
|
||||
import { timeHistory } from '../time_history';
|
||||
|
||||
export function diffTimeFactory(self) {
|
||||
let oldTime = _.clone(self.time);
|
||||
return function () {
|
||||
if (areTimePickerValsDifferent(self.time, oldTime)) {
|
||||
timeHistory.add(self.time);
|
||||
self.emit('update');
|
||||
self.emit('fetch');
|
||||
}
|
||||
oldTime = _.clone(self.time);
|
||||
};
|
||||
}
|
|
@ -1,61 +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 sinon from 'sinon';
|
||||
import expect from 'expect.js';
|
||||
import { diffTimeFactory } from '../lib/diff_time';
|
||||
|
||||
describe('time diff watcher', () => {
|
||||
let diffTime;
|
||||
let update;
|
||||
let fetch;
|
||||
let timefilter;
|
||||
|
||||
beforeEach(() => {
|
||||
update = sinon.spy();
|
||||
fetch = sinon.spy();
|
||||
timefilter = {
|
||||
time: {
|
||||
from: 0,
|
||||
to: 1
|
||||
},
|
||||
emit: function (eventType) {
|
||||
if (eventType === 'update') update();
|
||||
if (eventType === 'fetch') fetch();
|
||||
}
|
||||
};
|
||||
|
||||
diffTime = diffTimeFactory(timefilter);
|
||||
});
|
||||
|
||||
test('not emit anything if the time has not changed', () => {
|
||||
timefilter.time = { from: 0, to: 1 };
|
||||
diffTime();
|
||||
expect(update.called).to.be(false);
|
||||
expect(fetch.called).to.be(false);
|
||||
});
|
||||
|
||||
test('emit update and fetch if the time has changed', () => {
|
||||
timefilter.time = { from: 5, to: 10 };
|
||||
diffTime();
|
||||
expect(update.called).to.be(true);
|
||||
expect(fetch.called).to.be(true);
|
||||
});
|
||||
|
||||
});
|
|
@ -17,11 +17,15 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { uiModules } from '../../modules';
|
||||
import qs from 'querystring';
|
||||
|
||||
uiModules.get('kibana').config(function ($provide) {
|
||||
$provide.decorator('timefilter', function ($delegate) {
|
||||
$delegate.init();
|
||||
return $delegate;
|
||||
});
|
||||
});
|
||||
export function parseQueryString() {
|
||||
// window.location.search is an empty string
|
||||
// get search from href
|
||||
const hrefSplit = window.location.href.split('?');
|
||||
if (hrefSplit.length <= 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return qs.parse(hrefSplit[1]);
|
||||
}
|
|
@ -20,142 +20,174 @@
|
|||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { calculateBounds, getTime } from './get_time';
|
||||
import '../state_management/global_state';
|
||||
import '../config';
|
||||
import { EventsProvider } from '../events';
|
||||
import { diffTimeFactory } from './lib/diff_time';
|
||||
import { diffIntervalFactory } from './lib/diff_interval';
|
||||
import { parseQueryString } from 'ui/timefilter/lib/parse_querystring';
|
||||
import { SimpleEmitter } from 'ui/utils/simple_emitter';
|
||||
import uiRoutes from '../routes';
|
||||
import { uiModules } from '../modules';
|
||||
import { createLegacyClass } from '../utils/legacy_class';
|
||||
import chrome from 'ui/chrome';
|
||||
import { areTimePickerValsDifferent } from './lib/diff_time_picker_vals';
|
||||
import { timeHistory } from './time_history';
|
||||
|
||||
class Timefilter extends SimpleEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
this.isTimeRangeSelectorEnabled = false;
|
||||
this.isAutoRefreshSelectorEnabled = false;
|
||||
this._time = chrome.getUiSettingsClient().get('timepicker:timeDefaults');
|
||||
this._refreshInterval = chrome.getUiSettingsClient().get('timepicker:refreshIntervalDefaults');
|
||||
}
|
||||
|
||||
getTime = () => {
|
||||
return _.clone(this._time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates timefilter time.
|
||||
* Emits 'timeUpdate' and 'fetch' events when time changes
|
||||
* @param {Object} time
|
||||
* @property {string|moment} time.from
|
||||
* @property {string|moment} time.to
|
||||
* @property {string} time.mode (quick | relative | absolute)
|
||||
*/
|
||||
setTime = (time) => {
|
||||
// Object.assign used for partially composed updates
|
||||
const newTime = Object.assign(this.getTime(), time);
|
||||
if (areTimePickerValsDifferent(this.getTime(), newTime)) {
|
||||
this._time = newTime;
|
||||
timeHistory.add(newTime);
|
||||
this.emit('timeUpdate');
|
||||
this.emit('fetch');
|
||||
}
|
||||
}
|
||||
|
||||
getRefreshInterval = () => {
|
||||
return _.clone(this._refreshInterval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set timefilter refresh interval.
|
||||
* @param {Object} refreshInterval
|
||||
* @property {number} time.value
|
||||
* @property {boolean} time.pause
|
||||
*/
|
||||
setRefreshInterval = (refreshInterval) => {
|
||||
// Object.assign used for partially composed updates
|
||||
const newRefreshInterval = Object.assign(this.getRefreshInterval(), refreshInterval);
|
||||
if (areTimePickerValsDifferent(this.getRefreshInterval(), newRefreshInterval)) {
|
||||
this._refreshInterval = newRefreshInterval;
|
||||
this.emit('refreshIntervalUpdate');
|
||||
if (!newRefreshInterval.pause && newRefreshInterval.value !== 0) {
|
||||
this.emit('fetch');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toggleRefresh = () => {
|
||||
this.setRefreshInterval({ pause: !this._refreshInterval.pause });
|
||||
}
|
||||
|
||||
createFilter = (indexPattern, timeRange) => {
|
||||
return getTime(indexPattern, timeRange ? timeRange : this._time, this.getForceNow());
|
||||
}
|
||||
|
||||
getBounds = () => {
|
||||
return this.calculateBounds(this._time);
|
||||
}
|
||||
|
||||
getForceNow = () => {
|
||||
const forceNow = parseQueryString().forceNow;
|
||||
if (!forceNow) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ticks = Date.parse(forceNow);
|
||||
if (isNaN(ticks)) {
|
||||
throw new Error(`forceNow query parameter, ${forceNow}, can't be parsed by Date.parse`);
|
||||
}
|
||||
return new Date(ticks);
|
||||
}
|
||||
|
||||
calculateBounds = (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.emit('enabledUpdated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the time bounds selector part of the time filter
|
||||
*/
|
||||
disableTimeRangeSelector = () => {
|
||||
this.isTimeRangeSelectorEnabled = false;
|
||||
this.emit('enabledUpdated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the auto refresh part of the time filter
|
||||
*/
|
||||
enableAutoRefreshSelector = () => {
|
||||
this.isAutoRefreshSelectorEnabled = true;
|
||||
this.emit('enabledUpdated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the auto refresh part of the time filter
|
||||
*/
|
||||
disableAutoRefreshSelector = () => {
|
||||
this.isAutoRefreshSelectorEnabled = false;
|
||||
this.emit('enabledUpdated');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const timefilter = new Timefilter();
|
||||
|
||||
// TODO
|
||||
// remove everything underneath once globalState is no longer an angular service
|
||||
// and listener can be registered without angular.
|
||||
function convertISO8601(stringTime) {
|
||||
const obj = moment(stringTime, 'YYYY-MM-DDTHH:mm:ss.SSSZ', true);
|
||||
return obj.isValid() ? obj : 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) => {
|
||||
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));
|
||||
|
||||
globalState.on('fetch_with_changes', () => {
|
||||
// clone and default to {} in one
|
||||
const newTime = _.defaults({}, globalState.time, timeDefaults);
|
||||
const newRefreshInterval = _.defaults({}, globalState.refreshInterval, refreshIntervalDefaults);
|
||||
|
||||
if (newTime) {
|
||||
if (newTime.to) newTime.to = convertISO8601(newTime.to);
|
||||
if (newTime.from) newTime.from = convertISO8601(newTime.from);
|
||||
}
|
||||
|
||||
timefilter.setTime(newTime);
|
||||
timefilter.setRefreshInterval(newRefreshInterval);
|
||||
});
|
||||
});
|
||||
|
||||
uiRoutes
|
||||
.addSetupWork(function (timefilter) {
|
||||
return timefilter.init();
|
||||
});
|
||||
|
||||
uiModules
|
||||
.get('kibana')
|
||||
.service('timefilter', function (Private, globalState, $rootScope, config, $location) {
|
||||
const Events = Private(EventsProvider);
|
||||
|
||||
function convertISO8601(stringTime) {
|
||||
const obj = moment(stringTime, 'YYYY-MM-DDTHH:mm:ss.SSSZ', true);
|
||||
return obj.isValid() ? obj : stringTime;
|
||||
}
|
||||
|
||||
createLegacyClass(Timefilter).inherits(Events);
|
||||
function Timefilter() {
|
||||
Timefilter.Super.call(this);
|
||||
|
||||
const self = this;
|
||||
const diffTime = diffTimeFactory(self);
|
||||
const diffInterval = diffIntervalFactory(self);
|
||||
|
||||
self.isTimeRangeSelectorEnabled = false;
|
||||
self.isAutoRefreshSelectorEnabled = false;
|
||||
|
||||
self.init = _.once(function () {
|
||||
const timeDefaults = config.get('timepicker:timeDefaults');
|
||||
const refreshIntervalDefaults = config.get('timepicker:refreshIntervalDefaults');
|
||||
|
||||
// These can be date math strings or moments.
|
||||
self.time = _.defaults(globalState.time || {}, timeDefaults);
|
||||
self.refreshInterval = _.defaults(globalState.refreshInterval || {}, refreshIntervalDefaults);
|
||||
|
||||
globalState.on('fetch_with_changes', function () {
|
||||
// clone and default to {} in one
|
||||
const newTime = _.defaults({}, globalState.time, timeDefaults);
|
||||
const newRefreshInterval = _.defaults({}, globalState.refreshInterval, refreshIntervalDefaults);
|
||||
|
||||
if (newTime) {
|
||||
if (newTime.to) newTime.to = convertISO8601(newTime.to);
|
||||
if (newTime.from) newTime.from = convertISO8601(newTime.from);
|
||||
}
|
||||
|
||||
self.time = newTime;
|
||||
self.refreshInterval = newRefreshInterval;
|
||||
});
|
||||
});
|
||||
|
||||
$rootScope.$$timefilter = self;
|
||||
|
||||
$rootScope.$watchMulti([
|
||||
'$$timefilter.time',
|
||||
'$$timefilter.time.from',
|
||||
'$$timefilter.time.to',
|
||||
'$$timefilter.time.mode'
|
||||
], diffTime);
|
||||
|
||||
$rootScope.$watchMulti([
|
||||
'$$timefilter.refreshInterval',
|
||||
'$$timefilter.refreshInterval.pause',
|
||||
'$$timefilter.refreshInterval.value'
|
||||
], diffInterval);
|
||||
}
|
||||
|
||||
Timefilter.prototype.update = function () {
|
||||
$rootScope.$apply();
|
||||
};
|
||||
|
||||
Timefilter.prototype.getForceNow = function () {
|
||||
const query = $location.search().forceNow;
|
||||
if (!query) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ticks = Date.parse(query);
|
||||
if (isNaN(ticks)) {
|
||||
throw new Error(`forceNow query parameter can't be parsed`);
|
||||
}
|
||||
return new Date(ticks);
|
||||
};
|
||||
|
||||
Timefilter.prototype.get = function (indexPattern, timeRange) {
|
||||
return getTime(indexPattern, timeRange ? timeRange : this.time, this.getForceNow());
|
||||
};
|
||||
|
||||
Timefilter.prototype.calculateBounds = function (timeRange) {
|
||||
return calculateBounds(timeRange, { forceNow: this.getForceNow() });
|
||||
};
|
||||
|
||||
Timefilter.prototype.getBounds = function () {
|
||||
return this.calculateBounds(this.time);
|
||||
};
|
||||
|
||||
Timefilter.prototype.getActiveBounds = function () {
|
||||
if (this.isTimeRangeSelectorEnabled) {
|
||||
return this.getBounds();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the time bounds selector part of the time filter
|
||||
*/
|
||||
Timefilter.prototype.enableTimeRangeSelector = function () {
|
||||
this.isTimeRangeSelectorEnabled = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the time bounds selector part of the time filter
|
||||
*/
|
||||
Timefilter.prototype.disableTimeRangeSelector = function () {
|
||||
this.isTimeRangeSelectorEnabled = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the auto refresh part of the time filter
|
||||
*/
|
||||
Timefilter.prototype.enableAutoRefreshSelector = function () {
|
||||
this.isAutoRefreshSelectorEnabled = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the auto refresh part of the time filter
|
||||
*/
|
||||
Timefilter.prototype.disableAutoRefreshSelector = function () {
|
||||
this.isAutoRefreshSelectorEnabled = false;
|
||||
};
|
||||
|
||||
return new Timefilter();
|
||||
.addSetupWork((globalState) => {
|
||||
return registerTimefilterWithGlobalState(globalState);
|
||||
});
|
||||
|
|
265
src/ui/public/timefilter/timefilter.test.js
Normal file
265
src/ui/public/timefilter/timefilter.test.js
Normal file
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
jest.mock('ui/chrome',
|
||||
() => ({
|
||||
getUiSettingsClient: () => {
|
||||
return {
|
||||
get: (key) => {
|
||||
switch(key) {
|
||||
case 'timepicker:timeDefaults':
|
||||
return { from: 'now-15m', to: 'now', mode: 'quick' };
|
||||
case 'timepicker:refreshIntervalDefaults':
|
||||
return { pause: false, value: 0 };
|
||||
default:
|
||||
throw new Error(`Unexpected config key: ${key}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
}), { virtual: true });
|
||||
|
||||
jest.mock('ui/timefilter/lib/parse_querystring',
|
||||
() => ({
|
||||
parseQueryString: () => {
|
||||
return {
|
||||
// Can not access local variable from within a mock
|
||||
forceNow: global.nowTime
|
||||
};
|
||||
},
|
||||
}), { virtual: true });
|
||||
|
||||
import sinon from 'sinon';
|
||||
import expect from 'expect.js';
|
||||
import { timefilter } from './timefilter';
|
||||
|
||||
function stubNowTime(nowTime) {
|
||||
global.nowTime = nowTime;
|
||||
}
|
||||
|
||||
function clearNowTimeStub() {
|
||||
delete global.nowTime;
|
||||
}
|
||||
|
||||
describe('setTime', () => {
|
||||
let update;
|
||||
let fetch;
|
||||
|
||||
beforeEach(() => {
|
||||
update = sinon.spy();
|
||||
fetch = sinon.spy();
|
||||
timefilter.setTime({
|
||||
from: 0,
|
||||
to: 1,
|
||||
mode: 'absolute'
|
||||
});
|
||||
timefilter.on('timeUpdate', update);
|
||||
timefilter.on('fetch', fetch);
|
||||
});
|
||||
|
||||
test('should update time', () => {
|
||||
timefilter.setTime({ from: 5, to: 10, mode: 'absolute' });
|
||||
expect(timefilter.getTime()).to.eql({ from: 5, to: 10, mode: 'absolute' });
|
||||
});
|
||||
|
||||
test('should allow partial updates to time', () => {
|
||||
timefilter.setTime({ from: 5, to: 10 });
|
||||
expect(timefilter.getTime()).to.eql({ from: 5, to: 10, mode: 'absolute' });
|
||||
});
|
||||
|
||||
test('not emit anything if the time has not changed', () => {
|
||||
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 });
|
||||
expect(update.called).to.be(true);
|
||||
expect(fetch.called).to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('setRefreshInterval', () => {
|
||||
|
||||
let update;
|
||||
let fetch;
|
||||
|
||||
beforeEach(() => {
|
||||
update = sinon.spy();
|
||||
fetch = sinon.spy();
|
||||
timefilter.setRefreshInterval({
|
||||
pause: false,
|
||||
value: 0
|
||||
});
|
||||
timefilter.on('refreshIntervalUpdate', update);
|
||||
timefilter.on('fetch', fetch);
|
||||
});
|
||||
|
||||
test('should update refresh interval', () => {
|
||||
timefilter.setRefreshInterval({ pause: true, value: 10 });
|
||||
expect(timefilter.getRefreshInterval()).to.eql({ pause: true, value: 10 });
|
||||
});
|
||||
|
||||
test('should allow partial updates to refresh interval', () => {
|
||||
timefilter.setRefreshInterval({ value: 10 });
|
||||
expect(timefilter.getRefreshInterval()).to.eql({ pause: false, value: 10 });
|
||||
});
|
||||
|
||||
test('not emit anything if nothing has changed', () => {
|
||||
timefilter.setRefreshInterval({ pause: false, value: 0 });
|
||||
expect(update.called).to.be(false);
|
||||
expect(fetch.called).to.be(false);
|
||||
});
|
||||
|
||||
test('emit only an update when paused', () => {
|
||||
timefilter.setRefreshInterval({ pause: true, value: 5000 });
|
||||
expect(update.called).to.be(true);
|
||||
expect(fetch.called).to.be(false);
|
||||
});
|
||||
|
||||
test('emit update, not fetch, when switching to value: 0', () => {
|
||||
timefilter.setRefreshInterval({ pause: false, value: 5000 });
|
||||
expect(update.calledOnce).to.be(true);
|
||||
expect(fetch.calledOnce).to.be(true);
|
||||
|
||||
timefilter.setRefreshInterval({ pause: false, value: 0 });
|
||||
expect(update.calledTwice).to.be(true);
|
||||
expect(fetch.calledTwice).to.be(false);
|
||||
});
|
||||
|
||||
test('should emit update, not fetch, when moving from unpaused to paused', () => {
|
||||
timefilter.setRefreshInterval({ pause: false, value: 5000 });
|
||||
expect(update.calledOnce).to.be(true);
|
||||
expect(fetch.calledOnce).to.be(true);
|
||||
|
||||
timefilter.setRefreshInterval({ pause: true, value: 5000 });
|
||||
expect(update.calledTwice).to.be(true);
|
||||
expect(fetch.calledTwice).to.be(false);
|
||||
});
|
||||
|
||||
test('should emit update and fetch when unpaused', () => {
|
||||
timefilter.setRefreshInterval({ pause: true, value: 5000 });
|
||||
expect(update.calledOnce).to.be(true);
|
||||
expect(fetch.calledOnce).to.be(false);
|
||||
|
||||
timefilter.setRefreshInterval({ pause: false, value: 5000 });
|
||||
expect(update.calledTwice).to.be(true);
|
||||
expect(fetch.calledOnce).to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('isTimeRangeSelectorEnabled', () => {
|
||||
let update;
|
||||
|
||||
beforeEach(() => {
|
||||
update = sinon.spy();
|
||||
timefilter.on('enabledUpdated', update);
|
||||
});
|
||||
|
||||
test('should emit updated when disabled', () => {
|
||||
timefilter.disableTimeRangeSelector();
|
||||
expect(timefilter.isTimeRangeSelectorEnabled).to.be(false);
|
||||
expect(update.called).to.be(true);
|
||||
});
|
||||
|
||||
test('should emit updated when enabled', () => {
|
||||
timefilter.enableTimeRangeSelector();
|
||||
expect(timefilter.isTimeRangeSelectorEnabled).to.be(true);
|
||||
expect(update.called).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAutoRefreshSelectorEnabled', () => {
|
||||
let update;
|
||||
|
||||
beforeEach(() => {
|
||||
update = sinon.spy();
|
||||
timefilter.on('enabledUpdated', update);
|
||||
});
|
||||
|
||||
test('should emit updated when disabled', () => {
|
||||
timefilter.disableAutoRefreshSelector();
|
||||
expect(timefilter.isAutoRefreshSelectorEnabled).to.be(false);
|
||||
expect(update.called).to.be(true);
|
||||
});
|
||||
|
||||
test('should emit updated when enabled', () => {
|
||||
timefilter.enableAutoRefreshSelector();
|
||||
expect(timefilter.isAutoRefreshSelectorEnabled).to.be(true);
|
||||
expect(update.called).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculateBounds', () => {
|
||||
|
||||
const fifteenMinutesInMilliseconds = 15 * 60 * 1000;
|
||||
const clockNowTicks = new Date(2000, 1, 1, 0, 0, 0, 0).valueOf();
|
||||
|
||||
let clock;
|
||||
|
||||
beforeEach(() => {
|
||||
clock = sinon.useFakeTimers(clockNowTicks);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clock.restore();
|
||||
clearNowTimeStub();
|
||||
});
|
||||
|
||||
test('uses clock time by default', () => {
|
||||
const timeRange = {
|
||||
from: 'now-15m',
|
||||
to: 'now'
|
||||
};
|
||||
|
||||
stubNowTime(undefined);
|
||||
const result = timefilter.calculateBounds(timeRange);
|
||||
expect(result.min.valueOf()).to.eql(clockNowTicks - fifteenMinutesInMilliseconds);
|
||||
expect(result.max.valueOf()).to.eql(clockNowTicks);
|
||||
});
|
||||
|
||||
test('uses forceNow string', () => {
|
||||
const timeRange = {
|
||||
from: 'now-15m',
|
||||
to: 'now'
|
||||
};
|
||||
|
||||
const forceNowString = '1999-01-01T00:00:00.000Z';
|
||||
stubNowTime(forceNowString);
|
||||
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);
|
||||
});
|
||||
|
||||
test(`throws Error if forceNow can't be parsed`, () => {
|
||||
const timeRange = {
|
||||
from: 'now-15m',
|
||||
to: 'now'
|
||||
};
|
||||
|
||||
stubNowTime('not_a_parsable_date');
|
||||
expect(() => timefilter.calculateBounds(timeRange)).to.throwError();
|
||||
});
|
||||
});
|
||||
|
|
@ -17,27 +17,20 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import $ from 'jquery';
|
||||
import sinon from 'sinon';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
describe('kbnGlobalTimepicker', function () {
|
||||
const sandbox = sinon.createSandbox();
|
||||
|
||||
let compile;
|
||||
let scope;
|
||||
|
||||
beforeEach(() => {
|
||||
ngMock.module('kibana');
|
||||
ngMock.inject(($compile, $rootScope, timefilter) => {
|
||||
ngMock.inject(($compile, $rootScope) => {
|
||||
scope = $rootScope.$new();
|
||||
compile = (timefilterStubProperties = {}) => {
|
||||
Object.keys(timefilterStubProperties).forEach((key) => {
|
||||
sandbox.stub(timefilter, key).value(timefilterStubProperties[key]);
|
||||
});
|
||||
|
||||
compile = () => {
|
||||
const $el = $('<kbn-global-timepicker></kbn-global-timepicker>');
|
||||
$el.data('$kbnTopNavController', {}); // Mock the kbnTopNav
|
||||
$compile($el)(scope);
|
||||
|
@ -47,10 +40,6 @@ describe('kbnGlobalTimepicker', function () {
|
|||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('injects the timepicker into the DOM', () => {
|
||||
const $el = compile();
|
||||
expect($el.attr('data-test-subj')).to.be('globalTimepicker');
|
||||
|
@ -59,17 +48,16 @@ describe('kbnGlobalTimepicker', function () {
|
|||
it('sets data-shared-timefilter-* using the timefilter when auto-refresh selector is enabled', function () {
|
||||
const minString = '2000-01-01T00:00:00Z';
|
||||
const maxString = '2001-01-01T00:00:00Z';
|
||||
const bounds = {
|
||||
min: moment(minString),
|
||||
max: moment(maxString),
|
||||
};
|
||||
const timefilter = {
|
||||
isAutoRefreshSelectorEnabled: true,
|
||||
isTimeRangeSelectorEnabled: false,
|
||||
getBounds: () => bounds
|
||||
};
|
||||
|
||||
const $el = compile(timefilter);
|
||||
timefilter.enableAutoRefreshSelector();
|
||||
timefilter.disableTimeRangeSelector();
|
||||
timefilter.setTime({
|
||||
from: minString,
|
||||
to: maxString,
|
||||
mode: 'absolute'
|
||||
});
|
||||
|
||||
const $el = compile();
|
||||
|
||||
expect($el.attr('data-shared-timefilter-from')).to.eql(minString);
|
||||
expect($el.attr('data-shared-timefilter-to')).to.eql(maxString);
|
||||
|
@ -78,17 +66,16 @@ describe('kbnGlobalTimepicker', function () {
|
|||
it('sets data-shared-timefilter-* using the timefilter when time range selector is enabled', function () {
|
||||
const minString = '2000-01-01T00:00:00Z';
|
||||
const maxString = '2001-01-01T00:00:00Z';
|
||||
const bounds = {
|
||||
min: moment(minString),
|
||||
max: moment(maxString),
|
||||
};
|
||||
const timefilter = {
|
||||
isAutoRefreshSelectorEnabled: false,
|
||||
isTimeRangeSelectorEnabled: true,
|
||||
getBounds: () => bounds
|
||||
};
|
||||
|
||||
const $el = compile(timefilter);
|
||||
timefilter.disableAutoRefreshSelector();
|
||||
timefilter.enableTimeRangeSelector();
|
||||
timefilter.setTime({
|
||||
from: minString,
|
||||
to: maxString,
|
||||
mode: 'absolute'
|
||||
});
|
||||
|
||||
const $el = compile();
|
||||
|
||||
expect($el.attr('data-shared-timefilter-from')).to.eql(minString);
|
||||
expect($el.attr('data-shared-timefilter-to')).to.eql(maxString);
|
||||
|
@ -97,17 +84,16 @@ describe('kbnGlobalTimepicker', function () {
|
|||
it(`doesn't set data-shared-timefilter-* when auto-refresh and time range selectors are both disabled`, function () {
|
||||
const minString = '2000-01-01T00:00:00Z';
|
||||
const maxString = '2001-01-01T00:00:00Z';
|
||||
const bounds = {
|
||||
min: moment(minString),
|
||||
max: moment(maxString),
|
||||
};
|
||||
const timefilter = {
|
||||
isAutoRefreshSelectorEnabled: false,
|
||||
isTimeRangeSelectorEnabled: false,
|
||||
getBounds: () => bounds
|
||||
};
|
||||
|
||||
const $el = compile(timefilter);
|
||||
timefilter.disableAutoRefreshSelector();
|
||||
timefilter.disableTimeRangeSelector();
|
||||
timefilter.setTime({
|
||||
from: minString,
|
||||
to: maxString,
|
||||
mode: 'absolute'
|
||||
});
|
||||
|
||||
const $el = compile();
|
||||
|
||||
expect($el.attr('data-shared-timefilter-from')).to.eql('');
|
||||
expect($el.attr('data-shared-timefilter-to')).to.eql('');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div
|
||||
ng-show="timefilter.isAutoRefreshSelectorEnabled || timefilter.isTimeRangeSelectorEnabled"
|
||||
ng-show="timefilterValues.isAutoRefreshSelectorEnabled || timefilterValues.isTimeRangeSelectorEnabled"
|
||||
data-shared-timefilter-from="{{ getSharedTimeFilterFromDate() }}"
|
||||
data-shared-timefilter-to="{{ getSharedTimeFilterToDate() }}"
|
||||
class="kuiLocalMenu"
|
||||
|
@ -7,39 +7,39 @@
|
|||
>
|
||||
<button
|
||||
class="kuiLocalMenuItem"
|
||||
aria-label="{{ timefilter.refreshInterval.pause ? 'Resume refreshing data' : 'Pause refreshing data' }}"
|
||||
aria-label="{{ timefilterValues.refreshInterval.pause ? 'Resume refreshing data' : 'Pause refreshing data' }}"
|
||||
ng-click="toggleRefresh()"
|
||||
ng-show="timefilter.isAutoRefreshSelectorEnabled && (timefilter.refreshInterval.value > 0)"
|
||||
ng-show="timefilterValues.isAutoRefreshSelectorEnabled && (timefilterValues.refreshInterval.value > 0)"
|
||||
data-test-subj="globalTimepickerAutoRefreshButton"
|
||||
data-test-subj-state="{{ timefilter.refreshInterval.pause ? 'inactive' : 'active' }}"
|
||||
data-test-subj-state="{{ timefilterValues.refreshInterval.pause ? 'inactive' : 'active' }}"
|
||||
>
|
||||
<span
|
||||
class="kuiIcon"
|
||||
aria-hidden="true"
|
||||
ng-class="timefilter.refreshInterval.pause ? 'fa-play' : 'fa-pause'"
|
||||
ng-class="timefilterValues.refreshInterval.pause ? 'fa-play' : 'fa-pause'"
|
||||
></span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="kuiLocalMenuItem navbar-timepicker-auto-refresh-desc"
|
||||
ng-class="{'kuiLocalMenuItem-isSelected': kbnTopNav.isCurrent('interval') }"
|
||||
ng-show="timefilter.isAutoRefreshSelectorEnabled"
|
||||
ng-show="timefilterValues.isAutoRefreshSelectorEnabled"
|
||||
ng-click="kbnTopNav.toggle('interval')"
|
||||
>
|
||||
<span ng-show="timefilter.refreshInterval.value === 0">
|
||||
<span ng-show="timefilterValues.refreshInterval.value === 0">
|
||||
<span aria-hidden="true" class="kuiIcon fa-repeat"></span> Auto-refresh
|
||||
</span>
|
||||
|
||||
<span
|
||||
ng-show="timefilter.refreshInterval.value > 0"
|
||||
aria-label="{{ 'Data will refresh every ' + timefilter.refreshInterval.display }}"
|
||||
ng-show="timefilterValues.refreshInterval.value > 0"
|
||||
aria-label="{{ 'Data will refresh every ' + timefilterValues.refreshInterval.display }}"
|
||||
>
|
||||
{{ timefilter.refreshInterval.display }}
|
||||
{{ timefilterValues.refreshInterval.display }}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
ng-show="timefilter.isTimeRangeSelectorEnabled"
|
||||
ng-show="timefilterValues.isTimeRangeSelectorEnabled"
|
||||
class="kuiLocalMenuItem"
|
||||
ng-click="back()"
|
||||
aria-label="Move backward in time"
|
||||
|
@ -52,7 +52,7 @@
|
|||
</button>
|
||||
|
||||
<button
|
||||
ng-show="timefilter.isTimeRangeSelectorEnabled"
|
||||
ng-show="timefilterValues.isTimeRangeSelectorEnabled"
|
||||
data-test-subj="globalTimepickerButton"
|
||||
class="kuiLocalMenuItem navbar-timepicker-time-desc"
|
||||
ng-class="{'kuiLocalMenuItem-isSelected': kbnTopNav.isCurrent('filter')}"
|
||||
|
@ -62,14 +62,14 @@
|
|||
>
|
||||
<span aria-hidden="true" class="kuiIcon fa-clock-o"></span>
|
||||
<pretty-duration
|
||||
from="timefilter.time.from"
|
||||
to="timefilter.time.to"
|
||||
from="timefilterValues.time.from"
|
||||
to="timefilterValues.time.to"
|
||||
data-test-subj="globalTimepickerRange"
|
||||
></pretty-duration>
|
||||
</button>
|
||||
|
||||
<button
|
||||
ng-show="timefilter.isTimeRangeSelectorEnabled"
|
||||
ng-show="timefilterValues.isTimeRangeSelectorEnabled"
|
||||
class="kuiLocalMenuItem"
|
||||
ng-click="forward()"
|
||||
aria-label="Move forward in time"
|
||||
|
|
|
@ -18,50 +18,68 @@
|
|||
*/
|
||||
|
||||
import { uiModules } from '../modules';
|
||||
import { once, clone } from 'lodash';
|
||||
|
||||
import toggleHtml from './kbn_global_timepicker.html';
|
||||
import { timeNavigation } from './time_navigation';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
uiModules
|
||||
.get('kibana')
|
||||
.directive('kbnGlobalTimepicker', (timefilter, globalState, $rootScope) => {
|
||||
const listenForUpdates = once($scope => {
|
||||
$scope.$listen(timefilter, 'update', () => {
|
||||
globalState.time = clone(timefilter.time);
|
||||
globalState.refreshInterval = clone(timefilter.refreshInterval);
|
||||
.directive('kbnGlobalTimepicker', (globalState) => {
|
||||
const listenForUpdates = ($scope) => {
|
||||
$scope.$listenAndDigestAsync(timefilter, 'refreshIntervalUpdate', () => {
|
||||
globalState.refreshInterval = timefilter.getRefreshInterval();
|
||||
globalState.time = timefilter.getTime();
|
||||
globalState.save();
|
||||
setTimefilterValues($scope);
|
||||
});
|
||||
});
|
||||
$scope.$listenAndDigestAsync(timefilter, 'timeUpdate', () => {
|
||||
globalState.refreshInterval = timefilter.getRefreshInterval();
|
||||
globalState.time = timefilter.getTime();
|
||||
globalState.save();
|
||||
setTimefilterValues($scope);
|
||||
});
|
||||
$scope.$listenAndDigestAsync(timefilter, 'enabledUpdated', () => {
|
||||
setTimefilterValues($scope);
|
||||
});
|
||||
};
|
||||
|
||||
function setTimefilterValues($scope) {
|
||||
$scope.timefilterValues = {
|
||||
refreshInterval: timefilter.getRefreshInterval(),
|
||||
time: timefilter.getTime(),
|
||||
isAutoRefreshSelectorEnabled: timefilter.isAutoRefreshSelectorEnabled,
|
||||
isTimeRangeSelectorEnabled: timefilter.isTimeRangeSelectorEnabled,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
template: toggleHtml,
|
||||
replace: true,
|
||||
require: '^kbnTopNav',
|
||||
link: ($scope, element, attributes, kbnTopNav) => {
|
||||
listenForUpdates($rootScope);
|
||||
listenForUpdates($scope);
|
||||
|
||||
$rootScope.timefilter = timefilter;
|
||||
$rootScope.toggleRefresh = () => {
|
||||
timefilter.refreshInterval.pause = !timefilter.refreshInterval.pause;
|
||||
setTimefilterValues($scope);
|
||||
$scope.toggleRefresh = () => {
|
||||
timefilter.toggleRefresh();
|
||||
};
|
||||
|
||||
$scope.forward = function () {
|
||||
timefilter.time = timeNavigation.stepForward(timefilter.getBounds());
|
||||
timefilter.setTime(timeNavigation.stepForward(timefilter.getBounds()));
|
||||
};
|
||||
|
||||
$scope.back = function () {
|
||||
timefilter.time = timeNavigation.stepBackward(timefilter.getBounds());
|
||||
timefilter.setTime(timeNavigation.stepBackward(timefilter.getBounds()));
|
||||
};
|
||||
|
||||
$scope.updateFilter = function (from, to) {
|
||||
timefilter.time.from = from;
|
||||
timefilter.time.to = to;
|
||||
$scope.updateFilter = function (from, to, mode) {
|
||||
timefilter.setTime({ from, to, mode });
|
||||
kbnTopNav.close('filter');
|
||||
};
|
||||
|
||||
$scope.updateInterval = function (interval) {
|
||||
timefilter.refreshInterval = interval;
|
||||
timefilter.setRefreshInterval(interval);
|
||||
kbnTopNav.close('interval');
|
||||
};
|
||||
|
||||
|
|
|
@ -165,7 +165,7 @@ module.directive('kbnTimepicker', function (refreshIntervals) {
|
|||
};
|
||||
|
||||
$scope.setQuick = function (from, to) {
|
||||
$scope.onFilterSelect({ from, to });
|
||||
$scope.onFilterSelect({ from, to, mode: TIME_MODES.QUICK });
|
||||
};
|
||||
|
||||
$scope.setToNow = function (key) {
|
||||
|
@ -203,7 +203,8 @@ module.directive('kbnTimepicker', function (refreshIntervals) {
|
|||
$scope.applyRelative = function () {
|
||||
$scope.onFilterSelect({
|
||||
from: getRelativeString('from'),
|
||||
to: getRelativeString('to')
|
||||
to: getRelativeString('to'),
|
||||
mode: TIME_MODES.RELATIVE,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -224,7 +225,8 @@ module.directive('kbnTimepicker', function (refreshIntervals) {
|
|||
$scope.applyAbsolute = function () {
|
||||
$scope.onFilterSelect({
|
||||
from: moment($scope.absolute.from),
|
||||
to: moment($scope.absolute.to)
|
||||
to: moment($scope.absolute.to),
|
||||
mode: TIME_MODES.ABSOLUTE,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1,254 +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 _ from 'lodash';
|
||||
import expect from 'expect.js';
|
||||
import moment from 'moment';
|
||||
import ngMock from 'ng_mock';
|
||||
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
|
||||
import { UtilsBrushEventProvider } from '../brush_event';
|
||||
|
||||
describe('brushEvent', function () {
|
||||
const DAY_IN_MS = 24 * 60 * 60 * 1000;
|
||||
const JAN_01_2014 = 1388559600000;
|
||||
let brushEventFn;
|
||||
let timefilter;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function (Private, $injector, _timefilter_) {
|
||||
brushEventFn = Private(UtilsBrushEventProvider);
|
||||
timefilter = _timefilter_;
|
||||
}));
|
||||
|
||||
it('is a function that returns a function', function () {
|
||||
expect(brushEventFn).to.be.a(Function);
|
||||
expect(brushEventFn({})).to.be.a(Function);
|
||||
});
|
||||
|
||||
describe('returned function', function () {
|
||||
let $state;
|
||||
let brushEvent;
|
||||
|
||||
const baseState = {
|
||||
filters: [],
|
||||
};
|
||||
|
||||
const baseEvent = {
|
||||
data: {
|
||||
fieldFormatter: _.constant({}),
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(ngMock.inject(function (Private) {
|
||||
baseEvent.data.indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
|
||||
$state = _.cloneDeep(baseState);
|
||||
brushEvent = brushEventFn($state);
|
||||
}));
|
||||
|
||||
it('should be a function', function () {
|
||||
expect(brushEvent).to.be.a(Function);
|
||||
});
|
||||
|
||||
it('ignores event when data.xAxisField not provided', function () {
|
||||
const event = _.cloneDeep(baseEvent);
|
||||
brushEvent(event);
|
||||
expect($state)
|
||||
.not.have.property('$newFilters');
|
||||
});
|
||||
|
||||
describe('handles an event when the x-axis field is a date field', function () {
|
||||
describe('date field is index pattern timefield', function () {
|
||||
let dateEvent;
|
||||
const dateField = {
|
||||
name: 'time',
|
||||
type: 'date'
|
||||
};
|
||||
|
||||
beforeEach(ngMock.inject(function () {
|
||||
dateEvent = _.cloneDeep(baseEvent);
|
||||
dateEvent.data.xAxisField = dateField;
|
||||
}));
|
||||
|
||||
it('by ignoring the event when range spans zero time', function () {
|
||||
const event = _.cloneDeep(dateEvent);
|
||||
event.range = [JAN_01_2014, JAN_01_2014];
|
||||
brushEvent(event);
|
||||
expect($state)
|
||||
.not.have.property('$newFilters');
|
||||
});
|
||||
|
||||
it('by updating the timefilter', function () {
|
||||
const event = _.cloneDeep(dateEvent);
|
||||
event.range = [JAN_01_2014, JAN_01_2014 + DAY_IN_MS];
|
||||
brushEvent(event);
|
||||
expect(timefilter.time.mode).to.be('absolute');
|
||||
expect(moment.isMoment(timefilter.time.from))
|
||||
.to.be(true);
|
||||
// Set to a baseline timezone for comparison.
|
||||
expect(timefilter.time.from.utcOffset(0).format('YYYY-MM-DD'))
|
||||
.to.equal('2014-01-01');
|
||||
expect(moment.isMoment(timefilter.time.to))
|
||||
.to.be(true);
|
||||
// Set to a baseline timezone for comparison.
|
||||
expect(timefilter.time.to.utcOffset(0).format('YYYY-MM-DD'))
|
||||
.to.equal('2014-01-02');
|
||||
});
|
||||
});
|
||||
|
||||
describe('date field is not index pattern timefield', function () {
|
||||
let dateEvent;
|
||||
const dateField = {
|
||||
name: 'anotherTimeField',
|
||||
type: 'date'
|
||||
};
|
||||
|
||||
beforeEach(ngMock.inject(function () {
|
||||
dateEvent = _.cloneDeep(baseEvent);
|
||||
dateEvent.data.xAxisField = dateField;
|
||||
}));
|
||||
|
||||
it('creates a new range filter', function () {
|
||||
const event = _.cloneDeep(dateEvent);
|
||||
const rangeBegin = JAN_01_2014;
|
||||
const rangeEnd = rangeBegin + DAY_IN_MS;
|
||||
event.range = [rangeBegin, rangeEnd];
|
||||
brushEvent(event);
|
||||
expect($state)
|
||||
.to.have.property('$newFilters');
|
||||
expect($state.filters.length)
|
||||
.to.equal(0);
|
||||
expect($state.$newFilters.length)
|
||||
.to.equal(1);
|
||||
expect($state.$newFilters[0].range.anotherTimeField.gte)
|
||||
.to.equal(rangeBegin);
|
||||
expect($state.$newFilters[0].range.anotherTimeField.lt)
|
||||
.to.equal(rangeEnd);
|
||||
expect($state.$newFilters[0].range.anotherTimeField).to.have.property('format');
|
||||
expect($state.$newFilters[0].range.anotherTimeField.format)
|
||||
.to.equal('epoch_millis');
|
||||
});
|
||||
|
||||
it('converts Date fields to milliseconds', function () {
|
||||
const event = _.cloneDeep(dateEvent);
|
||||
const rangeBeginMs = JAN_01_2014;
|
||||
const rangeEndMs = rangeBeginMs + DAY_IN_MS;
|
||||
const rangeBegin = new Date(rangeBeginMs);
|
||||
const rangeEnd = new Date(rangeEndMs);
|
||||
event.range = [rangeBegin, rangeEnd];
|
||||
brushEvent(event);
|
||||
expect($state)
|
||||
.to.have.property('$newFilters');
|
||||
expect($state.filters.length)
|
||||
.to.equal(0);
|
||||
expect($state.$newFilters.length)
|
||||
.to.equal(1);
|
||||
expect($state.$newFilters[0].range.anotherTimeField.gte)
|
||||
.to.equal(rangeBeginMs);
|
||||
expect($state.$newFilters[0].range.anotherTimeField.lt)
|
||||
.to.equal(rangeEndMs);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('handles an event when the x-axis field is a number', function () {
|
||||
let numberEvent;
|
||||
const numberField = {
|
||||
name: 'numberField',
|
||||
type: 'number'
|
||||
};
|
||||
|
||||
beforeEach(ngMock.inject(function () {
|
||||
numberEvent = _.cloneDeep(baseEvent);
|
||||
numberEvent.data.xAxisField = numberField;
|
||||
}));
|
||||
|
||||
it('by ignoring the event when range does not span at least 2 values', function () {
|
||||
const event = _.cloneDeep(numberEvent);
|
||||
event.range = [1];
|
||||
brushEvent(event);
|
||||
expect($state)
|
||||
.not.have.property('$newFilters');
|
||||
});
|
||||
|
||||
it('by creating a new filter', function () {
|
||||
const event = _.cloneDeep(numberEvent);
|
||||
event.range = [1, 2, 3, 4];
|
||||
brushEvent(event);
|
||||
expect($state)
|
||||
.to.have.property('$newFilters');
|
||||
expect($state.filters.length)
|
||||
.to.equal(0);
|
||||
expect($state.$newFilters.length)
|
||||
.to.equal(1);
|
||||
expect($state.$newFilters[0].range.numberField.gte)
|
||||
.to.equal(1);
|
||||
expect($state.$newFilters[0].range.numberField.lt)
|
||||
.to.equal(4);
|
||||
expect($state.$newFilters[0].range.numberField).not.to.have.property('format');
|
||||
});
|
||||
|
||||
it('by updating the existing range filter', function () {
|
||||
const event = _.cloneDeep(numberEvent);
|
||||
event.range = [3, 7];
|
||||
$state.filters.push({
|
||||
meta: {
|
||||
key: 'numberField'
|
||||
},
|
||||
range: { gte: 1, lt: 4 }
|
||||
});
|
||||
brushEvent(event);
|
||||
expect($state)
|
||||
.not.have.property('$newFilters');
|
||||
expect($state.filters.length)
|
||||
.to.equal(1);
|
||||
expect($state.filters[0].range.numberField.gte)
|
||||
.to.equal(3);
|
||||
expect($state.filters[0].range.numberField.lt)
|
||||
.to.equal(7);
|
||||
});
|
||||
|
||||
it('by updating the existing scripted filter', function () {
|
||||
const event = _.cloneDeep(numberEvent);
|
||||
event.range = [3, 7];
|
||||
$state.filters.push({
|
||||
meta: {
|
||||
key: 'numberField'
|
||||
},
|
||||
script: {
|
||||
script: {
|
||||
params: {
|
||||
gte: 1,
|
||||
lt: 4
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
brushEvent(event);
|
||||
expect($state)
|
||||
.not.have.property('$newFilters');
|
||||
expect($state.filters.length)
|
||||
.to.equal(1);
|
||||
expect($state.filters[0].script.script.params.gte)
|
||||
.to.equal(3);
|
||||
expect($state.filters[0].script.script.params.lt)
|
||||
.to.equal(7);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
256
src/ui/public/utils/__tests__/brush_event.test.js
Normal file
256
src/ui/public/utils/__tests__/brush_event.test.js
Normal file
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
jest.mock('ui/chrome',
|
||||
() => ({
|
||||
getUiSettingsClient: () => {
|
||||
return {
|
||||
get: (key) => {
|
||||
switch(key) {
|
||||
case 'timepicker:timeDefaults':
|
||||
return { from: 'now-15m', to: 'now', mode: 'quick' };
|
||||
case 'timepicker:refreshIntervalDefaults':
|
||||
return { display: 'Off', pause: false, value: 0 };
|
||||
default:
|
||||
throw new Error(`Unexpected config key: ${key}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
}), { virtual: true });
|
||||
|
||||
import _ from 'lodash';
|
||||
import expect from 'expect.js';
|
||||
import moment from 'moment';
|
||||
import { onBrushEvent } from '../brush_event';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
describe('brushEvent', () => {
|
||||
const DAY_IN_MS = 24 * 60 * 60 * 1000;
|
||||
const JAN_01_2014 = 1388559600000;
|
||||
|
||||
let $state;
|
||||
|
||||
const baseState = {
|
||||
filters: [],
|
||||
};
|
||||
|
||||
const baseEvent = {
|
||||
data: {
|
||||
fieldFormatter: _.constant({}),
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
baseEvent.data.indexPattern = {
|
||||
id: 'logstash-*',
|
||||
timeFieldName: 'time'
|
||||
};
|
||||
$state = _.cloneDeep(baseState);
|
||||
});
|
||||
|
||||
test('should be a function', () => {
|
||||
expect(onBrushEvent).to.be.a(Function);
|
||||
});
|
||||
|
||||
test('ignores event when data.xAxisField not provided', () => {
|
||||
const event = _.cloneDeep(baseEvent);
|
||||
onBrushEvent(event, $state);
|
||||
expect($state)
|
||||
.not.have.property('$newFilters');
|
||||
});
|
||||
|
||||
describe('handles an event when the x-axis field is a date field', () => {
|
||||
describe('date field is index pattern timefield', () => {
|
||||
let dateEvent;
|
||||
const dateField = {
|
||||
name: 'time',
|
||||
type: 'date'
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
dateEvent = _.cloneDeep(baseEvent);
|
||||
dateEvent.data.xAxisField = dateField;
|
||||
});
|
||||
|
||||
test('by ignoring the event when range spans zero time', () => {
|
||||
const event = _.cloneDeep(dateEvent);
|
||||
event.range = [JAN_01_2014, JAN_01_2014];
|
||||
onBrushEvent(event, $state);
|
||||
expect($state)
|
||||
.not.have.property('$newFilters');
|
||||
});
|
||||
|
||||
test('by updating the timefilter', () => {
|
||||
const event = _.cloneDeep(dateEvent);
|
||||
event.range = [JAN_01_2014, JAN_01_2014 + DAY_IN_MS];
|
||||
onBrushEvent(event, $state);
|
||||
expect(timefilter.getTime().mode).to.be('absolute');
|
||||
expect(moment.isMoment(timefilter.getTime().from))
|
||||
.to.be(true);
|
||||
// Set to a baseline timezone for comparison.
|
||||
expect(timefilter.getTime().from.utcOffset(0).format('YYYY-MM-DD'))
|
||||
.to.equal('2014-01-01');
|
||||
expect(moment.isMoment(timefilter.getTime().to))
|
||||
.to.be(true);
|
||||
// Set to a baseline timezone for comparison.
|
||||
expect(timefilter.getTime().to.utcOffset(0).format('YYYY-MM-DD'))
|
||||
.to.equal('2014-01-02');
|
||||
});
|
||||
});
|
||||
|
||||
describe('date field is not index pattern timefield', () => {
|
||||
let dateEvent;
|
||||
const dateField = {
|
||||
name: 'anotherTimeField',
|
||||
type: 'date'
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
dateEvent = _.cloneDeep(baseEvent);
|
||||
dateEvent.data.xAxisField = dateField;
|
||||
});
|
||||
|
||||
test('creates a new range filter', () => {
|
||||
const event = _.cloneDeep(dateEvent);
|
||||
const rangeBegin = JAN_01_2014;
|
||||
const rangeEnd = rangeBegin + DAY_IN_MS;
|
||||
event.range = [rangeBegin, rangeEnd];
|
||||
onBrushEvent(event, $state);
|
||||
expect($state)
|
||||
.to.have.property('$newFilters');
|
||||
expect($state.filters.length)
|
||||
.to.equal(0);
|
||||
expect($state.$newFilters.length)
|
||||
.to.equal(1);
|
||||
expect($state.$newFilters[0].range.anotherTimeField.gte)
|
||||
.to.equal(rangeBegin);
|
||||
expect($state.$newFilters[0].range.anotherTimeField.lt)
|
||||
.to.equal(rangeEnd);
|
||||
expect($state.$newFilters[0].range.anotherTimeField).to.have.property('format');
|
||||
expect($state.$newFilters[0].range.anotherTimeField.format)
|
||||
.to.equal('epoch_millis');
|
||||
});
|
||||
|
||||
test('converts Date fields to milliseconds', () => {
|
||||
const event = _.cloneDeep(dateEvent);
|
||||
const rangeBeginMs = JAN_01_2014;
|
||||
const rangeEndMs = rangeBeginMs + DAY_IN_MS;
|
||||
const rangeBegin = new Date(rangeBeginMs);
|
||||
const rangeEnd = new Date(rangeEndMs);
|
||||
event.range = [rangeBegin, rangeEnd];
|
||||
onBrushEvent(event, $state);
|
||||
expect($state)
|
||||
.to.have.property('$newFilters');
|
||||
expect($state.filters.length)
|
||||
.to.equal(0);
|
||||
expect($state.$newFilters.length)
|
||||
.to.equal(1);
|
||||
expect($state.$newFilters[0].range.anotherTimeField.gte)
|
||||
.to.equal(rangeBeginMs);
|
||||
expect($state.$newFilters[0].range.anotherTimeField.lt)
|
||||
.to.equal(rangeEndMs);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('handles an event when the x-axis field is a number', () => {
|
||||
let numberEvent;
|
||||
const numberField = {
|
||||
name: 'numberField',
|
||||
type: 'number'
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
numberEvent = _.cloneDeep(baseEvent);
|
||||
numberEvent.data.xAxisField = numberField;
|
||||
});
|
||||
|
||||
test('by ignoring the event when range does not span at least 2 values', () => {
|
||||
const event = _.cloneDeep(numberEvent);
|
||||
event.range = [1];
|
||||
onBrushEvent(event, $state);
|
||||
expect($state).not.have.property('$newFilters');
|
||||
});
|
||||
|
||||
test('by creating a new filter', () => {
|
||||
const event = _.cloneDeep(numberEvent);
|
||||
event.range = [1, 2, 3, 4];
|
||||
onBrushEvent(event, $state);
|
||||
expect($state)
|
||||
.to.have.property('$newFilters');
|
||||
expect($state.filters.length)
|
||||
.to.equal(0);
|
||||
expect($state.$newFilters.length)
|
||||
.to.equal(1);
|
||||
expect($state.$newFilters[0].range.numberField.gte)
|
||||
.to.equal(1);
|
||||
expect($state.$newFilters[0].range.numberField.lt)
|
||||
.to.equal(4);
|
||||
expect($state.$newFilters[0].range.numberField).not.to.have.property('format');
|
||||
});
|
||||
|
||||
test('by updating the existing range filter', () => {
|
||||
const event = _.cloneDeep(numberEvent);
|
||||
event.range = [3, 7];
|
||||
$state.filters.push({
|
||||
meta: {
|
||||
key: 'numberField'
|
||||
},
|
||||
range: { gte: 1, lt: 4 }
|
||||
});
|
||||
onBrushEvent(event, $state);
|
||||
expect($state)
|
||||
.not.have.property('$newFilters');
|
||||
expect($state.filters.length)
|
||||
.to.equal(1);
|
||||
expect($state.filters[0].range.numberField.gte)
|
||||
.to.equal(3);
|
||||
expect($state.filters[0].range.numberField.lt)
|
||||
.to.equal(7);
|
||||
});
|
||||
|
||||
test('by updating the existing scripted filter', () => {
|
||||
const event = _.cloneDeep(numberEvent);
|
||||
event.range = [3, 7];
|
||||
$state.filters.push({
|
||||
meta: {
|
||||
key: 'numberField'
|
||||
},
|
||||
script: {
|
||||
script: {
|
||||
params: {
|
||||
gte: 1,
|
||||
lt: 4
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
onBrushEvent(event, $state);
|
||||
expect($state)
|
||||
.not.have.property('$newFilters');
|
||||
expect($state.filters.length)
|
||||
.to.equal(1);
|
||||
expect($state.filters[0].script.script.params.gte)
|
||||
.to.equal(3);
|
||||
expect($state.filters[0].script.script.params.lt)
|
||||
.to.equal(7);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -20,73 +20,72 @@
|
|||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { buildRangeFilter } from '../filter_manager/lib/range';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
export function UtilsBrushEventProvider(timefilter) {
|
||||
return $state => {
|
||||
return event => {
|
||||
if (!event.data.xAxisField) {
|
||||
return;
|
||||
}
|
||||
export function onBrushEvent(event, $state) {
|
||||
if (!event.data.xAxisField) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isDate = event.data.xAxisField.type === 'date';
|
||||
const isNumber = event.data.xAxisField.type === 'number';
|
||||
const isDate = event.data.xAxisField.type === 'date';
|
||||
const isNumber = event.data.xAxisField.type === 'number';
|
||||
|
||||
if (isDate &&
|
||||
event.data.xAxisField.name === event.data.indexPattern.timeFieldName) {
|
||||
setTimefilter();
|
||||
} else if (isDate || isNumber) {
|
||||
setRange();
|
||||
}
|
||||
if (isDate &&
|
||||
event.data.xAxisField.name === event.data.indexPattern.timeFieldName) {
|
||||
setTimefilter();
|
||||
} else if (isDate || isNumber) {
|
||||
setRange();
|
||||
}
|
||||
|
||||
function setTimefilter() {
|
||||
const from = moment(event.range[0]);
|
||||
const to = moment(event.range[1]);
|
||||
function setTimefilter() {
|
||||
const from = moment(event.range[0]);
|
||||
const to = moment(event.range[1]);
|
||||
|
||||
if (to - from === 0) return;
|
||||
if (to - from === 0) return;
|
||||
|
||||
timefilter.time.from = from;
|
||||
timefilter.time.to = to;
|
||||
timefilter.time.mode = 'absolute';
|
||||
}
|
||||
timefilter.setTime({
|
||||
from,
|
||||
to,
|
||||
mode: 'absolute'
|
||||
});
|
||||
}
|
||||
|
||||
function setRange() {
|
||||
if (event.range.length <= 1) return;
|
||||
function setRange() {
|
||||
if (event.range.length <= 1) return;
|
||||
|
||||
const existingFilter = $state.filters.find(filter => (
|
||||
filter.meta && filter.meta.key === event.data.xAxisField.name
|
||||
));
|
||||
const existingFilter = $state.filters.find(filter => (
|
||||
filter.meta && filter.meta.key === event.data.xAxisField.name
|
||||
));
|
||||
|
||||
const min = event.range[0];
|
||||
const max = event.range[event.range.length - 1];
|
||||
let range;
|
||||
if (isDate) {
|
||||
range = {
|
||||
gte: moment(min).valueOf(),
|
||||
lt: moment(max).valueOf(),
|
||||
format: 'epoch_millis'
|
||||
};
|
||||
} else {
|
||||
range = {
|
||||
gte: min,
|
||||
lt: max
|
||||
};
|
||||
}
|
||||
const min = event.range[0];
|
||||
const max = event.range[event.range.length - 1];
|
||||
let range;
|
||||
if (isDate) {
|
||||
range = {
|
||||
gte: moment(min).valueOf(),
|
||||
lt: moment(max).valueOf(),
|
||||
format: 'epoch_millis'
|
||||
};
|
||||
} else {
|
||||
range = {
|
||||
gte: min,
|
||||
lt: max
|
||||
};
|
||||
}
|
||||
|
||||
if (_.has(existingFilter, 'range')) {
|
||||
existingFilter.range[event.data.xAxisField.name] = range;
|
||||
} else if (_.has(existingFilter, 'script.script.params.gte')
|
||||
&& _.has(existingFilter, 'script.script.params.lt')) {
|
||||
existingFilter.script.script.params.gte = min;
|
||||
existingFilter.script.script.params.lt = max;
|
||||
} else {
|
||||
const newFilter = buildRangeFilter(
|
||||
event.data.xAxisField,
|
||||
range,
|
||||
event.data.indexPattern,
|
||||
event.data.xAxisFormatter);
|
||||
$state.$newFilters = [newFilter];
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
if (_.has(existingFilter, 'range')) {
|
||||
existingFilter.range[event.data.xAxisField.name] = range;
|
||||
} else if (_.has(existingFilter, 'script.script.params.gte')
|
||||
&& _.has(existingFilter, 'script.script.params.lt')) {
|
||||
existingFilter.script.script.params.gte = min;
|
||||
existingFilter.script.script.params.lt = max;
|
||||
} else {
|
||||
const newFilter = buildRangeFilter(
|
||||
event.data.xAxisField,
|
||||
range,
|
||||
event.data.indexPattern,
|
||||
event.data.xAxisFormatter);
|
||||
$state.$newFilters = [newFilter];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,12 +21,13 @@ import _ from 'lodash';
|
|||
import { SearchSourceProvider } from '../../courier/data_source/search_source';
|
||||
import { VisRequestHandlersRegistryProvider } from '../../registry/vis_request_handlers';
|
||||
import { calculateObjectHash } from '../lib/calculate_object_hash';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { getRequestInspectorStats, getResponseInspectorStats } from '../../courier/utils/courier_inspector_utils';
|
||||
import { tabifyAggResponse } from '../../agg_response/tabify/tabify';
|
||||
|
||||
import { FormattedData } from '../../inspector/adapters';
|
||||
|
||||
const CourierRequestHandlerProvider = function (Private, courier, timefilter) {
|
||||
const CourierRequestHandlerProvider = function (Private, courier) {
|
||||
const SearchSource = Private(SearchSourceProvider);
|
||||
|
||||
/**
|
||||
|
@ -105,7 +106,7 @@ const CourierRequestHandlerProvider = function (Private, courier, timefilter) {
|
|||
});
|
||||
|
||||
timeFilterSearchSource.set('filter', () => {
|
||||
return timefilter.get(searchSource.get('index'), timeRange);
|
||||
return timefilter.createFilter(searchSource.get('index'), timeRange);
|
||||
});
|
||||
|
||||
requestSearchSource.set('filter', filters);
|
||||
|
|
|
@ -32,13 +32,14 @@ import _ from 'lodash';
|
|||
import { VisTypesRegistryProvider } from '../registry/vis_types';
|
||||
import { AggConfigs } from './agg_configs';
|
||||
import { PersistedState } from '../persisted_state';
|
||||
import { UtilsBrushEventProvider } from '../utils/brush_event';
|
||||
import { onBrushEvent } from '../utils/brush_event';
|
||||
import { FilterBarQueryFilterProvider } from '../filter_bar/query_filter';
|
||||
import { FilterBarClickHandlerProvider } from '../filter_bar/filter_bar_click_handler';
|
||||
import { updateVisualizationConfig } from './vis_update';
|
||||
import { queryManagerFactory } from '../query_manager';
|
||||
import { SearchSourceProvider } from '../courier/data_source/search_source';
|
||||
import { SavedObjectsClientProvider } from '../saved_objects';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
import { Inspector } from '../inspector';
|
||||
import { RequestAdapter, DataAdapter } from '../inspector/adapters';
|
||||
|
@ -55,9 +56,8 @@ const getTerms = (table, columnIndex, rowIndex) => {
|
|||
}))];
|
||||
};
|
||||
|
||||
export function VisProvider(Private, Promise, indexPatterns, timefilter, getAppState) {
|
||||
export function VisProvider(Private, Promise, indexPatterns, getAppState) {
|
||||
const visTypes = Private(VisTypesRegistryProvider);
|
||||
const brushEvent = Private(UtilsBrushEventProvider);
|
||||
const queryFilter = Private(FilterBarQueryFilterProvider);
|
||||
const filterBarClickHandler = Private(FilterBarClickHandlerProvider);
|
||||
const SearchSource = Private(SearchSourceProvider);
|
||||
|
@ -108,8 +108,7 @@ export function VisProvider(Private, Promise, indexPatterns, timefilter, getAppS
|
|||
}
|
||||
queryFilter.addFilters(filter);
|
||||
}, brush: (event) => {
|
||||
const appState = getAppState();
|
||||
brushEvent(appState)(event);
|
||||
onBrushEvent(event, getAppState());
|
||||
}
|
||||
},
|
||||
createInheritedSearchSource: (parentSearchSource) => {
|
||||
|
|
|
@ -34,7 +34,7 @@ import {
|
|||
|
||||
uiModules
|
||||
.get('kibana/directive', ['ngSanitize'])
|
||||
.directive('visualize', function ($timeout, Notifier, Private, timefilter, getAppState, Promise) {
|
||||
.directive('visualize', function ($timeout, Notifier, Private, getAppState, Promise) {
|
||||
const notify = new Notifier({ location: 'Visualize' });
|
||||
const requestHandlers = Private(VisRequestHandlersRegistryProvider);
|
||||
const responseHandlers = Private(VisResponseHandlersRegistryProvider);
|
||||
|
|
|
@ -4,6 +4,27 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
jest.mock(
|
||||
'ui/chrome',
|
||||
() => ({
|
||||
getUiSettingsClient: () => {
|
||||
return {
|
||||
get: key => {
|
||||
switch (key) {
|
||||
case 'timepicker:timeDefaults':
|
||||
return { from: 'now-15m', to: 'now', mode: 'quick' };
|
||||
case 'timepicker:refreshIntervalDefaults':
|
||||
return { display: 'Off', pause: false, value: 0 };
|
||||
default:
|
||||
throw new Error(`Unexpected config key: ${key}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}),
|
||||
{ virtual: true }
|
||||
);
|
||||
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
|
|
@ -4,6 +4,27 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
jest.mock(
|
||||
'ui/chrome',
|
||||
() => ({
|
||||
getUiSettingsClient: () => {
|
||||
return {
|
||||
get: key => {
|
||||
switch (key) {
|
||||
case 'timepicker:timeDefaults':
|
||||
return { from: 'now-15m', to: 'now', mode: 'quick' };
|
||||
case 'timepicker:refreshIntervalDefaults':
|
||||
return { display: 'Off', pause: false, value: 0 };
|
||||
default:
|
||||
throw new Error(`Unexpected config key: ${key}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}),
|
||||
{ virtual: true }
|
||||
);
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import TransactionOverview from '../view';
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import CustomPlot from '../CustomPlot';
|
||||
import { getTimefilter } from '../../../../utils/timepicker';
|
||||
import { asMillis, tpmUnit, asInteger } from '../../../../utils/formatters';
|
||||
import styled from 'styled-components';
|
||||
import { units, unit, px } from '../../../../style/variables';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import moment from 'moment';
|
||||
|
||||
const ChartsWrapper = styled.div`
|
||||
display: flex;
|
||||
|
@ -42,9 +43,12 @@ export class Charts extends Component {
|
|||
onHover = hoverIndex => this.setState({ hoverIndex });
|
||||
onMouseLeave = () => this.setState({ hoverIndex: null });
|
||||
onSelectionEnd = selection => {
|
||||
const timefilter = getTimefilter();
|
||||
this.setState({ hoverIndex: null });
|
||||
timefilter.setTime(selection.start, selection.end);
|
||||
timefilter.setTime({
|
||||
from: moment(selection.start).toISOString(),
|
||||
to: moment(selection.end).toISOString(),
|
||||
mode: 'absolute'
|
||||
});
|
||||
};
|
||||
|
||||
getResponseTimeTickFormatter = t => {
|
||||
|
|
|
@ -4,13 +4,12 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import chrome from 'ui/chrome';
|
||||
import 'ui/autoload/all';
|
||||
import { updateTimePicker } from '../../store/urlParams';
|
||||
import { timefilter, registerTimefilterWithGlobalState } from 'ui/timefilter';
|
||||
|
||||
let globalTimefilter;
|
||||
let currentInterval;
|
||||
|
||||
// hack to wait for angular template to be ready
|
||||
|
@ -37,7 +36,7 @@ export function initTimepicker(history, dispatch, callback) {
|
|||
|
||||
uiModules
|
||||
.get('app/apm', [])
|
||||
.controller('TimePickerController', ($scope, timefilter, globalState) => {
|
||||
.controller('TimePickerController', ($scope, globalState) => {
|
||||
// Add APM feedback menu
|
||||
// TODO: move this somewhere else
|
||||
$scope.topNavMenu = [];
|
||||
|
@ -49,55 +48,41 @@ export function initTimepicker(history, dispatch, callback) {
|
|||
});
|
||||
|
||||
history.listen(() => {
|
||||
updateRefreshRate(dispatch, timefilter);
|
||||
updateRefreshRate(dispatch);
|
||||
globalState.fetch();
|
||||
});
|
||||
timefilter.setTime = (from, to) => {
|
||||
timefilter.time.from = moment(from).toISOString();
|
||||
timefilter.time.to = moment(to).toISOString();
|
||||
$scope.$apply();
|
||||
};
|
||||
timefilter.enableTimeRangeSelector();
|
||||
timefilter.enableAutoRefreshSelector();
|
||||
timefilter.init();
|
||||
|
||||
updateRefreshRate(dispatch, timefilter);
|
||||
updateRefreshRate(dispatch);
|
||||
|
||||
timefilter.on('update', () => dispatch(getAction(timefilter)));
|
||||
$scope.$listen(timefilter, 'timeUpdate', () =>
|
||||
dispatch(updateTimePickerAction())
|
||||
);
|
||||
|
||||
// hack to access timefilter outside Angular
|
||||
globalTimefilter = timefilter;
|
||||
registerTimefilterWithGlobalState(globalState);
|
||||
|
||||
Promise.all([waitForAngularReady]).then(callback);
|
||||
});
|
||||
}
|
||||
|
||||
function getAction(timefilter) {
|
||||
function updateTimePickerAction() {
|
||||
return updateTimePicker({
|
||||
min: timefilter.getBounds().min.toISOString(),
|
||||
max: timefilter.getBounds().max.toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
function updateRefreshRate(dispatch, timefilter) {
|
||||
const refreshInterval = timefilter.refreshInterval.value;
|
||||
function updateRefreshRate(dispatch) {
|
||||
const refreshInterval = timefilter.getRefreshInterval().value;
|
||||
if (currentInterval) {
|
||||
clearInterval(currentInterval);
|
||||
}
|
||||
|
||||
if (refreshInterval > 0 && !timefilter.refreshInterval.pause) {
|
||||
if (refreshInterval > 0 && !timefilter.getRefreshInterval().pause) {
|
||||
currentInterval = setInterval(
|
||||
() => dispatch(getAction(timefilter)),
|
||||
() => dispatch(updateTimePickerAction()),
|
||||
refreshInterval
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function getTimefilter() {
|
||||
if (!globalTimefilter) {
|
||||
throw new Error(
|
||||
'Timepicker must be initialized before calling getTimefilter'
|
||||
);
|
||||
}
|
||||
return globalTimefilter;
|
||||
}
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
import 'ngreact';
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
const module = uiModules.get('apps/ml', ['react']);
|
||||
|
||||
import { AnomaliesTable } from './anomalies_table';
|
||||
|
||||
module.directive('mlAnomaliesTable', function ($injector) {
|
||||
const timefilter = $injector.get('timefilter');
|
||||
const reactDirective = $injector.get('reactDirective');
|
||||
|
||||
return reactDirective(
|
||||
|
|
|
@ -18,7 +18,6 @@ import {
|
|||
EuiPopover
|
||||
} from '@elastic/eui';
|
||||
|
||||
import 'ui/timefilter';
|
||||
import chrome from 'ui/chrome';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
|
||||
|
|
|
@ -21,10 +21,10 @@ import { calculateTextWidth } from 'plugins/ml/util/string_utils';
|
|||
import { IntervalHelperProvider } from 'plugins/ml/util/ml_time_buckets';
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
const module = uiModules.get('apps/ml');
|
||||
|
||||
module.directive('mlDocumentCountChart', function (
|
||||
timefilter,
|
||||
Private,
|
||||
mlChartTooltipService) {
|
||||
|
||||
|
|
|
@ -11,7 +11,9 @@ import moment from 'moment';
|
|||
|
||||
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||
|
||||
export function FullTimeRangeSelectorServiceProvider(timefilter, Notifier) {
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
export function FullTimeRangeSelectorServiceProvider(Notifier) {
|
||||
const notify = new Notifier();
|
||||
|
||||
function setFullTimeRange(indexPattern, query) {
|
||||
|
@ -22,8 +24,10 @@ export function FullTimeRangeSelectorServiceProvider(timefilter, Notifier) {
|
|||
query
|
||||
})
|
||||
.then((resp) => {
|
||||
timefilter.time.from = moment(resp.start.epoch).toISOString();
|
||||
timefilter.time.to = moment(resp.end.epoch).toISOString();
|
||||
timefilter.setTime({
|
||||
from: moment(resp.start.epoch).toISOString(),
|
||||
to: moment(resp.end.epoch).toISOString()
|
||||
});
|
||||
})
|
||||
.catch((resp) => {
|
||||
notify.error(resp);
|
||||
|
|
|
@ -21,10 +21,11 @@ import { isTimeSeriesViewJob } from 'plugins/ml/../common/util/job_utils';
|
|||
import { mlJobService } from 'plugins/ml/services/job_service';
|
||||
import { JobSelectServiceProvider } from 'plugins/ml/components/job_select_list/job_select_service';
|
||||
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml');
|
||||
|
||||
module.directive('mlJobSelectList', function (Private, timefilter) {
|
||||
module.directive('mlJobSelectList', function (Private) {
|
||||
return {
|
||||
restrict: 'AE',
|
||||
replace: true,
|
||||
|
@ -254,8 +255,10 @@ module.directive('mlJobSelectList', function (Private, timefilter) {
|
|||
if (times.length) {
|
||||
const min = _.min(times);
|
||||
const max = _.max(times);
|
||||
timefilter.time.from = moment(min).toISOString();
|
||||
timefilter.time.to = moment(max).toISOString();
|
||||
timefilter.setTime({
|
||||
from: moment(min).toISOString(),
|
||||
to: moment(max).toISOString()
|
||||
});
|
||||
}
|
||||
}
|
||||
mlJobSelectService.jobSelectListState.applyTimeRange = $scope.applyTimeRange;
|
||||
|
@ -360,8 +363,10 @@ module.directive('mlJobSelectList', function (Private, timefilter) {
|
|||
}
|
||||
|
||||
$scope.useTimeRange = function (job) {
|
||||
timefilter.time.from = job.timeRange.fromMoment.toISOString();
|
||||
timefilter.time.to = job.timeRange.toMoment.toISOString();
|
||||
timefilter.setTime({
|
||||
from: job.timeRange.fromMoment.toISOString(),
|
||||
to: job.timeRange.toMoment.toISOString()
|
||||
});
|
||||
};
|
||||
},
|
||||
link: function (scope, element, attrs) {
|
||||
|
|
|
@ -49,6 +49,7 @@ uiRoutes
|
|||
}
|
||||
});
|
||||
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml');
|
||||
|
||||
|
@ -59,7 +60,6 @@ module
|
|||
$timeout,
|
||||
$window,
|
||||
Private,
|
||||
timefilter,
|
||||
AppState) {
|
||||
|
||||
timefilter.enableTimeRangeSelector();
|
||||
|
@ -143,7 +143,7 @@ module
|
|||
|
||||
|
||||
// Refresh the data when the time range is altered.
|
||||
$scope.$listen(timefilter, 'fetch', function () {
|
||||
$scope.$listenAndDigestAsync(timefilter, 'fetch', function () {
|
||||
$scope.earliest = timefilter.getActiveBounds().min.valueOf();
|
||||
$scope.latest = timefilter.getActiveBounds().max.valueOf();
|
||||
loadOverallStats();
|
||||
|
|
|
@ -19,13 +19,13 @@ import moment from 'moment';
|
|||
import rison from 'rison-node';
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
import 'ui/timefilter';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import template from './explorer_charts_container.html';
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml');
|
||||
|
||||
module.directive('mlExplorerChartsContainer', function ($window, timefilter) {
|
||||
module.directive('mlExplorerChartsContainer', function ($window) {
|
||||
|
||||
function link(scope, element) {
|
||||
// Create a div for the tooltip.
|
||||
|
|
|
@ -39,6 +39,7 @@ import { mlJobService } from 'plugins/ml/services/job_service';
|
|||
import { mlFieldFormatService } from 'plugins/ml/services/field_format_service';
|
||||
import { JobSelectServiceProvider } from 'plugins/ml/components/job_select_list/job_select_service';
|
||||
import { isTimeSeriesViewDetector } from 'plugins/ml/../common/util/job_utils';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
uiRoutes
|
||||
.when('/explorer/?', {
|
||||
|
@ -59,7 +60,6 @@ module.controller('MlExplorerController', function (
|
|||
$timeout,
|
||||
AppState,
|
||||
Private,
|
||||
timefilter,
|
||||
mlCheckboxShowChartsService,
|
||||
mlExplorerDashboardService,
|
||||
mlSelectLimitService,
|
||||
|
@ -246,7 +246,7 @@ module.controller('MlExplorerController', function (
|
|||
};
|
||||
|
||||
// Refresh all the data when the time range is altered.
|
||||
$scope.$listen(timefilter, 'fetch', () => {
|
||||
$scope.$listenAndDigestAsync(timefilter, 'fetch', () => {
|
||||
loadOverallData();
|
||||
clearSelectedAnomalies();
|
||||
});
|
||||
|
|
|
@ -18,6 +18,7 @@ import { isTimeSeriesViewJob } from 'plugins/ml/../common/util/job_utils';
|
|||
import { toLocaleString, mlEscape } from 'plugins/ml/util/string_utils';
|
||||
import { copyTextToClipboard } from 'plugins/ml/util/clipboard_utils';
|
||||
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { checkLicense } from 'plugins/ml/license/check_license';
|
||||
import { checkGetJobsPrivilege, checkPermission, createPermissionFailureMessage } from 'plugins/ml/privilege/check_privilege';
|
||||
|
@ -60,7 +61,6 @@ module.controller('MlJobsList',
|
|||
$timeout,
|
||||
$compile,
|
||||
$modal,
|
||||
timefilter,
|
||||
kbnUrl,
|
||||
Private,
|
||||
mlDatafeedService) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import angular from 'angular';
|
|||
import 'ace';
|
||||
|
||||
import { parseInterval } from 'ui/utils/parse_interval';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { checkLicense } from 'plugins/ml/license/check_license';
|
||||
|
@ -69,7 +70,6 @@ module.controller('MlNewJob',
|
|||
$route,
|
||||
$location,
|
||||
$modal,
|
||||
timefilter,
|
||||
mlDatafeedService,
|
||||
mlConfirmModalService) {
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ import $ from 'jquery';
|
|||
import d3 from 'd3';
|
||||
import angular from 'angular';
|
||||
import moment from 'moment';
|
||||
import 'ui/timefilter';
|
||||
|
||||
import { TimeBuckets } from 'ui/time_buckets';
|
||||
import { numTicksForDateFormat } from 'plugins/ml/util/chart_utils';
|
||||
|
|
|
@ -13,8 +13,9 @@ import { IntervalHelperProvider } from 'plugins/ml/util/ml_time_buckets';
|
|||
import { calculateTextWidth } from 'plugins/ml/util/string_utils';
|
||||
import { mlResultsService } from 'plugins/ml/services/results_service';
|
||||
import { mlSimpleJobSearchService } from 'plugins/ml/jobs/new_job/simple/components/utils/search_service';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
export function ChartDataUtilsProvider(Private, timefilter) {
|
||||
export function ChartDataUtilsProvider(Private) {
|
||||
const TimeBuckets = Private(IntervalHelperProvider);
|
||||
|
||||
function loadDocCountData(formConfig, chartData) {
|
||||
|
|
|
@ -43,6 +43,7 @@ import { mlMessageBarService } from 'plugins/ml/components/messagebar/messagebar
|
|||
import { initPromise } from 'plugins/ml/util/promise';
|
||||
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||
import template from './create_job.html';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
uiRoutes
|
||||
.when('/jobs/new_job/simple/multi_metric', {
|
||||
|
@ -65,7 +66,6 @@ module
|
|||
.controller('MlCreateMultiMetricJob', function (
|
||||
$scope,
|
||||
$route,
|
||||
timefilter,
|
||||
Private,
|
||||
AppState) {
|
||||
|
||||
|
@ -250,8 +250,8 @@ module
|
|||
|
||||
function setTime() {
|
||||
$scope.ui.bucketSpanValid = true;
|
||||
$scope.formConfig.start = dateMath.parse(timefilter.time.from).valueOf();
|
||||
$scope.formConfig.end = dateMath.parse(timefilter.time.to).valueOf();
|
||||
$scope.formConfig.start = dateMath.parse(timefilter.getTime().from).valueOf();
|
||||
$scope.formConfig.end = dateMath.parse(timefilter.getTime().to).valueOf();
|
||||
$scope.formConfig.format = 'epoch_millis';
|
||||
|
||||
const bucketSpanInterval = parseInterval($scope.formConfig.bucketSpan);
|
||||
|
@ -688,7 +688,7 @@ module
|
|||
populateAppStateSettings(appState, $scope);
|
||||
});
|
||||
|
||||
$scope.$listen(timefilter, 'fetch', () => {
|
||||
$scope.$listenAndDigestAsync(timefilter, 'fetch', () => {
|
||||
$scope.loadVis();
|
||||
if ($scope.formConfig.splitField !== undefined) {
|
||||
$scope.setModelMemoryLimit();
|
||||
|
|
|
@ -42,6 +42,7 @@ import { FullTimeRangeSelectorServiceProvider } from 'plugins/ml/components/full
|
|||
import { mlMessageBarService } from 'plugins/ml/components/messagebar/messagebar_service';
|
||||
import { initPromise } from 'plugins/ml/util/promise';
|
||||
import template from './create_job.html';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
uiRoutes
|
||||
.when('/jobs/new_job/simple/population', {
|
||||
|
@ -65,7 +66,6 @@ module
|
|||
$scope,
|
||||
$route,
|
||||
$timeout,
|
||||
timefilter,
|
||||
Private,
|
||||
AppState) {
|
||||
|
||||
|
@ -265,8 +265,8 @@ module
|
|||
|
||||
function setTime() {
|
||||
$scope.ui.bucketSpanValid = true;
|
||||
$scope.formConfig.start = dateMath.parse(timefilter.time.from).valueOf();
|
||||
$scope.formConfig.end = dateMath.parse(timefilter.time.to).valueOf();
|
||||
$scope.formConfig.start = dateMath.parse(timefilter.getTime().from).valueOf();
|
||||
$scope.formConfig.end = dateMath.parse(timefilter.getTime().to).valueOf();
|
||||
$scope.formConfig.format = 'epoch_millis';
|
||||
|
||||
const bucketSpanInterval = parseInterval($scope.formConfig.bucketSpan);
|
||||
|
@ -695,7 +695,7 @@ module
|
|||
populateAppStateSettings(appState, $scope);
|
||||
});
|
||||
|
||||
$scope.$listen(timefilter, 'fetch', $scope.loadVis);
|
||||
$scope.$listenAndDigestAsync(timefilter, 'fetch', $scope.loadVis);
|
||||
|
||||
angular.element(window).resize(() => {
|
||||
resize();
|
||||
|
|
|
@ -15,8 +15,9 @@ import { mlFieldFormatService } from 'plugins/ml/services/field_format_service';
|
|||
import { mlJobService } from 'plugins/ml/services/job_service';
|
||||
import { createJobForSaving } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
||||
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
export function PopulationJobServiceProvider(timefilter, Private) {
|
||||
export function PopulationJobServiceProvider(Private) {
|
||||
|
||||
const TimeBuckets = Private(IntervalHelperProvider);
|
||||
const OVER_FIELD_EXAMPLES_COUNT = 40;
|
||||
|
|
|
@ -25,6 +25,7 @@ import { mlMessageBarService } from 'plugins/ml/components/messagebar/messagebar
|
|||
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||
import { initPromise } from 'plugins/ml/util/promise';
|
||||
import template from './create_job.html';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
uiRoutes
|
||||
.when('/jobs/new_job/simple/recognize', {
|
||||
|
@ -47,13 +48,11 @@ module
|
|||
$scope,
|
||||
$window,
|
||||
$route,
|
||||
timefilter,
|
||||
Private) {
|
||||
|
||||
const mlCreateRecognizerJobsService = Private(CreateRecognizerJobsServiceProvider);
|
||||
timefilter.disableTimeRangeSelector();
|
||||
timefilter.disableAutoRefreshSelector();
|
||||
$scope.tt = timefilter;
|
||||
const msgs = mlMessageBarService;
|
||||
|
||||
const SAVE_STATE = {
|
||||
|
@ -344,8 +343,8 @@ module
|
|||
$scope.formConfig.start = resp.start.epoch;
|
||||
$scope.formConfig.end = resp.end.epoch;
|
||||
} else {
|
||||
$scope.formConfig.start = dateMath.parse(timefilter.time.from).valueOf();
|
||||
$scope.formConfig.end = dateMath.parse(timefilter.time.to).valueOf();
|
||||
$scope.formConfig.start = dateMath.parse(timefilter.getTime().from).valueOf();
|
||||
$scope.formConfig.end = dateMath.parse(timefilter.getTime().to).valueOf();
|
||||
}
|
||||
let jobsCounter = 0;
|
||||
let datafeedCounter = 0;
|
||||
|
|
|
@ -14,7 +14,6 @@ import $ from 'jquery';
|
|||
import d3 from 'd3';
|
||||
import angular from 'angular';
|
||||
import moment from 'moment';
|
||||
import 'ui/timefilter';
|
||||
|
||||
import { TimeBuckets } from 'ui/time_buckets';
|
||||
import { drawLineChartDots, numTicksForDateFormat } from 'plugins/ml/util/chart_utils';
|
||||
|
|
|
@ -44,6 +44,8 @@ import { initPromise } from 'plugins/ml/util/promise';
|
|||
|
||||
import template from './create_job.html';
|
||||
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
uiRoutes
|
||||
.when('/jobs/new_job/simple/single_metric', {
|
||||
template,
|
||||
|
@ -66,7 +68,6 @@ module
|
|||
$scope,
|
||||
$route,
|
||||
$filter,
|
||||
timefilter,
|
||||
Private,
|
||||
AppState) {
|
||||
|
||||
|
@ -245,8 +246,8 @@ module
|
|||
|
||||
function setTime() {
|
||||
$scope.ui.bucketSpanValid = true;
|
||||
$scope.formConfig.start = dateMath.parse(timefilter.time.from).valueOf();
|
||||
$scope.formConfig.end = dateMath.parse(timefilter.time.to).valueOf();
|
||||
$scope.formConfig.start = dateMath.parse(timefilter.getTime().from).valueOf();
|
||||
$scope.formConfig.end = dateMath.parse(timefilter.getTime().to).valueOf();
|
||||
$scope.formConfig.format = 'epoch_millis';
|
||||
|
||||
const bucketSpanInterval = parseInterval($scope.formConfig.bucketSpan);
|
||||
|
@ -566,7 +567,7 @@ module
|
|||
mlFullTimeRangeSelectorService.setFullTimeRange($scope.ui.indexPattern, $scope.formConfig.combinedQuery);
|
||||
};
|
||||
|
||||
$scope.$listen(timefilter, 'fetch', $scope.loadVis);
|
||||
$scope.$listenAndDigestAsync(timefilter, 'fetch', $scope.loadVis);
|
||||
|
||||
$scope.$on('$destroy', () => {
|
||||
globalForceStop = true;
|
||||
|
|
|
@ -19,6 +19,7 @@ import { loadIndexPatterns, getIndexPatterns } from 'plugins/ml/util/index_utils
|
|||
import { checkMlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
||||
import { initPromise } from 'plugins/ml/util/promise';
|
||||
import template from './index_or_search.html';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
uiRoutes
|
||||
.when('/jobs/new_job', {
|
||||
|
@ -42,10 +43,7 @@ import { uiModules } from 'ui/modules';
|
|||
const module = uiModules.get('apps/ml');
|
||||
|
||||
module.controller('MlNewJobStepIndexOrSearch',
|
||||
function (
|
||||
$scope,
|
||||
$route,
|
||||
timefilter) {
|
||||
function ($scope) {
|
||||
|
||||
timefilter.disableTimeRangeSelector(); // remove time picker from top of page
|
||||
timefilter.disableAutoRefreshSelector(); // remove time picker from top of page
|
||||
|
|
|
@ -21,6 +21,7 @@ import { addItemToRecentlyAccessed } from 'plugins/ml/util/recently_accessed';
|
|||
import { checkMlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
||||
import { initPromise } from 'plugins/ml/util/promise';
|
||||
import template from './job_type.html';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
uiRoutes
|
||||
.when('/jobs/new_job/step/job_type', {
|
||||
|
@ -42,8 +43,7 @@ const module = uiModules.get('apps/ml');
|
|||
module.controller('MlNewJobStepJobType',
|
||||
function (
|
||||
$scope,
|
||||
$route,
|
||||
timefilter) {
|
||||
$route) {
|
||||
|
||||
timefilter.disableTimeRangeSelector(); // remove time picker from top of page
|
||||
timefilter.disableAutoRefreshSelector(); // remove time picker from top of page
|
||||
|
|
|
@ -22,6 +22,8 @@ import { initPromise } from 'plugins/ml/util/promise';
|
|||
|
||||
import template from './calendars_list.html';
|
||||
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
uiRoutes
|
||||
.when('/settings/calendars_list', {
|
||||
template,
|
||||
|
@ -43,7 +45,6 @@ module.controller('MlCalendarsList',
|
|||
$route,
|
||||
$location,
|
||||
pagerFactory,
|
||||
timefilter,
|
||||
mlConfirmModalService) {
|
||||
|
||||
$scope.permissions = {
|
||||
|
|
|
@ -14,6 +14,8 @@ import { initPromise } from 'plugins/ml/util/promise';
|
|||
|
||||
import template from './settings.html';
|
||||
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
uiRoutes
|
||||
.when('/settings', {
|
||||
template,
|
||||
|
@ -29,9 +31,7 @@ import { uiModules } from 'ui/modules';
|
|||
const module = uiModules.get('apps/ml');
|
||||
|
||||
module.controller('MlSettings',
|
||||
function (
|
||||
$scope,
|
||||
timefilter) {
|
||||
function () {
|
||||
|
||||
timefilter.disableTimeRangeSelector(); // remove time picker from top of page
|
||||
timefilter.disableAutoRefreshSelector(); // remove time picker from top of page
|
||||
|
|
|
@ -22,8 +22,6 @@ import {
|
|||
EuiToolTip
|
||||
} from '@elastic/eui';
|
||||
|
||||
import 'ui/timefilter';
|
||||
|
||||
// don't use something like plugins/ml/../common
|
||||
// because it won't work with the jest tests
|
||||
import { FORECAST_REQUEST_STATE, JOB_STATE } from '../../../common/constants/states';
|
||||
|
@ -402,6 +400,7 @@ ForecastingModal.propTypes = {
|
|||
detectorIndex: PropTypes.number,
|
||||
entities: PropTypes.array,
|
||||
loadForForecastId: PropTypes.func,
|
||||
timefilter: PropTypes.object,
|
||||
};
|
||||
|
||||
export { ForecastingModal };
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
|
||||
import 'ngreact';
|
||||
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml', ['react']);
|
||||
|
||||
import { ForecastingModal } from './forecasting_modal';
|
||||
|
||||
module.directive('mlForecastingModal', function ($injector) {
|
||||
const timefilter = $injector.get('timefilter');
|
||||
const reactDirective = $injector.get('reactDirective');
|
||||
return reactDirective(
|
||||
ForecastingModal,
|
||||
|
|
|
@ -22,7 +22,6 @@ import {
|
|||
} from '@elastic/eui';
|
||||
|
||||
import { formatDate } from '@elastic/eui/lib/services/format';
|
||||
import 'ui/timefilter';
|
||||
|
||||
const TIME_FORMAT = 'MMMM Do YYYY, HH:mm:ss';
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import $ from 'jquery';
|
|||
import angular from 'angular';
|
||||
import d3 from 'd3';
|
||||
import moment from 'moment';
|
||||
import 'ui/timefilter';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
import { ResizeChecker } from 'ui/resize_checker';
|
||||
|
||||
|
@ -40,7 +40,6 @@ const module = uiModules.get('apps/ml');
|
|||
module.directive('mlTimeseriesChart', function (
|
||||
$compile,
|
||||
$timeout,
|
||||
timefilter,
|
||||
Private,
|
||||
mlChartTooltipService) {
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import 'plugins/ml/components/controls';
|
|||
|
||||
import { notify } from 'ui/notify';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import 'ui/timefilter';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { parseInterval } from 'ui/utils/parse_interval';
|
||||
import { checkLicense } from 'plugins/ml/license/check_license';
|
||||
import { checkGetJobsPrivilege, checkPermission } from 'plugins/ml/privilege/check_privilege';
|
||||
|
@ -70,7 +70,6 @@ module.controller('MlTimeSeriesExplorerController', function (
|
|||
$route,
|
||||
$timeout,
|
||||
Private,
|
||||
timefilter,
|
||||
AppState,
|
||||
mlSelectIntervalService,
|
||||
mlSelectSeverityService) {
|
||||
|
@ -505,8 +504,8 @@ module.controller('MlTimeSeriesExplorerController', function (
|
|||
forecastId
|
||||
).then((resp) => {
|
||||
const bounds = timefilter.getActiveBounds();
|
||||
const earliest = moment(resp.earliest || timefilter.time.from);
|
||||
const latest = moment(resp.latest || timefilter.time.to);
|
||||
const earliest = moment(resp.earliest || timefilter.getTime().from);
|
||||
const latest = moment(resp.latest || timefilter.getTime().to);
|
||||
|
||||
// Store forecast ID in the appState.
|
||||
$scope.appState.mlTimeSeriesExplorer.forecastId = forecastId;
|
||||
|
@ -529,8 +528,10 @@ module.controller('MlTimeSeriesExplorerController', function (
|
|||
if (earliest.isBefore(bounds.min) || latest.isAfter(bounds.max)) {
|
||||
const earliestMs = Math.min(earliest.valueOf(), bounds.min.valueOf());
|
||||
const latestMs = Math.max(latest.valueOf(), bounds.max.valueOf());
|
||||
timefilter.time.from = moment(earliestMs).toISOString();
|
||||
timefilter.time.to = moment(latestMs).toISOString();
|
||||
timefilter.setTime({
|
||||
from: moment(earliestMs).toISOString(),
|
||||
to: moment(latestMs).toISOString()
|
||||
});
|
||||
} else {
|
||||
// Refresh to show the requested forecast data.
|
||||
$scope.refresh();
|
||||
|
@ -559,7 +560,7 @@ module.controller('MlTimeSeriesExplorerController', function (
|
|||
};
|
||||
|
||||
// Refresh the data when the time range is altered.
|
||||
$scope.$listen(timefilter, 'fetch', $scope.refresh);
|
||||
$scope.$listenAndDigestAsync(timefilter, 'fetch', $scope.refresh);
|
||||
|
||||
// Add a watcher for auto-refresh of the time filter to refresh all the data.
|
||||
const refreshWatcher = Private(refreshIntervalWatcher);
|
||||
|
|
|
@ -22,7 +22,7 @@ const unitsDesc = dateMath.unitsDesc;
|
|||
const largeMax = unitsDesc.indexOf('w'); // Multiple units of week or longer converted to days for ES intervals.
|
||||
|
||||
import { TimeBuckets } from 'ui/time_buckets';
|
||||
export function IntervalHelperProvider(Private, timefilter, config) {
|
||||
export function IntervalHelperProvider(Private, config) {
|
||||
|
||||
const calcAuto = Private(TimeBucketsCalcAutoIntervalProvider);
|
||||
inherits(MlTimeBuckets, TimeBuckets);
|
||||
|
|
|
@ -4,37 +4,42 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
/*
|
||||
* Watches for changes to the refresh interval of the page time filter,
|
||||
* so that listeners can be notified when the auto-refresh interval has elapsed.
|
||||
*/
|
||||
|
||||
export function refreshIntervalWatcher($rootScope, $timeout) {
|
||||
export function refreshIntervalWatcher($timeout) {
|
||||
|
||||
let refresher;
|
||||
let listener;
|
||||
|
||||
function init(listener) {
|
||||
const onRefreshIntervalChange = () => {
|
||||
if (refresher) {
|
||||
$timeout.cancel(refresher);
|
||||
}
|
||||
const interval = timefilter.getRefreshInterval();
|
||||
if (interval.value > 0 && !interval.pause) {
|
||||
function startRefresh() {
|
||||
refresher = $timeout(() => {
|
||||
startRefresh();
|
||||
listener();
|
||||
}, interval.value);
|
||||
}
|
||||
startRefresh();
|
||||
}
|
||||
};
|
||||
|
||||
$rootScope.$watchCollection('timefilter.refreshInterval', (interval) => {
|
||||
if (refresher) {
|
||||
$timeout.cancel(refresher);
|
||||
}
|
||||
if (interval.value > 0 && !interval.pause) {
|
||||
function startRefresh() {
|
||||
refresher = $timeout(() => {
|
||||
startRefresh();
|
||||
listener();
|
||||
}, interval.value);
|
||||
}
|
||||
startRefresh();
|
||||
}
|
||||
});
|
||||
function init(listenerCallback) {
|
||||
listener = listenerCallback;
|
||||
timefilter.on('refreshIntervalUpdate', onRefreshIntervalChange);
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
$timeout.cancel(refresher);
|
||||
timefilter.off('refreshIntervalUpdate', onRefreshIntervalChange);
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -9,9 +9,10 @@ import moment from 'moment';
|
|||
import { render } from 'react-dom';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { Beat } from 'plugins/monitoring/components/beats/beat';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
const uiModule = uiModules.get('monitoring/directives', []);
|
||||
uiModule.directive('monitoringBeatsBeat', (timefilter) => {
|
||||
uiModule.directive('monitoringBeatsBeat', () => {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
|
@ -20,10 +21,10 @@ uiModule.directive('monitoringBeatsBeat', (timefilter) => {
|
|||
link(scope, $el) {
|
||||
|
||||
function onBrush({ xaxis }) {
|
||||
scope.$evalAsync(() => {
|
||||
timefilter.time.from = moment(xaxis.from);
|
||||
timefilter.time.to = moment(xaxis.to);
|
||||
timefilter.time.mode = 'absolute';
|
||||
timefilter.setTime({
|
||||
from: moment(xaxis.from),
|
||||
to: moment(xaxis.to),
|
||||
mode: 'absolute',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -9,9 +9,10 @@ import moment from 'moment';
|
|||
import { render } from 'react-dom';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { BeatsOverview } from 'plugins/monitoring/components/beats/overview';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
const uiModule = uiModules.get('monitoring/directives', []);
|
||||
uiModule.directive('monitoringBeatsOverview', (timefilter) => {
|
||||
uiModule.directive('monitoringBeatsOverview', () => {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
|
@ -20,10 +21,10 @@ uiModule.directive('monitoringBeatsOverview', (timefilter) => {
|
|||
link(scope, $el) {
|
||||
|
||||
function onBrush({ xaxis }) {
|
||||
scope.$evalAsync(() => {
|
||||
timefilter.time.from = moment(xaxis.from);
|
||||
timefilter.time.to = moment(xaxis.to);
|
||||
timefilter.time.mode = 'absolute';
|
||||
timefilter.setTime({
|
||||
from: moment(xaxis.from),
|
||||
to: moment(xaxis.to),
|
||||
mode: 'absolute'
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -17,9 +17,10 @@ import {
|
|||
import { Tooltip } from 'pivotal-ui/react/tooltip';
|
||||
import { OverlayTrigger } from 'pivotal-ui/react/overlay-trigger';
|
||||
import { KuiInfoButton } from '@kbn/ui-framework/components';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
const uiModule = uiModules.get('plugins/monitoring/directives', []);
|
||||
uiModule.directive('monitoringChart', (timefilter) => {
|
||||
uiModule.directive('monitoringChart', () => {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
|
@ -31,10 +32,10 @@ uiModule.directive('monitoringChart', (timefilter) => {
|
|||
const units = getUnits(series);
|
||||
|
||||
function onBrush({ xaxis }) {
|
||||
scope.$evalAsync(() => {
|
||||
timefilter.time.from = moment(xaxis.from);
|
||||
timefilter.time.to = moment(xaxis.to);
|
||||
timefilter.time.mode = 'absolute';
|
||||
timefilter.setTime({
|
||||
from: moment(xaxis.from),
|
||||
to: moment(xaxis.to),
|
||||
mode: 'absolute'
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import { MonitoringTable } from 'plugins/monitoring/components/table';
|
|||
import { Sparkline } from 'plugins/monitoring/components/sparkline';
|
||||
import { SORT_ASCENDING } from '../../../../common/constants';
|
||||
import { formatMetric } from '../../../lib/format_number';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
const filterFields = [ 'id' ];
|
||||
const columns = [
|
||||
|
@ -105,7 +106,6 @@ const pipelineRowFactory = (onPipelineClick, onBrush, tooltipXValueFormatter, to
|
|||
const uiModule = uiModules.get('monitoring/directives', []);
|
||||
uiModule.directive('monitoringLogstashPipelineListing', ($injector) => {
|
||||
const kbnUrl = $injector.get('kbnUrl');
|
||||
const timefilter = $injector.get('timefilter');
|
||||
const config = $injector.get('config');
|
||||
|
||||
const dateFormat = config.get('dateFormat');
|
||||
|
@ -124,10 +124,10 @@ uiModule.directive('monitoringLogstashPipelineListing', ($injector) => {
|
|||
link: function (scope, $el) {
|
||||
|
||||
function onBrush(xaxis) {
|
||||
scope.$evalAsync(() => {
|
||||
timefilter.time.from = moment(xaxis.from);
|
||||
timefilter.time.to = moment(xaxis.to);
|
||||
timefilter.time.mode = 'absolute';
|
||||
timefilter.setTime({
|
||||
from: moment(xaxis.from),
|
||||
to: moment(xaxis.to),
|
||||
mode: 'absolute'
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@ import { uiModules } from 'ui/modules';
|
|||
import { Sparkline } from 'plugins/monitoring/components/sparkline';
|
||||
import moment from 'moment';
|
||||
import { formatMetric } from '../../lib/format_number';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
const uiModule = uiModules.get('plugins/monitoring/directives', []);
|
||||
uiModule.directive('sparkline', ($injector) => {
|
||||
const timefilter = $injector.get('timefilter');
|
||||
const config = $injector.get('config');
|
||||
|
||||
const dateFormat = config.get('dateFormat');
|
||||
|
@ -27,10 +27,10 @@ uiModule.directive('sparkline', ($injector) => {
|
|||
link(scope, $elem) {
|
||||
|
||||
function onBrush(xaxis) {
|
||||
scope.$evalAsync(() => {
|
||||
timefilter.time.from = moment(xaxis.from);
|
||||
timefilter.time.to = moment(xaxis.to);
|
||||
timefilter.time.mode = 'absolute';
|
||||
timefilter.setTime({
|
||||
from: moment(xaxis.from),
|
||||
to: moment(xaxis.to),
|
||||
mode: 'absolute'
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
*/
|
||||
|
||||
import { ajaxErrorHandlersProvider } from './ajax_error_handler';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
export function getPageData($injector, api) {
|
||||
const $http = $injector.get('$http');
|
||||
const globalState = $injector.get('globalState');
|
||||
const timefilter = $injector.get('timefilter');
|
||||
const timeBounds = timefilter.getBounds();
|
||||
|
||||
return $http
|
||||
|
|
|
@ -4,104 +4,111 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import ngMock from 'ng_mock';
|
||||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import { executorProvider } from '../executor_provider';
|
||||
import EventEmitter from 'events';
|
||||
import Promise from 'bluebird';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
describe('$executor service', () => {
|
||||
|
||||
let scope;
|
||||
let executor;
|
||||
let timefilter;
|
||||
let $timeout;
|
||||
let onSpy;
|
||||
let offSpy;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
|
||||
beforeEach(ngMock.inject(function (_$rootScope_) {
|
||||
scope = _$rootScope_.$new();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
$timeout = sinon.spy(setTimeout);
|
||||
$timeout.cancel = (id) => clearTimeout(id);
|
||||
|
||||
timefilter = new EventEmitter();
|
||||
onSpy = sinon.spy((...args) => timefilter.addListener(...args));
|
||||
offSpy = sinon.spy((...args) => timefilter.removeListener(...args));
|
||||
|
||||
timefilter.on = onSpy;
|
||||
timefilter.off = offSpy;
|
||||
|
||||
timefilter.refreshInterval = {
|
||||
timefilter.setRefreshInterval({
|
||||
pause: false,
|
||||
value: 0
|
||||
};
|
||||
executor = executorProvider(Promise, $timeout, timefilter);
|
||||
});
|
||||
|
||||
executor = executorProvider(Promise, $timeout);
|
||||
});
|
||||
|
||||
afterEach(() => executor.destroy());
|
||||
|
||||
it('should register listener for fetch upon start', () => {
|
||||
executor.start();
|
||||
expect(onSpy.calledTwice).to.equal(true);
|
||||
expect(onSpy.firstCall.args[0]).to.equal('fetch');
|
||||
expect(onSpy.firstCall.args[1].name).to.equal('reFetch');
|
||||
executor.start(scope);
|
||||
const listeners = timefilter.listeners('fetch');
|
||||
const handlerFunc = listeners.find(listener => {
|
||||
return listener.name === 'reFetch';
|
||||
});
|
||||
expect(handlerFunc).to.not.be.null;
|
||||
});
|
||||
|
||||
it('should register listener for update upon start', () => {
|
||||
executor.start();
|
||||
expect(onSpy.calledTwice).to.equal(true);
|
||||
expect(onSpy.secondCall.args[0]).to.equal('update');
|
||||
expect(onSpy.secondCall.args[1].name).to.equal('killIfPaused');
|
||||
it('should register listener for refreshIntervalUpdate upon start', () => {
|
||||
executor.start(scope);
|
||||
const listeners = timefilter.listeners('refreshIntervalUpdate');
|
||||
const handlerFunc = listeners.find(listener => {
|
||||
return listener.name === 'killIfPaused';
|
||||
});
|
||||
expect(handlerFunc).to.not.be.null;
|
||||
});
|
||||
|
||||
it('should not call $timeout if the timefilter is not paused and set to zero', () => {
|
||||
executor.start();
|
||||
executor.start(scope);
|
||||
expect($timeout.callCount).to.equal(0);
|
||||
});
|
||||
|
||||
it('should call $timeout if the timefilter is not paused and set to 1000ms', () => {
|
||||
timefilter.refreshInterval.value = 1000;
|
||||
executor.start();
|
||||
timefilter.setRefreshInterval({
|
||||
value: 1000
|
||||
});
|
||||
executor.start(scope);
|
||||
expect($timeout.callCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('should execute function if ingorePause is passed (interval set to 1000ms)', (done) => {
|
||||
timefilter.refreshInterval.value = 1000;
|
||||
executor.register({ execute: () => Promise.resolve().then(() => done(), done) });
|
||||
executor.start({ ignorePaused: true });
|
||||
});
|
||||
|
||||
it('should execute function if timefilter is not paused and interval set to 1000ms', (done) => {
|
||||
timefilter.refreshInterval.value = 1000;
|
||||
timefilter.setRefreshInterval({
|
||||
value: 1000
|
||||
});
|
||||
executor.register({ execute: () => Promise.resolve().then(() => done(), done) });
|
||||
executor.start();
|
||||
executor.start(scope);
|
||||
});
|
||||
|
||||
it('should execute function multiple times', (done) => {
|
||||
let calls = 0;
|
||||
timefilter.refreshInterval.value = 10;
|
||||
timefilter.setRefreshInterval({
|
||||
value: 10
|
||||
});
|
||||
executor.register({ execute: () => {
|
||||
if (calls++ > 1) { done(); }
|
||||
return Promise.resolve();
|
||||
} });
|
||||
executor.start();
|
||||
executor.start(scope);
|
||||
});
|
||||
|
||||
it('should call handleResponse', (done) => {
|
||||
timefilter.refreshInterval.value = 10;
|
||||
timefilter.setRefreshInterval({
|
||||
value: 10
|
||||
});
|
||||
executor.register({
|
||||
execute: () => Promise.resolve(),
|
||||
handleResponse: () => done()
|
||||
});
|
||||
executor.start();
|
||||
executor.start(scope);
|
||||
});
|
||||
|
||||
it('should call handleError', (done) => {
|
||||
timefilter.refreshInterval.value = 10;
|
||||
timefilter.setRefreshInterval({
|
||||
value: 10
|
||||
});
|
||||
executor.register({
|
||||
execute: () => Promise.reject(new Error('reject test')),
|
||||
handleError: () => done()
|
||||
});
|
||||
executor.start();
|
||||
executor.start(scope);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
const uiModule = uiModules.get('monitoring/clusters');
|
||||
uiModule.service('monitoringClusters', ($injector) => {
|
||||
return (clusterUuid, ccs) => {
|
||||
const timefilter = $injector.get('timefilter');
|
||||
const { min, max } = timefilter.getBounds();
|
||||
|
||||
// append clusterUuid if the parameter is given
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { defaults } from 'lodash';
|
||||
export function executorProvider(Promise, $timeout, timefilter) {
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
export function executorProvider(Promise, $timeout) {
|
||||
|
||||
const queue = [];
|
||||
let executionTimer;
|
||||
|
@ -30,8 +30,6 @@ export function executorProvider(Promise, $timeout, timefilter) {
|
|||
*/
|
||||
function cancel() {
|
||||
killTimer();
|
||||
timefilter.off('update', killIfPaused);
|
||||
timefilter.off('fetch', reFetch);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,7 +71,7 @@ export function executorProvider(Promise, $timeout, timefilter) {
|
|||
}
|
||||
|
||||
function killIfPaused() {
|
||||
if (timefilter.refreshInterval.pause) {
|
||||
if (timefilter.getRefreshInterval().pause) {
|
||||
killTimer();
|
||||
}
|
||||
}
|
||||
|
@ -83,10 +81,8 @@ export function executorProvider(Promise, $timeout, timefilter) {
|
|||
* @returns {void}
|
||||
*/
|
||||
function start() {
|
||||
timefilter.on('fetch', reFetch);
|
||||
timefilter.on('update', killIfPaused);
|
||||
if ((ignorePaused || timefilter.refreshInterval.pause === false) && timefilter.refreshInterval.value > 0) {
|
||||
executionTimer = $timeout(run, timefilter.refreshInterval.value);
|
||||
if ((ignorePaused || timefilter.getRefreshInterval().pause === false) && timefilter.getRefreshInterval().value > 0) {
|
||||
executionTimer = $timeout(run, timefilter.getRefreshInterval().value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,17 +91,9 @@ export function executorProvider(Promise, $timeout, timefilter) {
|
|||
*/
|
||||
return {
|
||||
register,
|
||||
start(options = {}) {
|
||||
options = defaults(options, {
|
||||
ignorePaused: false,
|
||||
now: false
|
||||
});
|
||||
if (options.now) {
|
||||
return run();
|
||||
}
|
||||
if (options.ignorePaused) {
|
||||
ignorePaused = options.ignorePaused;
|
||||
}
|
||||
start($scope) {
|
||||
$scope.$listenAndDigestAsync(timefilter, 'fetch', reFetch);
|
||||
$scope.$listenAndDigestAsync(timefilter, 'refreshIntervalUpdate', killIfPaused);
|
||||
start();
|
||||
},
|
||||
run,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import { spy, stub } from 'sinon';
|
||||
import expect from 'expect.js';
|
||||
import { MonitoringViewBaseController } from '../';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
/*
|
||||
* Mostly copied from base_table_controller test, with modifications
|
||||
|
@ -17,28 +18,10 @@ describe('MonitoringViewBaseController', function () {
|
|||
let $injector;
|
||||
let $scope;
|
||||
let opts;
|
||||
let timefilter;
|
||||
let titleService;
|
||||
let executorService;
|
||||
|
||||
let isTimeRangeSelectorEnabled;
|
||||
let isAutoRefreshSelectorEnabled;
|
||||
|
||||
before(() => {
|
||||
timefilter = {
|
||||
enableTimeRangeSelector: () => {
|
||||
isTimeRangeSelectorEnabled = true;
|
||||
},
|
||||
enableAutoRefreshSelector: () => {
|
||||
isAutoRefreshSelectorEnabled = true;
|
||||
},
|
||||
disableTimeRangeSelector: () => {
|
||||
isTimeRangeSelectorEnabled = false;
|
||||
},
|
||||
disableAutoRefreshSelector: () => {
|
||||
isAutoRefreshSelectorEnabled = false;
|
||||
},
|
||||
};
|
||||
titleService = spy();
|
||||
executorService = {
|
||||
register: spy(),
|
||||
|
@ -47,7 +30,6 @@ describe('MonitoringViewBaseController', function () {
|
|||
|
||||
const injectorGetStub = stub();
|
||||
injectorGetStub.withArgs('title').returns(titleService);
|
||||
injectorGetStub.withArgs('timefilter').returns(timefilter);
|
||||
injectorGetStub.withArgs('$executor').returns(executorService);
|
||||
injectorGetStub.withArgs('localStorage').throws('localStorage should not be used by this class');
|
||||
$injector = { get: injectorGetStub };
|
||||
|
@ -89,8 +71,8 @@ describe('MonitoringViewBaseController', function () {
|
|||
|
||||
describe('time filter', () => {
|
||||
it('enables timepicker and auto refresh #1', () => {
|
||||
expect(isTimeRangeSelectorEnabled).to.be(true);
|
||||
expect(isAutoRefreshSelectorEnabled).to.be(true);
|
||||
expect(timefilter.isTimeRangeSelectorEnabled).to.be(true);
|
||||
expect(timefilter.isAutoRefreshSelectorEnabled).to.be(true);
|
||||
});
|
||||
|
||||
it('enables timepicker and auto refresh #2', () => {
|
||||
|
@ -100,8 +82,8 @@ describe('MonitoringViewBaseController', function () {
|
|||
};
|
||||
ctrl = new MonitoringViewBaseController(opts);
|
||||
|
||||
expect(isTimeRangeSelectorEnabled).to.be(true);
|
||||
expect(isAutoRefreshSelectorEnabled).to.be(true);
|
||||
expect(timefilter.isTimeRangeSelectorEnabled).to.be(true);
|
||||
expect(timefilter.isAutoRefreshSelectorEnabled).to.be(true);
|
||||
});
|
||||
|
||||
it('disables timepicker and enables auto refresh', () => {
|
||||
|
@ -111,8 +93,8 @@ describe('MonitoringViewBaseController', function () {
|
|||
};
|
||||
ctrl = new MonitoringViewBaseController(opts);
|
||||
|
||||
expect(isTimeRangeSelectorEnabled).to.be(false);
|
||||
expect(isAutoRefreshSelectorEnabled).to.be(true);
|
||||
expect(timefilter.isTimeRangeSelectorEnabled).to.be(false);
|
||||
expect(timefilter.isAutoRefreshSelectorEnabled).to.be(true);
|
||||
});
|
||||
|
||||
it('enables timepicker and disables auto refresh', () => {
|
||||
|
@ -122,8 +104,8 @@ describe('MonitoringViewBaseController', function () {
|
|||
};
|
||||
ctrl = new MonitoringViewBaseController(opts);
|
||||
|
||||
expect(isTimeRangeSelectorEnabled).to.be(true);
|
||||
expect(isAutoRefreshSelectorEnabled).to.be(false);
|
||||
expect(timefilter.isTimeRangeSelectorEnabled).to.be(true);
|
||||
expect(timefilter.isAutoRefreshSelectorEnabled).to.be(false);
|
||||
});
|
||||
|
||||
it('disables timepicker and auto refresh', () => {
|
||||
|
@ -136,8 +118,8 @@ describe('MonitoringViewBaseController', function () {
|
|||
};
|
||||
ctrl = new MonitoringViewBaseController(opts);
|
||||
|
||||
expect(isTimeRangeSelectorEnabled).to.be(false);
|
||||
expect(isAutoRefreshSelectorEnabled).to.be(false);
|
||||
expect(timefilter.isTimeRangeSelectorEnabled).to.be(false);
|
||||
expect(timefilter.isAutoRefreshSelectorEnabled).to.be(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@ import template from './index.html';
|
|||
import { MonitoringViewBaseController } from 'plugins/monitoring/views';
|
||||
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
||||
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
function getPageData($injector) {
|
||||
const globalState = $injector.get('globalState');
|
||||
const timefilter = $injector.get('timefilter');
|
||||
const $http = $injector.get('$http');
|
||||
const Private = $injector.get('Private');
|
||||
const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/alerts`;
|
||||
|
|
|
@ -9,6 +9,7 @@ import moment from 'moment';
|
|||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import { getPageData } from '../lib/get_page_data';
|
||||
import { PageLoading } from 'plugins/monitoring/components';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
/**
|
||||
* Class to manage common instantiation behaviors in a view controller
|
||||
|
@ -69,7 +70,6 @@ export class MonitoringViewBaseController {
|
|||
options = {}
|
||||
}) {
|
||||
const titleService = $injector.get('title');
|
||||
const timefilter = $injector.get('timefilter');
|
||||
const $executor = $injector.get('$executor');
|
||||
|
||||
titleService($scope.cluster, title);
|
||||
|
@ -108,7 +108,7 @@ export class MonitoringViewBaseController {
|
|||
$executor.register({
|
||||
execute: () => this.updateData()
|
||||
});
|
||||
$executor.start();
|
||||
$executor.start($scope);
|
||||
$scope.$on('$destroy', () => {
|
||||
if (this.reactNodeId) { // WIP https://github.com/elastic/x-pack-kibana/issues/5198
|
||||
unmountComponentAtNode(document.getElementById(this.reactNodeId));
|
||||
|
@ -119,10 +119,10 @@ export class MonitoringViewBaseController {
|
|||
// needed for chart pages
|
||||
this.onBrush = ({ xaxis }) => {
|
||||
const { to, from } = xaxis;
|
||||
$scope.$evalAsync(() => {
|
||||
timefilter.time.from = moment(from);
|
||||
timefilter.time.to = moment(to);
|
||||
timefilter.time.mode = 'absolute';
|
||||
timefilter.setTime({
|
||||
from: moment(from),
|
||||
to: moment(to),
|
||||
mode: 'absolute'
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
*/
|
||||
|
||||
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
export function getPageData($injector) {
|
||||
const $http = $injector.get('$http');
|
||||
const $route = $injector.get('$route');
|
||||
const globalState = $injector.get('globalState');
|
||||
const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/beats/beat/${$route.current.params.beatUuid}`;
|
||||
const timefilter = $injector.get('timefilter');
|
||||
const timeBounds = timefilter.getBounds();
|
||||
|
||||
return $http.post(url, {
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
*/
|
||||
|
||||
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
export function getPageData($injector) {
|
||||
const $http = $injector.get('$http');
|
||||
const globalState = $injector.get('globalState');
|
||||
const timefilter = $injector.get('timefilter');
|
||||
const timeBounds = timefilter.getBounds();
|
||||
const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/beats/beats`;
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
*/
|
||||
|
||||
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
export function getPageData($injector) {
|
||||
const $http = $injector.get('$http');
|
||||
const globalState = $injector.get('globalState');
|
||||
const timefilter = $injector.get('timefilter');
|
||||
const timeBounds = timefilter.getBounds();
|
||||
const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/beats`;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import uiRoutes from 'ui/routes';
|
||||
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
||||
import template from './index.html';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
uiRoutes.when('/overview', {
|
||||
template,
|
||||
|
@ -21,7 +22,6 @@ uiRoutes.when('/overview', {
|
|||
}
|
||||
},
|
||||
controller($injector, $scope) {
|
||||
const timefilter = $injector.get('timefilter');
|
||||
timefilter.enableTimeRangeSelector();
|
||||
timefilter.enableAutoRefreshSelector();
|
||||
|
||||
|
@ -41,7 +41,7 @@ uiRoutes.when('/overview', {
|
|||
}
|
||||
});
|
||||
|
||||
$executor.start();
|
||||
$executor.start($scope);
|
||||
|
||||
$scope.$on('$destroy', $executor.destroy);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue