Parcourir la source

More work on templating, added an embry of a dashboard json edit view

Torkel Ödegaard il y a 11 ans
Parent
commit
f9cd4a4470

+ 6 - 2
src/app/controllers/dashboardNavCtrl.js

@@ -111,8 +111,6 @@ function (angular, _, moment, config, store) {
       window.saveAs(blob, $scope.dashboard.title + '-' + new Date().getTime());
     };
 
-    // function $scope.zoom
-    // factor :: Zoom factor, so 0.5 = cuts timespan in half, 2 doubles timespan
     $scope.zoom = function(factor) {
       var _range = timeSrv.timeRange();
       var _timespan = (_range.to.valueOf() - _range.from.valueOf());
@@ -138,6 +136,12 @@ function (angular, _, moment, config, store) {
       $scope.grafana.style = $scope.dashboard.style;
     };
 
+    $scope.editJson = function() {
+      var editScope = $rootScope.$new();
+      editScope.json = angular.toJson($scope.dashboard, true);
+      $scope.emitAppEvent('show-dash-editor', { src: 'app/partials/edit_json.html', scope: editScope });
+    };
+
     $scope.openSaveDropdown = function() {
       $scope.isFavorite = playlistSrv.isCurrentFavorite($scope.dashboard);
       $scope.saveDropdownOpened = true;

+ 14 - 0
src/app/controllers/jsonEditorCtrl.js

@@ -0,0 +1,14 @@
+define([
+  'angular',
+  'lodash'
+],
+function (angular) {
+  'use strict';
+
+  var module = angular.module('grafana.controllers');
+
+  module.controller('JsonEditorCtrl', function() {
+
+  });
+
+});

+ 3 - 3
src/app/controllers/submenuCtrl.js

@@ -18,7 +18,7 @@ function (angular, app, _) {
     $scope.init = function() {
       $scope.panel = $scope.pulldown;
       $scope.row = $scope.pulldown;
-      $scope.templateParameters = $scope.dashboard.templating.list;
+      $scope.variables = $scope.dashboard.templating.list;
     };
 
     $scope.disableAnnotation = function (annotation) {
@@ -26,8 +26,8 @@ function (angular, app, _) {
       $rootScope.$broadcast('refresh');
     };
 
-    $scope.filterOptionSelected = function(param, option) {
-      templateValuesSrv.filterOptionSelected(param, option);
+    $scope.setVariableValue = function(param, option) {
+      templateValuesSrv.setVariableValue(param, option);
     };
 
     $scope.init();

+ 15 - 14
src/app/controllers/templateEditorCtrl.js

@@ -21,13 +21,13 @@ function (angular, _) {
       $scope.editor = { index: 0 };
       $scope.datasources = datasourceSrv.getMetricSources();
       $scope.currentDatasource = _.findWhere($scope.datasources, { default: true });
-      $scope.templateParameters = templateSrv.templateParameters;
+      $scope.variables = templateSrv.variables;
       $scope.reset();
 
-      _.each($scope.templateParameters, function(param) {
-        if (param.datasource === void 0) {
-          param.datasource = null;
-          param.type = 'query';
+      _.each($scope.variables, function(variable) {
+        if (variable.datasource === void 0) {
+          variable.datasource = null;
+          variable.type = 'query';
         }
       });
 
@@ -40,19 +40,19 @@ function (angular, _) {
 
     $scope.add = function() {
       $scope.current.datasource = $scope.currentDatasource.name;
-      $scope.templateParameters.push($scope.current);
+      $scope.variables.push($scope.current);
       $scope.reset();
       $scope.editor.index = 0;
     };
 
     $scope.runQuery = function() {
-      templateValuesSrv.updateValuesFor($scope.current);
+      templateValuesSrv.updateOptions($scope.current);
     };
 
-    $scope.edit = function(param) {
-      $scope.current = param;
+    $scope.edit = function(variable) {
+      $scope.current = variable;
       $scope.currentIsNew = false;
-      $scope.currentDatasource = _.findWhere($scope.datasources, { name: param.datasource });
+      $scope.currentDatasource = _.findWhere($scope.datasources, { name: variable.datasource });
 
       if (!$scope.currentDatasource) {
         $scope.currentDatasource = $scope.datasources[0];
@@ -62,6 +62,7 @@ function (angular, _) {
     };
 
     $scope.update = function() {
+      templateValuesSrv.updateOptions($scope.current);
       $scope.reset();
       $scope.editor.index = 0;
     };
@@ -73,14 +74,14 @@ function (angular, _) {
 
     $scope.typeChanged = function () {
       if ($scope.current.type === 'time period') {
-        $scope.current.options = ['auto', '1m', '10m', '30m', '1h', '6h', '12h', '1d', '7d', '14d', '30d'];
+        $scope.current.query = 'auto,1m,10m,30m,1h,6h,12h,1d,7d,14d,30d';
         $scope.current.auto_count = 10;
       }
     };
 
-    $scope.removeTemplateParam = function(templateParam) {
-      var index = _.indexOf($scope.templateParameters, templateParam);
-      $scope.templateParameters.splice(index, 1);
+    $scope.removeVariable = function(variable) {
+      var index = _.indexOf($scope.variables, variable);
+      $scope.variables.splice(index, 1);
     };
 
   });

+ 1 - 0
src/app/directives/all.js

@@ -15,6 +15,7 @@ define([
   './bodyClass',
   './addGraphiteFunc',
   './graphiteFuncEditor',
+  './templateParamSelector',
   './grafanaVersionCheck',
   './influxdbFuncEditor'
 ], function () {});

+ 85 - 0
src/app/directives/templateParamSelector.js

@@ -0,0 +1,85 @@
+define([
+  'angular',
+  'app',
+  'lodash',
+  'jquery',
+],
+function (angular, app, _, $) {
+  'use strict';
+
+  angular
+    .module('grafana.directives')
+    .directive('templateParamSelector', function($compile) {
+      var inputTemplate = '<input type="text" data-provide="typeahead" ' +
+                            ' class="grafana-target-segment-input input-medium"' +
+                            ' spellcheck="false" style="display:none"></input>';
+
+      var buttonTemplate = '<a  class="grafana-target-segment tabindex="1">{{variable.current.text}}</a>';
+
+      return {
+        link: function($scope, elem) {
+          var $input = $(inputTemplate);
+          var $button = $(buttonTemplate);
+          var variable = $scope.variable;
+          var options = _.map(variable.options, function(option) {
+            return option.text;
+          });
+
+          $input.appendTo(elem);
+          $button.appendTo(elem);
+
+          function updateVariableValue(value) {
+            $scope.$apply(function() {
+              var selected = _.findWhere(variable.options, { text: value });
+              if (!selected) {
+                selected = { text: value, value: value };
+              }
+              $scope.setVariableValue($scope.variable, selected);
+            });
+          }
+
+          $input.attr('data-provide', 'typeahead');
+          $input.typeahead({
+            source: options,
+            minLength: 0,
+            items: 10,
+            updater: function(value) {
+              updateVariableValue(value);
+              $input.trigger('blur');
+              return '';
+            }
+          });
+
+          var typeahead = $input.data('typeahead');
+          typeahead.lookup = function () {
+            this.query = this.$element.val() || '';
+            return this.process(this.source);
+          };
+
+          $button.click(function() {
+            $input.css('width', ($button.width() + 16) + 'px');
+
+            $button.hide();
+            $input.show();
+            $input.focus();
+
+            var typeahead = $input.data('typeahead');
+            if (typeahead) {
+              $input.val('');
+              typeahead.lookup();
+            }
+
+          });
+
+          $input.blur(function() {
+            if ($input.val() !== '') { updateVariableValue($input.val()); }
+            $input.hide();
+            $button.show();
+            $button.focus();
+          });
+
+          $compile(elem.contents())($scope);
+        }
+      };
+    });
+});

+ 3 - 0
src/app/partials/dashboard_topnav.html

@@ -46,6 +46,9 @@
 						<li ng-show="isFavorite">
 							<a class="link" ng-click="removeAsFavorite()">Remove as favorite</a>
 						</li>
+						<li>
+							<a class="link" ng-click="editJson()">Edit Json</a>
+						</li>
 						<li>
 							<a class="link" ng-click="exportDashboard()">Export dashboard</a>
 						</li>

+ 18 - 0
src/app/partials/edit_json.html

@@ -0,0 +1,18 @@
+<div>
+
+	<div class="dashboard-editor-header">
+		<div class="dashboard-editor-title">
+			<i class="icon icon-edit"></i>
+		  Edit / View JSON
+		</div>
+	</div>
+
+	<div class="dashboard-editor-body" style="height: 500px">
+		<textarea ng-model="json" rows="20" spellcheck="false" style="width: 90%; color: white"></textarea>
+	</div>
+
+	<div class="dashboard-editor-footer">
+		<button type="button" class="btn btn-success pull-right" ng-click="dismiss();">Close</button>
+	</div>
+
+</div>

+ 10 - 13
src/app/partials/submenu.html

@@ -18,21 +18,18 @@
 					</ul>
 
 					<ul class="grafana-segment-list">
-						<li ng-repeat-start="param in templateParameters" class="grafana-target-segment template-param-name">
-							{{param.name}}:
+						<li class="small grafana-target-segment">
+							<strong>VARIABLES</strong>
+						</li>
+						<li ng-repeat-start="variable in variables" class="grafana-target-segment template-param-name">
+							{{variable.name}}:
 						</li>
 
-						<li ng-repeat-end>
-							<div class="dropdown">
-								<a class="dropdown-toggle grafana-target-segment" data-toggle="dropdown">
-									{{param.current.text}}
-								</a>
-								<ul class="dropdown-menu">
-									<li ng-repeat="option in param.options">
-										<a ng-click="filterOptionSelected(param, option)">{{option.text}}</a>
-									</li>
-								</ul>
-							</div>
+						<li ng-repeat-end template-param-selector>
+						</li>
+
+						<li class="small grafana-target-segment">
+							<strong>ANNOTATIONS</strong>
 						</li>
 
 						<li ng-repeat="annotation in dashboard.annotations.list" class="grafana-target-segment annotation-segment" ng-class="{'annotation-disabled': !annotation.enable}">

+ 10 - 10
src/app/partials/templating_editor.html

@@ -17,25 +17,25 @@
 
 			<div class="editor-row row">
 				<div class="span8">
-					<div ng-if="templateParameters.length === 0">
+					<div ng-if="variables.length === 0">
 						<em>No replacements defined</em>
 					</div>
 					<table class="grafana-options-table">
-						<tr ng-repeat="templateParam in templateParameters">
+						<tr ng-repeat="variable in variables">
 							<td style="width: 1%">
-								[[{{templateParam.name}}]]
+								[[{{variable.name}}]]
 							</td>
 							<td class="max-width" style="max-width: 200px;">
-								{{templateParam.query}}
+								{{variable.query}}
 							</td>
 							<td style="width: 1%">
-								<a ng-click="edit(templateParam)" class="btn btn-success btn-mini">
+								<a ng-click="edit(variable)" class="btn btn-success btn-mini">
 									<i class="icon-edit"></i>
 									Edit
 								</a>
 							</td>
 							<td style="width: 1%">
-								<a ng-click="removeTemplateParam(templateParam)" class="btn btn-danger btn-mini">
+								<a ng-click="removeVariable(variable)" class="btn btn-danger btn-mini">
 									<i class="icon-remove"></i>
 								</a>
 							</td>
@@ -49,7 +49,7 @@
 		<div ng-if="editor.index == 1 || (editor.index == 2 && !currentIsNew)">
 			<div class="editor-row">
 				<div class="editor-option">
-					<label class="small">Replacement name</label>
+					<label class="small">Variable name</label>
 					<input type="text" class="input-medium" ng-model='current.name' placeholder="name"></input>
 				</div>
 				<div class="editor-option">
@@ -69,7 +69,7 @@
 			<div ng-show="current.type === 'time period'">
 				<div class="editor-option">
 					<label class="small">Values</label>
-					<input type="text" class="input-xxlarge" array-join ng-model='current.options' ng-change="csvValuesChanged()" ng-model-onblur placeholder="name"></input>
+					<input type="text" class="input-xxlarge" ng-model='current.query' placeholder="name"></input>
 				</div>
 				<div class="editor-option">
 					<label class="small">Auto period count <tip>The number you want to divide the time range in</tip></label>
@@ -86,7 +86,7 @@
 			<div ng-show="current.type === 'query'">
 				<div class="editor-row">
 					<div class="editor-option form-inline">
-						<label class="small">Metric name query</label>
+						<label class="small">Variable values query</label>
 						<input type="text" class="input-xxlarge" ng-model='current.query' placeholder="apps.servers.*"></input>
 						<button class="btn btn-small btn-success" ng-click="runQuery()" bs-tooltip="'Execute query'" data-placement="right"><i class="icon-play"></i></button>
 					</div>
@@ -102,7 +102,7 @@
 
 				<div class="editor-row" style="margin-top: 10px;">
 					<div class="editor-option form-inline">
-						<label class="small">Current replacement values</label>
+						<label class="small">Variable values</label>
 						<ul>
 							<li ng-repeat="option in current.options">
 								{{option.text}}

+ 7 - 7
src/app/services/templateSrv.js

@@ -11,25 +11,25 @@ function (angular, _) {
 
   module.service('templateSrv', function($q, $routeParams) {
 
-    this.init = function(templateParameters) {
+    this.init = function(variables) {
       this.templateSettings = { interpolate : /\[\[([\s\S]+?)\]\]/g };
-      this.templateParameters = templateParameters;
+      this.variables = variables;
       this.updateTemplateData(true);
     };
 
     this.updateTemplateData = function(initial) {
       var _templateData = {};
-      _.each(this.templateParameters, function(templateParameter) {
+      _.each(this.variables, function(variable) {
         if (initial) {
-          var urlValue = $routeParams[ templateParameter.name ];
+          var urlValue = $routeParams[ variable.name ];
           if (urlValue) {
-            templateParameter.current = { text: urlValue, value: urlValue };
+            variable.current = { text: urlValue, value: urlValue };
           }
         }
-        if (!templateParameter.current || !templateParameter.current.value) {
+        if (!variable.current || !variable.current.value) {
           return;
         }
-        _templateData[templateParameter.name] = templateParameter.current.value;
+        _templateData[variable.name] = variable.current.value;
       });
       this._templateData = _templateData;
     };

+ 32 - 24
src/app/services/templateValuesSrv.js

@@ -13,24 +13,24 @@ function (angular, _) {
     var self = this;
 
     this.init = function(dashboard) {
-      this.templateParameters = dashboard.templating.list;
+      this.variables = dashboard.templating.list;
 
-      templateSrv.init(this.templateParameters);
+      templateSrv.init(this.variables);
 
-      for (var i = 0; i < this.templateParameters.length; i++) {
-        var param = this.templateParameters[i];
+      for (var i = 0; i < this.variables.length; i++) {
+        var param = this.variables[i];
         if (param.refresh) {
-          this.updateValuesFor(param);
+          this.updateOptions(param);
         }
       }
     };
 
-    this.filterOptionSelected = function(templateParameter, option, recursive) {
-      templateParameter.current = option;
+    this.setVariableValue = function(variable, option, recursive) {
+      variable.current = option;
 
       templateSrv.updateTemplateData();
 
-      return this.applyFilterToOtherFilters(templateParameter)
+      return this.applyFilterToOtherFilters(variable)
         .then(function() {
           if (!recursive) {
             $rootScope.$broadcast('refresh');
@@ -38,46 +38,54 @@ function (angular, _) {
         });
     };
 
-    this.applyFilterToOtherFilters = function(updatedTemplatedParam) {
-      var promises = _.map(self.templateParameters, function(templateParam) {
-        if (templateParam === updatedTemplatedParam) {
+    this.applyFilterToOtherFilters = function(updatedVariable) {
+      var promises = _.map(self.variables, function(otherVariable) {
+        if (otherVariable === updatedVariable) {
           return;
         }
-        if (templateParam.query.indexOf('[[' + updatedTemplatedParam.name + ']]') !== -1) {
-          return self.applyFilter(templateParam);
+        if (otherVariable.query.indexOf('[[' + updatedVariable.name + ']]') !== -1) {
+          return self.updateOptions(otherVariable);
         }
       });
 
       return $q.all(promises);
     };
 
-    this.updateValuesFor = function(templateParam) {
-      var datasource = datasourceSrv.get(templateParam.datasource);
-      return datasource.metricFindQuery(templateParam.query)
+    this.updateOptions = function(variable) {
+      if (variable.type === 'time period') {
+        variable.options = _.map(variable.query.split(','), function(text) {
+          return { text: text, value: text };
+        });
+        self.setVariableValue(variable, variable.options[0]);
+        return;
+      }
+
+      var datasource = datasourceSrv.get(variable.datasource);
+      return datasource.metricFindQuery(variable.query)
         .then(function (results) {
-          templateParam.options = _.map(results, function(node) {
+          variable.options = _.map(results, function(node) {
             return { text: node.text, value: node.text };
           });
 
-          if (templateParam.includeAll) {
+          if (variable.includeAll) {
             var allExpr = '{';
-            _.each(templateParam.options, function(option) {
+            _.each(variable.options, function(option) {
               allExpr += option.text + ',';
             });
             allExpr = allExpr.substring(0, allExpr.length - 1) + '}';
-            templateParam.options.unshift({text: 'All', value: allExpr});
+            variable.options.unshift({text: 'All', value: allExpr});
           }
 
           // if parameter has current value
           // if it exists in options array keep value
-          if (templateParam.current) {
-            var currentExists = _.findWhere(templateParam.options, { value: templateParam.current.value });
+          if (variable.current) {
+            var currentExists = _.findWhere(variable.options, { value: variable.current.value });
             if (currentExists) {
-              return self.filterOptionSelected(templateParam, templateParam.current, true);
+              return self.setVariableValue(variable, variable.current, true);
             }
           }
 
-          return self.filterOptionSelected(templateParam, templateParam.options[0], true);
+          return self.setVariableValue(variable, variable.options[0], true);
         });
     };
 

+ 45 - 0
src/test/specs/templateValuesSrv-specs.js

@@ -0,0 +1,45 @@
+define([
+  'mocks/dashboard-mock',
+  'lodash',
+  'services/templateValuesSrv'
+], function(dashboardMock) {
+  'use strict';
+
+  describe('templateValuesSrv', function() {
+    var _templateValuesSrv;
+    var _dashboard;
+
+    beforeEach(module('grafana.services'));
+    beforeEach(module(function($provide) {
+      $provide.value('datasourceSrv', {});
+      $provide.value('templateSrv', {
+        updateTemplateData: function() {}
+      });
+      _dashboard = dashboardMock.create();
+    }));
+
+    beforeEach(inject(function(templateValuesSrv) {
+      _templateValuesSrv = templateValuesSrv;
+    }));
+
+    describe('update time period variable options', function() {
+      var variable = {
+        type: 'time period',
+        query: 'auto,1s,2h,5h,1d',
+        name: 'test'
+      };
+
+      beforeEach(function() {
+        _templateValuesSrv.updateOptions(variable);
+      });
+
+      it('should update options array', function() {
+        expect(variable.options.length).to.be(5);
+        expect(variable.options[1].text).to.be('1s');
+        expect(variable.options[1].value).to.be('1s');
+      });
+    });
+
+  });
+
+});

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

@@ -127,6 +127,7 @@ require([
     'specs/seriesOverridesCtrl-specs',
     'specs/timeSrv-specs',
     'specs/templateSrv-specs',
+    'specs/templateValuesSrv-specs',
     'specs/kbn-format-specs',
     'specs/dashboardSrv-specs',
     'specs/dashboardViewStateSrv-specs',