Browse Source

feat(influxdb editor): more progress

Torkel Ödegaard 10 năm trước cách đây
mục cha
commit
f053b41645

+ 5 - 8
public/app/plugins/datasource/influxdb/partials/query.editor.html

@@ -65,22 +65,20 @@
 
 
 	<div ng-hide="target.rawQuery">
 	<div ng-hide="target.rawQuery">
 
 
-		<div class="tight-form" ng-repeat="parts in select">
+		<div class="tight-form" ng-repeat="parts in selectParts">
 			<ul class="tight-form-list">
 			<ul class="tight-form-list">
 				<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
 				<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
 					<span ng-show="$index === 0">SELECT</span>
 					<span ng-show="$index === 0">SELECT</span>
 				</li>
 				</li>
-				<li ng-repeat="func in parts">
-					<span influx-query-part-editor class="tight-form-item tight-form-func">
-					</span>
+				<li ng-repeat="part in parts">
+					<influx-query-part-editor part="part" class="tight-form-item tight-form-func"></influx-query-part-editor>
 				</li>
 				</li>
 			</ul>
 			</ul>
-
 			<ul class="tight-form-list pull-right">
 			<ul class="tight-form-list pull-right">
 				<li class="tight-form-item last" ng-show="$index === 0">
 				<li class="tight-form-item last" ng-show="$index === 0">
 					<a class="pointer" ng-click="addSelect()"><i class="fa fa-plus"></i></a>
 					<a class="pointer" ng-click="addSelect()"><i class="fa fa-plus"></i></a>
 				</li>
 				</li>
-				<li class="tight-form-item last" ng-show="target.fields.length > 1">
+				<li class="tight-form-item last" ng-show="target.select.length > 1">
 					<a class="pointer" ng-click="removeSelect($index)"><i class="fa fa-minus"></i></a>
 					<a class="pointer" ng-click="removeSelect($index)"><i class="fa fa-minus"></i></a>
 				</li>
 				</li>
 			</ul>
 			</ul>
@@ -93,8 +91,7 @@
 					<span ng-show="$index === 0">GROUP BY</span>
 					<span ng-show="$index === 0">GROUP BY</span>
 				</li>
 				</li>
 				<li ng-if="groupBy.type === 'time'">
 				<li ng-if="groupBy.type === 'time'">
-					<span influx-query-part-editor class="tight-form-item tight-form-func">
-					</span>
+					<influx-query-part-editor part="groupByParts" class="tight-form-item tight-form-func"></influx-query-part-editor>
 				</li>
 				</li>
 				<!-- <li class="dropdown" ng&#45;if="groupBy.type === 'time'"> -->
 				<!-- <li class="dropdown" ng&#45;if="groupBy.type === 'time'"> -->
 					<!-- 	<a class="tight&#45;form&#45;item pointer" data&#45;toggle="dropdown" bs&#45;tooltip="'Insert missing values, important when stacking'" data&#45;placement="right"> -->
 					<!-- 	<a class="tight&#45;form&#45;item pointer" data&#45;toggle="dropdown" bs&#45;tooltip="'Insert missing values, important when stacking'" data&#45;placement="right"> -->

+ 6 - 0
public/app/plugins/datasource/influxdb/partials/query_part.html

@@ -0,0 +1,6 @@
+<div class="tight-form-func-controls">
+	<span class="pointer fa fa-question-circle"></span>
+	<span class="pointer fa fa-remove" ></span>
+</div>
+
+<a ng-click="toggleControls()">{{part.def.name}}</a><span>(</span><span class="query-part-parameters"></span><span>)</span>

+ 22 - 20
public/app/plugins/datasource/influxdb/query_ctrl.js

@@ -10,7 +10,7 @@ function (angular, _, InfluxQueryBuilder, queryPart) {
 
 
   var module = angular.module('grafana.controllers');
   var module = angular.module('grafana.controllers');
 
 
-  module.controller('InfluxQueryCtrl', function($scope, $timeout, $sce, templateSrv, $q, uiSegmentSrv) {
+  module.controller('InfluxQueryCtrl', function($scope, templateSrv, $q, uiSegmentSrv) {
 
 
     $scope.init = function() {
     $scope.init = function() {
       if (!$scope.target) { return; }
       if (!$scope.target) { return; }
@@ -19,25 +19,14 @@ function (angular, _, InfluxQueryBuilder, queryPart) {
       target.tags = target.tags || [];
       target.tags = target.tags || [];
       target.groupBy = target.groupBy || [{type: 'time', interval: 'auto'}];
       target.groupBy = target.groupBy || [{type: 'time', interval: 'auto'}];
       target.fields = target.fields || [{name: 'value'}];
       target.fields = target.fields || [{name: 'value'}];
-      target.select = target.select || [[{type: 'field', params: ['value']}]];
-      target.select[0] = [
-        {type: 'field', params: ['value']},
-        {type: 'mean', params: []},
-        {type: 'derivate', params: ['10s']},
-        {type: 'math', params: ['/ 100']},
-        {type: 'alias', params: ['google']},
-      ];
+      target.select = target.select || [[
+        {name: 'field', params: ['value']},
+        {name: 'mean', params: []},
+      ]];
 
 
-      $scope.select = _.map(target.select, function(parts) {
-        return _.map(parts, function(part) {
-          var partModel = queryPart.create(part.type);
-          partModel.params = part.params;
-          partModel.updateText();
-          return partModel;
-        });
-      });
+      $scope.updateSelectParts();
 
 
-      $scope.func = queryPart.create('time', { withDefaultParams: true });
+      $scope.groupByParts = queryPart.create({name: 'time', params:['$interval']});
 
 
       $scope.queryBuilder = new InfluxQueryBuilder(target);
       $scope.queryBuilder = new InfluxQueryBuilder(target);
 
 
@@ -89,14 +78,27 @@ function (angular, _, InfluxQueryBuilder, queryPart) {
     };
     };
 
 
     $scope.addSelect = function() {
     $scope.addSelect = function() {
-      $scope.target.fields.push({name: "select field", func: 'mean'});
+      $scope.target.select.push([
+        {name: 'field', params: ['value']},
+        {name: 'mean', params: []},
+      ]);
+      $scope.updateSelectParts();
     };
     };
 
 
     $scope.removeSelect = function(index) {
     $scope.removeSelect = function(index) {
-      $scope.target.fields.splice(index, 1);
+      $scope.target.select.splice(index, 1);
+      $scope.updateSelectParts();
       $scope.get_data();
       $scope.get_data();
     };
     };
 
 
+    $scope.updateSelectParts = function() {
+      $scope.selectParts = _.map($scope.target.select, function(parts) {
+        return _.map(parts, function(part) {
+          return queryPart.create(part);
+        });
+      });
+    };
+
     $scope.changeFunction = function(func) {
     $scope.changeFunction = function(func) {
       $scope.target.function = func;
       $scope.target.function = func;
       $scope.$parent.get_data();
       $scope.$parent.get_data();

+ 0 - 168
public/app/plugins/datasource/influxdb/query_part.js

@@ -1,168 +0,0 @@
-define([
-  'lodash',
-  'jquery'
-],
-function (_, $) {
-  'use strict';
-
-  var index = [];
-  var categories = {
-    Combine: [],
-    Transform: [],
-    Calculate: [],
-    Filter: [],
-    Special: []
-  };
-
-  function addFuncDef(funcDef) {
-    funcDef.params = funcDef.params || [];
-    funcDef.defaultParams = funcDef.defaultParams || [];
-
-    if (funcDef.category) {
-      funcDef.category.push(funcDef);
-    }
-    index[funcDef.name] = funcDef;
-    index[funcDef.shortName || funcDef.name] = funcDef;
-  }
-
-  addFuncDef({
-    name: 'field',
-    category: categories.Transform,
-    params: [{type: 'field'}],
-    defaultParams: ['value'],
-  });
-
-  addFuncDef({
-    name: 'mean',
-    category: categories.Transform,
-    params: [],
-    defaultParams: [],
-  });
-
-  addFuncDef({
-    name: 'derivate',
-    category: categories.Transform,
-    params: [{ name: "rate", type: "interval", options: ['1s', '10s', '1m', '5min', '10m', '15m', '1h'] }],
-    defaultParams: ['10s'],
-  });
-
-  addFuncDef({
-    name: 'time',
-    category: categories.Transform,
-    params: [{ name: "rate", type: "interval", options: ['$interval', '1s', '10s', '1m', '5min', '10m', '15m', '1h'] }],
-    defaultParams: ['$interval'],
-  });
-
-  addFuncDef({
-    name: 'math',
-    category: categories.Transform,
-    params: [{ name: "expr", type: "string"}],
-    defaultParams: [' / 100'],
-  });
-
-  addFuncDef({
-    name: 'alias',
-    category: categories.Transform,
-    params: [{ name: "name", type: "string"}],
-    defaultParams: ['alias'],
-  });
-
-  _.each(categories, function(funcList, catName) {
-    categories[catName] = _.sortBy(funcList, 'name');
-  });
-
-  function FuncInstance(funcDef, options) {
-    this.def = funcDef;
-    this.params = [];
-
-    if (options && options.withDefaultParams) {
-      this.params = funcDef.defaultParams.slice(0);
-    }
-
-    this.updateText();
-  }
-
-  FuncInstance.prototype.render = function(metricExp) {
-    var str = this.def.name + '(';
-    var parameters = _.map(this.params, function(value, index) {
-
-      var paramType = this.def.params[index].type;
-      if (paramType === 'int' || paramType === 'value_or_series' || paramType === 'boolean') {
-        return value;
-      }
-      else if (paramType === 'int_or_interval' && $.isNumeric(value)) {
-        return value;
-      }
-
-      return "'" + value + "'";
-
-    }, this);
-
-    if (metricExp) {
-      parameters.unshift(metricExp);
-    }
-
-    return str + parameters.join(', ') + ')';
-  };
-
-  FuncInstance.prototype._hasMultipleParamsInString = function(strValue, index) {
-    if (strValue.indexOf(',') === -1) {
-      return false;
-    }
-
-    return this.def.params[index + 1] && this.def.params[index + 1].optional;
-  };
-
-  FuncInstance.prototype.updateParam = function(strValue, index) {
-    // handle optional parameters
-    // if string contains ',' and next param is optional, split and update both
-    if (this._hasMultipleParamsInString(strValue, index)) {
-      _.each(strValue.split(','), function(partVal, idx) {
-        this.updateParam(partVal.trim(), idx);
-      }, this);
-      return;
-    }
-
-    if (strValue === '' && this.def.params[index].optional) {
-      this.params.splice(index, 1);
-    }
-    else {
-      this.params[index] = strValue;
-    }
-
-    this.updateText();
-  };
-
-  FuncInstance.prototype.updateText = function () {
-    if (this.params.length === 0) {
-      this.text = this.def.name + '()';
-      return;
-    }
-
-    var text = this.def.name + '(';
-    text += this.params.join(', ');
-    text += ')';
-    this.text = text;
-  };
-
-  return {
-    create: function(funcDef, options) {
-      if (_.isString(funcDef)) {
-        if (!index[funcDef]) {
-          throw { message: 'Method not found ' + name };
-        }
-        funcDef = index[funcDef];
-      }
-      return new FuncInstance(funcDef, options);
-    },
-
-    getFuncDef: function(name) {
-      return index[name];
-    },
-
-    getCategories: function() {
-      return categories;
-    }
-  };
-
-});

+ 165 - 0
public/app/plugins/datasource/influxdb/query_part.ts

@@ -0,0 +1,165 @@
+///<reference path="../../../headers/common.d.ts" />
+
+import _ = require('lodash');
+
+var index = [];
+var categories = {
+  Combine: [],
+  Transform: [],
+  Calculate: [],
+  Filter: [],
+  Special: []
+};
+
+class QueryPartDef {
+  name: string;
+  params: any[];
+  defaultParams: any[];
+
+  constructor(options: any) {
+    this.name = options.name;
+    this.params = options.params;
+    this.defaultParams = options.defaultParams;
+  }
+
+  static register(options: any) {
+    index[options.name] = new QueryPartDef(options);
+  }
+}
+
+QueryPartDef.register({
+  name: 'field',
+  category: categories.Transform,
+  params: [{type: 'field'}],
+  defaultParams: ['value'],
+});
+
+QueryPartDef.register({
+  name: 'mean',
+  category: categories.Transform,
+  params: [],
+  defaultParams: [],
+});
+
+QueryPartDef.register({
+  name: 'derivate',
+  category: categories.Transform,
+  params: [{ name: "rate", type: "interval", options: ['1s', '10s', '1m', '5min', '10m', '15m', '1h'] }],
+  defaultParams: ['10s'],
+});
+
+QueryPartDef.register({
+  name: 'time',
+  category: categories.Transform,
+  params: [{ name: "rate", type: "interval", options: ['$interval', '1s', '10s', '1m', '5min', '10m', '15m', '1h'] }],
+  defaultParams: ['$interval'],
+});
+
+QueryPartDef.register({
+  name: 'math',
+  category: categories.Transform,
+  params: [{ name: "expr", type: "string"}],
+  defaultParams: [' / 100'],
+});
+
+QueryPartDef.register({
+  name: 'alias',
+  category: categories.Transform,
+  params: [{ name: "name", type: "string"}],
+  defaultParams: ['alias'],
+});
+
+class QueryPart {
+  part: any;
+  def: QueryPartDef;
+  params: any[];
+  text: string;
+
+  constructor(part: any) {
+    this.part = part;
+    this.def = index[part.name];
+    if (!this.def) {
+      throw {message: 'Could not find query part ' + part.name};
+    }
+
+    this.params = part.params || _.clone(this.def.defaultParams);
+  }
+
+  render(innerExpr: string) {
+    var str = this.def.name + '(';
+    var parameters = _.map(this.params, (value, index) => {
+
+      var paramType = this.def.params[index].type;
+      if (paramType === 'int' || paramType === 'value_or_series' || paramType === 'boolean') {
+        return value;
+      }
+      else if (paramType === 'int_or_interval' && _.isNumber(value)) {
+        return value;
+      }
+
+      return "'" + value + "'";
+
+    });
+
+    if (innerExpr) {
+      parameters.unshift(innerExpr);
+    }
+
+    return str + parameters.join(', ') + ')';
+  }
+
+  hasMultipleParamsInString (strValue, index) {
+    if (strValue.indexOf(',') === -1) {
+      return false;
+    }
+
+    return this.def.params[index + 1] && this.def.params[index + 1].optional;
+  }
+
+  updateParam (strValue, index) {
+    // handle optional parameters
+    // if string contains ',' and next param is optional, split and update both
+    if (this.hasMultipleParamsInString(strValue, index)) {
+      _.each(strValue.split(','), function(partVal: string, idx) {
+        this.updateParam(partVal.trim(), idx);
+      }, this);
+      return;
+    }
+
+    if (strValue === '' && this.def.params[index].optional) {
+      this.params.splice(index, 1);
+    }
+    else {
+      this.params[index] = strValue;
+    }
+
+    this.part.params = this.params;
+    this.updateText();
+  }
+
+  updateText() {
+    if (this.params.length === 0) {
+      this.text = this.def.name + '()';
+      return;
+    }
+
+    var text = this.def.name + '(';
+    text += this.params.join(', ');
+    text += ')';
+    this.text = text;
+  }
+}
+
+export = {
+  create: function(part): any {
+    return new QueryPart(part);
+  },
+
+  getFuncDef: function(name) {
+    return index[name];
+  },
+
+  getCategories: function() {
+    return categories;
+  }
+};

+ 27 - 112
public/app/plugins/datasource/influxdb/query_part_editor.js

@@ -10,33 +10,26 @@ function (angular, _, $) {
     .module('grafana.directives')
     .module('grafana.directives')
     .directive('influxQueryPartEditor', function($compile, templateSrv) {
     .directive('influxQueryPartEditor', function($compile, templateSrv) {
 
 
-      var funcSpanTemplate = '<a ng-click="">{{func.def.name}}</a><span>(</span>';
       var paramTemplate = '<input type="text" style="display:none"' +
       var paramTemplate = '<input type="text" style="display:none"' +
                           ' class="input-mini tight-form-func-param"></input>';
                           ' class="input-mini tight-form-func-param"></input>';
-
-      var funcControlsTemplate =
-         '<div class="tight-form-func-controls">' +
-           '<span class="pointer fa fa-question-circle"></span>' +
-           '<span class="pointer fa fa-remove" ></span>' +
-         '</div>';
-
       return {
       return {
-        restrict: 'A',
+        restrict: 'E',
+        templateUrl: 'app/plugins/datasource/influxdb/partials/query_part.html',
+        scope: {
+          part: "="
+        },
         link: function postLink($scope, elem) {
         link: function postLink($scope, elem) {
-          var $funcLink = $(funcSpanTemplate);
-          var $funcControls = $(funcControlsTemplate);
-          var func = $scope.func;
-          var funcDef = func.def;
-          var scheduledRelink = false;
-          var paramCountAtLink = 0;
+          var part = $scope.part;
+          var partDef = part.def;
+          var $paramsContainer = elem.find('.query-part-parameters');
+          var $controlsContainer = elem.find('.tight-form-func-controls');
 
 
           function clickFuncParam(paramIndex) {
           function clickFuncParam(paramIndex) {
             /*jshint validthis:true */
             /*jshint validthis:true */
-
             var $link = $(this);
             var $link = $(this);
             var $input = $link.next();
             var $input = $link.next();
 
 
-            $input.val(func.params[paramIndex]);
+            $input.val(part.params[paramIndex]);
             $input.css('width', ($link.width() + 16) + 'px');
             $input.css('width', ($link.width() + 16) + 'px');
 
 
             $link.hide();
             $link.hide();
@@ -51,32 +44,16 @@ function (angular, _, $) {
             }
             }
           }
           }
 
 
-          function scheduledRelinkIfNeeded() {
-            if (paramCountAtLink === func.params.length) {
-              return;
-            }
-
-            if (!scheduledRelink) {
-              scheduledRelink = true;
-              setTimeout(function() {
-                relink();
-                scheduledRelink = false;
-              }, 200);
-            }
-          }
-
           function inputBlur(paramIndex) {
           function inputBlur(paramIndex) {
             /*jshint validthis:true */
             /*jshint validthis:true */
             var $input = $(this);
             var $input = $(this);
             var $link = $input.prev();
             var $link = $input.prev();
             var newValue = $input.val();
             var newValue = $input.val();
 
 
-            if (newValue !== '' || func.def.params[paramIndex].optional) {
+            if (newValue !== '' || part.def.params[paramIndex].optional) {
               $link.html(templateSrv.highlightVariablesAsHtml(newValue));
               $link.html(templateSrv.highlightVariablesAsHtml(newValue));
 
 
-              func.updateParam($input.val(), paramIndex);
-              scheduledRelinkIfNeeded();
-
+              part.updateParam($input.val(), paramIndex);
               $scope.$apply($scope.targetChanged);
               $scope.$apply($scope.targetChanged);
             }
             }
 
 
@@ -99,8 +76,8 @@ function (angular, _, $) {
           function addTypeahead($input, paramIndex) {
           function addTypeahead($input, paramIndex) {
             $input.attr('data-provide', 'typeahead');
             $input.attr('data-provide', 'typeahead');
 
 
-            var options = funcDef.params[paramIndex].options;
-            if (funcDef.params[paramIndex].type === 'int') {
+            var options = partDef.params[paramIndex].options;
+            if (partDef.params[paramIndex].type === 'int') {
               options = _.map(options, function(val) { return val.toString(); });
               options = _.map(options, function(val) { return val.toString(); });
             }
             }
 
 
@@ -123,114 +100,52 @@ function (angular, _, $) {
             };
             };
           }
           }
 
 
-          function toggleFuncControls() {
+          $scope.toggleControls = function() {
             var targetDiv = elem.closest('.tight-form');
             var targetDiv = elem.closest('.tight-form');
 
 
             if (elem.hasClass('show-function-controls')) {
             if (elem.hasClass('show-function-controls')) {
               elem.removeClass('show-function-controls');
               elem.removeClass('show-function-controls');
               targetDiv.removeClass('has-open-function');
               targetDiv.removeClass('has-open-function');
-              $funcControls.hide();
+              $controlsContainer.hide();
               return;
               return;
             }
             }
 
 
             elem.addClass('show-function-controls');
             elem.addClass('show-function-controls');
             targetDiv.addClass('has-open-function');
             targetDiv.addClass('has-open-function');
-
-            $funcControls.show();
-          }
+            $controlsContainer.show();
+          };
 
 
           function addElementsAndCompile() {
           function addElementsAndCompile() {
-            $funcControls.appendTo(elem);
-            $funcLink.appendTo(elem);
-
-            _.each(funcDef.params, function(param, index) {
-              if (param.optional && func.params.length <= index) {
+            _.each(partDef.params, function(param, index) {
+              if (param.optional && part.params.length <= index) {
                 return;
                 return;
               }
               }
 
 
               if (index > 0) {
               if (index > 0) {
-                $('<span>, </span>').appendTo(elem);
+                $('<span>, </span>').appendTo($paramsContainer);
               }
               }
 
 
-              var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
-              var $paramLink = $('<a ng-click="" class="graphite-func-param-link">' + paramValue + '</a>');
+              var paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
+              var $paramLink = $('<a class="graphite-func-param-link pointer">' + paramValue + '</a>');
               var $input = $(paramTemplate);
               var $input = $(paramTemplate);
 
 
-              paramCountAtLink++;
-
-              $paramLink.appendTo(elem);
-              $input.appendTo(elem);
+              $paramLink.appendTo($paramsContainer);
+              $input.appendTo($paramsContainer);
 
 
               $input.blur(_.partial(inputBlur, index));
               $input.blur(_.partial(inputBlur, index));
               $input.keyup(inputKeyDown);
               $input.keyup(inputKeyDown);
               $input.keypress(_.partial(inputKeyPress, index));
               $input.keypress(_.partial(inputKeyPress, index));
               $paramLink.click(_.partial(clickFuncParam, index));
               $paramLink.click(_.partial(clickFuncParam, index));
 
 
-              if (funcDef.params[index].options) {
+              if (partDef.params[index].options) {
                 addTypeahead($input, index);
                 addTypeahead($input, index);
               }
               }
-
-            });
-
-            $('<span>)</span>').appendTo(elem);
-
-            $compile(elem.contents())($scope);
-          }
-
-          function ifJustAddedFocusFistParam() {
-            if ($scope.func.added) {
-              $scope.func.added = false;
-              setTimeout(function() {
-                elem.find('.graphite-func-param-link').first().click();
-              }, 10);
-            }
-          }
-
-          function registerFuncControlsToggle() {
-            $funcLink.click(toggleFuncControls);
-          }
-
-          function registerFuncControlsActions() {
-            $funcControls.click(function(e) {
-              var $target = $(e.target);
-              if ($target.hasClass('fa-remove')) {
-                toggleFuncControls();
-                $scope.$apply(function() {
-                  $scope.removeFunction($scope.func);
-                });
-                return;
-              }
-
-              if ($target.hasClass('fa-arrow-left')) {
-                $scope.$apply(function() {
-                  _.move($scope.functions, $scope.$index, $scope.$index - 1);
-                  $scope.targetChanged();
-                });
-                return;
-              }
-
-              if ($target.hasClass('fa-arrow-right')) {
-                $scope.$apply(function() {
-                  _.move($scope.functions, $scope.$index, $scope.$index + 1);
-                  $scope.targetChanged();
-                });
-                return;
-              }
-
-              if ($target.hasClass('fa-question-circle')) {
-                window.open("http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions." + funcDef.name,'_blank');
-                return;
-              }
             });
             });
           }
           }
 
 
           function relink() {
           function relink() {
-            elem.children().remove();
-
+            $paramsContainer.empty();
             addElementsAndCompile();
             addElementsAndCompile();
-            ifJustAddedFocusFistParam();
-            registerFuncControlsToggle();
-            registerFuncControlsActions();
           }
           }
 
 
           relink();
           relink();