From 8f50795a940dfb11e429be2e08366b97b928c10d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 31 Dec 2017 22:31:11 +0100 Subject: [PATCH] tech: url and query mobx store so now react components and containers can read and modify url path and query via mobx store --- .../AlertRuleList/AlertRuleList.tsx | 10 ++- public/app/core/components/grafana_app.ts | 9 +-- public/app/core/services/all.js | 2 +- .../{global_event_srv.ts => bridge_srv.ts} | 38 ++++++++-- ...l_event_srv.jest.ts => bridge_srv.jest.ts} | 5 +- .../app/features/alerting/alert_list_ctrl.ts | 74 ------------------- public/app/features/alerting/all.ts | 1 - .../alerting/partials/alert_list.html | 61 --------------- public/app/routes/ReactContainer.tsx | 2 +- public/app/routes/routes.ts | 3 +- public/app/stores/AlertListStore.ts | 6 +- public/app/stores/RootStore.ts | 5 ++ public/app/stores/ViewStore.ts | 35 +++++++++ public/app/stores/store.ts | 2 + 14 files changed, 93 insertions(+), 160 deletions(-) rename public/app/core/services/{global_event_srv.ts => bridge_srv.ts} (50%) rename public/app/core/specs/{global_event_srv.jest.ts => bridge_srv.jest.ts} (73%) delete mode 100644 public/app/features/alerting/alert_list_ctrl.ts delete mode 100644 public/app/features/alerting/partials/alert_list.html create mode 100644 public/app/stores/ViewStore.ts diff --git a/public/app/containers/AlertRuleList/AlertRuleList.tsx b/public/app/containers/AlertRuleList/AlertRuleList.tsx index ee72dd53b9d..1ec461925c2 100644 --- a/public/app/containers/AlertRuleList/AlertRuleList.tsx +++ b/public/app/containers/AlertRuleList/AlertRuleList.tsx @@ -25,12 +25,16 @@ export class AlertRuleList extends React.Component { constructor(props) { super(props); - this.props.store.nav.load('alerting', 'alert-list'); - this.props.store.alertList.loadRules(); + const store = this.props.store; + + store.nav.load('alerting', 'alert-list'); + store.alertList.setStateFilter(store.view.query.get('state') || 'all'); + store.alertList.loadRules(); } onStateFilterChanged = evt => { this.props.store.alertList.setStateFilter(evt.target.value); + this.props.store.view.updateQuery({ state: evt.target.value }); this.props.store.alertList.loadRules(); }; @@ -54,7 +58,7 @@ export class AlertRuleList extends React.Component {
- {this.stateFilters.map(AlertStateFilterOption)}
diff --git a/public/app/core/components/grafana_app.ts b/public/app/core/components/grafana_app.ts index defd04cefd9..eb51334f358 100644 --- a/public/app/core/components/grafana_app.ts +++ b/public/app/core/components/grafana_app.ts @@ -10,19 +10,18 @@ import { createStore } from 'app/stores/store'; export class GrafanaCtrl { /** @ngInject */ - constructor($scope, alertSrv, utilSrv, $rootScope, $controller, contextSrv, globalEventSrv, backendSrv) { + constructor($scope, alertSrv, utilSrv, $rootScope, $controller, contextSrv, bridgeSrv, backendSrv) { createStore(backendSrv); $scope.init = function() { $scope.contextSrv = contextSrv; - - $rootScope.appSubUrl = config.appSubUrl; + $scope.appSubUrl = config.appSubUrl; $scope._ = _; profiler.init(config, $rootScope); alertSrv.init(); utilSrv.init(); - globalEventSrv.init(); + bridgeSrv.init(); $scope.dashAlerts = alertSrv; }; @@ -54,7 +53,7 @@ export class GrafanaCtrl { } /** @ngInject */ -export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScope) { +export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScope, $location) { return { restrict: 'E', controller: GrafanaCtrl, diff --git a/public/app/core/services/all.js b/public/app/core/services/all.js index 0053d789cbe..0a973440c5e 100644 --- a/public/app/core/services/all.js +++ b/public/app/core/services/all.js @@ -8,6 +8,6 @@ define([ './segment_srv', './backend_srv', './dynamic_directive_srv', - './global_event_srv' + './bridge_srv' ], function () {}); diff --git a/public/app/core/services/global_event_srv.ts b/public/app/core/services/bridge_srv.ts similarity index 50% rename from public/app/core/services/global_event_srv.ts rename to public/app/core/services/bridge_srv.ts index 0569b9933d8..561f651b089 100644 --- a/public/app/core/services/global_event_srv.ts +++ b/public/app/core/services/bridge_srv.ts @@ -1,15 +1,16 @@ -import coreModule from 'app/core/core_module'; +import coreModule from 'app/core/core_module'; import config from 'app/core/config'; import appEvents from 'app/core/app_events'; +import { store } from 'app/stores/store'; +import { reaction } from 'mobx'; -// This service is for registering global events. -// Good for communication react > angular and vice verse -export class GlobalEventSrv { +// Services that handles angular -> mobx store sync & other react <-> angular sync +export class BridgeSrv { private appSubUrl; private fullPageReloadRoutes; /** @ngInject */ - constructor(private $location, private $timeout, private $window) { + constructor(private $location, private $timeout, private $window, private $rootScope) { this.appSubUrl = config.appSubUrl; this.fullPageReloadRoutes = ['/logout']; } @@ -25,6 +26,31 @@ export class GlobalEventSrv { } init() { + this.$rootScope.$on('$routeUpdate', (evt, data) => { + let angularUrl = this.$location.url(); + if (store.view.currentUrl !== angularUrl) { + store.view.updatePathAndQuery(this.$location.path(), this.$location.search()); + } + }); + + this.$rootScope.$on('$routeChangeSuccess', (evt, data) => { + let angularUrl = this.$location.url(); + if (store.view.currentUrl !== angularUrl) { + store.view.updatePathAndQuery(this.$location.path(), this.$location.search()); + } + }); + + reaction( + () => store.view.currentUrl, + currentUrl => { + let angularUrl = this.$location.url(); + if (angularUrl !== currentUrl) { + this.$location.url(currentUrl); + console.log('store updating angular $location.url', currentUrl); + } + } + ); + appEvents.on('location-change', payload => { const urlWithoutBase = this.stripBaseFromUrl(payload.href); if (this.fullPageReloadRoutes.indexOf(urlWithoutBase) > -1) { @@ -40,4 +66,4 @@ export class GlobalEventSrv { } } -coreModule.service('globalEventSrv', GlobalEventSrv); +coreModule.service('bridgeSrv', BridgeSrv); diff --git a/public/app/core/specs/global_event_srv.jest.ts b/public/app/core/specs/bridge_srv.jest.ts similarity index 73% rename from public/app/core/specs/global_event_srv.jest.ts rename to public/app/core/specs/bridge_srv.jest.ts index ba318b81cc7..08fc296986d 100644 --- a/public/app/core/specs/global_event_srv.jest.ts +++ b/public/app/core/specs/bridge_srv.jest.ts @@ -1,5 +1,4 @@ -import { GlobalEventSrv } from 'app/core/services/global_event_srv'; -import { beforeEach } from 'test/lib/common'; +import { GlobalEventSrv } from 'app/core/services/bridge_srv'; jest.mock('app/core/config', () => { return { @@ -7,7 +6,7 @@ jest.mock('app/core/config', () => { }; }); -describe('GlobalEventSrv', () => { +describe('BridgeSrv', () => { let searchSrv; beforeEach(() => { diff --git a/public/app/features/alerting/alert_list_ctrl.ts b/public/app/features/alerting/alert_list_ctrl.ts deleted file mode 100644 index 47cd8b2cad7..00000000000 --- a/public/app/features/alerting/alert_list_ctrl.ts +++ /dev/null @@ -1,74 +0,0 @@ -/// - -import _ from 'lodash'; -import moment from 'moment'; - -import { coreModule, appEvents } from 'app/core/core'; -import alertDef from './alert_def'; - -export class AlertListCtrl { - alerts: any; - stateFilters = [ - { text: 'All', value: null }, - { text: 'OK', value: 'ok' }, - { text: 'Not OK', value: 'not_ok' }, - { text: 'Alerting', value: 'alerting' }, - { text: 'No Data', value: 'no_data' }, - { text: 'Paused', value: 'paused' }, - ]; - filters = { - state: 'ALL', - }; - navModel: any; - - /** @ngInject */ - constructor(private backendSrv, private $location, navModelSrv) { - this.navModel = navModelSrv.getNav('alerting', 'alert-list', 0); - - var params = $location.search(); - this.filters.state = params.state || null; - this.loadAlerts(); - } - - filtersChanged() { - this.$location.search(this.filters); - } - - loadAlerts() { - this.backendSrv.get('/api/alerts', this.filters).then(result => { - this.alerts = _.map(result, alert => { - alert.stateModel = alertDef.getStateDisplayModel(alert.state); - alert.newStateDateAgo = moment(alert.newStateDate) - .fromNow() - .replace(' ago', ''); - if (alert.evalData && alert.evalData.no_data) { - alert.no_data = true; - } - return alert; - }); - }); - } - - pauseAlertRule(alertId: any) { - var alert = _.find(this.alerts, { id: alertId }); - - var payload = { - paused: alert.state !== 'paused', - }; - - this.backendSrv.post(`/api/alerts/${alert.id}/pause`, payload).then(result => { - alert.state = result.state; - alert.stateModel = alertDef.getStateDisplayModel(result.state); - }); - } - - openHowTo() { - appEvents.emit('show-modal', { - src: 'public/app/features/alerting/partials/alert_howto.html', - modalClass: 'confirm-modal', - model: {}, - }); - } -} - -coreModule.controller('AlertListCtrl', AlertListCtrl); diff --git a/public/app/features/alerting/all.ts b/public/app/features/alerting/all.ts index f44bc975057..91d3a4109e7 100644 --- a/public/app/features/alerting/all.ts +++ b/public/app/features/alerting/all.ts @@ -1,3 +1,2 @@ -import './alert_list_ctrl'; import './notifications_list_ctrl'; import './notification_edit_ctrl'; diff --git a/public/app/features/alerting/partials/alert_list.html b/public/app/features/alerting/partials/alert_list.html deleted file mode 100644 index e7eb2533463..00000000000 --- a/public/app/features/alerting/partials/alert_list.html +++ /dev/null @@ -1,61 +0,0 @@ - - -
- -
-
- -
- -
-
- -
-
- - - - How to add an alert - -
- -
- -
    -
  1. -
    -
    - -
    -
    -
    - -
    - - - {{alert.stateModel.text}} (due to no data) - for {{alert.newStateDateAgo}} -
    -
    - Error: "{{alert.executionError}}" -
    -
    -
    -
    -
  2. -
-
-
diff --git a/public/app/routes/ReactContainer.tsx b/public/app/routes/ReactContainer.tsx index 7db429f7dc2..b0f4e6b6ee5 100644 --- a/public/app/routes/ReactContainer.tsx +++ b/public/app/routes/ReactContainer.tsx @@ -13,7 +13,7 @@ function WrapInProvider(store, Component, props) { } /** @ngInject */ -export function reactContainer($route) { +export function reactContainer($route, $location) { return { restrict: 'E', template: '', diff --git a/public/app/routes/routes.ts b/public/app/routes/routes.ts index a6d1736c67c..ec65e4ec25a 100644 --- a/public/app/routes/routes.ts +++ b/public/app/routes/routes.ts @@ -226,8 +226,9 @@ export function setupAngularRoutes($routeProvider, $locationProvider) { controller: 'AlertListCtrl', controllerAs: 'ctrl', }) - .when('/alerting/list2', { + .when('/alerting/list', { template: '', + reloadOnSearch: false, resolve: { component: () => AlertRuleList, }, diff --git a/public/app/stores/AlertListStore.ts b/public/app/stores/AlertListStore.ts index a72341b47be..6abb4484bc6 100644 --- a/public/app/stores/AlertListStore.ts +++ b/public/app/stores/AlertListStore.ts @@ -61,14 +61,12 @@ export const AlertListStore = types loadRules: flow(function* load() { let backendSrv = getEnv(self).backendSrv; - let filters = { state: self.stateFilter }; - - let rules = yield backendSrv.get('/api/alerts', filters); + let apiRules = yield backendSrv.get('/api/alerts', filters); self.rules.clear(); - for (let rule of rules) { + for (let rule of apiRules) { setStateFields(rule, rule.state); if (rule.executionError) { diff --git a/public/app/stores/RootStore.ts b/public/app/stores/RootStore.ts index 729faf7b533..1b0640d4c71 100644 --- a/public/app/stores/RootStore.ts +++ b/public/app/stores/RootStore.ts @@ -3,6 +3,7 @@ import { SearchStore } from './SearchStore'; import { ServerStatsStore } from './ServerStatsStore'; import { NavStore } from './NavStore'; import { AlertListStore } from './AlertListStore'; +import { ViewStore } from './ViewStore'; export const RootStore = types.model({ search: types.optional(SearchStore, { @@ -15,6 +16,10 @@ export const RootStore = types.model({ alertList: types.optional(AlertListStore, { rules: [], }), + view: types.optional(ViewStore, { + path: '', + query: {}, + }), }); type IRootStoreType = typeof RootStore.Type; diff --git a/public/app/stores/ViewStore.ts b/public/app/stores/ViewStore.ts new file mode 100644 index 00000000000..8d4df403e72 --- /dev/null +++ b/public/app/stores/ViewStore.ts @@ -0,0 +1,35 @@ +import { types } from 'mobx-state-tree'; +import _ from 'lodash'; +import $ from 'jquery'; + +export const ViewStore = types + .model({ + path: types.string, + query: types.map(types.string), + }) + .views(self => ({ + get currentUrl() { + let path = self.path; + if (self.query.size) { + path += '?' + $.param(self.query.toJS()); + } + return path; + }, + })) + .actions(self => ({ + updatePathAndQuery(path: string, query: any) { + self.path = path; + self.query.clear(); + + for (let key of _.keys(query)) { + self.query.set(key, query[key]); + } + }, + + updateQuery(query: any) { + self.query.clear(); + for (let key of _.keys(query)) { + self.query.set(key, query[key]); + } + }, + })); diff --git a/public/app/stores/store.ts b/public/app/stores/store.ts index 3e40206a122..3405dc38157 100644 --- a/public/app/stores/store.ts +++ b/public/app/stores/store.ts @@ -11,4 +11,6 @@ export function createStore(backendSrv) { navTree: config.bootData.navTree, } ); + + return store; }