Explorar o código

support for loading function definitions from graphite

Dan Cech %!s(int64=8) %!d(string=hai) anos
pai
achega
3a4e05133e

+ 0 - 1
.editorconfig

@@ -8,7 +8,6 @@ charset = utf-8
 trim_trailing_whitespace = true
 insert_final_newline = true
 max_line_length = 120
-insert_final_newline = true
 
 [*.go]
 indent_style = tab

+ 2 - 1
package.json

@@ -135,7 +135,7 @@
     "clipboard": "^1.7.1",
     "d3": "^4.11.0",
     "d3-scale-chromatic": "^1.1.1",
-    "eventemitter3": "^2.0.2",
+    "eventemitter3": "^2.0.3",
     "file-saver": "^1.3.3",
     "jquery": "^3.2.1",
     "lodash": "^4.17.4",
@@ -154,6 +154,7 @@
     "react-select": "^1.1.0",
     "react-sizeme": "^2.3.6",
     "remarkable": "^1.7.1",
+    "rst2html": "github:thoward/rst2html#d6e2f21",
     "rxjs": "^5.4.3",
     "tether": "^1.4.0",
     "tether-drop": "https://github.com/torkelo/drop",

+ 69 - 71
public/app/plugins/datasource/graphite/add_graphite_func.js

@@ -2,13 +2,10 @@ define([
   'angular',
   'lodash',
   'jquery',
-  './gfunc',
 ],
-function (angular, _, $, gfunc) {
+function (angular, _, $) {
   'use strict';
 
-  gfunc = gfunc.default;
-
   angular
     .module('grafana.directives')
     .directive('graphiteAddFunc', function($compile) {
@@ -23,91 +20,92 @@ function (angular, _, $, gfunc) {
       return {
         link: function($scope, elem) {
           var ctrl = $scope.ctrl;
-          var graphiteVersion = ctrl.datasource.graphiteVersion;
-          var categories = gfunc.getCategories(graphiteVersion);
-          var allFunctions = getAllFunctionNames(categories);
-
-          $scope.functionMenu = createFunctionDropDownMenu(categories);
 
           var $input = $(inputTemplate);
           var $button = $(buttonTemplate);
+
           $input.appendTo(elem);
           $button.appendTo(elem);
 
-          $input.attr('data-provide', 'typeahead');
-          $input.typeahead({
-            source: allFunctions,
-            minLength: 1,
-            items: 10,
-            updater: function (value) {
-              var funcDef = gfunc.getFuncDef(value);
-              if (!funcDef) {
-                // try find close match
-                value = value.toLowerCase();
-                funcDef = _.find(allFunctions, function(funcName) {
-                  return funcName.toLowerCase().indexOf(value) === 0;
+          ctrl.datasource.getFuncDefs().then(function(funcDefs) {
+            var allFunctions = _.map(funcDefs, 'name').sort();
+
+            $scope.functionMenu = createFunctionDropDownMenu(funcDefs);
+
+            $input.attr('data-provide', 'typeahead');
+            $input.typeahead({
+              source: allFunctions,
+              minLength: 1,
+              items: 10,
+              updater: function (value) {
+                var funcDef = ctrl.datasource.getFuncDef(value);
+                if (!funcDef) {
+                  // try find close match
+                  value = value.toLowerCase();
+                  funcDef = _.find(allFunctions, function(funcName) {
+                    return funcName.toLowerCase().indexOf(value) === 0;
+                  });
+
+                  if (!funcDef) { return; }
+                }
+
+                $scope.$apply(function() {
+                  ctrl.addFunction(funcDef);
                 });
 
-                if (!funcDef) { return; }
+                $input.trigger('blur');
+                return '';
               }
-
-              $scope.$apply(function() {
-                ctrl.addFunction(funcDef);
-              });
-
-              $input.trigger('blur');
-              return '';
-            }
-          });
-
-          $button.click(function() {
-            $button.hide();
-            $input.show();
-            $input.focus();
-          });
-
-          $input.keyup(function() {
-            elem.toggleClass('open', $input.val() === '');
+            });
+
+            $button.click(function() {
+              $button.hide();
+              $input.show();
+              $input.focus();
+            });
+
+            $input.keyup(function() {
+              elem.toggleClass('open', $input.val() === '');
+            });
+
+            $input.blur(function() {
+              // clicking the function dropdown menu wont
+              // work if you remove class at once
+              setTimeout(function() {
+                $input.val('');
+                $input.hide();
+                $button.show();
+                elem.removeClass('open');
+              }, 200);
+            });
+
+            $compile(elem.contents())($scope);
           });
-
-          $input.blur(function() {
-            // clicking the function dropdown menu wont
-            // work if you remove class at once
-            setTimeout(function() {
-              $input.val('');
-              $input.hide();
-              $button.show();
-              elem.removeClass('open');
-            }, 200);
-          });
-
-          $compile(elem.contents())($scope);
         }
       };
     });
 
-  function getAllFunctionNames(categories) {
-    return _.reduce(categories, function(list, category) {
-      _.each(category, function(func) {
-        list.push(func.name);
-      });
-      return list;
-    }, []);
-  }
-
-  function createFunctionDropDownMenu(categories) {
-    return _.map(categories, function(list, category) {
-      var submenu = _.map(list, function(value) {
-        return {
-          text: value.name,
-          click: "ctrl.addFunction('" + value.name + "')",
-        };
+  function createFunctionDropDownMenu(funcDefs) {
+    var categories = {};
+
+    _.forEach(funcDefs, function(funcDef) {
+      if (!funcDef.category) {
+        return;
+      }
+      if (!categories[funcDef.category]) {
+        categories[funcDef.category] = [];
+      }
+      categories[funcDef.category].push({
+        text: funcDef.name,
+        click: "ctrl.addFunction('" + funcDef.name + "')",
       });
+    });
 
+    return _.sortBy(_.map(categories, function(submenu, category) {
       return {
         text: category,
-        submenu: submenu
+        submenu: _.sortBy(submenu, 'text')
       };
-    });
+    }), 'text');
   }
 });

+ 125 - 0
public/app/plugins/datasource/graphite/datasource.ts

@@ -1,6 +1,7 @@
 import _ from 'lodash';
 import * as dateMath from 'app/core/utils/datemath';
 import { isVersionGtOrEq, SemVersion } from 'app/core/utils/version';
+import gfunc from './gfunc';
 
 /** @ngInject */
 export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv) {
@@ -12,6 +13,7 @@ export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv
   this.cacheTimeout = instanceSettings.cacheTimeout;
   this.withCredentials = instanceSettings.withCredentials;
   this.render_method = instanceSettings.render_method || 'POST';
+  this.funcDefs = null;
 
   this.getQueryOptionsInfo = function() {
     return {
@@ -347,6 +349,125 @@ export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv
       });
   };
 
+  this.createFuncInstance = function(funcDef, options?) {
+    return gfunc.createFuncInstance(funcDef, options, this.funcDefs);
+  };
+
+  this.getFuncDef = function(name) {
+    return gfunc.getFuncDef(name, this.funcDefs);
+  };
+
+  this.getFuncDefs = function() {
+    let self = this;
+
+    if (self.funcDefs !== null) {
+      return Promise.resolve(self.funcDefs);
+    }
+
+    if (!supportsFunctionIndex(self.graphiteVersion)) {
+      self.funcDefs = gfunc.getFuncDefs(self.graphiteVersion);
+      return Promise.resolve(self.funcDefs);
+    }
+
+    let httpOptions = {
+      method: 'GET',
+      url: '/functions',
+    };
+
+    return self
+      .doGraphiteRequest(httpOptions)
+      .then(results => {
+        if (results.status !== 200 || typeof results.data !== 'object') {
+          self.funcDefs = gfunc.getFuncDefs(self.graphiteVersion);
+          return Promise.resolve(self.funcDefs);
+        }
+
+        self.funcDefs = {};
+        _.forEach(results.data || {}, (funcDef, funcName) => {
+          // skip graphite graph functions
+          if (funcDef.group === 'Graph') {
+            return;
+          }
+
+          var func = {
+            name: funcDef.name,
+            description: funcDef.description,
+            category: funcDef.group,
+            params: [],
+            defaultParams: [],
+            fake: false,
+          };
+
+          // get rid of the first "seriesList" param
+          if (/^seriesLists?$/.test(_.get(funcDef, 'params[0].type', ''))) {
+            // handle functions that accept multiple seriesLists
+            // we leave the param in place but mark it optional, so users can add more series if they wish
+            if (funcDef.params[0].multiple) {
+              funcDef.params[0].required = false;
+              // otherwise chop off the first param, it'll be handled separately
+            } else {
+              funcDef.params.shift();
+            }
+            // tag function as fake
+          } else {
+            func.fake = true;
+          }
+
+          _.forEach(funcDef.params, rawParam => {
+            var param = {
+              name: rawParam.name,
+              type: 'string',
+              optional: !rawParam.required,
+              multiple: !!rawParam.multiple,
+              options: undefined,
+            };
+
+            if (rawParam.default !== undefined) {
+              func.defaultParams.push(_.toString(rawParam.default));
+            } else if (rawParam.suggestions) {
+              func.defaultParams.push(_.toString(rawParam.suggestions[0]));
+            } else {
+              func.defaultParams.push('');
+            }
+
+            if (rawParam.type === 'boolean') {
+              param.type = 'boolean';
+              param.options = ['true', 'false'];
+            } else if (rawParam.type === 'integer') {
+              param.type = 'int';
+            } else if (rawParam.type === 'float') {
+              param.type = 'float';
+            } else if (rawParam.type === 'node') {
+              param.type = 'node';
+              param.options = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'];
+            } else if (rawParam.type === 'nodeOrTag') {
+              param.type = 'node_or_tag';
+              param.options = ['name', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'];
+            } else if (rawParam.type === 'intOrInterval') {
+              param.type = 'int_or_interval';
+            } else if (rawParam.type === 'seriesList') {
+              param.type = 'value_or_series';
+            }
+
+            if (rawParam.options) {
+              param.options = _.map(rawParam.options, _.toString);
+            } else if (rawParam.suggestions) {
+              param.options = _.map(rawParam.suggestions, _.toString);
+            }
+
+            func.params.push(param);
+          });
+
+          self.funcDefs[funcName] = func;
+        });
+        return self.funcDefs;
+      })
+      .catch(err => {
+        self.funcDefs = gfunc.getFuncDefs(self.graphiteVersion);
+        return self.funcDefs;
+      });
+  };
+
   this.testDatasource = function() {
     return this.metricFindQuery('*').then(function() {
       return { status: 'success', message: 'Data source is working' };
@@ -440,3 +561,7 @@ export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv
 function supportsTags(version: string): boolean {
   return isVersionGtOrEq(version, '1.1');
 }
+
+function supportsFunctionIndex(version: string): boolean {
+  return isVersionGtOrEq(version, '1.1');
+}

+ 60 - 19
public/app/plugins/datasource/graphite/func_editor.js

@@ -2,8 +2,9 @@ define([
   'angular',
   'lodash',
   'jquery',
+  'rst2html',
 ],
-function (angular, _, $) {
+function (angular, _, $, rst2html) {
   'use strict';
 
   angular
@@ -12,7 +13,7 @@ function (angular, _, $) {
 
       var funcSpanTemplate = '<a ng-click="">{{func.def.name}}</a><span>(</span>';
       var paramTemplate = '<input type="text" style="display:none"' +
-                          ' class="input-mini tight-form-func-param"></input>';
+                          ' class="input-small tight-form-func-param"></input>';
 
       var funcControlsTemplate =
          '<div class="tight-form-func-controls">' +
@@ -29,7 +30,6 @@ function (angular, _, $) {
           var $funcControls = $(funcControlsTemplate);
           var ctrl = $scope.ctrl;
           var func = $scope.func;
-          var funcDef = func.def;
           var scheduledRelink = false;
           var paramCountAtLink = 0;
 
@@ -37,11 +37,12 @@ function (angular, _, $) {
             /*jshint validthis:true */
 
             var $link = $(this);
+            var $comma = $link.prev('.comma');
             var $input = $link.next();
 
             $input.val(func.params[paramIndex]);
-            $input.css('width', ($link.width() + 16) + 'px');
 
+            $comma.removeClass('last');
             $link.hide();
             $input.show();
             $input.focus();
@@ -68,22 +69,42 @@ function (angular, _, $) {
             }
           }
 
+          function paramDef(index) {
+            if (index < func.def.params.length) {
+              return func.def.params[index];
+            }
+            if (_.last(func.def.params).multiple) {
+              return _.assign({}, _.last(func.def.params), {optional: true});
+            }
+            return {};
+          }
+
           function inputBlur(paramIndex) {
             /*jshint validthis:true */
             var $input = $(this);
+            if ($input.data('typeahead') && $input.data('typeahead').shown) {
+              return;
+            }
+
             var $link = $input.prev();
+            var $comma = $link.prev('.comma');
             var newValue = $input.val();
 
-            if (newValue !== '' || func.def.params[paramIndex].optional) {
+            if (newValue !== '' || paramDef(paramIndex).optional) {
               $link.html(templateSrv.highlightVariablesAsHtml(newValue));
 
-              func.updateParam($input.val(), paramIndex);
+              func.updateParam(newValue, paramIndex);
               scheduledRelinkIfNeeded();
 
               $scope.$apply(function() {
                 ctrl.targetChanged();
               });
 
+              if ($link.hasClass('last') && newValue === '') {
+                $comma.addClass('last');
+              } else {
+                $link.removeClass('last');
+              }
               $input.hide();
               $link.show();
             }
@@ -104,8 +125,8 @@ function (angular, _, $) {
           function addTypeahead($input, paramIndex) {
             $input.attr('data-provide', 'typeahead');
 
-            var options = funcDef.params[paramIndex].options;
-            if (funcDef.params[paramIndex].type === 'int') {
+            var options = paramDef(paramIndex).options;
+            if (paramDef(paramIndex).type === 'int') {
               options = _.map(options, function(val) { return val.toString(); });
             }
 
@@ -148,18 +169,34 @@ function (angular, _, $) {
             $funcControls.appendTo(elem);
             $funcLink.appendTo(elem);
 
-            _.each(funcDef.params, function(param, index) {
-              if (param.optional && func.params.length <= index) {
-                return;
+            var defParams = _.clone(func.def.params);
+            var lastParam = _.last(func.def.params);
+
+            while (func.params.length >= defParams.length && lastParam && lastParam.multiple) {
+              defParams.push(_.assign({}, lastParam, {optional: true}));
+            }
+
+            _.each(defParams, function(param, index) {
+              if (param.optional && func.params.length < index) {
+                return false;
+              }
+
+              var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
+
+              var last = (index >= func.params.length - 1) && param.optional && !paramValue;
+              if (last && param.multiple) {
+                paramValue = '+';
               }
 
               if (index > 0) {
-                $('<span>, </span>').appendTo(elem);
+                $('<span class="comma' + (last ? ' last' : '') + '">, </span>').appendTo(elem);
               }
 
-              var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
-              var $paramLink = $('<a ng-click="" class="graphite-func-param-link">' + paramValue + '</a>');
+              var $paramLink = $(
+                '<a ng-click="" class="graphite-func-param-link' + (last ? ' last' : '') + '">'
+                + (paramValue || '&nbsp;') + '</a>');
               var $input = $(paramTemplate);
+              $input.attr('placeholder', param.name);
 
               paramCountAtLink++;
 
@@ -171,10 +208,9 @@ function (angular, _, $) {
               $input.keypress(_.partial(inputKeyPress, index));
               $paramLink.click(_.partial(clickFuncParam, index));
 
-              if (funcDef.params[index].options) {
+              if (param.options) {
                 addTypeahead($input, index);
               }
-
             });
 
             $('<span>)</span>').appendTo(elem);
@@ -182,7 +218,7 @@ function (angular, _, $) {
             $compile(elem.contents())($scope);
           }
 
-          function ifJustAddedFocusFistParam() {
+          function ifJustAddedFocusFirstParam() {
             if ($scope.func.added) {
               $scope.func.added = false;
               setTimeout(function() {
@@ -223,7 +259,12 @@ function (angular, _, $) {
               }
 
               if ($target.hasClass('fa-question-circle')) {
-                window.open("http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions." + funcDef.name,'_blank');
+                if (func.def.description) {
+                  alert(rst2html(func.def.description));
+                } else {
+                  window.open(
+                    "http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions." + func.def.name,'_blank');
+                }
                 return;
               }
             });
@@ -233,7 +274,7 @@ function (angular, _, $) {
             elem.children().remove();
 
             addElementsAndCompile();
-            ifJustAddedFocusFistParam();
+            ifJustAddedFocusFirstParam();
             registerFuncControlsToggle();
             registerFuncControlsActions();
           }

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 122 - 211
public/app/plugins/datasource/graphite/gfunc.ts


+ 6 - 5
public/app/plugins/datasource/graphite/graphite_query.ts

@@ -1,8 +1,8 @@
 import _ from 'lodash';
-import gfunc from './gfunc';
 import { Parser } from './parser';
 
 export default class GraphiteQuery {
+  datasource: any;
   target: any;
   functions: any[];
   segments: any[];
@@ -15,7 +15,8 @@ export default class GraphiteQuery {
   scopedVars: any;
 
   /** @ngInject */
-  constructor(target, templateSrv?, scopedVars?) {
+  constructor(datasource, target, templateSrv?, scopedVars?) {
+    this.datasource = datasource;
     this.target = target;
     this.parseTarget();
 
@@ -86,7 +87,7 @@ export default class GraphiteQuery {
 
     switch (astNode.type) {
       case 'function':
-        var innerFunc = gfunc.createFuncInstance(astNode.name, {
+        var innerFunc = this.datasource.createFuncInstance(astNode.name, {
           withDefaultParams: false,
         });
         _.each(astNode.params, param => {
@@ -133,7 +134,7 @@ export default class GraphiteQuery {
 
   moveAliasFuncLast() {
     var aliasFunc = _.find(this.functions, function(func) {
-      return func.def.name === 'alias' || func.def.name === 'aliasByNode' || func.def.name === 'aliasByMetric';
+      return func.def.name.startsWith('alias');
     });
 
     if (aliasFunc) {
@@ -143,7 +144,7 @@ export default class GraphiteQuery {
   }
 
   addFunctionParameter(func, value) {
-    if (func.params.length >= func.def.params.length) {
+    if (func.params.length >= func.def.params.length && !_.get(_.last(func.def.params), 'multiple', false)) {
       throw { message: 'too many parameters for function ' + func.def.name };
     }
     func.params.push(value);

+ 1 - 1
public/app/plugins/datasource/graphite/partials/query.editor.html

@@ -18,7 +18,7 @@
         <gf-form-dropdown model="tag.operator" lookup-text="false" allow-custom="false" label-mode="true" css-class="query-segment-operator"
           get-options="ctrl.getTagOperators()"
           on-change="ctrl.tagChanged(tag, $index)"
-					min-input-width="30">
+          min-input-width="30">
         </gf-form-dropdown>
         <gf-form-dropdown model="tag.value" lookup-text="false" allow-custom="false" label-mode="true" css-class="query-segment-value"
           get-options="ctrl.getTagValues(tag, $index, $query)"

+ 3 - 5
public/app/plugins/datasource/graphite/query_ctrl.ts

@@ -2,7 +2,6 @@ import './add_graphite_func';
 import './func_editor';
 
 import _ from 'lodash';
-import gfunc from './gfunc';
 import GraphiteQuery from './graphite_query';
 import { QueryCtrl } from 'app/plugins/sdk';
 import appEvents from 'app/core/app_events';
@@ -26,7 +25,7 @@ export class GraphiteQueryCtrl extends QueryCtrl {
 
     if (this.target) {
       this.target.target = this.target.target || '';
-      this.queryModel = new GraphiteQuery(this.target, templateSrv);
+      this.queryModel = new GraphiteQuery(this.datasource, this.target, templateSrv);
       this.buildSegments();
     }
 
@@ -242,7 +241,7 @@ export class GraphiteQueryCtrl extends QueryCtrl {
   }
 
   addFunction(funcDef) {
-    var newFunc = gfunc.createFuncInstance(funcDef, {
+    var newFunc = this.datasource.createFuncInstance(funcDef, {
       withDefaultParams: true,
     });
     newFunc.added = true;
@@ -268,8 +267,7 @@ export class GraphiteQueryCtrl extends QueryCtrl {
   }
 
   addSeriesByTagFunc(tag) {
-    let funcDef = gfunc.getFuncDef('seriesByTag');
-    let newFunc = gfunc.createFuncInstance(funcDef, {
+    let newFunc = this.datasource.createFuncInstance('seriesByTag', {
       withDefaultParams: false,
     });
     let tagParam = `${tag}=select tag value`;

+ 6 - 5
public/app/plugins/datasource/graphite/specs/gfunc.jest.ts

@@ -5,7 +5,8 @@ describe('when creating func instance from func names', function() {
     var func = gfunc.createFuncInstance('sumSeries');
     expect(func).toBeTruthy();
     expect(func.def.name).toEqual('sumSeries');
-    expect(func.def.params.length).toEqual(5);
+    expect(func.def.params.length).toEqual(1);
+    expect(func.def.params[0].multiple).toEqual(true);
     expect(func.def.defaultParams.length).toEqual(1);
   });
 
@@ -74,10 +75,10 @@ describe('when rendering func instance', function() {
   });
 });
 
-describe('when requesting function categories', function() {
-  it('should return function categories', function() {
-    var catIndex = gfunc.getCategories('1.0');
-    expect(catIndex.Special.length).toBeGreaterThan(8);
+describe('when requesting function definitions', function() {
+  it('should return function definitions', function() {
+    var funcIndex = gfunc.getFuncDefs('1.0');
+    expect(Object.keys(funcIndex).length).toBeGreaterThan(8);
   });
 });
 

+ 17 - 4
public/app/plugins/datasource/graphite/specs/query_ctrl_specs.ts

@@ -24,6 +24,9 @@ describe('GraphiteQueryCtrl', function() {
       ctx.scope = $rootScope.$new();
       ctx.target = { target: 'aliasByNode(scaleToSeconds(test.prod.*,1),2)' };
       ctx.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
+      ctx.datasource.getFuncDefs = sinon.stub().returns(ctx.$q.when(gfunc.getFuncDefs('1.0')));
+      ctx.datasource.getFuncDef = gfunc.getFuncDef;
+      ctx.datasource.createFuncInstance = gfunc.createFuncInstance;
       ctx.panelCtrl = { panel: {} };
       ctx.panelCtrl = {
         panel: {
@@ -180,7 +183,21 @@ describe('GraphiteQueryCtrl', function() {
       ctx.ctrl.target.target = 'scaleToSeconds(#A, 60)';
       ctx.ctrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([{ expandable: false }]));
       ctx.ctrl.parseTarget();
+    });
+
+    it('should add function params', function() {
+      expect(ctx.ctrl.queryModel.segments.length).to.be(1);
+      expect(ctx.ctrl.queryModel.segments[0].value).to.be('#A');
+
+      expect(ctx.ctrl.queryModel.functions[0].params.length).to.be(1);
+      expect(ctx.ctrl.queryModel.functions[0].params[0]).to.be(60);
+    });
+
+    it('target should remain the same', function() {
+      expect(ctx.ctrl.target.target).to.be('scaleToSeconds(#A, 60)');
+    });
 
+    it('targetFull should include nested queries', function() {
       ctx.ctrl.panelCtrl.panel.targets = [
         {
           target: 'nested.query.count',
@@ -189,13 +206,9 @@ describe('GraphiteQueryCtrl', function() {
       ];
 
       ctx.ctrl.updateModelTarget();
-    });
 
-    it('target should remain the same', function() {
       expect(ctx.ctrl.target.target).to.be('scaleToSeconds(#A, 60)');
-    });
 
-    it('targetFull should include nexted queries', function() {
       expect(ctx.ctrl.target.targetFull).to.be('scaleToSeconds(nested.query.count, 60)');
     });
   });

+ 1 - 0
public/sass/components/_query_editor.scss

@@ -130,6 +130,7 @@ input[type="text"].tight-form-func-param {
 }
 
 input[type="text"].tight-form-func-param {
+  font-size: 0.875rem;
   background: transparent;
   border: none;
   margin: 0;

+ 8 - 0
public/sass/components/_query_part.scss

@@ -6,4 +6,12 @@
     min-width: 100px;
     text-align: center;
   }
+
+  .last {
+    display: none;
+  }
+
+  &:hover .last {
+    display: inline;
+  }
 }

+ 180 - 3
yarn.lock

@@ -263,6 +263,10 @@ acorn-dynamic-import@^2.0.0:
   dependencies:
     acorn "^4.0.3"
 
+acorn-es7-plugin@^1.0.12:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/acorn-es7-plugin/-/acorn-es7-plugin-1.1.7.tgz#f2ee1f3228a90eead1245f9ab1922eb2e71d336b"
+
 acorn-globals@^4.0.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.1.0.tgz#ab716025dbe17c54d3ef81d32ece2b2d99fe2538"
@@ -279,7 +283,7 @@ acorn@^3.0.4:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
 
-acorn@^4.0.3:
+acorn@^4.0.0, acorn@^4.0.3:
   version "4.0.13"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
 
@@ -525,6 +529,10 @@ array-equal@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
 
+array-filter@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83"
+
 array-filter@~0.0.0:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec"
@@ -1512,6 +1520,10 @@ call-limit@~1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/call-limit/-/call-limit-1.1.0.tgz#6fd61b03f3da42a2cd0ec2b60f02bd0e71991fea"
 
+call-signature@0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/call-signature/-/call-signature-0.0.2.tgz#a84abc825a55ef4cb2b028bd74e205a65b9a4996"
+
 caller-path@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
@@ -2072,6 +2084,10 @@ core-js@^1.0.0:
   version "1.2.7"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
 
+core-js@^2.0.0:
+  version "2.5.3"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e"
+
 core-js@^2.2.0, core-js@^2.4.0, core-js@^2.5.0:
   version "2.5.1"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
@@ -2744,6 +2760,10 @@ di@^0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c"
 
+diff-match-patch@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.0.tgz#1cc3c83a490d67f95d91e39f6ad1f2e086b63048"
+
 diff@3.3.1:
   version "3.3.1"
   resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.1.tgz#aa8567a6eed03c531fc89d3f711cd0e5259dec75"
@@ -2891,6 +2911,10 @@ each-async@^1.0.0:
     onetime "^1.0.0"
     set-immediate-shim "^1.0.0"
 
+eastasianwidth@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.1.1.tgz#44d656de9da415694467335365fb3147b8572b7c"
+
 ecc-jsbn@~0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
@@ -2939,6 +2963,20 @@ emojis-list@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
 
+empower-core@^0.6.2:
+  version "0.6.2"
+  resolved "https://registry.yarnpkg.com/empower-core/-/empower-core-0.6.2.tgz#5adef566088e31fba80ba0a36df47d7094169144"
+  dependencies:
+    call-signature "0.0.2"
+    core-js "^2.0.0"
+
+empower@^1.2.3:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/empower/-/empower-1.2.3.tgz#6f0da73447f4edd838fec5c60313a88ba5cb852b"
+  dependencies:
+    core-js "^2.0.0"
+    empower-core "^0.6.2"
+
 encodeurl@~1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20"
@@ -3266,6 +3304,12 @@ esprima@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
 
+espurify@^1.6.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/espurify/-/espurify-1.7.0.tgz#1c5cf6cbccc32e6f639380bd4f991fab9ba9d226"
+  dependencies:
+    core-js "^2.0.0"
+
 esrecurse@^4.1.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163"
@@ -3300,7 +3344,7 @@ eventemitter3@1.x.x:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
 
-eventemitter3@^2.0.2:
+eventemitter3@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba"
 
@@ -7058,7 +7102,7 @@ object-is@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6"
 
-object-keys@^1.0.11, object-keys@^1.0.8:
+object-keys@^1.0.0, object-keys@^1.0.11, object-keys@^1.0.8:
   version "1.0.11"
   resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d"
 
@@ -7809,6 +7853,94 @@ postcss@^6.0.0, postcss@^6.0.1, postcss@^6.0.8:
     source-map "^0.6.1"
     supports-color "^4.4.0"
 
+power-assert-context-formatter@^1.0.7:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/power-assert-context-formatter/-/power-assert-context-formatter-1.1.1.tgz#edba352d3ed8a603114d667265acce60d689ccdf"
+  dependencies:
+    core-js "^2.0.0"
+    power-assert-context-traversal "^1.1.1"
+
+power-assert-context-reducer-ast@^1.0.7:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/power-assert-context-reducer-ast/-/power-assert-context-reducer-ast-1.1.2.tgz#484a99e26f4973ff8832e5c5cc756702e6094174"
+  dependencies:
+    acorn "^4.0.0"
+    acorn-es7-plugin "^1.0.12"
+    core-js "^2.0.0"
+    espurify "^1.6.0"
+    estraverse "^4.2.0"
+
+power-assert-context-traversal@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/power-assert-context-traversal/-/power-assert-context-traversal-1.1.1.tgz#88cabca0d13b6359f07d3d3e8afa699264577ed9"
+  dependencies:
+    core-js "^2.0.0"
+    estraverse "^4.1.0"
+
+power-assert-formatter@^1.3.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/power-assert-formatter/-/power-assert-formatter-1.4.1.tgz#5dc125ed50a3dfb1dda26c19347f3bf58ec2884a"
+  dependencies:
+    core-js "^2.0.0"
+    power-assert-context-formatter "^1.0.7"
+    power-assert-context-reducer-ast "^1.0.7"
+    power-assert-renderer-assertion "^1.0.7"
+    power-assert-renderer-comparison "^1.0.7"
+    power-assert-renderer-diagram "^1.0.7"
+    power-assert-renderer-file "^1.0.7"
+
+power-assert-renderer-assertion@^1.0.7:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/power-assert-renderer-assertion/-/power-assert-renderer-assertion-1.1.1.tgz#cbfc0e77e0086a8f96af3f1d8e67b9ee7e28ce98"
+  dependencies:
+    power-assert-renderer-base "^1.1.1"
+    power-assert-util-string-width "^1.1.1"
+
+power-assert-renderer-base@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/power-assert-renderer-base/-/power-assert-renderer-base-1.1.1.tgz#96a650c6fd05ee1bc1f66b54ad61442c8b3f63eb"
+
+power-assert-renderer-comparison@^1.0.7:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/power-assert-renderer-comparison/-/power-assert-renderer-comparison-1.1.1.tgz#d7439d97d85156be4e30a00f2fb5a72514ce3c08"
+  dependencies:
+    core-js "^2.0.0"
+    diff-match-patch "^1.0.0"
+    power-assert-renderer-base "^1.1.1"
+    stringifier "^1.3.0"
+    type-name "^2.0.1"
+
+power-assert-renderer-diagram@^1.0.7:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/power-assert-renderer-diagram/-/power-assert-renderer-diagram-1.1.2.tgz#655f8f711935a9b6d541b86327654717c637a986"
+  dependencies:
+    core-js "^2.0.0"
+    power-assert-renderer-base "^1.1.1"
+    power-assert-util-string-width "^1.1.1"
+    stringifier "^1.3.0"
+
+power-assert-renderer-file@^1.0.7:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/power-assert-renderer-file/-/power-assert-renderer-file-1.1.1.tgz#a37e2bbd178ccacd04e78dbb79c92fe34933c5e7"
+  dependencies:
+    power-assert-renderer-base "^1.1.1"
+
+power-assert-util-string-width@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/power-assert-util-string-width/-/power-assert-util-string-width-1.1.1.tgz#be659eb7937fdd2e6c9a77268daaf64bd5b7c592"
+  dependencies:
+    eastasianwidth "^0.1.1"
+
+power-assert@^1.2.0:
+  version "1.4.4"
+  resolved "https://registry.yarnpkg.com/power-assert/-/power-assert-1.4.4.tgz#9295ea7437196f5a601fde420f042631186d7517"
+  dependencies:
+    define-properties "^1.1.2"
+    empower "^1.2.3"
+    power-assert-formatter "^1.3.1"
+    universal-deep-strict-equal "^1.2.1"
+    xtend "^4.0.0"
+
 prebuild-install@^2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.3.0.tgz#19481247df728b854ab57b187ce234211311b485"
@@ -8651,6 +8783,15 @@ restore-cursor@^1.0.1:
     exit-hook "^1.0.0"
     onetime "^1.0.0"
 
+restructured@0.0.11:
+  version "0.0.11"
+  resolved "https://registry.yarnpkg.com/restructured/-/restructured-0.0.11.tgz#f914f6b6f358b8e45d6d8ee268926cf1a783f710"
+  dependencies:
+    commander "^2.9.0"
+    lodash "^4.0.0"
+    power-assert "^1.2.0"
+    unist-util-map "^1.0.2"
+
 ret@~0.1.10:
   version "0.1.15"
   resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
@@ -8693,6 +8834,12 @@ rst-selector-parser@^2.2.3:
     lodash.flattendeep "^4.4.0"
     nearley "^2.7.10"
 
+"rst2html@github:thoward/rst2html#d6e2f21":
+  version "1.0.4"
+  resolved "https://codeload.github.com/thoward/rst2html/tar.gz/d6e2f219ea94ec7b1fe3cc918d152b67015ab04e"
+  dependencies:
+    restructured "0.0.11"
+
 run-async@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389"
@@ -9359,6 +9506,14 @@ string_decoder@~0.10.x:
   version "0.10.31"
   resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
 
+stringifier@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/stringifier/-/stringifier-1.3.0.tgz#def18342f6933db0f2dbfc9aa02175b448c17959"
+  dependencies:
+    core-js "^2.0.0"
+    traverse "^0.6.6"
+    type-name "^2.0.1"
+
 stringify-object@^3.2.0:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.2.1.tgz#2720c2eff940854c819f6ee252aaeb581f30624d"
@@ -9713,6 +9868,10 @@ tr46@^1.0.0:
   dependencies:
     punycode "^2.1.0"
 
+traverse@^0.6.6:
+  version "0.6.6"
+  resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
+
 trim-newlines@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
@@ -9826,6 +9985,10 @@ type-is@~1.6.15:
     media-typer "0.3.0"
     mime-types "~2.1.15"
 
+type-name@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/type-name/-/type-name-2.0.2.tgz#efe7d4123d8ac52afff7f40c7e4dec5266008fb4"
+
 typedarray@^0.0.6:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
@@ -9952,6 +10115,20 @@ unique-string@^1.0.0:
   dependencies:
     crypto-random-string "^1.0.0"
 
+unist-util-map@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/unist-util-map/-/unist-util-map-1.0.3.tgz#26a913d7cddb3cd3e9a886d135d37a3d1f54e514"
+  dependencies:
+    object-assign "^4.0.1"
+
+universal-deep-strict-equal@^1.2.1:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/universal-deep-strict-equal/-/universal-deep-strict-equal-1.2.2.tgz#0da4ac2f73cff7924c81fa4de018ca562ca2b0a7"
+  dependencies:
+    array-filter "^1.0.0"
+    indexof "0.0.1"
+    object-keys "^1.0.0"
+
 universalify@^0.1.0:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7"

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio