diff --git a/CHANGELOG.md b/CHANGELOG.md index 51806ab989d..de6f915f6ad 100644 --- a/CHANGELOG.md +++ b/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) diff --git a/sample/start_dashboard_html.html b/sample/start_dashboard_html.html new file mode 100644 index 00000000000..66a5f0430de --- /dev/null +++ b/sample/start_dashboard_html.html @@ -0,0 +1,56 @@ +
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+ +
+
diff --git a/src/app/controllers/graphiteTarget.js b/src/app/controllers/graphiteTarget.js index 681b23e7d05..3540c20f522 100644 --- a/src/app/controllers/graphiteTarget.js +++ b/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 () { diff --git a/src/app/controllers/row.js b/src/app/controllers/row.js index 0f020ff345d..5aa788d2228 100644 --- a/src/app/controllers/row.js +++ b/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; } }; diff --git a/src/app/dashboards/default.json b/src/app/dashboards/default.json index a31d4985779..c14f3d66242 100644 --- a/src/app/dashboards/default.json +++ b/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, diff --git a/src/app/dashboards/empty.json b/src/app/dashboards/empty.json index 25a375e5821..fc97a61d125 100644 --- a/src/app/dashboards/empty.json +++ b/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, diff --git a/src/app/dashboards/scripted.js b/src/app/dashboards/scripted.js index be797ddf918..b8f428a9834 100644 --- a/src/app/dashboards/scripted.js +++ b/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; \ No newline at end of file +return dashboard; diff --git a/src/app/panels/graph/module.js b/src/app/panels/graph/module.js index 2c9ba1cefa0..83da216b8d8 100644 --- a/src/app/panels/graph/module.js +++ b/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); diff --git a/src/app/panels/text/module.js b/src/app/panels/text/module.js index bebb79911df..98067cc7920 100644 --- a/src/app/panels/text/module.js +++ b/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); diff --git a/src/app/partials/dashboard.html b/src/app/partials/dashboard.html index e6843658966..0113e26981d 100644 --- a/src/app/partials/dashboard.html +++ b/src/app/partials/dashboard.html @@ -34,13 +34,13 @@ - + - {{row.title || 'Row '+$index}} + {{row.title || 'Row '+$index}}
-
+
diff --git a/src/app/partials/unsaved-changes.html b/src/app/partials/unsaved-changes.html index 87220e41e8d..b025575e7e7 100644 --- a/src/app/partials/unsaved-changes.html +++ b/src/app/partials/unsaved-changes.html @@ -4,7 +4,10 @@

Unsaved changes

- + + {{changes}} + + @@ -13,4 +16,4 @@
\ No newline at end of file +
diff --git a/src/app/services/dashboard/dashboardModel.js b/src/app/services/dashboard/dashboardModel.js index d069a4b43b2..ecb5af07494 100644 --- a/src/app/services/dashboard/dashboardModel.js +++ b/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); diff --git a/src/app/services/filterSrv.js b/src/app/services/filterSrv.js index 223f6190e68..cbd0af62364 100644 --- a/src/app/services/filterSrv.js +++ b/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); } }; diff --git a/src/app/services/graphite/gfunc.js b/src/app/services/graphite/gfunc.js index 63c28bc1dfc..07ca4e4f58c 100644 --- a/src/app/services/graphite/gfunc.js +++ b/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'] diff --git a/src/app/services/unsavedChangesSrv.js b/src/app/services/unsavedChangesSrv.js index 1c05ae86377..aacbabfd326 100644 --- a/src/app/services/unsavedChangesSrv.js +++ b/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; diff --git a/src/test/mocks/dashboard-mock.js b/src/test/mocks/dashboard-mock.js index 2ee9c659337..7ec0e5e9997 100644 --- a/src/test/mocks/dashboard-mock.js +++ b/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 }; diff --git a/src/test/specs/dashboardModel-specs.js b/src/test/specs/dashboardModel-specs.js new file mode 100644 index 00000000000..5b92caafe46 --- /dev/null +++ b/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); + }); + + }); + + +}); diff --git a/src/test/test-main.js b/src/test/test-main.js index 1d3ac1b57c5..79eb4db1165 100644 --- a/src/test/test-main.js +++ b/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();