Преглед на файлове

Dashboard schema simplifications, moved schema updates to dashboard model creation, removes irritating 'unsaved changes' dialogs that show for dashboard schema changes, Closes #532

Torkel Ödegaard преди 11 години
родител
ревизия
60f68abd31

+ 1 - 0
CHANGELOG.md

@@ -22,6 +22,7 @@
 **Changes**
 - [Issue #536](https://github.com/grafana/grafana/issues/536). Graphite: Use unix epoch for Graphite from/to for absolute time ranges
 - [Issue #641](https://github.com/grafana/grafana/issues/536). General: Dashboard save temp copy feature settings moved from dashboard to config.js, default is enabled, and ttl to 30 days
+- [Issue #532](https://github.com/grafana/grafana/issues/532). Schema: Dashboard schema changes, "Unsaved changes" should not appear for schema changes. All changes are backward compatible with old schema.
 
 **Fixes**
 - [Issue #545](https://github.com/grafana/grafana/issues/545). Chart: Fix formatting negative values (axis formats, legend values)

+ 56 - 0
sample/start_dashboard_html.html

@@ -0,0 +1,56 @@
+<br/>
+
+<div class="row-fluid">
+  <div class="span6">
+    <ul>
+      <li>
+        <a href="http://localhost:4567/docs#configuration" target="_blank">Configuration</a>
+      </li>
+      <li>
+        <a href="http://localhost:4567/docs/troubleshooting" target="_blank">Troubleshooting</a>
+      </li>
+      <li>
+        <a href="http://localhost:4567/docs/support" target="_blank">Support</a>
+      </li>
+      <li>
+        <a href="http://localhost:4567/docs/features/intro" target="_blank">Getting started</a>  (Must read!)
+      </li>
+    </ul>
+  </div>
+  <div class="span6">
+    <ul>
+      <li>
+        <a href="http://localhost:4567/docs/features/charts" target="_blank">Charts</a>
+      </li>
+      <li>
+        <a href="http://localhost:4567/docs/features/annotations" target="_blank">Annotations</a>
+      </li>
+      <li>
+        <a href="http://localhost:4567/docs/features/graphite" target="_blank">Graphite</a>
+      </li>
+      <li>
+        <a href="http://localhost:4567/docs/features/influxdb" target="_blank">InfluxDB</a>
+      </li>
+      <li>
+        <a href="http://localhost:4567/docs/features/opentsdb" target="_blank">OpenTSDB</a>
+      </li>
+    </ul>
+  </div>
+</div>
+
+<br/>
+
+<div class="row-fluid">
+  <div class="span12">
+    <ul>
+      <li>Ctrl+S saves the current dashboard</li>
+      <li>Ctrl+F Opens the dashboard finder (searches elastic search)</li>
+      <li>Ctrl+H Hide/show row controls</li>
+      <li>Click and drag graph title to move panel</li>
+      <li>Hit Escape to exit graph when in fullscreen or edit mode</li>
+      <li>Click the colored icon in the legend to change series color</li>
+      <li>Ctrl or Shift + Click legend name to hide other series</li>
+      <li>Click the Save icon in the menu to save the dashboard with a new name</li>
+    </ul>
+  </div>
+</div>

+ 4 - 0
src/app/controllers/graphiteTarget.js

@@ -188,6 +188,10 @@ function (angular, _, config, gfunc, Parser) {
       $scope.segments[segmentIndex].val = $scope.altSegments[altIndex].val;
       $scope.segments[segmentIndex].html = $scope.altSegments[altIndex].html;
 
+      if ($scope.functions.length > 0 && $scope.functions[0].def.fake) {
+        $scope.functions = [];
+      }
+
       if ($scope.altSegments[altIndex].expandable) {
         return checkOtherSegments(segmentIndex + 1)
           .then(function () {

+ 0 - 7
src/app/controllers/row.js

@@ -13,10 +13,8 @@ function (angular, app, _) {
       title: "Row",
       height: "150px",
       collapse: false,
-      collapsable: true,
       editable: true,
       panels: [],
-      notice: false
     };
 
     _.defaults($scope.row,_d);
@@ -26,16 +24,11 @@ function (angular, app, _) {
     };
 
     $scope.toggle_row = function(row) {
-      if(!row.collapsable) {
-        return;
-      }
       row.collapse = row.collapse ? false : true;
       if (!row.collapse) {
         $timeout(function() {
           $scope.$broadcast('render');
         });
-      } else {
-        row.notice = false;
       }
     };
 

+ 8 - 14
src/app/dashboards/default.json

@@ -1,13 +1,11 @@
 {
   "title": "Welcome to Grafana!",
-  "services": {
-    "filter": {
-      "list": [],
-      "time": {
-        "from": "now-6h",
-        "to": "now"
-      }
-    }
+  "time": {
+    "from": "now-6h",
+    "to": "now"
+  },
+  "templating": {
+    "list": []
   },
   "rows": [
     {
@@ -15,7 +13,6 @@
       "height": "150px",
       "editable": true,
       "collapse": false,
-      "collapsable": true,
       "panels": [
         {
           "error": false,
@@ -28,15 +25,13 @@
           "style": {},
           "title": "Welcome to Grafana"
         }
-      ],
-      "notice": false
+      ]
     },
     {
       "title": "test",
       "height": "250px",
       "editable": true,
       "collapse": false,
-      "collapsable": true,
       "panels": [
         {
           "span": 12,
@@ -86,8 +81,7 @@
           "aliasYAxis": {},
           "title": "Graphite test"
         }
-      ],
-      "notice": false
+      ]
     }
   ],
   "editable": true,

+ 7 - 11
src/app/dashboards/empty.json

@@ -1,13 +1,11 @@
 {
   "title": "New Dashboard",
-  "services": {
-    "filter": {
-      "list": [],
-      "time": {
-        "from": "now-6h",
-        "to": "now"
-      }
-    }
+  "time": {
+    "from": "now-6h",
+    "to": "now"
+  },
+  "templating": {
+    "list": []
   },
   "rows": [
     {
@@ -15,9 +13,7 @@
       "height": "250px",
       "editable": true,
       "collapse": false,
-      "collapsable": true,
-      "panels": [],
-      "notice": false
+      "panels": []
     }
   ],
   "editable": true,

+ 5 - 8
src/app/dashboards/scripted.js

@@ -28,16 +28,13 @@ timspan = '1d';
 // Intialize a skeleton with nothing but a rows array and service object
 dashboard = {
   rows : [],
-  services : {}
 };
 
 // Set a title
 dashboard.title = 'Scripted dash';
-dashboard.services.filter = {
-  time: {
-    from: "now-" + (ARGS.from || timspan),
-    to: "now"
-  }
+dashboard.time = {
+  from: "now-" + (ARGS.from || timspan),
+  to: "now"
 };
 
 var rows = 1;
@@ -59,7 +56,7 @@ for (var i = 0; i < rows; i++) {
     panels: [
       {
         title: 'Events',
-        type: 'graphite',
+        type: 'graph',
         span: 12,
         fill: 1,
         linewidth: 2,
@@ -77,4 +74,4 @@ for (var i = 0; i < rows; i++) {
 }
 
 
-return dashboard;
+return dashboard;

+ 1 - 26
src/app/panels/graph/module.js

@@ -185,32 +185,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
     _.defaults($scope.panel.tooltip, _d.tooltip);
     _.defaults($scope.panel.annotate, _d.annotate);
     _.defaults($scope.panel.grid, _d.grid);
-
-    // backward compatible stuff
-    if (_.isBoolean($scope.panel.legend)) {
-      $scope.panel.legend = { show: $scope.panel.legend };
-      _.defaults($scope.panel.legend, _d.legend);
-    }
-
-    if ($scope.panel.grid.min) {
-      $scope.panel.grid.leftMin = $scope.panel.grid.min;
-      delete $scope.panel.grid.min;
-    }
-
-    if ($scope.panel.grid.max) {
-      $scope.panel.grid.leftMax = $scope.panel.grid.max;
-      delete $scope.panel.grid.max;
-    }
-
-    if ($scope.panel.y_format) {
-      $scope.panel.y_formats[0] = $scope.panel.y_format;
-      delete $scope.panel.y_format;
-    }
-
-    if ($scope.panel.y2_format) {
-      $scope.panel.y_formats[1] = $scope.panel.y2_format;
-      delete $scope.panel.y2_format;
-    }
+    _.defaults($scope.panel.legend, _d.legend);
 
     $scope.init = function() {
       $scope.initBaseController(this, $scope);

+ 1 - 1
src/app/panels/text/module.js

@@ -36,7 +36,7 @@ function (angular, app, _, require) {
       style: {},
     };
 
-    _.defaults($scope.panel,_d);
+    _.defaults($scope.panel, _d);
 
     $scope.init = function() {
       $scope.initBaseController(this, $scope);

+ 3 - 3
src/app/partials/dashboard.html

@@ -34,13 +34,13 @@
                 <span class="row-button bgWarning" config-modal="app/partials/roweditor.html" class="pointer">
                   <i bs-tooltip="'Configure row'" data-placement="right" ng-show="row.editable" class="icon-cog pointer"></i>
                 </span>
-                <span class="row-button bgPrimary" ng-click="toggle_row(row)" ng-show="row.collapsable">
+                <span class="row-button bgPrimary" ng-click="toggle_row(row)">
                   <i bs-tooltip="'Expand row'" data-placement="right" class="icon-caret-left pointer" ></i>
                 </span>
-                <span class="row-button row-text" ng-click="toggle_row(row)" ng-class="{'pointer':row.collapsable}">{{row.title || 'Row '+$index}}</span>
+                <span class="row-button row-text" ng-click="toggle_row(row)">{{row.title || 'Row '+$index}}</span>
               </div>
               <div class="row-open" ng-show="!row.collapse">
-                <div ng-show="row.collapsable" class='row-tab bgPrimary' ng-click="toggle_row(row)">
+                <div class='row-tab bgPrimary' ng-click="toggle_row(row)">
                   <span class="row-tab-button">
                     <i class="icon-caret-right"></i>
                   </span>

+ 5 - 2
src/app/partials/unsaved-changes.html

@@ -4,7 +4,10 @@
 
   <h3 class="text-center"><i class="icon-warning-sign"></i> Unsaved changes</h3>
   <div class="row-fluid">
-	<span class="span3"></span>
+		<span class="span3">
+			{{changes}}
+
+		</span>
 	<button type="button" class="btn btn-success span2" ng-click="dismiss()">Cancel</button>
 	<button type="button" class="btn btn-success span2" ng-click="save();dismiss();">Save</button>
 	<button type="button" class="btn btn-warning span2" ng-click="ignore();dismiss();">Ignore</button>
@@ -13,4 +16,4 @@
 
 </div>
 <div class="modal-footer">
-</div>
+</div>

+ 64 - 10
src/app/services/dashboard/dashboardModel.js

@@ -2,7 +2,8 @@ define([
   'angular',
   'jquery',
   'kbn',
-  'underscore'
+  'underscore',
+  '../timer',
 ],
 function (angular, $, kbn, _) {
   'use strict';
@@ -17,7 +18,7 @@ function (angular, $, kbn, _) {
         data = {};
       }
 
-      this.title = data.title;
+      this.title = data.title || 'No Title';
       this.tags = data.tags || [];
       this.style = data.style || "dark";
       this.timezone = data.timezone || 'browser';
@@ -25,7 +26,8 @@ function (angular, $, kbn, _) {
       this.rows = data.rows || [];
       this.pulldowns = data.pulldowns || [];
       this.nav = data.nav || [];
-      this.services = data.services || {};
+      this.time = data.time || { from: 'now-6h', to: 'now' };
+      this.templating = data.templating || { list: [] };
 
       if (this.nav.length === 0) {
         this.nav.push({ type: 'timepicker' });
@@ -39,13 +41,7 @@ function (angular, $, kbn, _) {
         this.pulldowns.push({ type: 'annotations', enable: false });
       }
 
-      _.each(this.rows, function(row) {
-        _.each(row.panels, function(panel) {
-          if (panel.type === 'graphite') {
-            panel.type = 'graph';
-          }
-        });
-      });
+      this.updateSchema(data);
     }
 
     var p = DashboardModel.prototype;
@@ -76,6 +72,64 @@ function (angular, $, kbn, _) {
       }
     };
 
+    p.updateSchema = function(old) {
+      var i, j, row, panel;
+      var isChanged = false;
+
+      if (this.version === 2) {
+        return;
+      }
+
+      if (old.services) {
+        if (old.services.filter) {
+          this.time = old.services.filter.time;
+          this.templating.list = old.services.filter.list;
+        }
+        delete this.services;
+      }
+
+      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.type === 'graphite') {
+            panel.type = 'graph';
+            isChanged = true;
+          }
+
+          if (panel.type === 'graph') {
+            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;
+            }
+          }
+        }
+      }
+
+      this.version = 2;
+    };
+
     return {
       create: function(dashboard) {
         return new DashboardModel(dashboard);

+ 3 - 21
src/app/services/filterSrv.js

@@ -9,12 +9,6 @@ define([
   var module = angular.module('grafana.services');
 
   module.factory('filterSrv', function($rootScope, $timeout, $routeParams) {
-    // defaults
-    var _d = {
-      templateParameters: [],
-      time: {}
-    };
-
     var result = {
 
       updateTemplateData: function(initial) {
@@ -86,26 +80,14 @@ define([
 
       removeTemplateParameter: function(templateParameter) {
         this.templateParameters = _.without(this.templateParameters, templateParameter);
-        this.dashboard.services.filter.list = this.templateParameters;
+        this.dashboard.templating.list = this.templateParameters;
       },
 
       init: function(dashboard) {
-        _.defaults(this, _d);
         this.dashboard = dashboard;
         this.templateSettings = { interpolate : /\[\[([\s\S]+?)\]\]/g };
-
-        if (!this.dashboard.services.filter) {
-          this.dashboard.services.filter = {
-            list: [],
-            time: {
-              from: '1h',
-              to: 'now'
-            }
-          };
-        }
-
-        this.time = dashboard.services.filter.time;
-        this.templateParameters = dashboard.services.filter.list || [];
+        this.time = dashboard.time;
+        this.templateParameters = dashboard.templating.list;
         this.updateTemplateData(true);
       }
     };

+ 1 - 0
src/app/services/graphite/gfunc.js

@@ -216,6 +216,7 @@ function (_) {
 
   addFuncDef({
     name: 'randomWalk',
+    fake: true,
     category: categories.Special,
     params: [{ name: "name", type: "string", }],
     defaultParams: ['randomWalk']

+ 1 - 1
src/app/services/unsavedChangesSrv.js

@@ -74,7 +74,7 @@ function(angular, _, config) {
       var original = self.original;
 
       // ignore timespan changes
-      current.services.filter.time = original.services.filter.time = {};
+      current.time = original.time = {};
 
       current.refresh = original.refresh;
 

+ 3 - 5
src/test/mocks/dashboard-mock.js

@@ -18,11 +18,9 @@ define([],
         rows: [],
         pulldowns: [ { type: 'templating' },  { type: 'annotations' } ],
         nav: [ { type: 'timepicker' } ],
-        services: {
-          filter: {
-            time: {},
-            list: []
-          }
+        time: {},
+        templating: {
+          list: []
         },
         refresh: true
       };

+ 82 - 0
src/test/specs/dashboardModel-specs.js

@@ -0,0 +1,82 @@
+define([
+  'services/dashboard/dashboardModel'
+], function() {
+  'use strict';
+
+  describe('when creating new dashboard with defaults only', function() {
+    var model;
+
+    beforeEach(module('grafana.services'));
+
+    beforeEach(inject(function(dashboard) {
+      model = dashboard.create({});
+    }));
+
+    it('should have title', function() {
+      expect(model.title).to.be('No Title');
+    });
+
+    it('should have default properties', function() {
+      expect(model.rows.length).to.be(0);
+      expect(model.nav.length).to.be(1);
+      expect(model.pulldowns.length).to.be(2);
+    });
+
+  });
+
+  describe('when creating dashboard with old schema', function() {
+    var model;
+    var graph;
+
+    beforeEach(module('grafana.services'));
+
+    beforeEach(inject(function(dashboard) {
+      model = dashboard.create({
+        services: { filter: { time: { from: 'now-1d', to: 'now'}, list: [1] }},
+        rows: [
+          {
+            panels: [
+              {
+                type: 'graphite',
+                legend: true,
+                grid: { min: 1, max: 10 }
+              }
+            ]
+          }
+        ]
+      });
+
+      graph = model.rows[0].panels[0];
+
+    }));
+
+    it('should have title', function() {
+      expect(model.title).to.be('No Title');
+    });
+
+    it('should move time and filtering list', function() {
+      expect(model.time.from).to.be('now-1d');
+      expect(model.templating.list[0]).to.be(1);
+    });
+
+    it('graphite panel should change name too graph', function() {
+      expect(graph.type).to.be('graph');
+    });
+
+    it('update legend setting', function() {
+      expect(graph.legend.show).to.be(true);
+    });
+
+    it('update grid options', function() {
+      expect(graph.grid.leftMin).to.be(1);
+      expect(graph.grid.leftMax).to.be(10);
+    });
+
+    it('dashboard schema version should be set to latest', function() {
+      expect(model.version).to.be(2);
+    });
+
+  });
+
+
+});

+ 1 - 0
src/test/test-main.js

@@ -124,6 +124,7 @@ require([
     'specs/gfunc-specs',
     'specs/filterSrv-specs',
     'specs/kbn-format-specs',
+    'specs/dashboardModel-specs',
     'specs/influxSeries-specs'
   ], function () {
     window.__karma__.start();