diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index 09ff9dc781062..858e5cc967d8c 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -22,7 +22,7 @@ import { doesKueryExpressionHaveLuceneSyntaxError } from '@kbn/es-query'; import classNames from 'classnames'; import React, { Component } from 'react'; import { Storage } from 'ui/storage'; -import { timeHistory } from 'ui/timefilter/time_history'; +import { timeHistory } from 'ui/timefilter'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLink, EuiSuperDatePicker } from '@elastic/eui'; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx index d110c420cdc07..8d98e4d2f9b7a 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx @@ -18,7 +18,7 @@ */ import { Filter } from '@kbn/es-query'; -import { RefreshInterval, TimeRange } from 'ui/timefilter/timefilter'; +import { RefreshInterval, TimeRange } from 'ui/timefilter'; import { Query } from '../../query/query_bar'; export * from './components'; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx index 52a4b9d6e803e..a0fe853484eee 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx @@ -36,11 +36,12 @@ import { import { KbnUrl } from 'ui/url/kbn_url'; import { Filter } from '@kbn/es-query'; -import { TimeRange } from 'ui/timefilter/time_history'; +import { TimeRange } from 'ui/timefilter'; import { IndexPattern } from 'ui/index_patterns'; import { IPrivate } from 'ui/private'; import { StaticIndexPattern, Query, SavedQuery } from 'plugins/data'; import moment from 'moment'; +import { Subscription } from 'rxjs'; import { ViewMode } from '../../../embeddable_api/public/np_ready/public'; import { SavedObjectDashboard } from './saved_dashboard/saved_dashboard'; @@ -81,7 +82,6 @@ export interface DashboardAppScope extends ng.IScope { refreshInterval: any; }) => void; onFiltersUpdated: (filters: Filter[]) => void; - $listenAndDigestAsync: any; onCancelApplyFilters: () => void; onApplyFilters: (filters: Filter[]) => void; onQuerySaved: (savedQuery: SavedQuery) => void; @@ -93,7 +93,7 @@ export interface DashboardAppScope extends ng.IScope { showSaveQuery: boolean; kbnTopNav: any; enterEditMode: () => void; - $listen: any; + timefilterSubscriptions$: Subscription; } const app = uiModules.get('app/dashboard', [ diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx index d207cbb71ac4c..cbb8a66301e35 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx @@ -24,6 +24,7 @@ import angular from 'angular'; import { uniq } from 'lodash'; import chrome from 'ui/chrome'; +import { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; import { toastNotifications } from 'ui/notify'; // @ts-ignore @@ -515,23 +516,36 @@ export class DashboardAppController { } ); - $scope.$listenAndDigestAsync(timefilter, 'fetch', () => { - // The only reason this is here is so that search embeddables work on a dashboard with - // a refresh interval turned on. This kicks off the search poller. It should be - // refactored so no embeddables need to listen to the timefilter directly but instead - // the container tells it when to reload. - courier.fetch(); - }); + $scope.timefilterSubscriptions$ = new Subscription(); + // The only reason this is here is so that search embeddables work on a dashboard with + // a refresh interval turned on. This kicks off the search poller. It should be + // refactored so no embeddables need to listen to the timefilter directly but instead + // the container tells it when to reload. + $scope.timefilterSubscriptions$.add( + subscribeWithScope($scope, timefilter.getFetch$(), { + next: () => { + courier.fetch(); + }, + }) + ); - $scope.$listenAndDigestAsync(timefilter, 'refreshIntervalUpdate', () => { - updateState(); - refreshDashboardContainer(); - }); + $scope.timefilterSubscriptions$.add( + subscribeWithScope($scope, timefilter.getRefreshIntervalUpdate$(), { + next: () => { + updateState(); + refreshDashboardContainer(); + }, + }) + ); - $scope.$listenAndDigestAsync(timefilter, 'timeUpdate', () => { - updateState(); - refreshDashboardContainer(); - }); + $scope.timefilterSubscriptions$.add( + subscribeWithScope($scope, timefilter.getTimeUpdate$(), { + next: () => { + updateState(); + refreshDashboardContainer(); + }, + }) + ); function updateViewMode(newMode: ViewMode) { $scope.topNavMenu = getTopNavConfig( @@ -799,6 +813,8 @@ export class DashboardAppController { $scope.$on('$destroy', () => { updateSubscription.unsubscribe(); + $scope.timefilterSubscriptions$.unsubscribe(); + dashboardStateManager.destroy(); if (inputSubscription) { inputSubscription.unsubscribe(); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state.test.ts b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state.test.ts index a713e13679816..9e9f5c3fe0b37 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state.test.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state.test.ts @@ -42,12 +42,15 @@ describe('DashboardState', function() { getRefreshInterval: jest.fn(), disableTimeRangeSelector: jest.fn(), enableAutoRefreshSelector: jest.fn(), - off: jest.fn(), - on: jest.fn(), getActiveBounds: () => {}, enableTimeRangeSelector: () => {}, isAutoRefreshSelectorEnabled: true, isTimeRangeSelectorEnabled: true, + getAutoRefreshFetch$: jest.fn(), + getEnabledUpdated$: jest.fn(), + getRefreshIntervalUpdate$: jest.fn(), + getFetch$: jest.fn(), + getTimeUpdate$: jest.fn(), }; function initDashboardState() { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/lib/update_saved_dashboard.ts b/src/legacy/core_plugins/kibana/public/dashboard/lib/update_saved_dashboard.ts index a43bee881c631..93f487ba6d502 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/lib/update_saved_dashboard.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/lib/update_saved_dashboard.ts @@ -19,8 +19,7 @@ import _ from 'lodash'; import { AppState } from 'ui/state_management/app_state'; -import { Timefilter } from 'ui/timefilter'; -import { RefreshInterval } from 'ui/timefilter/timefilter'; +import { Timefilter, RefreshInterval } from 'ui/timefilter'; import { FilterUtils } from './filter_utils'; import { SavedObjectDashboard } from '../saved_dashboard/saved_dashboard'; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.d.ts b/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.d.ts index 1f752833f45a8..68fd8f0a5a976 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.d.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.d.ts @@ -20,7 +20,7 @@ import { SearchSource } from 'ui/courier'; import { SavedObject } from 'ui/saved_objects/saved_object'; import moment from 'moment'; -import { RefreshInterval } from 'ui/timefilter/timefilter'; +import { RefreshInterval } from 'ui/timefilter'; import { Query } from 'src/legacy/core_plugins/data/public'; import { Filter } from '@kbn/es-query'; diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index c3aadfd614607..8b7f00b5ae4a2 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -21,6 +21,7 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import React from 'react'; import angular from 'angular'; +import { Subscription } from 'rxjs'; import moment from 'moment'; import chrome from 'ui/chrome'; import dateMath from '@elastic/datemath'; @@ -208,8 +209,7 @@ function discoverController( requests: new RequestAdapter() }; - let filterUpdateSubscription; - let filterFetchSubscription; + const subscriptions = new Subscription(); timefilter.disableTimeRangeSelector(); timefilter.disableAutoRefreshSelector(); @@ -235,8 +235,7 @@ function discoverController( const savedSearch = $route.current.locals.savedSearch; $scope.$on('$destroy', () => { savedSearch.destroy(); - if (filterFetchSubscription) filterFetchSubscription.unsubscribe(); - if (filterUpdateSubscription) filterUpdateSubscription.unsubscribe(); + subscriptions.unsubscribe(); }); const $appStatus = $scope.appStatus = this.appStatus = { @@ -563,10 +562,19 @@ function discoverController( $scope.updateDataSource() .then(function () { - $scope.$listen(timefilter, 'autoRefreshFetch', $scope.fetch); - $scope.$listen(timefilter, 'refreshIntervalUpdate', $scope.updateRefreshInterval); - $scope.$listen(timefilter, 'timeUpdate', $scope.updateTime); - $scope.$listen(timefilter, 'fetch', $scope.fetch); + subscriptions.add(subscribeWithScope($scope, timefilter.getAutoRefreshFetch$(), { + next: $scope.fetch + })); + + subscriptions.add(subscribeWithScope($scope, timefilter.getRefreshIntervalUpdate$(), { + next: $scope.updateRefreshInterval + })); + subscriptions.add(subscribeWithScope($scope, timefilter.getTimeUpdate$(), { + next: $scope.updateTime + })); + subscriptions.add(subscribeWithScope($scope, timefilter.getFetch$(), { + next: $scope.fetch + })); $scope.$watchCollection('state.sort', function (sort) { if (!sort) return; @@ -579,19 +587,19 @@ function discoverController( }); // update data source when filters update - filterUpdateSubscription = subscribeWithScope($scope, queryFilter.getUpdates$(), { + subscriptions.add(subscribeWithScope($scope, queryFilter.getUpdates$(), { next: () => { $scope.filters = queryFilter.getFilters(); $scope.updateDataSource().then(function () { $state.save(); }); } - }); + })); // fetch data when filters fire fetch event - filterFetchSubscription = subscribeWithScope($scope, queryFilter.getUpdates$(), { + subscriptions.add(subscribeWithScope($scope, queryFilter.getUpdates$(), { next: $scope.fetch - }); + })); // update data source when hitting forward/back and the query changes $scope.$listen($state, 'fetch_with_changes', function (diff) { diff --git a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts index 8288e3d1d1d7a..614bbd8b12a64 100644 --- a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts +++ b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts @@ -25,11 +25,10 @@ import { SearchSource } from 'ui/courier'; import { StaticIndexPattern } from 'ui/index_patterns'; import { RequestAdapter } from 'ui/inspector/adapters'; import { Adapters } from 'ui/inspector/types'; -import { getTime } from 'ui/timefilter/get_time'; import { Subscription } from 'rxjs'; import * as Rx from 'rxjs'; import { Filter, FilterStateStore } from '@kbn/es-query'; -import { TimeRange } from 'ui/timefilter/time_history'; +import { getTime, TimeRange } from 'ui/timefilter'; import { Query, onlyDisabledFiltersChanged } from '../../../../data/public'; import { APPLY_FILTER_TRIGGER, diff --git a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable_factory.ts b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable_factory.ts index c76b3c7fbc5fd..9cbf1f2dace29 100644 --- a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable_factory.ts +++ b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable_factory.ts @@ -22,7 +22,7 @@ import { capabilities } from 'ui/capabilities'; import { i18n } from '@kbn/i18n'; import chrome from 'ui/chrome'; import { IPrivate } from 'ui/private'; -import { TimeRange } from 'ui/timefilter/time_history'; +import { TimeRange } from 'ui/timefilter'; import { FilterBarQueryFilterProvider } from 'ui/filter_manager/query_filter'; import { EmbeddableFactory, diff --git a/src/legacy/core_plugins/kibana/public/discover/embeddable/types.ts b/src/legacy/core_plugins/kibana/public/discover/embeddable/types.ts index 43648d575014e..104d298f50c33 100644 --- a/src/legacy/core_plugins/kibana/public/discover/embeddable/types.ts +++ b/src/legacy/core_plugins/kibana/public/discover/embeddable/types.ts @@ -18,7 +18,7 @@ */ import { StaticIndexPattern } from 'ui/index_patterns'; -import { TimeRange } from 'ui/timefilter/time_history'; +import { TimeRange } from 'ui/timefilter'; import { Query } from 'src/legacy/core_plugins/data/public'; import { Filter } from '@kbn/es-query'; import { SavedSearch } from '../types'; diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js index 4dac5e26d3a4b..7b5ad8632d896 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js @@ -18,6 +18,7 @@ */ import _ from 'lodash'; +import { Subscription } from 'rxjs'; import { i18n } from '@kbn/i18n'; import '../saved_visualizations/saved_visualizations'; import './visualization_editor'; @@ -403,12 +404,16 @@ function VisEditor( } }; - const updateRefreshInterval = () => { - $scope.refreshInterval = timefilter.getRefreshInterval(); - }; + const subscriptions = new Subscription(); - $scope.$listenAndDigestAsync(timefilter, 'timeUpdate', updateTimeRange); - $scope.$listenAndDigestAsync(timefilter, 'refreshIntervalUpdate', updateRefreshInterval); + subscriptions.add(subscribeWithScope($scope, timefilter.getRefreshIntervalUpdate$(), { + next: () => { + $scope.refreshInterval = timefilter.getRefreshInterval(); + } + })); + subscriptions.add(subscribeWithScope($scope, timefilter.getTimeUpdate$(), { + next: updateTimeRange + })); // update the searchSource when query updates $scope.fetch = function () { @@ -419,15 +424,15 @@ function VisEditor( }; // update the searchSource when filters update - const filterUpdateSubscription = subscribeWithScope($scope, queryFilter.getUpdates$(), { + subscriptions.add(subscribeWithScope($scope, queryFilter.getUpdates$(), { next: () => { $scope.filters = queryFilter.getFilters(); $scope.globalFilters = queryFilter.getGlobalFilters(); } - }); - const filterFetchSubscription = subscribeWithScope($scope, queryFilter.getFetches$(), { + })); + subscriptions.add(subscribeWithScope($scope, queryFilter.getFetches$(), { next: $scope.fetch - }); + })); $scope.$on('$destroy', function () { if ($scope._handler) { @@ -435,8 +440,7 @@ function VisEditor( } savedVis.destroy(); stateMonitor.destroy(); - filterUpdateSubscription.unsubscribe(); - filterFetchSubscription.unsubscribe(); + subscriptions.unsubscribe(); }); if (!$scope.chrome.getVisible()) { diff --git a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts index bbe610e5e7755..d750010a132d6 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts @@ -29,7 +29,7 @@ import { } from 'ui/visualize/loader/types'; import { Subscription } from 'rxjs'; import * as Rx from 'rxjs'; -import { TimeRange } from 'ui/timefilter/time_history'; +import { TimeRange } from 'ui/timefilter'; import { Filter } from '@kbn/es-query'; import { EmbeddableInput, diff --git a/src/legacy/core_plugins/timelion/public/app.js b/src/legacy/core_plugins/timelion/public/app.js index 01a9d8dc57b9b..29e56f77fd837 100644 --- a/src/legacy/core_plugins/timelion/public/app.js +++ b/src/legacy/core_plugins/timelion/public/app.js @@ -326,7 +326,7 @@ app.controller('timelion', function ( }; $scope.$listen($scope.state, 'fetch_with_changes', $scope.search); - $scope.$listen(timefilter, 'fetch', $scope.search); + timefilter.getFetch$().subscribe($scope.search); $scope.opts = { saveExpression: saveExpression, diff --git a/src/legacy/ui/public/courier/courier.js b/src/legacy/ui/public/courier/courier.js index bd242aefc8f42..a317932e51118 100644 --- a/src/legacy/ui/public/courier/courier.js +++ b/src/legacy/ui/public/courier/courier.js @@ -54,7 +54,7 @@ uiModules.get('kibana/courier').service('courier', ($rootScope, Private) => { } }; - $rootScope.$listen(timefilter, 'refreshIntervalUpdate', updateRefreshInterval); + const refreshIntervalSubscription = timefilter.getRefreshIntervalUpdate$().subscribe(updateRefreshInterval); const closeOnFatal = _.once(() => { // If there was a fatal error, then stop future searches. We want to use pause instead of @@ -68,6 +68,8 @@ uiModules.get('kibana/courier').service('courier', ($rootScope, Private) => { if (searchRequestQueue.getCount()) { throw new Error('Aborting all pending requests failed.'); } + + refreshIntervalSubscription.unsubscribe(); }); addFatalErrorCallback(closeOnFatal); diff --git a/src/legacy/ui/public/courier/search_poll/search_poll.js b/src/legacy/ui/public/courier/search_poll/search_poll.js index ac921a9a2cf93..91c866c14aa49 100644 --- a/src/legacy/ui/public/courier/search_poll/search_poll.js +++ b/src/legacy/ui/public/courier/search_poll/search_poll.js @@ -82,7 +82,7 @@ export function SearchPollProvider(Private, Promise) { // We use resolve() here instead of try() because the latter won't trigger a $digest // when the promise resolves. this._searchPromise = Promise.resolve().then(() => { - timefilter.emit('autoRefreshFetch'); + timefilter.notifyShouldFetch(); const requests = searchRequestQueue.getInactive(); // The promise returned from fetchSearchRequests() only resolves when the requests complete. diff --git a/src/legacy/ui/public/directives/listen/listen.js b/src/legacy/ui/public/directives/listen/listen.js index 3c2e3ddb50021..b877e8ee6b08e 100644 --- a/src/legacy/ui/public/directives/listen/listen.js +++ b/src/legacy/ui/public/directives/listen/listen.js @@ -37,21 +37,4 @@ uiModules.get('kibana') emitter.off(eventName, handler); }); }; - - /** - * 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); - }; - }); diff --git a/src/legacy/ui/public/timefilter/index.js b/src/legacy/ui/public/timefilter/index.js deleted file mode 100644 index bf4227aecf442..0000000000000 --- a/src/legacy/ui/public/timefilter/index.js +++ /dev/null @@ -1,20 +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. - */ - -export { timefilter, registerTimefilterWithGlobalState } from './timefilter'; diff --git a/src/legacy/ui/public/timefilter/index.d.ts b/src/legacy/ui/public/timefilter/index.ts similarity index 75% rename from src/legacy/ui/public/timefilter/index.d.ts rename to src/legacy/ui/public/timefilter/index.ts index ce1ae86bb93ba..234ee50cece1a 100644 --- a/src/legacy/ui/public/timefilter/index.d.ts +++ b/src/legacy/ui/public/timefilter/index.ts @@ -17,5 +17,9 @@ * under the License. */ -export { timefilter, Timefilter } from './timefilter'; -export { timeHistory, TimeRange } from './time_history'; +// @ts-ignore +export { registerTimefilterWithGlobalState } from './timefilter'; +export { timefilter, Timefilter, RefreshInterval } from './timefilter'; +export { timeHistory, TimeRange, TimeHistory } from './time_history'; + +export { getTime } from './get_time'; diff --git a/src/legacy/ui/public/timefilter/timefilter.d.ts b/src/legacy/ui/public/timefilter/timefilter.d.ts index 4d56b9885db4b..fb9808298c6d0 100644 --- a/src/legacy/ui/public/timefilter/timefilter.d.ts +++ b/src/legacy/ui/public/timefilter/timefilter.d.ts @@ -18,6 +18,7 @@ */ import { Moment } from 'moment'; +import { Observable } from 'rxjs'; import { TimeRange } from './time_history'; import { RefreshInterval } from '../../../../plugins/data/public'; @@ -27,6 +28,11 @@ export { RefreshInterval, TimeRange }; export interface Timefilter { time: TimeRange; + getEnabledUpdated$: () => Observable; + getTimeUpdate$: () => Observable; + getRefreshIntervalUpdate$: () => Observable; + getAutoRefreshFetch$: () => Observable; + getFetch$: () => Observable; getTime: () => TimeRange; setTime: (timeRange: TimeRange) => void; setRefreshInterval: (refreshInterval: RefreshInterval) => void; @@ -36,8 +42,6 @@ export interface Timefilter { disableTimeRangeSelector: () => void; enableAutoRefreshSelector: () => void; enableTimeRangeSelector: () => void; - off: (event: string, reload: () => void) => void; - on: (event: string, reload: () => void) => void; isAutoRefreshSelectorEnabled: boolean; isTimeRangeSelectorEnabled: boolean; } diff --git a/src/legacy/ui/public/timefilter/timefilter.js b/src/legacy/ui/public/timefilter/timefilter.js index f2d2f4dea5c6b..fa85d175690a9 100644 --- a/src/legacy/ui/public/timefilter/timefilter.js +++ b/src/legacy/ui/public/timefilter/timefilter.js @@ -18,24 +18,60 @@ */ import _ from 'lodash'; +import { Subject, BehaviorSubject } from 'rxjs'; import moment from 'moment'; import { calculateBounds, getTime } from './get_time'; -import { parseQueryString } from 'ui/timefilter/lib/parse_querystring'; -import { SimpleEmitter } from 'ui/utils/simple_emitter'; +import { parseQueryString } from './lib/parse_querystring'; +import { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; import uiRoutes from '../routes'; import chrome from 'ui/chrome'; import { areTimePickerValsDifferent } from './lib/diff_time_picker_vals'; import { timeHistory } from './time_history'; -class Timefilter extends SimpleEmitter { +class Timefilter { constructor() { - super(); + + // Fired when isTimeRangeSelectorEnabled \ isAutoRefreshSelectorEnabled are toggled + this.enabledUpdated$ = new BehaviorSubject(); + + // Fired when a user changes the timerange + this.timeUpdate$ = new Subject(); + + // Fired when a user changes the the autorefresh settings + this.refreshIntervalUpdate$ = new Subject(); + + // Used when search poll triggers an auto refresh + this.autoRefreshFetch$ = new Subject(); + + this.fetch$ = new Subject(); + this.isTimeRangeSelectorEnabled = false; this.isAutoRefreshSelectorEnabled = false; this._time = chrome.getUiSettingsClient().get('timepicker:timeDefaults'); this.setRefreshInterval(chrome.getUiSettingsClient().get('timepicker:refreshIntervalDefaults')); } + getEnabledUpdated$ = () => { + return this.enabledUpdated$.asObservable(); + } + + getTimeUpdate$ = () => { + return this.timeUpdate$.asObservable(); + } + + getRefreshIntervalUpdate$ = () => { + return this.refreshIntervalUpdate$.asObservable(); + } + + getAutoRefreshFetch$ = () => { + return this.autoRefreshFetch$.asObservable(); + } + + getFetch$ = () => { + return this.fetch$.asObservable(); + } + + getTime = () => { const { from, to } = this._time; return { @@ -61,8 +97,8 @@ class Timefilter extends SimpleEmitter { to: newTime.to, }; timeHistory.add(this._time); - this.emit('timeUpdate'); - this.emit('fetch'); + this.timeUpdate$.next(); + this.fetch$.next(); } } @@ -91,9 +127,9 @@ class Timefilter extends SimpleEmitter { // Only send out an event if we already had a previous refresh interval (not for the initial set) // and the old and new refresh interval are actually different. if (prevRefreshInterval && areTimePickerValsDifferent(prevRefreshInterval, newRefreshInterval)) { - this.emit('refreshIntervalUpdate'); + this.refreshIntervalUpdate$.next(); if (!newRefreshInterval.pause && newRefreshInterval.value !== 0) { - this.emit('fetch'); + this.fetch$.next(); } } } @@ -138,7 +174,7 @@ class Timefilter extends SimpleEmitter { */ enableTimeRangeSelector = () => { this.isTimeRangeSelectorEnabled = true; - this.emit('enabledUpdated'); + this.enabledUpdated$.next(); } /** @@ -146,7 +182,7 @@ class Timefilter extends SimpleEmitter { */ disableTimeRangeSelector = () => { this.isTimeRangeSelectorEnabled = false; - this.emit('enabledUpdated'); + this.enabledUpdated$.next(); } /** @@ -154,7 +190,7 @@ class Timefilter extends SimpleEmitter { */ enableAutoRefreshSelector = () => { this.isAutoRefreshSelectorEnabled = true; - this.emit('enabledUpdated'); + this.enabledUpdated$.next(); } /** @@ -162,7 +198,11 @@ class Timefilter extends SimpleEmitter { */ disableAutoRefreshSelector = () => { this.isAutoRefreshSelectorEnabled = false; - this.emit('enabledUpdated'); + this.enabledUpdated$.next(); + } + + notifyShouldFetch = () => { + this.autoRefreshFetch$.next(); } } @@ -209,9 +249,14 @@ export const registerTimefilterWithGlobalState = _.once((globalState, $rootScope globalState.save(); }; - $rootScope.$listenAndDigestAsync(timefilter, 'refreshIntervalUpdate', updateGlobalStateWithTime); + subscribeWithScope($rootScope, timefilter.getRefreshIntervalUpdate$(), { + next: updateGlobalStateWithTime + }); + + subscribeWithScope($rootScope, timefilter.getTimeUpdate$(), { + next: updateGlobalStateWithTime + }); - $rootScope.$listenAndDigestAsync(timefilter, 'timeUpdate', updateGlobalStateWithTime); }); uiRoutes diff --git a/src/legacy/ui/public/timefilter/timefilter.test.js b/src/legacy/ui/public/timefilter/timefilter.test.js index 084d03b129339..64a43b259dd3f 100644 --- a/src/legacy/ui/public/timefilter/timefilter.test.js +++ b/src/legacy/ui/public/timefilter/timefilter.test.js @@ -64,6 +64,8 @@ function clearNowTimeStub() { describe('setTime', () => { let update; let fetch; + let updateSub; + let fetchSub; beforeEach(() => { update = sinon.spy(); @@ -72,8 +74,13 @@ describe('setTime', () => { from: 0, to: 1, }); - timefilter.on('timeUpdate', update); - timefilter.on('fetch', fetch); + updateSub = timefilter.getTimeUpdate$().subscribe(update); + fetchSub = timefilter.getFetch$().subscribe(fetch); + }); + + afterEach(() => { + updateSub.unsubscribe(); + fetchSub.unsubscribe(); }); test('should update time', () => { @@ -116,9 +123,10 @@ describe('setTime', () => { }); describe('setRefreshInterval', () => { - let update; let fetch; + let fetchSub; + let refreshSub; beforeEach(() => { update = sinon.spy(); @@ -127,8 +135,13 @@ describe('setRefreshInterval', () => { pause: false, value: 0 }); - timefilter.on('refreshIntervalUpdate', update); - timefilter.on('fetch', fetch); + refreshSub = timefilter.getRefreshIntervalUpdate$().subscribe(update); + fetchSub = timefilter.getFetch$().subscribe(fetch); + }); + + afterEach(() => { + refreshSub.unsubscribe(); + fetchSub.unsubscribe(); }); test('should update refresh interval', () => { @@ -203,10 +216,15 @@ describe('setRefreshInterval', () => { describe('isTimeRangeSelectorEnabled', () => { let update; + let updateSub; beforeEach(() => { update = sinon.spy(); - timefilter.on('enabledUpdated', update); + updateSub = timefilter.getEnabledUpdated$().subscribe(update); + }); + + afterEach(() => { + updateSub.unsubscribe(); }); test('should emit updated when disabled', () => { @@ -224,10 +242,15 @@ describe('isTimeRangeSelectorEnabled', () => { describe('isAutoRefreshSelectorEnabled', () => { let update; + let updateSub; beforeEach(() => { update = sinon.spy(); - timefilter.on('enabledUpdated', update); + updateSub = timefilter.getEnabledUpdated$().subscribe(update); + }); + + afterEach(() => { + updateSub.unsubscribe(); }); test('should emit updated when disabled', () => { diff --git a/src/legacy/ui/public/vis/request_handlers/request_handlers.d.ts b/src/legacy/ui/public/vis/request_handlers/request_handlers.d.ts index 27d57e65a1289..476b3fa68473e 100644 --- a/src/legacy/ui/public/vis/request_handlers/request_handlers.d.ts +++ b/src/legacy/ui/public/vis/request_handlers/request_handlers.d.ts @@ -17,7 +17,7 @@ * under the License. */ -import { TimeRange } from 'ui/timefilter/time_history'; +import { TimeRange } from 'ui/timefilter'; import { Query } from 'src/legacy/core_plugins/data/public'; import { Filter } from '@kbn/es-query'; import { SearchSource } from '../../courier'; diff --git a/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.mocks.ts b/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.mocks.ts index 6adea30c94fb4..a7414865d7433 100644 --- a/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.mocks.ts +++ b/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.mocks.ts @@ -19,7 +19,7 @@ jest.useFakeTimers(); -import { EventEmitter } from 'events'; +import { Subject } from 'rxjs'; jest.mock('ui/notify', () => ({ toastNotifications: jest.fn(), @@ -34,7 +34,16 @@ jest.mock('./pipeline_helpers/utilities', () => ({ getTableAggs: jest.fn(), })); -export const timefilter = new EventEmitter(); +const autoRefreshFetchSub = new Subject(); + +export const timefilter = { + _triggerAutoRefresh: () => { + autoRefreshFetchSub.next(); + }, + getAutoRefreshFetch$: () => { + return autoRefreshFetchSub.asObservable(); + }, +}; jest.doMock('../../timefilter', () => ({ timefilter })); jest.mock('../../inspector', () => ({ diff --git a/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts b/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts index d6a5ede713c6c..9a16405398efb 100644 --- a/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts +++ b/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts @@ -86,7 +86,7 @@ describe('EmbeddedVisualizeHandler', () => { describe('autoFetch', () => { it('should trigger a reload when autoFetch=true and auto refresh happens', () => { const spy = jest.spyOn(handler, 'fetchAndRender'); - timefilter.emit('autoRefreshFetch'); + timefilter._triggerAutoRefresh(); jest.runAllTimers(); expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledWith(true); @@ -110,7 +110,7 @@ describe('EmbeddedVisualizeHandler', () => { } ); const spy = jest.spyOn(handler, 'fetchAndRender'); - timefilter.emit('autoRefreshFetch'); + timefilter._triggerAutoRefresh(); jest.runAllTimers(); expect(spy).not.toHaveBeenCalled(); }); diff --git a/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.ts b/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.ts index 774ccb4d2a069..1a0155246e87f 100644 --- a/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.ts +++ b/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.ts @@ -105,6 +105,7 @@ export class EmbeddedVisualizeHandler { private actions: any = {}; private events$: Rx.Observable; private autoFetch: boolean; + private autoRefreshFetchSubscription: Rx.Subscription | undefined; constructor( private readonly element: HTMLElement, @@ -167,7 +168,7 @@ export class EmbeddedVisualizeHandler { this.vis.on('reload', this.reload); this.uiState.on('change', this.onUiStateChange); if (autoFetch) { - timefilter.on('autoRefreshFetch', this.reload); + this.autoRefreshFetchSubscription = timefilter.getAutoRefreshFetch$().subscribe(this.reload); } // This is a hack to give maps visualizations access to data in the @@ -269,7 +270,7 @@ export class EmbeddedVisualizeHandler { this.destroyed = true; this.debouncedFetchAndRender.cancel(); if (this.autoFetch) { - timefilter.off('autoRefreshFetch', this.reload); + if (this.autoRefreshFetchSubscription) this.autoRefreshFetchSubscription.unsubscribe(); } this.vis.removeListener('reload', this.reload); this.vis.removeListener('update', this.handleVisUpdate); diff --git a/src/legacy/ui/public/visualize/loader/types.ts b/src/legacy/ui/public/visualize/loader/types.ts index 77e86ae063082..595a0649ec5ef 100644 --- a/src/legacy/ui/public/visualize/loader/types.ts +++ b/src/legacy/ui/public/visualize/loader/types.ts @@ -18,7 +18,7 @@ */ import { Filter } from '@kbn/es-query'; -import { TimeRange } from 'ui/timefilter/time_history'; +import { TimeRange } from 'ui/timefilter'; import { Query } from 'src/legacy/core_plugins/data/public'; import { SavedObject } from 'ui/saved_objects/saved_object'; diff --git a/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.test.js b/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.test.js index 0a5a885739f86..e6fb1957fec41 100644 --- a/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.test.js @@ -14,7 +14,7 @@ jest.mock('ui/vis/editors/default/schemas', () => { }); jest.mock('../../kibana_services', () => {}); jest.mock('ui/vis/agg_configs', () => {}); -jest.mock('ui/timefilter/timefilter', () => {}); +jest.mock('ui/timefilter', () => {}); jest.mock('../vector_layer', () => {}); diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js index 6d64b468d14f7..afb7314908c4c 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js @@ -11,7 +11,7 @@ import { SearchSource } from '../../kibana_services'; import { createExtentFilter } from '../../elasticsearch_geo_utils'; -import { timefilter } from 'ui/timefilter/timefilter'; +import { timefilter } from 'ui/timefilter'; import _ from 'lodash'; import { AggConfigs } from 'ui/vis/agg_configs'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.test.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.test.js index 65bfd993c871a..67a25769ee5c3 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.test.js @@ -12,7 +12,7 @@ jest.mock('ui/vis/editors/default/schemas', () => ({ })); jest.mock('../../kibana_services', () => {}); jest.mock('ui/vis/agg_configs', () => {}); -jest.mock('ui/timefilter/timefilter', () => {}); +jest.mock('ui/timefilter', () => {}); const indexPatternTitle = 'myIndex'; const termFieldName = 'myTermField'; diff --git a/x-pack/legacy/plugins/ml/public/components/navigation_menu/top_nav/top_nav.tsx b/x-pack/legacy/plugins/ml/public/components/navigation_menu/top_nav/top_nav.tsx index 61b4594ae008b..6e38b37c33a24 100644 --- a/x-pack/legacy/plugins/ml/public/components/navigation_menu/top_nav/top_nav.tsx +++ b/x-pack/legacy/plugins/ml/public/components/navigation_menu/top_nav/top_nav.tsx @@ -5,8 +5,9 @@ */ import React, { FC, Fragment, useState, useEffect } from 'react'; +import { Subscription } from 'rxjs'; import { EuiSuperDatePicker } from '@elastic/eui'; -import { TimeHistory, TimeRange } from 'ui/timefilter/time_history'; +import { TimeHistory, TimeRange } from 'ui/timefilter'; import { mlTimefilterRefresh$ } from '../../../services/timefilter_refresh_service'; import { useUiContext } from '../../../contexts/ui/use_ui_context'; @@ -44,14 +45,13 @@ export const TopNav: FC = () => { const dateFormat = chrome.getUiSettingsClient().get('dateFormat'); useEffect(() => { - timefilter.on('refreshIntervalUpdate', timefilterUpdateListener); - timefilter.on('timeUpdate', timefilterUpdateListener); - timefilter.on('enabledUpdated', timefilterUpdateListener); + const subscriptions = new Subscription(); + subscriptions.add(timefilter.getRefreshIntervalUpdate$().subscribe(timefilterUpdateListener)); + subscriptions.add(timefilter.getTimeUpdate$().subscribe(timefilterUpdateListener)); + subscriptions.add(timefilter.getEnabledUpdated$().subscribe(timefilterUpdateListener)); return function cleanup() { - timefilter.off('refreshIntervalUpdate', timefilterUpdateListener); - timefilter.off('timeUpdate', timefilterUpdateListener); - timefilter.off('enabledUpdated', timefilterUpdateListener); + subscriptions.unsubscribe(); }; }, []); diff --git a/x-pack/legacy/plugins/ml/public/contexts/ui/ui_context.tsx b/x-pack/legacy/plugins/ml/public/contexts/ui/ui_context.tsx index 52a90f795e5bd..4cb97cf5639fe 100644 --- a/x-pack/legacy/plugins/ml/public/contexts/ui/ui_context.tsx +++ b/x-pack/legacy/plugins/ml/public/contexts/ui/ui_context.tsx @@ -7,8 +7,7 @@ import React from 'react'; import chrome from 'ui/chrome'; -import { timefilter } from 'ui/timefilter'; -import { timeHistory } from 'ui/timefilter/time_history'; +import { timefilter, timeHistory } from 'ui/timefilter'; // This provides ui/* based imports via React Context. // Because these dependencies can use regular imports, diff --git a/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/use_refresh_interval.ts b/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/use_refresh_interval.ts index c2acf63eb407e..c4086ba6225bb 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/use_refresh_interval.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/use_refresh_interval.ts @@ -21,12 +21,14 @@ export const useRefreshInterval = ( const { refresh } = useRefreshTransformList(); useEffect(() => { let transformRefreshInterval: null | number = null; + const refreshIntervalSubscription = timefilter + .getRefreshIntervalUpdate$() + .subscribe(setAutoRefresh); timefilter.disableTimeRangeSelector(); timefilter.enableAutoRefreshSelector(); initAutoRefresh(); - initAutoRefreshUpdate(); function initAutoRefresh() { const { value } = timefilter.getRefreshInterval(); @@ -42,13 +44,6 @@ export const useRefreshInterval = ( setAutoRefresh(); } - function initAutoRefreshUpdate() { - // update the interval if it changes - timefilter.on('refreshIntervalUpdate', () => { - setAutoRefresh(); - }); - } - function setAutoRefresh() { const { value, pause } = timefilter.getRefreshInterval(); if (pause) { @@ -71,6 +66,9 @@ export const useRefreshInterval = ( } function clearRefreshInterval() { + if (refreshIntervalSubscription) { + refreshIntervalSubscription.unsubscribe(); + } setBlockRefresh(true); if (transformRefreshInterval !== null) { window.clearInterval(transformRefreshInterval); @@ -79,6 +77,7 @@ export const useRefreshInterval = ( // useEffect cleanup return () => { + refreshIntervalSubscription.unsubscribe(); clearRefreshInterval(); }; }, []); // [] as comparator makes sure this only runs once diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/analytics_list/use_refresh_interval.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/analytics_list/use_refresh_interval.ts index 09e689dfd2a27..cfd900b303aa3 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/analytics_list/use_refresh_interval.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/analytics_list/use_refresh_interval.ts @@ -22,11 +22,13 @@ export const useRefreshInterval = ( useEffect(() => { let analyticsRefreshInterval: null | number = null; + const refreshIntervalSubscription = timefilter + .getRefreshIntervalUpdate$() + .subscribe(setAutoRefresh); timefilter.disableTimeRangeSelector(); timefilter.enableAutoRefreshSelector(); initAutoRefresh(); - initAutoRefreshUpdate(); function initAutoRefresh() { const { value } = timefilter.getRefreshInterval(); @@ -42,13 +44,6 @@ export const useRefreshInterval = ( setAutoRefresh(); } - function initAutoRefreshUpdate() { - // update the interval if it changes - timefilter.on('refreshIntervalUpdate', () => { - setAutoRefresh(); - }); - } - function setAutoRefresh() { const { value, pause } = timefilter.getRefreshInterval(); if (pause) { @@ -79,6 +74,7 @@ export const useRefreshInterval = ( // useEffect cleanup return () => { + refreshIntervalSubscription.unsubscribe(); clearRefreshInterval(); }; }, []); // [] as comparator makes sure this only runs once diff --git a/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/page.tsx b/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/page.tsx index 82f1d985d41b7..c2929df8b3e9b 100644 --- a/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/page.tsx +++ b/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/page.tsx @@ -166,9 +166,9 @@ export const Page: FC = () => { const [nonMetricFieldQuery, setNonMetricFieldQuery] = useState(defaults.nonMetricFieldQuery); useEffect(() => { - timefilter.on('timeUpdate', loadOverallStats); + const timeUpdateSubscription = timefilter.getTimeUpdate$().subscribe(loadOverallStats); return () => { - timefilter.off('timeUpdate', loadOverallStats); + timeUpdateSubscription.unsubscribe(); }; }, []); diff --git a/x-pack/legacy/plugins/ml/public/explorer/explorer_controller.js b/x-pack/legacy/plugins/ml/public/explorer/explorer_controller.js index b1dcd55aed599..693c884a7bb90 100644 --- a/x-pack/legacy/plugins/ml/public/explorer/explorer_controller.js +++ b/x-pack/legacy/plugins/ml/public/explorer/explorer_controller.js @@ -203,11 +203,11 @@ module.controller('MlExplorerController', function ( })); // Refresh all the data when the time range is altered. - $scope.$listenAndDigestAsync(timefilter, 'fetch', () => { + subscriptions.add(timefilter.getFetch$().subscribe(() => { if ($scope.jobSelectionUpdateInProgress === false) { explorer$.next({ action: EXPLORER_ACTION.RELOAD }); } - }); + })); subscriptions.add(subscribeAppStateToObservable(AppState, 'mlShowCharts', showCharts$, () => $rootScope.$applyAsync())); subscriptions.add(subscribeAppStateToObservable(AppState, 'mlSelectInterval', interval$, () => $rootScope.$applyAsync())); diff --git a/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js b/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js index aab502dacbcff..39bea2d1c205e 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js +++ b/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js @@ -69,6 +69,7 @@ export class JobsListView extends Component { this.showCreateWatchFlyout = () => {}; this.blockRefresh = false; + this.refreshIntervalSubscription = null; } componentDidMount() { @@ -100,7 +101,7 @@ export class JobsListView extends Component { componentWillUnmount() { if (this.props.isManagementTable === undefined) { - timefilter.off('refreshIntervalUpdate'); + if (this.refreshIntervalSubscription) this.refreshIntervalSubscription.unsubscribe(); deletingJobsRefreshTimeout = null; this.clearRefreshInterval(); } @@ -122,8 +123,8 @@ export class JobsListView extends Component { initAutoRefreshUpdate() { // update the interval if it changes - timefilter.on('refreshIntervalUpdate', () => { - this.setAutoRefresh(); + this.refreshIntervalSubscription = timefilter.getRefreshIntervalUpdate$().subscribe({ + next: this.setAutoRefresh }); } diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job/simple/multi_metric/create_job/create_job_controller.js b/x-pack/legacy/plugins/ml/public/jobs/new_job/simple/multi_metric/create_job/create_job_controller.js index 6a3a5ccda38ff..d21ab0fa0c79d 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job/simple/multi_metric/create_job/create_job_controller.js +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job/simple/multi_metric/create_job/create_job_controller.js @@ -18,6 +18,7 @@ import dateMath from '@elastic/datemath'; import angular from 'angular'; import uiRoutes from 'ui/routes'; +import { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; import { checkLicenseExpired } from 'plugins/ml/license/check_license'; import { checkCreateJobsPrivilege } from 'plugins/ml/privilege/check_privilege'; import { MlTimeBuckets } from 'plugins/ml/util/ml_time_buckets'; @@ -757,15 +758,18 @@ module preLoadJob($scope, appState); }); - $scope.$listenAndDigestAsync(timefilter, 'fetch', () => { - $scope.loadVis(); - if ($scope.formConfig.splitField !== undefined) { - $scope.setModelMemoryLimit(); + const fetchSub = subscribeWithScope($scope, timefilter.getFetch$(), { + next: () => { + $scope.loadVis(); + if ($scope.formConfig.splitField !== undefined) { + $scope.setModelMemoryLimit(); + } } }); $scope.$on('$destroy', () => { globalForceStop = true; angular.element(window).off('resize'); + fetchSub.unsubscribe(); }); }); diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job/simple/population/create_job/create_job_controller.js b/x-pack/legacy/plugins/ml/public/jobs/new_job/simple/population/create_job/create_job_controller.js index 2fed600db9e2c..f1dcd383570b0 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job/simple/population/create_job/create_job_controller.js +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job/simple/population/create_job/create_job_controller.js @@ -43,6 +43,7 @@ import { PopulationJobServiceProvider } from './create_job_service'; import { mlMessageBarService } from 'plugins/ml/components/messagebar/messagebar_service'; import template from './create_job.html'; import { timefilter } from 'ui/timefilter'; +import { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; uiRoutes .when('/jobs/new_job/simple/population', { @@ -737,10 +738,13 @@ module preLoadJob($scope, appState); }); - $scope.$listenAndDigestAsync(timefilter, 'fetch', $scope.loadVis); + const fetchSub = subscribeWithScope($scope, timefilter.getFetch$(), { + next: $scope.loadVis + }); $scope.$on('$destroy', () => { globalForceStop = true; angular.element(window).off('resize'); + fetchSub.unsubscribe(); }); }); diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job/simple/single_metric/create_job/create_job_controller.js b/x-pack/legacy/plugins/ml/public/jobs/new_job/simple/single_metric/create_job/create_job_controller.js index 6ffb4efa9b74b..b5dd73aa54203 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job/simple/single_metric/create_job/create_job_controller.js +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job/simple/single_metric/create_job/create_job_controller.js @@ -18,6 +18,7 @@ import dateMath from '@elastic/datemath'; import angular from 'angular'; import uiRoutes from 'ui/routes'; +import { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; import { getSafeAggregationName } from 'plugins/ml/../common/util/job_utils'; import { checkLicenseExpired } from 'plugins/ml/license/check_license'; import { checkCreateJobsPrivilege } from 'plugins/ml/privilege/check_privilege'; @@ -621,10 +622,13 @@ module moveToAdvancedJobCreation(job); }; - $scope.$listenAndDigestAsync(timefilter, 'fetch', $scope.loadVis); + const fetchSub = subscribeWithScope($scope, timefilter.getFetch$(), { + next: $scope.loadVis + }); $scope.$on('$destroy', () => { globalForceStop = true; + fetchSub.unsubscribe(); }); $scope.$evalAsync(() => { diff --git a/x-pack/legacy/plugins/ml/public/timeseriesexplorer/timeseriesexplorer.js b/x-pack/legacy/plugins/ml/public/timeseriesexplorer/timeseriesexplorer.js index 9a5437199f5b2..5441d6ccd74c0 100644 --- a/x-pack/legacy/plugins/ml/public/timeseriesexplorer/timeseriesexplorer.js +++ b/x-pack/legacy/plugins/ml/public/timeseriesexplorer/timeseriesexplorer.js @@ -882,7 +882,8 @@ export class TimeSeriesExplorer extends React.Component { timefilter.enableTimeRangeSelector(); timefilter.enableAutoRefreshSelector(); - timefilter.on('timeUpdate', this.refresh); + + this.subscriptions.add(timefilter.getTimeUpdate$().subscribe(this.refresh)); // Required to redraw the time series chart when the container is resized. this.resizeChecker = new ResizeChecker(this.resizeRef.current); @@ -894,7 +895,6 @@ export class TimeSeriesExplorer extends React.Component { componentWillUnmount() { this.subscriptions.unsubscribe(); - this.props.timefilter.off('timeUpdate', this.refresh); this.resizeChecker.destroy(); this.unsubscribeFromGlobalState(); } diff --git a/x-pack/legacy/plugins/ml/public/util/chart_utils.test.js b/x-pack/legacy/plugins/ml/public/util/chart_utils.test.js index d61b4f3edcfe0..eea6691c944b8 100644 --- a/x-pack/legacy/plugins/ml/public/util/chart_utils.test.js +++ b/x-pack/legacy/plugins/ml/public/util/chart_utils.test.js @@ -31,16 +31,6 @@ jest.mock('ui/chrome', }, }), { 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 d3 from 'd3'; import moment from 'moment'; import { mount } from 'enzyme'; diff --git a/x-pack/legacy/plugins/monitoring/public/services/__tests__/executor_provider.js b/x-pack/legacy/plugins/monitoring/public/services/__tests__/executor_provider.js index 36ab9431058eb..4c72902c294bc 100644 --- a/x-pack/legacy/plugins/monitoring/public/services/__tests__/executor_provider.js +++ b/x-pack/legacy/plugins/monitoring/public/services/__tests__/executor_provider.js @@ -37,24 +37,6 @@ describe('$executor service', () => { afterEach(() => executor.destroy()); - it('should register listener for fetch upon start', () => { - 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 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(scope); expect($timeout.callCount).to.equal(0); diff --git a/x-pack/legacy/plugins/monitoring/public/services/executor_provider.js b/x-pack/legacy/plugins/monitoring/public/services/executor_provider.js index 7dd54e2a6ec27..70e0bbf6b0f80 100644 --- a/x-pack/legacy/plugins/monitoring/public/services/executor_provider.js +++ b/x-pack/legacy/plugins/monitoring/public/services/executor_provider.js @@ -5,9 +5,12 @@ */ import { timefilter } from 'ui/timefilter'; +import { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; +import { Subscription } from 'rxjs'; export function executorProvider(Promise, $timeout) { const queue = []; + const subscriptions = new Subscription(); let executionTimer; let ignorePaused = false; @@ -46,6 +49,7 @@ export function executorProvider(Promise, $timeout) { * @returns {void} */ function destroy() { + subscriptions.unsubscribe(); cancel(); ignorePaused = false; queue.splice(0, queue.length); @@ -92,8 +96,12 @@ export function executorProvider(Promise, $timeout) { return { register, start($scope) { - $scope.$listenAndDigestAsync(timefilter, 'fetch', reFetch); - $scope.$listenAndDigestAsync(timefilter, 'refreshIntervalUpdate', killIfPaused); + subscriptions.add(subscribeWithScope($scope, timefilter.getFetch$(), { + next: reFetch + })); + subscriptions.add(subscribeWithScope($scope, timefilter.getRefreshIntervalUpdate$(), { + next: killIfPaused + })); start(); }, run, diff --git a/x-pack/legacy/plugins/monitoring/public/views/no_data/controller.js b/x-pack/legacy/plugins/monitoring/public/views/no_data/controller.js index 371ef8e2bcc10..1576b80235c44 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/no_data/controller.js +++ b/x-pack/legacy/plugins/monitoring/public/views/no_data/controller.js @@ -16,15 +16,19 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { NoData } from 'plugins/monitoring/components'; import { timefilter } from 'ui/timefilter'; import { I18nContext } from 'ui/i18n'; -import 'ui/directives/listen'; import { CODE_PATH_LICENSE } from '../../../common/constants'; const REACT_NODE_ID_NO_DATA = 'noDataReact'; +// NoDataController watches all this attributes. +// After consulting with @Chris Roberson, decided to move this out of the class for now. +let timeUpdateSubscription; + export class NoDataController { + constructor($injector, $scope) { const $executor = $injector.get('$executor'); - this.enableTimefilter($executor, $scope); + this.enableTimefilter($executor); this.registerCleanup($scope, $executor); Object.assign(this, this.getDefaultModel()); @@ -103,10 +107,12 @@ export class NoDataController { $executor.start($scope); // start the executor to keep refreshing the search for data } - enableTimefilter($executor, $scope) { + enableTimefilter($executor) { timefilter.enableTimeRangeSelector(); timefilter.enableAutoRefreshSelector(); - $scope.$listen(timefilter, 'timeUpdate', () => $executor.run()); // re-fetch if they change the time filter + + // re-fetch if they change the time filter + timeUpdateSubscription = timefilter.getTimeUpdate$().subscribe(() => $executor.run()); } registerCleanup($scope, $executor) { @@ -114,6 +120,7 @@ export class NoDataController { $scope.$on('$destroy', () => { $executor.destroy(); unmountComponentAtNode(document.getElementById(REACT_NODE_ID_NO_DATA)); + if (timeUpdateSubscription) { timeUpdateSubscription.unsubscribe(); } }); } }