Sfoglia il codice sorgente

tech(templating): refactoring, updated dashboardSrv to typescript

Torkel Ödegaard 9 anni fa
parent
commit
524f505664

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

@@ -7,7 +7,7 @@ define([
   './rowCtrl',
   './rowCtrl',
   './shareModalCtrl',
   './shareModalCtrl',
   './shareSnapshotCtrl',
   './shareSnapshotCtrl',
-  './dashboardSrv',
+  './dashboard_srv',
   './keybindings',
   './keybindings',
   './viewStateSrv',
   './viewStateSrv',
   './timeSrv',
   './timeSrv',

+ 0 - 552
public/app/features/dashboard/dashboardSrv.js

@@ -1,552 +0,0 @@
-define([
-  'angular',
-  'jquery',
-  'lodash',
-  'moment',
-],
-function (angular, $, _, moment) {
-  'use strict';
-
-  var module = angular.module('grafana.services');
-
-  module.factory('dashboardSrv', function(contextSrv)  {
-
-    function DashboardModel (data, meta) {
-      if (!data) {
-        data = {};
-      }
-
-      this.id = data.id || null;
-      this.title = data.title || 'No Title';
-      this.autoUpdate = data.autoUpdate;
-      this.description = data.description;
-      this.tags = data.tags || [];
-      this.style = data.style || "dark";
-      this.timezone = data.timezone || '';
-      this.editable = data.editable !== false;
-      this.hideControls = data.hideControls || false;
-      this.sharedCrosshair = data.sharedCrosshair || false;
-      this.rows = data.rows || [];
-      this.time = data.time || { from: 'now-6h', to: 'now' };
-      this.timepicker = data.timepicker || {};
-      this.templating = this._ensureListExist(data.templating);
-      this.annotations = this._ensureListExist(data.annotations);
-      this.refresh = data.refresh;
-      this.snapshot = data.snapshot;
-      this.schemaVersion = data.schemaVersion || 0;
-      this.version = data.version || 0;
-      this.links = data.links || [];
-      this.gnetId = data.gnetId || null;
-      this._updateSchema(data);
-      this._initMeta(meta);
-    }
-
-    var p = DashboardModel.prototype;
-
-    p._initMeta = function(meta) {
-      meta = meta || {};
-
-      meta.canShare = meta.canShare !== false;
-      meta.canSave = meta.canSave !== false;
-      meta.canStar = meta.canStar !== false;
-      meta.canEdit = meta.canEdit !== false;
-
-      if (!this.editable) {
-        meta.canEdit = false;
-        meta.canDelete = false;
-        meta.canSave = false;
-        this.hideControls = true;
-      }
-
-      this.meta = meta;
-    };
-
-    // cleans meta data and other non peristent state
-    p.getSaveModelClone = function() {
-      var copy = $.extend(true, {}, this);
-      delete copy.meta;
-      return copy;
-    };
-
-    p._ensureListExist = function (data) {
-      if (!data) { data = {}; }
-      if (!data.list) { data.list = []; }
-      return data;
-    };
-
-    p.getNextPanelId = function() {
-      var i, j, row, panel, max = 0;
-      for (i = 0; i < this.rows.length; i++) {
-        row = this.rows[i];
-        for (j = 0; j < row.panels.length; j++) {
-          panel = row.panels[j];
-          if (panel.id > max) { max = panel.id; }
-        }
-      }
-      return max + 1;
-    };
-
-    p.forEachPanel = function(callback) {
-      var i, j, row;
-      for (i = 0; i < this.rows.length; i++) {
-        row = this.rows[i];
-        for (j = 0; j < row.panels.length; j++) {
-          callback(row.panels[j], j, row, i);
-        }
-      }
-    };
-
-    p.getPanelById = function(id) {
-      for (var i = 0; i < this.rows.length; i++) {
-        var row = this.rows[i];
-        for (var j = 0; j < row.panels.length; j++) {
-          var panel = row.panels[j];
-          if (panel.id === id) {
-            return panel;
-          }
-        }
-      }
-      return null;
-    };
-
-    p.rowSpan = function(row) {
-      return _.reduce(row.panels, function(p,v) {
-        return p + v.span;
-      },0);
-    };
-
-    p.addPanel = function(panel, row) {
-      var rowSpan = this.rowSpan(row);
-      var panelCount = row.panels.length;
-      var space = (12 - rowSpan) - panel.span;
-      panel.id = this.getNextPanelId();
-
-      // try to make room of there is no space left
-      if (space <= 0) {
-        if (panelCount === 1) {
-          row.panels[0].span = 6;
-          panel.span = 6;
-        }
-        else if (panelCount === 2) {
-          row.panels[0].span = 4;
-          row.panels[1].span = 4;
-          panel.span = 4;
-        }
-      }
-
-      row.panels.push(panel);
-    };
-
-    p.isSubmenuFeaturesEnabled = function() {
-      var visableTemplates = _.filter(this.templating.list, function(template) {
-        return template.hideVariable === undefined || template.hideVariable === false;
-      });
-
-      return visableTemplates.length > 0 || this.annotations.list.length > 0 || this.links.length > 0;
-    };
-
-    p.getPanelInfoById = function(panelId) {
-      var result = {};
-      _.each(this.rows, function(row) {
-        _.each(row.panels, function(panel, index) {
-          if (panel.id === panelId) {
-            result.panel = panel;
-            result.row = row;
-            result.index = index;
-          }
-        });
-      });
-
-      if (!result.panel) {
-        return null;
-      }
-
-      return result;
-    };
-
-    p.duplicatePanel = function(panel, row) {
-      var rowIndex = _.indexOf(this.rows, row);
-      var newPanel = angular.copy(panel);
-      newPanel.id = this.getNextPanelId();
-
-      delete newPanel.repeat;
-      delete newPanel.repeatIteration;
-      delete newPanel.repeatPanelId;
-      delete newPanel.scopedVars;
-
-      var currentRow = this.rows[rowIndex];
-      currentRow.panels.push(newPanel);
-      return newPanel;
-    };
-
-    p.formatDate = function(date, format) {
-      date = moment.isMoment(date) ? date : moment(date);
-      format = format || 'YYYY-MM-DD HH:mm:ss';
-      this.timezone = this.getTimezone();
-
-      return this.timezone === 'browser' ?
-        moment(date).format(format) :
-        moment.utc(date).format(format);
-    };
-
-    p.getRelativeTime = function(date) {
-      date = moment.isMoment(date) ? date : moment(date);
-
-      return this.timezone === 'browser' ?
-        moment(date).fromNow() :
-        moment.utc(date).fromNow();
-    };
-
-    p.getNextQueryLetter = function(panel) {
-      var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
-
-      return _.find(letters, function(refId) {
-        return _.every(panel.targets, function(other) {
-          return other.refId !== refId;
-        });
-      });
-    };
-
-    p.isTimezoneUtc = function() {
-      return this.getTimezone() === 'utc';
-    };
-
-    p.getTimezone = function() {
-      return this.timezone ? this.timezone : contextSrv.user.timezone;
-    };
-
-    p._updateSchema = function(old) {
-      var i, j, k;
-      var oldVersion = this.schemaVersion;
-      var panelUpgrades = [];
-      this.schemaVersion = 13;
-
-      if (oldVersion === this.schemaVersion) {
-        return;
-      }
-
-      // version 2 schema changes
-      if (oldVersion < 2) {
-
-        if (old.services) {
-          if (old.services.filter) {
-            this.time = old.services.filter.time;
-            this.templating.list = old.services.filter.list || [];
-          }
-          delete this.services;
-        }
-
-        panelUpgrades.push(function(panel) {
-          // rename panel type
-          if (panel.type === 'graphite') {
-            panel.type = 'graph';
-          }
-
-          if (panel.type !== 'graph') {
-            return;
-          }
-
-          if (_.isBoolean(panel.legend)) { panel.legend = { show: panel.legend }; }
-
-          if (panel.grid) {
-            if (panel.grid.min) {
-              panel.grid.leftMin = panel.grid.min;
-              delete panel.grid.min;
-            }
-
-            if (panel.grid.max) {
-              panel.grid.leftMax = panel.grid.max;
-              delete panel.grid.max;
-            }
-          }
-
-          if (panel.y_format) {
-            panel.y_formats[0] = panel.y_format;
-            delete panel.y_format;
-          }
-
-          if (panel.y2_format) {
-            panel.y_formats[1] = panel.y2_format;
-            delete panel.y2_format;
-          }
-        });
-      }
-
-      // schema version 3 changes
-      if (oldVersion < 3) {
-        // ensure panel ids
-        var maxId = this.getNextPanelId();
-        panelUpgrades.push(function(panel) {
-          if (!panel.id) {
-            panel.id = maxId;
-            maxId += 1;
-          }
-        });
-      }
-
-      // schema version 4 changes
-      if (oldVersion < 4) {
-        // move aliasYAxis changes
-        panelUpgrades.push(function(panel) {
-          if (panel.type !== 'graph') { return; }
-          _.each(panel.aliasYAxis, function(value, key) {
-            panel.seriesOverrides = [{ alias: key, yaxis: value }];
-          });
-          delete panel.aliasYAxis;
-        });
-      }
-
-      if (oldVersion < 6) {
-        // move pulldowns to new schema
-        var annotations = _.find(old.pulldowns, { type: 'annotations' });
-
-        if (annotations) {
-          this.annotations = {
-            list: annotations.annotations || [],
-          };
-        }
-
-        // update template variables
-        for (i = 0 ; i < this.templating.list.length; i++) {
-          var variable = this.templating.list[i];
-          if (variable.datasource === void 0) { variable.datasource = null; }
-          if (variable.type === 'filter') { variable.type = 'query'; }
-          if (variable.type === void 0) { variable.type = 'query'; }
-          if (variable.allFormat === void 0) { variable.allFormat = 'glob'; }
-        }
-      }
-
-      if (oldVersion < 7) {
-        if (old.nav && old.nav.length) {
-          this.timepicker = old.nav[0];
-          delete this.nav;
-        }
-
-        // ensure query refIds
-        panelUpgrades.push(function(panel) {
-          _.each(panel.targets, function(target) {
-            if (!target.refId) {
-              target.refId = this.getNextQueryLetter(panel);
-            }
-          }.bind(this));
-        });
-      }
-
-      if (oldVersion < 8) {
-        panelUpgrades.push(function(panel) {
-          _.each(panel.targets, function(target) {
-            // update old influxdb query schema
-            if (target.fields && target.tags && target.groupBy) {
-              if (target.rawQuery) {
-                delete target.fields;
-                delete target.fill;
-              } else {
-                target.select = _.map(target.fields, function(field) {
-                  var parts = [];
-                  parts.push({type: 'field', params: [field.name]});
-                  parts.push({type: field.func, params: []});
-                  if (field.mathExpr) {
-                    parts.push({type: 'math', params: [field.mathExpr]});
-                  }
-                  if (field.asExpr) {
-                    parts.push({type: 'alias', params: [field.asExpr]});
-                  }
-                  return parts;
-                });
-                delete target.fields;
-                _.each(target.groupBy, function(part) {
-                  if (part.type === 'time' && part.interval)  {
-                    part.params = [part.interval];
-                    delete part.interval;
-                  }
-                  if (part.type === 'tag' && part.key) {
-                    part.params = [part.key];
-                    delete part.key;
-                  }
-                });
-
-                if (target.fill) {
-                  target.groupBy.push({type: 'fill', params: [target.fill]});
-                  delete target.fill;
-                }
-              }
-            }
-          });
-        });
-      }
-
-      // schema version 9 changes
-      if (oldVersion < 9) {
-        // move aliasYAxis changes
-        panelUpgrades.push(function(panel) {
-          if (panel.type !== 'singlestat' && panel.thresholds !== "") { return; }
-
-          if (panel.thresholds) {
-            var k = panel.thresholds.split(",");
-
-            if (k.length >= 3) {
-              k.shift();
-              panel.thresholds = k.join(",");
-            }
-          }
-        });
-      }
-
-      // schema version 10 changes
-      if (oldVersion < 10) {
-        // move aliasYAxis changes
-        panelUpgrades.push(function(panel) {
-          if (panel.type !== 'table') { return; }
-
-          _.each(panel.styles, function(style) {
-            if (style.thresholds && style.thresholds.length >= 3) {
-              var k = style.thresholds;
-              k.shift();
-              style.thresholds = k;
-            }
-          });
-        });
-      }
-
-      if (oldVersion < 12) {
-        // update template variables
-        _.each(this.templating.list, function(templateVariable) {
-          if (templateVariable.refresh) { templateVariable.refresh = 1; }
-          if (!templateVariable.refresh) { templateVariable.refresh = 0; }
-          if (templateVariable.hideVariable) {
-            templateVariable.hide = 2;
-          } else if (templateVariable.hideLabel) {
-            templateVariable.hide = 1;
-          } else {
-            templateVariable.hide = 0;
-          }
-        });
-      }
-
-      if (oldVersion < 12) {
-        // update graph yaxes changes
-        panelUpgrades.push(function(panel) {
-          if (panel.type !== 'graph') { return; }
-          if (!panel.grid) { return; }
-
-          if (!panel.yaxes) {
-            panel.yaxes = [
-              {
-                show: panel['y-axis'],
-                min: panel.grid.leftMin,
-                max: panel.grid.leftMax,
-                logBase: panel.grid.leftLogBase,
-                format: panel.y_formats[0],
-                label: panel.leftYAxisLabel,
-              },
-              {
-                show: panel['y-axis'],
-                min: panel.grid.rightMin,
-                max: panel.grid.rightMax,
-                logBase: panel.grid.rightLogBase,
-                format: panel.y_formats[1],
-                label: panel.rightYAxisLabel,
-              }
-            ];
-
-            panel.xaxis = {
-              show: panel['x-axis'],
-            };
-
-            delete panel.grid.leftMin;
-            delete panel.grid.leftMax;
-            delete panel.grid.leftLogBase;
-            delete panel.grid.rightMin;
-            delete panel.grid.rightMax;
-            delete panel.grid.rightLogBase;
-            delete panel.y_formats;
-            delete panel.leftYAxisLabel;
-            delete panel.rightYAxisLabel;
-            delete panel['y-axis'];
-            delete panel['x-axis'];
-          }
-        });
-      }
-
-      if (oldVersion < 13) {
-        // update graph yaxes changes
-        panelUpgrades.push(function(panel) {
-          if (panel.type !== 'graph') { return; }
-
-          panel.thresholds = [];
-          var t1 = {}, t2 = {};
-
-          if (panel.grid.threshold1 !== null) {
-            t1.value = panel.grid.threshold1;
-            if (panel.grid.thresholdLine) {
-              t1.line = true;
-              t1.lineColor = panel.grid.threshold1Color;
-            } else {
-              t1.fill = true;
-              t1.fillColor = panel.grid.threshold1Color;
-            }
-          }
-
-          if (panel.grid.threshold2 !== null) {
-            t2.value = panel.grid.threshold2;
-            if (panel.grid.thresholdLine) {
-              t2.line = true;
-              t2.lineColor = panel.grid.threshold2Color;
-            } else {
-              t2.fill = true;
-              t2.fillColor = panel.grid.threshold2Color;
-            }
-          }
-
-          if (_.isNumber(t1.value)) {
-            if (_.isNumber(t2.value)) {
-              if (t1.value > t2.value) {
-                t1.op = t2.op = '<';
-                panel.thresholds.push(t2);
-                panel.thresholds.push(t1);
-              } else {
-                t1.op = t2.op = '>';
-                panel.thresholds.push(t2);
-                panel.thresholds.push(t1);
-              }
-            } else {
-              t1.op = '>';
-              panel.thresholds.push(t1);
-            }
-          }
-
-          delete panel.grid.threshold1;
-          delete panel.grid.threshold1Color;
-          delete panel.grid.threshold2;
-          delete panel.grid.threshold2Color;
-          delete panel.grid.thresholdLine;
-        });
-      }
-
-      if (panelUpgrades.length === 0) {
-        return;
-      }
-
-      for (i = 0; i < this.rows.length; i++) {
-        var row = this.rows[i];
-        for (j = 0; j < row.panels.length; j++) {
-          for (k = 0; k < panelUpgrades.length; k++) {
-            panelUpgrades[k].call(this, row.panels[j]);
-          }
-        }
-      }
-    };
-
-    return {
-      create: function(dashboard, meta) {
-        return new DashboardModel(dashboard, meta);
-      },
-      setCurrent: function(dashboard) {
-        this.currentDashboard = dashboard;
-      },
-      getCurrent: function() {
-        return this.currentDashboard;
-      },
-    };
-  });
-});

+ 0 - 2
public/app/features/dashboard/dashboard_ctrl.ts

@@ -87,8 +87,6 @@ export class DashboardCtrl {
       };
       };
 
 
       $scope.templateVariableUpdated = function() {
       $scope.templateVariableUpdated = function() {
-        console.log('dynamic update');
-        dynamicDashboardSrv.update($scope.dashboard);
       };
       };
 
 
       $scope.updateSubmenuVisibility = function() {
       $scope.updateSubmenuVisibility = function() {

+ 582 - 0
public/app/features/dashboard/dashboard_srv.ts

@@ -0,0 +1,582 @@
+///<reference path="../../headers/common.d.ts" />
+
+import config from 'app/core/config';
+import angular from 'angular';
+import moment from 'moment';
+import _ from 'lodash';
+import $ from 'jquery';
+
+import coreModule from 'app/core/core_module';
+
+export class DashboardModel {
+  id: any;
+  title: any;
+  autoUpdate: any;
+  description: any;
+  tags: any;
+  style: any;
+  timezone: any;
+  editable: any;
+  hideControls: any;
+  sharedCrosshair: any;
+  rows: any;
+  time: any;
+  timepicker: any;
+  templating: any;
+  annotations: any;
+  refresh: any;
+  snapshot: any;
+  schemaVersion: number;
+  version: number;
+  links: any;
+  gnetId: any;
+  meta: any;
+  contextSrv: any;
+
+  constructor(data, meta, contextSrv) {
+    if (!data) {
+      data = {};
+    }
+
+    this.contextSrv = contextSrv;
+    this.id = data.id || null;
+    this.title = data.title || 'No Title';
+    this.autoUpdate = data.autoUpdate;
+    this.description = data.description;
+    this.tags = data.tags || [];
+    this.style = data.style || "dark";
+    this.timezone = data.timezone || '';
+    this.editable = data.editable !== false;
+    this.hideControls = data.hideControls || false;
+    this.sharedCrosshair = data.sharedCrosshair || false;
+    this.rows = data.rows || [];
+    this.time = data.time || { from: 'now-6h', to: 'now' };
+    this.timepicker = data.timepicker || {};
+    this.templating = this.ensureListExist(data.templating);
+    this.annotations = this.ensureListExist(data.annotations);
+    this.refresh = data.refresh;
+    this.snapshot = data.snapshot;
+    this.schemaVersion = data.schemaVersion || 0;
+    this.version = data.version || 0;
+    this.links = data.links || [];
+    this.gnetId = data.gnetId || null;
+
+    this.updateSchema(data);
+    this.initMeta(meta);
+  }
+
+  private initMeta(meta) {
+    meta = meta || {};
+
+    meta.canShare = meta.canShare !== false;
+    meta.canSave = meta.canSave !== false;
+    meta.canStar = meta.canStar !== false;
+    meta.canEdit = meta.canEdit !== false;
+
+    if (!this.editable) {
+      meta.canEdit = false;
+      meta.canDelete = false;
+      meta.canSave = false;
+      this.hideControls = true;
+    }
+
+    this.meta = meta;
+  }
+
+  // cleans meta data and other non peristent state
+  getSaveModelClone() {
+    var copy = $.extend(true, {}, this);
+    delete copy.meta;
+    return copy;
+  }
+
+  private ensureListExist(data) {
+    if (!data) { data = {}; }
+    if (!data.list) { data.list = []; }
+    return data;
+  }
+
+  getNextPanelId() {
+    var i, j, row, panel, max = 0;
+    for (i = 0; i < this.rows.length; i++) {
+      row = this.rows[i];
+      for (j = 0; j < row.panels.length; j++) {
+        panel = row.panels[j];
+        if (panel.id > max) { max = panel.id; }
+      }
+    }
+    return max + 1;
+  }
+
+  forEachPanel(callback) {
+    var i, j, row;
+    for (i = 0; i < this.rows.length; i++) {
+      row = this.rows[i];
+      for (j = 0; j < row.panels.length; j++) {
+        callback(row.panels[j], j, row, i);
+      }
+    }
+  }
+
+  getPanelById(id) {
+    for (var i = 0; i < this.rows.length; i++) {
+      var row = this.rows[i];
+      for (var j = 0; j < row.panels.length; j++) {
+        var panel = row.panels[j];
+        if (panel.id === id) {
+          return panel;
+        }
+      }
+    }
+    return null;
+  }
+
+  rowSpan(row) {
+    return _.reduce(row.panels, function(p,v) {
+      return p + v.span;
+    },0);
+  };
+
+  addPanel(panel, row) {
+    var rowSpan = this.rowSpan(row);
+    var panelCount = row.panels.length;
+    var space = (12 - rowSpan) - panel.span;
+    panel.id = this.getNextPanelId();
+
+    // try to make room of there is no space left
+    if (space <= 0) {
+      if (panelCount === 1) {
+        row.panels[0].span = 6;
+        panel.span = 6;
+      } else if (panelCount === 2) {
+        row.panels[0].span = 4;
+        row.panels[1].span = 4;
+        panel.span = 4;
+      }
+    }
+
+    row.panels.push(panel);
+  }
+
+  isSubmenuFeaturesEnabled() {
+    var visableTemplates = _.filter(this.templating.list, function(template) {
+      return template.hideVariable === undefined || template.hideVariable === false;
+    });
+
+    return visableTemplates.length > 0 || this.annotations.list.length > 0 || this.links.length > 0;
+  }
+
+  getPanelInfoById(panelId) {
+    var result: any = {};
+    _.each(this.rows, function(row) {
+      _.each(row.panels, function(panel, index) {
+        if (panel.id === panelId) {
+          result.panel = panel;
+          result.row = row;
+          result.index = index;
+        }
+      });
+    });
+
+    if (!result.panel) {
+      return null;
+    }
+
+    return result;
+  }
+
+  duplicatePanel(panel, row) {
+    var rowIndex = _.indexOf(this.rows, row);
+    var newPanel = angular.copy(panel);
+    newPanel.id = this.getNextPanelId();
+
+    delete newPanel.repeat;
+    delete newPanel.repeatIteration;
+    delete newPanel.repeatPanelId;
+    delete newPanel.scopedVars;
+
+    var currentRow = this.rows[rowIndex];
+    currentRow.panels.push(newPanel);
+    return newPanel;
+  }
+
+  formatDate(date, format) {
+    date = moment.isMoment(date) ? date : moment(date);
+    format = format || 'YYYY-MM-DD HH:mm:ss';
+    this.timezone = this.getTimezone();
+
+    return this.timezone === 'browser' ?
+      moment(date).format(format) :
+      moment.utc(date).format(format);
+  }
+
+  getRelativeTime(date) {
+    date = moment.isMoment(date) ? date : moment(date);
+
+    return this.timezone === 'browser' ?
+      moment(date).fromNow() :
+      moment.utc(date).fromNow();
+  }
+
+  getNextQueryLetter(panel) {
+    var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+
+    return _.find(letters, function(refId) {
+      return _.every(panel.targets, function(other) {
+        return other.refId !== refId;
+      });
+    });
+  }
+
+  isTimezoneUtc() {
+    return this.getTimezone() === 'utc';
+  }
+
+  getTimezone() {
+    return this.timezone ? this.timezone : this.contextSrv.user.timezone;
+  }
+
+  private updateSchema(old) {
+    var i, j, k;
+    var oldVersion = this.schemaVersion;
+    var panelUpgrades = [];
+    this.schemaVersion = 13;
+
+    if (oldVersion === this.schemaVersion) {
+      return;
+    }
+
+    // version 2 schema changes
+    if (oldVersion < 2) {
+
+      if (old.services) {
+        if (old.services.filter) {
+          this.time = old.services.filter.time;
+          this.templating.list = old.services.filter.list || [];
+        }
+      }
+
+      panelUpgrades.push(function(panel) {
+        // rename panel type
+        if (panel.type === 'graphite') {
+          panel.type = 'graph';
+        }
+
+        if (panel.type !== 'graph') {
+          return;
+        }
+
+        if (_.isBoolean(panel.legend)) { panel.legend = { show: panel.legend }; }
+
+        if (panel.grid) {
+          if (panel.grid.min) {
+            panel.grid.leftMin = panel.grid.min;
+            delete panel.grid.min;
+          }
+
+          if (panel.grid.max) {
+            panel.grid.leftMax = panel.grid.max;
+            delete panel.grid.max;
+          }
+        }
+
+        if (panel.y_format) {
+          panel.y_formats[0] = panel.y_format;
+          delete panel.y_format;
+        }
+
+        if (panel.y2_format) {
+          panel.y_formats[1] = panel.y2_format;
+          delete panel.y2_format;
+        }
+      });
+    }
+
+    // schema version 3 changes
+    if (oldVersion < 3) {
+      // ensure panel ids
+      var maxId = this.getNextPanelId();
+      panelUpgrades.push(function(panel) {
+        if (!panel.id) {
+          panel.id = maxId;
+          maxId += 1;
+        }
+      });
+    }
+
+    // schema version 4 changes
+    if (oldVersion < 4) {
+      // move aliasYAxis changes
+      panelUpgrades.push(function(panel) {
+        if (panel.type !== 'graph') { return; }
+        _.each(panel.aliasYAxis, function(value, key) {
+          panel.seriesOverrides = [{ alias: key, yaxis: value }];
+        });
+        delete panel.aliasYAxis;
+      });
+    }
+
+    if (oldVersion < 6) {
+      // move pulldowns to new schema
+      var annotations = _.find(old.pulldowns, { type: 'annotations' });
+
+      if (annotations) {
+        this.annotations = {
+          list: annotations.annotations || [],
+        };
+      }
+
+      // update template variables
+      for (i = 0 ; i < this.templating.list.length; i++) {
+        var variable = this.templating.list[i];
+        if (variable.datasource === void 0) { variable.datasource = null; }
+        if (variable.type === 'filter') { variable.type = 'query'; }
+        if (variable.type === void 0) { variable.type = 'query'; }
+        if (variable.allFormat === void 0) { variable.allFormat = 'glob'; }
+      }
+    }
+
+    if (oldVersion < 7) {
+      if (old.nav && old.nav.length) {
+        this.timepicker = old.nav[0];
+      }
+
+      // ensure query refIds
+      panelUpgrades.push(function(panel) {
+        _.each(panel.targets, function(target) {
+          if (!target.refId) {
+            target.refId = this.getNextQueryLetter(panel);
+            }
+          }.bind(this));
+        });
+      }
+
+      if (oldVersion < 8) {
+        panelUpgrades.push(function(panel) {
+          _.each(panel.targets, function(target) {
+            // update old influxdb query schema
+            if (target.fields && target.tags && target.groupBy) {
+              if (target.rawQuery) {
+                delete target.fields;
+                delete target.fill;
+              } else {
+                target.select = _.map(target.fields, function(field) {
+                  var parts = [];
+                  parts.push({type: 'field', params: [field.name]});
+                  parts.push({type: field.func, params: []});
+                  if (field.mathExpr) {
+                    parts.push({type: 'math', params: [field.mathExpr]});
+                  }
+                  if (field.asExpr) {
+                    parts.push({type: 'alias', params: [field.asExpr]});
+                  }
+                  return parts;
+                });
+                delete target.fields;
+                _.each(target.groupBy, function(part) {
+                  if (part.type === 'time' && part.interval)  {
+                    part.params = [part.interval];
+                    delete part.interval;
+                  }
+                  if (part.type === 'tag' && part.key) {
+                    part.params = [part.key];
+                    delete part.key;
+                  }
+                });
+
+                if (target.fill) {
+                  target.groupBy.push({type: 'fill', params: [target.fill]});
+                  delete target.fill;
+                }
+              }
+            }
+          });
+        });
+      }
+
+      // schema version 9 changes
+      if (oldVersion < 9) {
+        // move aliasYAxis changes
+        panelUpgrades.push(function(panel) {
+          if (panel.type !== 'singlestat' && panel.thresholds !== "") { return; }
+
+          if (panel.thresholds) {
+            var k = panel.thresholds.split(",");
+
+            if (k.length >= 3) {
+              k.shift();
+              panel.thresholds = k.join(",");
+            }
+          }
+        });
+      }
+
+      // schema version 10 changes
+      if (oldVersion < 10) {
+        // move aliasYAxis changes
+        panelUpgrades.push(function(panel) {
+          if (panel.type !== 'table') { return; }
+
+          _.each(panel.styles, function(style) {
+            if (style.thresholds && style.thresholds.length >= 3) {
+              var k = style.thresholds;
+              k.shift();
+              style.thresholds = k;
+            }
+          });
+        });
+      }
+
+      if (oldVersion < 12) {
+        // update template variables
+        _.each(this.templating.list, function(templateVariable) {
+          if (templateVariable.refresh) { templateVariable.refresh = 1; }
+          if (!templateVariable.refresh) { templateVariable.refresh = 0; }
+          if (templateVariable.hideVariable) {
+            templateVariable.hide = 2;
+          } else if (templateVariable.hideLabel) {
+            templateVariable.hide = 1;
+          } else {
+            templateVariable.hide = 0;
+          }
+        });
+      }
+
+      if (oldVersion < 12) {
+        // update graph yaxes changes
+        panelUpgrades.push(function(panel) {
+          if (panel.type !== 'graph') { return; }
+          if (!panel.grid) { return; }
+
+          if (!panel.yaxes) {
+            panel.yaxes = [
+              {
+                show: panel['y-axis'],
+                min: panel.grid.leftMin,
+                max: panel.grid.leftMax,
+                logBase: panel.grid.leftLogBase,
+                format: panel.y_formats[0],
+                label: panel.leftYAxisLabel,
+              },
+              {
+                show: panel['y-axis'],
+                min: panel.grid.rightMin,
+                max: panel.grid.rightMax,
+                logBase: panel.grid.rightLogBase,
+                format: panel.y_formats[1],
+                label: panel.rightYAxisLabel,
+              }
+            ];
+
+            panel.xaxis = {
+              show: panel['x-axis'],
+            };
+
+            delete panel.grid.leftMin;
+            delete panel.grid.leftMax;
+            delete panel.grid.leftLogBase;
+            delete panel.grid.rightMin;
+            delete panel.grid.rightMax;
+            delete panel.grid.rightLogBase;
+            delete panel.y_formats;
+            delete panel.leftYAxisLabel;
+            delete panel.rightYAxisLabel;
+            delete panel['y-axis'];
+            delete panel['x-axis'];
+          }
+        });
+      }
+
+      if (oldVersion < 13) {
+        // update graph yaxes changes
+        panelUpgrades.push(function(panel) {
+          if (panel.type !== 'graph') { return; }
+
+          panel.thresholds = [];
+          var t1: any = {}, t2: any = {};
+
+          if (panel.grid.threshold1 !== null) {
+            t1.value = panel.grid.threshold1;
+            if (panel.grid.thresholdLine) {
+              t1.line = true;
+              t1.lineColor = panel.grid.threshold1Color;
+            } else {
+              t1.fill = true;
+              t1.fillColor = panel.grid.threshold1Color;
+            }
+          }
+
+          if (panel.grid.threshold2 !== null) {
+            t2.value = panel.grid.threshold2;
+            if (panel.grid.thresholdLine) {
+              t2.line = true;
+              t2.lineColor = panel.grid.threshold2Color;
+            } else {
+              t2.fill = true;
+              t2.fillColor = panel.grid.threshold2Color;
+            }
+          }
+
+          if (_.isNumber(t1.value)) {
+            if (_.isNumber(t2.value)) {
+              if (t1.value > t2.value) {
+                t1.op = t2.op = '<';
+                panel.thresholds.push(t2);
+                panel.thresholds.push(t1);
+              } else {
+                t1.op = t2.op = '>';
+                panel.thresholds.push(t2);
+                panel.thresholds.push(t1);
+              }
+            } else {
+              t1.op = '>';
+              panel.thresholds.push(t1);
+            }
+          }
+
+          delete panel.grid.threshold1;
+          delete panel.grid.threshold1Color;
+          delete panel.grid.threshold2;
+          delete panel.grid.threshold2Color;
+          delete panel.grid.thresholdLine;
+        });
+      }
+
+      if (panelUpgrades.length === 0) {
+        return;
+      }
+
+      for (i = 0; i < this.rows.length; i++) {
+        var row = this.rows[i];
+        for (j = 0; j < row.panels.length; j++) {
+          for (k = 0; k < panelUpgrades.length; k++) {
+            panelUpgrades[k].call(this, row.panels[j]);
+          }
+        }
+      }
+    }
+}
+
+
+export class DashboardSrv {
+  currentDashboard: any;
+
+  /** @ngInject */
+  constructor(private contextSrv) {
+  }
+
+  create(dashboard, meta) {
+    return new DashboardModel(dashboard, meta, this.contextSrv);
+  }
+
+  setCurrent(dashboard) {
+    this.currentDashboard = dashboard;
+  }
+
+  getCurrent() {
+    return this.currentDashboard;
+  }
+}
+
+coreModule.service('dashboardSrv', DashboardSrv);
+

+ 379 - 0
public/app/features/dashboard/specs/dashboard_srv_specs.ts

@@ -0,0 +1,379 @@
+import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
+
+import {DashboardSrv} from '../dashboard_srv';
+
+describe('dashboardSrv', function() {
+  var _dashboardSrv;
+
+  beforeEach(() => {
+    _dashboardSrv = new DashboardSrv({});
+  });
+
+  describe('when creating new dashboard with defaults only', function() {
+    var model;
+
+    beforeEach(function() {
+      model = _dashboardSrv.create({}, {});
+    });
+
+    it('should have title', function() {
+      expect(model.title).to.be('No Title');
+    });
+
+    it('should have meta', function() {
+      expect(model.meta.canSave).to.be(true);
+      expect(model.meta.canShare).to.be(true);
+    });
+
+    it('should have default properties', function() {
+      expect(model.rows.length).to.be(0);
+    });
+  });
+
+  describe('when getting next panel id', function() {
+    var model;
+
+    beforeEach(function() {
+      model = _dashboardSrv.create({
+        rows: [{ panels: [{ id: 5 }]}]
+      });
+    });
+
+    it('should return max id + 1', function() {
+      expect(model.getNextPanelId()).to.be(6);
+    });
+  });
+
+  describe('row and panel manipulation', function() {
+    var dashboard;
+
+    beforeEach(function() {
+      dashboard = _dashboardSrv.create({});
+    });
+
+    it('row span should sum spans', function() {
+      var spanLeft = dashboard.rowSpan({ panels: [{ span: 2 }, { span: 3 }] });
+      expect(spanLeft).to.be(5);
+    });
+
+    it('adding default should split span in half', function() {
+      dashboard.rows = [{ panels: [{ span: 12, id: 7 }] }];
+      dashboard.addPanel({span: 4}, dashboard.rows[0]);
+
+      expect(dashboard.rows[0].panels[0].span).to.be(6);
+      expect(dashboard.rows[0].panels[1].span).to.be(6);
+      expect(dashboard.rows[0].panels[1].id).to.be(8);
+    });
+
+    it('duplicate panel should try to add it to same row', function() {
+      var panel = { span: 4, attr: '123', id: 10 };
+      dashboard.rows = [{ panels: [panel] }];
+      dashboard.duplicatePanel(panel, dashboard.rows[0]);
+
+      expect(dashboard.rows[0].panels[0].span).to.be(4);
+      expect(dashboard.rows[0].panels[1].span).to.be(4);
+      expect(dashboard.rows[0].panels[1].attr).to.be('123');
+      expect(dashboard.rows[0].panels[1].id).to.be(11);
+    });
+
+    it('duplicate panel should remove repeat data', function() {
+      var panel = { span: 4, attr: '123', id: 10, repeat: 'asd', scopedVars: { test: 'asd' }};
+      dashboard.rows = [{ panels: [panel] }];
+      dashboard.duplicatePanel(panel, dashboard.rows[0]);
+
+      expect(dashboard.rows[0].panels[1].repeat).to.be(undefined);
+      expect(dashboard.rows[0].panels[1].scopedVars).to.be(undefined);
+    });
+
+  });
+
+  describe('when creating dashboard with editable false', function() {
+    var model;
+
+    beforeEach(function() {
+      model = _dashboardSrv.create({
+        editable: false
+      });
+    });
+
+    it('should set editable false', function() {
+      expect(model.editable).to.be(false);
+    });
+
+  });
+
+  describe('when creating dashboard with old schema', function() {
+    var model;
+    var graph;
+    var singlestat;
+    var table;
+
+    beforeEach(function() {
+      model = _dashboardSrv.create({
+        services: { filter: { time: { from: 'now-1d', to: 'now'}, list: [{}] }},
+        pulldowns: [
+          {type: 'filtering', enable: true},
+          {type: 'annotations', enable: true, annotations: [{name: 'old'}]}
+        ],
+        rows: [
+          {
+            panels: [
+              {
+                type: 'graph', legend: true, aliasYAxis: { test: 2 },
+                y_formats: ['kbyte', 'ms'],
+                grid: {
+                  min: 1,
+                  max: 10,
+                  rightMin: 5,
+                  rightMax: 15,
+                  leftLogBase: 1,
+                  rightLogBase: 2,
+                  threshold1: 200,
+                  threshold2: 400,
+                  threshold1Color: 'yellow',
+                  threshold2Color: 'red',
+                },
+                leftYAxisLabel: 'left label',
+                targets: [{refId: 'A'}, {}],
+              },
+              {
+                type: 'singlestat', legend: true, thresholds: '10,20,30', aliasYAxis: { test: 2 }, grid: { min: 1, max: 10 },
+                targets: [{refId: 'A'}, {}],
+              },
+              {
+                type: 'table', legend: true, styles: [{ thresholds: ["10", "20", "30"]}, { thresholds: ["100", "200", "300"]}],
+                targets: [{refId: 'A'}, {}],
+              }
+            ]
+          }
+        ]
+      });
+
+      graph = model.rows[0].panels[0];
+      singlestat = model.rows[0].panels[1];
+      table = model.rows[0].panels[2];
+    });
+
+    it('should have title', function() {
+      expect(model.title).to.be('No Title');
+    });
+
+    it('should have panel id', function() {
+      expect(graph.id).to.be(1);
+    });
+
+    it('should move time and filtering list', function() {
+      expect(model.time.from).to.be('now-1d');
+      expect(model.templating.list[0].allFormat).to.be('glob');
+    });
+
+    it('graphite panel should change name too graph', function() {
+      expect(graph.type).to.be('graph');
+    });
+
+    it('single stat panel should have two thresholds', function() {
+      expect(singlestat.thresholds).to.be('20,30');
+    });
+
+    it('queries without refId should get it', function() {
+      expect(graph.targets[1].refId).to.be('B');
+    });
+
+    it('update legend setting', function() {
+      expect(graph.legend.show).to.be(true);
+    });
+
+    it('move aliasYAxis to series override', function() {
+      expect(graph.seriesOverrides[0].alias).to.be("test");
+      expect(graph.seriesOverrides[0].yaxis).to.be(2);
+    });
+
+    it('should move pulldowns to new schema', function() {
+      expect(model.annotations.list[0].name).to.be('old');
+    });
+
+    it('table panel should only have two thresholds values', function() {
+      expect(table.styles[0].thresholds[0]).to.be("20");
+      expect(table.styles[0].thresholds[1]).to.be("30");
+      expect(table.styles[1].thresholds[0]).to.be("200");
+      expect(table.styles[1].thresholds[1]).to.be("300");
+    });
+
+    it('graph grid to yaxes options', function() {
+      expect(graph.yaxes[0].min).to.be(1);
+      expect(graph.yaxes[0].max).to.be(10);
+      expect(graph.yaxes[0].format).to.be('kbyte');
+      expect(graph.yaxes[0].label).to.be('left label');
+      expect(graph.yaxes[0].logBase).to.be(1);
+      expect(graph.yaxes[1].min).to.be(5);
+      expect(graph.yaxes[1].max).to.be(15);
+      expect(graph.yaxes[1].format).to.be('ms');
+      expect(graph.yaxes[1].logBase).to.be(2);
+
+      expect(graph.grid.rightMax).to.be(undefined);
+      expect(graph.grid.rightLogBase).to.be(undefined);
+      expect(graph.y_formats).to.be(undefined);
+    });
+
+    it('dashboard schema version should be set to latest', function() {
+      expect(model.schemaVersion).to.be(13);
+    });
+
+    it('graph thresholds should be migrated', function() {
+      expect(graph.thresholds.length).to.be(2);
+      expect(graph.thresholds[0].op).to.be('>');
+      expect(graph.thresholds[0].value).to.be(400);
+      expect(graph.thresholds[0].fillColor).to.be('red');
+      expect(graph.thresholds[1].value).to.be(200);
+      expect(graph.thresholds[1].fillColor).to.be('yellow');
+    });
+  });
+
+  describe('when creating dashboard model with missing list for annoations or templating', function() {
+    var model;
+
+    beforeEach(function() {
+      model = _dashboardSrv.create({
+        annotations: {
+          enable: true,
+        },
+        templating: {
+          enable: true
+        }
+      });
+    });
+
+    it('should add empty list', function() {
+      expect(model.annotations.list.length).to.be(0);
+      expect(model.templating.list.length).to.be(0);
+    });
+  });
+
+  describe('Given editable false dashboard', function() {
+    var model;
+
+    beforeEach(function() {
+      model = _dashboardSrv.create({
+        editable:  false,
+      });
+    });
+
+    it('Should set meta canEdit and canSave to false', function() {
+      expect(model.meta.canSave).to.be(false);
+      expect(model.meta.canEdit).to.be(false);
+    });
+
+    it('getSaveModelClone should remove meta', function() {
+      var clone = model.getSaveModelClone();
+      expect(clone.meta).to.be(undefined);
+    });
+  });
+
+  describe('when loading dashboard with old influxdb query schema', function() {
+    var model;
+    var target;
+
+    beforeEach(function() {
+      model = _dashboardSrv.create({
+        rows: [{
+          panels: [{
+            type: 'graph',
+            grid: {},
+            yaxes: [{}, {}],
+            targets: [{
+              "alias": "$tag_datacenter $tag_source $col",
+              "column": "value",
+              "measurement": "logins.count",
+              "fields": [
+                {
+                  "func": "mean",
+                  "name": "value",
+                  "mathExpr": "*2",
+                  "asExpr": "value"
+                },
+                {
+                  "name": "one-minute",
+                  "func": "mean",
+                  "mathExpr": "*3",
+                  "asExpr": "one-minute"
+                }
+              ],
+              "tags": [],
+              "fill": "previous",
+              "function": "mean",
+              "groupBy": [
+                {
+                  "interval": "auto",
+                  "type": "time"
+                },
+                {
+                  "key": "source",
+                  "type": "tag"
+                },
+                {
+                  "type": "tag",
+                  "key": "datacenter"
+                }
+              ],
+            }]
+          }]
+        }]
+      });
+
+      target = model.rows[0].panels[0].targets[0];
+    });
+
+    it('should update query schema', function() {
+      expect(target.fields).to.be(undefined);
+      expect(target.select.length).to.be(2);
+      expect(target.select[0].length).to.be(4);
+      expect(target.select[0][0].type).to.be('field');
+      expect(target.select[0][1].type).to.be('mean');
+      expect(target.select[0][2].type).to.be('math');
+      expect(target.select[0][3].type).to.be('alias');
+    });
+
+  });
+
+  describe('when creating dashboard model with missing list for annoations or templating', function() {
+    var model;
+
+    beforeEach(function() {
+      model = _dashboardSrv.create({
+        annotations: {
+          enable: true,
+        },
+        templating: {
+          enable: true
+        }
+      });
+    });
+
+    it('should add empty list', function() {
+      expect(model.annotations.list.length).to.be(0);
+      expect(model.templating.list.length).to.be(0);
+    });
+  });
+
+  describe('Formatting epoch timestamp when timezone is set as utc', function() {
+    var dashboard;
+
+    beforeEach(function() {
+      dashboard = _dashboardSrv.create({
+        timezone: 'utc',
+      });
+    });
+
+    it('Should format timestamp with second resolution by default', function() {
+      expect(dashboard.formatDate(1234567890000)).to.be('2009-02-13 23:31:30');
+    });
+
+    it('Should format timestamp with second resolution even if second format is passed as parameter', function() {
+      expect(dashboard.formatDate(1234567890007,'YYYY-MM-DD HH:mm:ss')).to.be('2009-02-13 23:31:30');
+    });
+
+    it('Should format timestamp with millisecond resolution if format is passed as parameter', function() {
+      expect(dashboard.formatDate(1234567890007,'YYYY-MM-DD HH:mm:ss.SSS')).to.be('2009-02-13 23:31:30.007');
+    });
+  });
+});

+ 2 - 1
public/app/features/dashboard/specs/dynamic_dashboard_srv_specs.ts

@@ -1,6 +1,6 @@
 import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
 import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
 
 
-import 'app/features/dashboard/dashboardSrv';
+import {DashboardSrv} from '../dashboard_srv';
 import {DynamicDashboardSrv} from '../dynamic_dashboard_srv';
 import {DynamicDashboardSrv} from '../dynamic_dashboard_srv';
 
 
 function dynamicDashScenario(desc, func)  {
 function dynamicDashScenario(desc, func)  {
@@ -10,6 +10,7 @@ function dynamicDashScenario(desc, func)  {
 
 
     ctx.setup = function (setupFunc) {
     ctx.setup = function (setupFunc) {
 
 
+      beforeEach(angularMocks.module('grafana.core'));
       beforeEach(angularMocks.module('grafana.services'));
       beforeEach(angularMocks.module('grafana.services'));
       beforeEach(angularMocks.module(function($provide) {
       beforeEach(angularMocks.module(function($provide) {
         $provide.value('contextSrv', {
         $provide.value('contextSrv', {

+ 0 - 388
public/test/specs/dashboardSrv-specs.js

@@ -1,388 +0,0 @@
-define([
-  'app/features/dashboard/dashboardSrv'
-], function() {
-  'use strict';
-
-  describe('dashboardSrv', function() {
-    var _dashboardSrv;
-
-    beforeEach(module('grafana.services'));
-    beforeEach(module(function($provide) {
-      $provide.value('contextSrv', {
-      });
-    }));
-
-    beforeEach(inject(function(dashboardSrv) {
-      _dashboardSrv = dashboardSrv;
-    }));
-
-    describe('when creating new dashboard with defaults only', function() {
-      var model;
-
-      beforeEach(function() {
-        model = _dashboardSrv.create({}, {});
-      });
-
-      it('should have title', function() {
-        expect(model.title).to.be('No Title');
-      });
-
-      it('should have meta', function() {
-        expect(model.meta.canSave).to.be(true);
-        expect(model.meta.canShare).to.be(true);
-      });
-
-      it('should have default properties', function() {
-        expect(model.rows.length).to.be(0);
-      });
-    });
-
-    describe('when getting next panel id', function() {
-      var model;
-
-      beforeEach(function() {
-        model = _dashboardSrv.create({
-          rows: [{ panels: [{ id: 5 }]}]
-        });
-      });
-
-      it('should return max id + 1', function() {
-        expect(model.getNextPanelId()).to.be(6);
-      });
-    });
-
-    describe('row and panel manipulation', function() {
-      var dashboard;
-
-      beforeEach(function() {
-        dashboard = _dashboardSrv.create({});
-      });
-
-      it('row span should sum spans', function() {
-        var spanLeft = dashboard.rowSpan({ panels: [{ span: 2 }, { span: 3 }] });
-        expect(spanLeft).to.be(5);
-      });
-
-      it('adding default should split span in half', function() {
-        dashboard.rows = [{ panels: [{ span: 12, id: 7 }] }];
-        dashboard.addPanel({span: 4}, dashboard.rows[0]);
-
-        expect(dashboard.rows[0].panels[0].span).to.be(6);
-        expect(dashboard.rows[0].panels[1].span).to.be(6);
-        expect(dashboard.rows[0].panels[1].id).to.be(8);
-      });
-
-      it('duplicate panel should try to add it to same row', function() {
-        var panel = { span: 4, attr: '123', id: 10 };
-        dashboard.rows = [{ panels: [panel] }];
-        dashboard.duplicatePanel(panel, dashboard.rows[0]);
-
-        expect(dashboard.rows[0].panels[0].span).to.be(4);
-        expect(dashboard.rows[0].panels[1].span).to.be(4);
-        expect(dashboard.rows[0].panels[1].attr).to.be('123');
-        expect(dashboard.rows[0].panels[1].id).to.be(11);
-      });
-
-      it('duplicate panel should remove repeat data', function() {
-        var panel = { span: 4, attr: '123', id: 10, repeat: 'asd', scopedVars: { test: 'asd' }};
-        dashboard.rows = [{ panels: [panel] }];
-        dashboard.duplicatePanel(panel, dashboard.rows[0]);
-
-        expect(dashboard.rows[0].panels[1].repeat).to.be(undefined);
-        expect(dashboard.rows[0].panels[1].scopedVars).to.be(undefined);
-      });
-
-    });
-
-    describe('when creating dashboard with editable false', function() {
-      var model;
-
-      beforeEach(function() {
-        model = _dashboardSrv.create({
-          editable: false
-        });
-      });
-
-      it('should set editable false', function() {
-        expect(model.editable).to.be(false);
-      });
-
-    });
-
-    describe('when creating dashboard with old schema', function() {
-      var model;
-      var graph;
-      var singlestat;
-      var table;
-
-      beforeEach(function() {
-        model = _dashboardSrv.create({
-          services: { filter: { time: { from: 'now-1d', to: 'now'}, list: [{}] }},
-          pulldowns: [
-            {type: 'filtering', enable: true},
-            {type: 'annotations', enable: true, annotations: [{name: 'old'}]}
-          ],
-          rows: [
-            {
-              panels: [
-                {
-                  type: 'graph', legend: true, aliasYAxis: { test: 2 },
-                  y_formats: ['kbyte', 'ms'],
-                  grid: {
-                    min: 1,
-                    max: 10,
-                    rightMin: 5,
-                    rightMax: 15,
-                    leftLogBase: 1,
-                    rightLogBase: 2,
-                    threshold1: 200,
-                    threshold2: 400,
-                    threshold1Color: 'yellow',
-                    threshold2Color: 'red',
-                  },
-                  leftYAxisLabel: 'left label',
-                  targets: [{refId: 'A'}, {}],
-                },
-                {
-                  type: 'singlestat', legend: true, thresholds: '10,20,30', aliasYAxis: { test: 2 }, grid: { min: 1, max: 10 },
-                  targets: [{refId: 'A'}, {}],
-                },
-                {
-                  type: 'table', legend: true, styles: [{ thresholds: ["10", "20", "30"]}, { thresholds: ["100", "200", "300"]}],
-                  targets: [{refId: 'A'}, {}],
-                }
-              ]
-            }
-          ]
-        });
-
-        graph = model.rows[0].panels[0];
-        singlestat = model.rows[0].panels[1];
-        table = model.rows[0].panels[2];
-      });
-
-      it('should have title', function() {
-        expect(model.title).to.be('No Title');
-      });
-
-      it('should have panel id', function() {
-        expect(graph.id).to.be(1);
-      });
-
-      it('should move time and filtering list', function() {
-        expect(model.time.from).to.be('now-1d');
-        expect(model.templating.list[0].allFormat).to.be('glob');
-      });
-
-      it('graphite panel should change name too graph', function() {
-        expect(graph.type).to.be('graph');
-      });
-
-      it('single stat panel should have two thresholds', function() {
-        expect(singlestat.thresholds).to.be('20,30');
-      });
-
-      it('queries without refId should get it', function() {
-        expect(graph.targets[1].refId).to.be('B');
-      });
-
-      it('update legend setting', function() {
-        expect(graph.legend.show).to.be(true);
-      });
-
-      it('move aliasYAxis to series override', function() {
-        expect(graph.seriesOverrides[0].alias).to.be("test");
-        expect(graph.seriesOverrides[0].yaxis).to.be(2);
-      });
-
-      it('should move pulldowns to new schema', function() {
-        expect(model.annotations.list[0].name).to.be('old');
-      });
-
-      it('table panel should only have two thresholds values', function() {
-        expect(table.styles[0].thresholds[0]).to.be("20");
-        expect(table.styles[0].thresholds[1]).to.be("30");
-        expect(table.styles[1].thresholds[0]).to.be("200");
-        expect(table.styles[1].thresholds[1]).to.be("300");
-      });
-
-      it('graph grid to yaxes options', function() {
-        expect(graph.yaxes[0].min).to.be(1);
-        expect(graph.yaxes[0].max).to.be(10);
-        expect(graph.yaxes[0].format).to.be('kbyte');
-        expect(graph.yaxes[0].label).to.be('left label');
-        expect(graph.yaxes[0].logBase).to.be(1);
-        expect(graph.yaxes[1].min).to.be(5);
-        expect(graph.yaxes[1].max).to.be(15);
-        expect(graph.yaxes[1].format).to.be('ms');
-        expect(graph.yaxes[1].logBase).to.be(2);
-
-        expect(graph.grid.rightMax).to.be(undefined);
-        expect(graph.grid.rightLogBase).to.be(undefined);
-        expect(graph.y_formats).to.be(undefined);
-      });
-
-      it('dashboard schema version should be set to latest', function() {
-        expect(model.schemaVersion).to.be(13);
-      });
-
-      it('graph thresholds should be migrated', function() {
-        expect(graph.thresholds.length).to.be(2);
-        expect(graph.thresholds[0].op).to.be('>');
-        expect(graph.thresholds[0].value).to.be(400);
-        expect(graph.thresholds[0].fillColor).to.be('red');
-        expect(graph.thresholds[1].value).to.be(200);
-        expect(graph.thresholds[1].fillColor).to.be('yellow');
-      });
-    });
-
-    describe('when creating dashboard model with missing list for annoations or templating', function() {
-      var model;
-
-      beforeEach(function() {
-        model = _dashboardSrv.create({
-          annotations: {
-            enable: true,
-          },
-          templating: {
-            enable: true
-          }
-        });
-      });
-
-      it('should add empty list', function() {
-        expect(model.annotations.list.length).to.be(0);
-        expect(model.templating.list.length).to.be(0);
-      });
-    });
-
-    describe('Given editable false dashboard', function() {
-      var model;
-
-      beforeEach(function() {
-        model = _dashboardSrv.create({
-          editable:  false,
-        });
-      });
-
-      it('Should set meta canEdit and canSave to false', function() {
-        expect(model.meta.canSave).to.be(false);
-        expect(model.meta.canEdit).to.be(false);
-      });
-
-      it('getSaveModelClone should remove meta', function() {
-        var clone = model.getSaveModelClone();
-        expect(clone.meta).to.be(undefined);
-      });
-    });
-
-    describe('when loading dashboard with old influxdb query schema', function() {
-      var model;
-      var target;
-
-      beforeEach(function() {
-        model = _dashboardSrv.create({
-          rows: [{
-            panels: [{
-              type: 'graph',
-              grid: {},
-              yaxes: [{}, {}],
-              targets: [{
-                "alias": "$tag_datacenter $tag_source $col",
-                "column": "value",
-                "measurement": "logins.count",
-                "fields": [
-                  {
-                    "func": "mean",
-                    "name": "value",
-                    "mathExpr": "*2",
-                    "asExpr": "value"
-                  },
-                  {
-                    "name": "one-minute",
-                    "func": "mean",
-                    "mathExpr": "*3",
-                    "asExpr": "one-minute"
-                  }
-                ],
-                "tags": [],
-                "fill": "previous",
-                "function": "mean",
-                "groupBy": [
-                  {
-                    "interval": "auto",
-                    "type": "time"
-                  },
-                  {
-                    "key": "source",
-                    "type": "tag"
-                  },
-                  {
-                    "type": "tag",
-                    "key": "datacenter"
-                  }
-                ],
-              }]
-            }]
-          }]
-        });
-
-        target = model.rows[0].panels[0].targets[0];
-      });
-
-      it('should update query schema', function() {
-        expect(target.fields).to.be(undefined);
-        expect(target.select.length).to.be(2);
-        expect(target.select[0].length).to.be(4);
-        expect(target.select[0][0].type).to.be('field');
-        expect(target.select[0][1].type).to.be('mean');
-        expect(target.select[0][2].type).to.be('math');
-        expect(target.select[0][3].type).to.be('alias');
-      });
-
-    });
-
-    describe('when creating dashboard model with missing list for annoations or templating', function() {
-      var model;
-
-      beforeEach(function() {
-        model = _dashboardSrv.create({
-          annotations: {
-            enable: true,
-          },
-          templating: {
-            enable: true
-          }
-        });
-      });
-
-      it('should add empty list', function() {
-        expect(model.annotations.list.length).to.be(0);
-        expect(model.templating.list.length).to.be(0);
-      });
-    });
-
-    describe('Formatting epoch timestamp when timezone is set as utc', function() {
-      var dashboard;
-
-      beforeEach(function() {
-        dashboard = _dashboardSrv.create({
-          timezone: 'utc',
-        });
-      });
-
-      it('Should format timestamp with second resolution by default', function() {
-        expect(dashboard.formatDate(1234567890000)).to.be('2009-02-13 23:31:30');
-      });
-
-      it('Should format timestamp with second resolution even if second format is passed as parameter', function() {
-        expect(dashboard.formatDate(1234567890007,'YYYY-MM-DD HH:mm:ss')).to.be('2009-02-13 23:31:30');
-      });
-
-      it('Should format timestamp with millisecond resolution if format is passed as parameter', function() {
-        expect(dashboard.formatDate(1234567890007,'YYYY-MM-DD HH:mm:ss.SSS')).to.be('2009-02-13 23:31:30.007');
-      });
-    });
-
-  });
-});

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

@@ -1,6 +1,6 @@
 define([
 define([
   'app/features/dashboard/unsavedChangesSrv',
   'app/features/dashboard/unsavedChangesSrv',
-  'app/features/dashboard/dashboardSrv'
+  'app/features/dashboard/dashboard_srv'
 ], function() {
 ], function() {
   'use strict';
   'use strict';