Просмотр исходного кода

feat(templating): progress on variable system refactoring, #6048

Torkel Ödegaard 9 лет назад
Родитель
Сommit
5ded88fa4d

+ 1 - 0
public/app/core/services/context_srv.ts

@@ -9,6 +9,7 @@ export class User {
   isGrafanaAdmin: any;
   isSignedIn: any;
   orgRole: any;
+  timezone: string;
 
   constructor() {
     if (config.bootData.user) {

+ 18 - 10
public/app/features/dashboard/dashboard_srv.ts

@@ -6,6 +6,8 @@ import moment from 'moment';
 import _ from 'lodash';
 import $ from 'jquery';
 
+import {Emitter} from 'app/core/core';
+import {contextSrv} from 'app/core/services/context_srv';
 import coreModule from 'app/core/core_module';
 
 export class DashboardModel {
@@ -31,14 +33,14 @@ export class DashboardModel {
   links: any;
   gnetId: any;
   meta: any;
-  contextSrv: any;
+  events: any;
 
-  constructor(data, meta, contextSrv) {
+  constructor(data, meta) {
     if (!data) {
       data = {};
     }
 
-    this.contextSrv = contextSrv;
+    this.events = new Emitter();
     this.id = data.id || null;
     this.title = data.title || 'No Title';
     this.autoUpdate = data.autoUpdate;
@@ -85,8 +87,18 @@ export class DashboardModel {
 
   // cleans meta data and other non peristent state
   getSaveModelClone() {
+    // temp remove stuff
+    var events = this.events;
+    var meta = this.meta;
+    delete this.events;
+    delete this.meta;
+
+    events.emit('prepare-save-model');
     var copy = $.extend(true, {}, this);
-    delete copy.meta;
+
+    // restore properties
+    this.events = events;
+    this.meta = meta;
     return copy;
   }
 
@@ -233,7 +245,7 @@ export class DashboardModel {
   }
 
   getTimezone() {
-    return this.timezone ? this.timezone : this.contextSrv.user.timezone;
+    return this.timezone ? this.timezone : contextSrv.user.timezone;
   }
 
   private updateSchema(old) {
@@ -561,12 +573,8 @@ export class DashboardModel {
 export class DashboardSrv {
   currentDashboard: any;
 
-  /** @ngInject */
-  constructor(private contextSrv) {
-  }
-
   create(dashboard, meta) {
-    return new DashboardModel(dashboard, meta, this.contextSrv);
+    return new DashboardModel(dashboard, meta);
   }
 
   setCurrent(dashboard) {

+ 1 - 1
public/app/features/dashboard/specs/dashboard_srv_specs.ts

@@ -6,7 +6,7 @@ describe('dashboardSrv', function() {
   var _dashboardSrv;
 
   beforeEach(() => {
-    _dashboardSrv = new DashboardSrv({});
+    _dashboardSrv = new DashboardSrv();
   });
 
   describe('when creating new dashboard with defaults only', function() {

+ 1 - 1
public/app/features/dashboard/viewStateSrv.js

@@ -185,7 +185,7 @@ function (angular, _, $) {
     DashboardViewState.prototype.enterFullscreen = function(panelScope) {
       var ctrl = panelScope.ctrl;
 
-      ctrl.editMode = this.state.edit && this.$scope.dashboardMeta.canEdit;
+      ctrl.editMode = this.state.edit && this.dashboard.meta.canEdit;
       ctrl.fullscreen = true;
 
       this.oldTimeRange = ctrl.range;

+ 0 - 1
public/app/features/templating/all.ts

@@ -1,5 +1,4 @@
 import './templateSrv';
-import './templateValuesSrv';
 import './editorCtrl';
 
 import {VariableSrv} from './variable_srv';

+ 1 - 3
public/app/features/templating/interval_variable.ts

@@ -42,9 +42,7 @@ export class IntervalVariable implements Variable {
       return {text: text.trim(), value: text.trim()};
     });
 
-    if (this.auto) {
-      this.updateAutoValue();
-    }
+    this.updateAutoValue();
   }
 
   dependsOn(variable) {

+ 0 - 88
public/app/features/templating/specs/variabe_srv_init_specs.ts

@@ -1,88 +0,0 @@
-import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
-
-import moment from 'moment';
-import helpers from 'test/specs/helpers';
-import '../all';
-
-describe('VariableSrv Init', function() {
-  var ctx = new helpers.ControllerTestContext();
-
-  beforeEach(angularMocks.module('grafana.core'));
-  beforeEach(angularMocks.module('grafana.controllers'));
-  beforeEach(angularMocks.module('grafana.services'));
-
-  beforeEach(ctx.providePhase(['datasourceSrv', 'timeSrv', 'templateSrv', '$location']));
-  beforeEach(angularMocks.inject(($rootScope, $q, $location, $injector) => {
-    ctx.$q = $q;
-    ctx.$rootScope = $rootScope;
-    ctx.$location = $location;
-    ctx.variableSrv = $injector.get('variableSrv');
-    ctx.variableSrv.init({templating: {list: []}});
-    ctx.$rootScope.$digest();
-  }));
-
-  function describeInitSceneario(desc, fn) {
-    describe(desc, function() {
-      var scenario: any = {
-        urlParams: {},
-        setup: setupFn => {
-          scenario.setupFn = setupFn;
-        }
-      };
-
-      beforeEach(function() {
-        scenario.setupFn();
-        var ds: any = {};
-        ds.metricFindQuery = sinon.stub().returns(ctx.$q.when(scenario.queryResult));
-        ctx.datasourceSrv.get = sinon.stub().returns(ctx.$q.when(ds));
-        ctx.datasourceSrv.getMetricSources = sinon.stub().returns(scenario.metricSources);
-
-        ctx.$location.search = sinon.stub().returns(scenario.urlParams);
-
-        ctx.dashboard = {templating: {list: scenario.variables}};
-        ctx.variableSrv.init(ctx.dashboard);
-        ctx.$rootScope.$digest();
-
-        scenario.variables = ctx.variableSrv.variables;
-      });
-
-      fn(scenario);
-    });
-  }
-
-  describeInitSceneario('when setting query variable via url', scenario => {
-    scenario.setup(() => {
-      scenario.variables = [{
-        name: 'apps',
-        type: 'query',
-        current: {text: "test", value: "test"},
-        options: [{text: "test", value: "test"}]
-      }];
-      scenario.urlParams["var-apps"] = "new";
-    });
-
-    it('should update current value', () => {
-      expect(scenario.variables[0].current.value).to.be("new");
-      expect(scenario.variables[0].current.text).to.be("new");
-    });
-  });
-
-  describeInitSceneario('when setting custom variable via url', scenario => {
-    scenario.setup(() => {
-      scenario.variables = [{
-        name: 'apps',
-        type: 'custom',
-        current: {text: "test", value: "test"},
-        options: [{text: "test", value: "test"}]
-      }];
-      scenario.urlParams["var-apps"] = "new";
-    });
-
-    it('should update current value', () => {
-      expect(scenario.variables[0].current.value).to.be("new");
-      expect(scenario.variables[0].current.text).to.be("new");
-    });
-  });
-
-});
-

+ 4 - 4
public/app/features/templating/specs/variable_srv_init_specs.ts

@@ -1,8 +1,10 @@
 import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
 
+import '../all';
+
 import _ from 'lodash';
 import helpers from 'test/specs/helpers';
-import '../all';
+import {Emitter} from 'app/core/core';
 
 describe('VariableSrv init', function() {
   var ctx = new helpers.ControllerTestContext();
@@ -17,7 +19,6 @@ describe('VariableSrv init', function() {
     ctx.$rootScope = $rootScope;
     ctx.$location = $location;
     ctx.variableSrv = $injector.get('variableSrv');
-    ctx.variableSrv.init({templating: {list: []}});
     ctx.$rootScope.$digest();
   }));
 
@@ -39,8 +40,8 @@ describe('VariableSrv init', function() {
         ctx.datasourceSrv.getMetricSources = sinon.stub().returns(scenario.metricSources);
 
         ctx.$location.search = sinon.stub().returns(scenario.urlParams);
+        ctx.dashboard = {templating: {list: scenario.variables}, events: new Emitter()};
 
-        ctx.dashboard = {templating: {list: scenario.variables}};
         ctx.variableSrv.init(ctx.dashboard);
         ctx.$rootScope.$digest();
 
@@ -137,6 +138,5 @@ describe('VariableSrv init', function() {
     });
   });
 
-
 });
 

+ 7 - 2
public/app/features/templating/specs/variable_srv_specs.ts

@@ -1,8 +1,10 @@
 import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
 
+import '../all';
+
 import moment from 'moment';
 import helpers from 'test/specs/helpers';
-import '../all';
+import {Emitter} from 'app/core/core';
 
 describe('VariableSrv', function() {
   var ctx = new helpers.ControllerTestContext();
@@ -17,7 +19,10 @@ describe('VariableSrv', function() {
     ctx.$rootScope = $rootScope;
     ctx.$location = $location;
     ctx.variableSrv = $injector.get('variableSrv');
-    ctx.variableSrv.init({templating: {list: []}});
+    ctx.variableSrv.init({
+      templating: {list: []},
+      events: new Emitter(),
+    });
     ctx.$rootScope.$digest();
   }));
 

+ 89 - 70
public/app/features/templating/variable_srv.ts

@@ -6,7 +6,6 @@ import $ from 'jquery';
 import kbn from 'app/core/utils/kbn';
 import coreModule from 'app/core/core_module';
 import appEvents from 'app/core/app_events';
-import {IntervalVariable} from './interval_variable';
 import {Variable} from './variable';
 
 export var variableConstructorMap: any = {};
@@ -14,95 +13,115 @@ export var variableConstructorMap: any = {};
 export class VariableSrv {
   dashboard: any;
   variables: any;
-
   variableLock: any;
 
   /** @ngInject */
-  constructor(
-    private $rootScope,
-    private $q,
-    private $location,
-    private $injector,
-    private templateSrv) {
-    }
+  constructor(private $rootScope, private $q, private $location, private $injector, private templateSrv) {
+    // update time variant variables
+    // $rootScope.onAppEvent('refresh', this.onDashboardRefresh.bind(this), $rootScope);
+  }
 
-    init(dashboard) {
-      this.variableLock = {};
-      this.dashboard = dashboard;
-      this.variables = dashboard.templating.list.map(this.createVariableFromModel.bind(this));
-      this.templateSrv.init(this.variables);
+  init(dashboard) {
+    this.variableLock = {};
+    this.dashboard = dashboard;
 
-      var queryParams = this.$location.search();
+    // create working class models representing variables
+    this.variables = dashboard.templating.list.map(this.createVariableFromModel.bind(this));
+    this.templateSrv.init(this.variables);
 
-      for (let variable of this.variables) {
-        this.variableLock[variable.name] = this.$q.defer();
-      }
+    // register event to sync back to persisted model
+    this.dashboard.events.on('prepare-save-model', this.syncToDashboardModel.bind(this));
 
-      return this.$q.all(this.variables.map(variable => {
-        return this.processVariable(variable, queryParams);
-      }));
+    // init variables
+    for (let variable of this.variables) {
+      this.variableLock[variable.name] = this.$q.defer();
     }
 
-    processVariable(variable, queryParams) {
-      var dependencies = [];
-      var lock = this.variableLock[variable.name];
-
-      for (let otherVariable of this.variables) {
-        if (variable.dependsOn(otherVariable)) {
-          dependencies.push(this.variableLock[otherVariable.name].promise);
-        }
+    var queryParams = this.$location.search();
+    return this.$q.all(this.variables.map(variable => {
+      return this.processVariable(variable, queryParams);
+    }));
+  }
+
+  onDashboardRefresh() {
+    // var promises = this.variables
+    // .filter(variable => variable.refresh === 2)
+    // .map(variable => {
+    //   var previousOptions = variable.options.slice();
+    //
+    //   return self.updateOptions(variable).then(function () {
+    //     return self.variableUpdated(variable).then(function () {
+    //       // check if current options changed due to refresh
+    //       if (angular.toJson(previousOptions) !== angular.toJson(variable.options)) {
+    //         $rootScope.appEvent('template-variable-value-updated');
+    //       }
+    //     });
+    //   });
+    // });
+    //
+    // return this.$q.all(promises);
+  }
+
+  processVariable(variable, queryParams) {
+    var dependencies = [];
+    var lock = this.variableLock[variable.name];
+
+    for (let otherVariable of this.variables) {
+      if (variable.dependsOn(otherVariable)) {
+        dependencies.push(this.variableLock[otherVariable.name].promise);
       }
-
-      return this.$q.all(dependencies).then(() => {
-        var urlValue = queryParams['var-' + variable.name];
-        if (urlValue !== void 0) {
-          return variable.setValueFromUrl(urlValue).then(lock.resolve);
-        }
-
-        if (variable.refresh === 1 || variable.refresh === 2) {
-          return variable.updateOptions().then(lock.resolve);
-        }
-
-        lock.resolve();
-      }).finally(() => {
-        delete this.variableLock[variable.name];
-      });
     }
 
-    createVariableFromModel(model) {
-      var ctor = variableConstructorMap[model.type];
-      if (!ctor) {
-        throw "Unable to find variable constructor for " + model.type;
+    return this.$q.all(dependencies).then(() => {
+      var urlValue = queryParams['var-' + variable.name];
+      if (urlValue !== void 0) {
+        return variable.setValueFromUrl(urlValue).then(lock.resolve);
       }
 
-      var variable = this.$injector.instantiate(ctor, {model: model});
-      return variable;
-    }
+      if (variable.refresh === 1 || variable.refresh === 2) {
+        return variable.updateOptions().then(lock.resolve);
+      }
 
-    addVariable(model) {
-      var variable = this.createVariableFromModel(model);
-      this.variables.push(this.createVariableFromModel(variable));
-      return variable;
-    }
+      lock.resolve();
+    }).finally(() => {
+      delete this.variableLock[variable.name];
+    });
+  }
 
-    syncToDashboardModel() {
-      this.dashboard.templating.list = this.variables.map(variable => {
-        return variable.model;
-      });
+  createVariableFromModel(model) {
+    var ctor = variableConstructorMap[model.type];
+    if (!ctor) {
+      throw "Unable to find variable constructor for " + model.type;
     }
 
-    updateOptions(variable) {
-      return variable.updateOptions();
+    var variable = this.$injector.instantiate(ctor, {model: model});
+    return variable;
+  }
+
+  addVariable(model) {
+    var variable = this.createVariableFromModel(model);
+    this.variables.push(this.createVariableFromModel(variable));
+    return variable;
+  }
+
+  syncToDashboardModel() {
+    this.dashboard.templating.list = this.variables.map(variable => {
+      return variable.model;
+    });
+  }
+
+  updateOptions(variable) {
+    return variable.updateOptions();
+  }
+
+  variableUpdated(variable) {
+    // if there is a variable lock ignore cascading update because we are in a boot up scenario
+    if (this.variableLock[variable.name]) {
+      return this.$q.when();
     }
 
-    variableUpdated(variable) {
-      // if there is a variable lock ignore cascading update because we are in a boot up scenario
-      if (this.variableLock[variable.name]) {
-        return this.$q.when();
-      }
-
-      // cascade updates to variables that use this variable
-      var promises = _.map(this.variables, otherVariable => {
+    // cascade updates to variables that use this variable
+    var promises = _.map(this.variables, otherVariable => {
         if (otherVariable === variable) {
           return;
         }

+ 16 - 0
public/test/core/utils/emitter_specs.ts

@@ -24,6 +24,22 @@ describe("Emitter", () => {
       expect(sub2Called).to.be(true);
     });
 
+    it('when subscribing twice', () => {
+      var events = new Emitter();
+      var sub1Called = 0;
+
+      function handler() {
+        sub1Called += 1;
+      }
+
+      events.on('test', handler);
+      events.on('test', handler);
+
+      events.emit('test', null);
+
+      expect(sub1Called).to.be(2);
+    });
+
     it('should handle errors', () => {
       var events = new Emitter();
       var sub1Called = 0;

+ 1 - 0
public/test/specs/unsavedChangesSrv-specs.js

@@ -14,6 +14,7 @@ define([
     var dash;
     var scope;
 
+    beforeEach(module('grafana.core'));
     beforeEach(module('grafana.services'));
     beforeEach(module(function($provide) {
       $provide.value('contextSrv', _contextSrvStub);