Переглянути джерело

feat(templating): progress on variable system refactoring, #6048

Torkel Ödegaard 9 роки тому
батько
коміт
9d6ecc6361

+ 1 - 0
public/app/features/dashboard/dashboard_ctrl.ts

@@ -87,6 +87,7 @@ export class DashboardCtrl {
       };
       };
 
 
       $scope.templateVariableUpdated = function() {
       $scope.templateVariableUpdated = function() {
+        dynamicDashboardSrv.update($scope.dashboard);
       };
       };
 
 
       $scope.updateSubmenuVisibility = function() {
       $scope.updateSubmenuVisibility = function() {

+ 4 - 2
public/app/features/templating/custom_variable.ts

@@ -23,13 +23,15 @@ export class CustomVariable implements Variable {
     multi: false,
     multi: false,
   };
   };
 
 
+  supportsMulti = true;
+
   /** @ngInject */
   /** @ngInject */
   constructor(private model, private timeSrv, private templateSrv, private variableSrv) {
   constructor(private model, private timeSrv, private templateSrv, private variableSrv) {
     assignModelProperties(this, model, this.defaults);
     assignModelProperties(this, model, this.defaults);
   }
   }
 
 
   setValue(option) {
   setValue(option) {
-    this.variableSrv.setOptionAsCurrent(this, option);
+    return this.variableSrv.setOptionAsCurrent(this, option);
   }
   }
 
 
   getModel() {
   getModel() {
@@ -47,7 +49,7 @@ export class CustomVariable implements Variable {
       this.addAllOption();
       this.addAllOption();
     }
     }
 
 
-    return Promise.resolve();
+    return this.variableSrv.validateVariableSelectionState(this);
   }
   }
 
 
   addAllOption() {
   addAllOption() {

+ 3 - 2
public/app/features/templating/datasource_variable.ts

@@ -15,7 +15,7 @@ export class DatasourceVariable implements Variable {
     name: '',
     name: '',
     hide: 0,
     hide: 0,
     label: '',
     label: '',
-    current: {text: '', value: ''}
+    current: {text: '', value: ''},
     regex: '',
     regex: '',
     options: [],
     options: [],
     query: '',
     query: '',
@@ -32,7 +32,7 @@ export class DatasourceVariable implements Variable {
   }
   }
 
 
   setValue(option) {
   setValue(option) {
-    this.variableSrv.setOptionAsCurrent(this, option);
+    return this.variableSrv.setOptionAsCurrent(this, option);
   }
   }
 
 
   updateOptions() {
   updateOptions() {
@@ -63,6 +63,7 @@ export class DatasourceVariable implements Variable {
     }
     }
 
 
     this.options = options;
     this.options = options;
+    return this.variableSrv.validateVariableSelectionState(this);
   }
   }
 
 
   dependsOn(variable) {
   dependsOn(variable) {

+ 3 - 35
public/app/features/templating/editor_ctrl.ts

@@ -10,7 +10,7 @@ import appEvents from 'app/core/app_events';
 export class VariableEditorCtrl {
 export class VariableEditorCtrl {
 
 
   /** @ngInject */
   /** @ngInject */
-  constructor(private $scope, private datasourceSrv, private variableSrv) {
+  constructor(private $scope, private datasourceSrv, private variableSrv, templateSrv) {
     $scope.variableTypes = [
     $scope.variableTypes = [
       {value: "query",      text: "Query"},
       {value: "query",      text: "Query"},
       {value: "adhoc",      text: "Ad hoc filters"},
       {value: "adhoc",      text: "Ad hoc filters"},
@@ -27,7 +27,7 @@ export class VariableEditorCtrl {
     ];
     ];
 
 
     $scope.sortOptions = [
     $scope.sortOptions = [
-      {value: 0, text: "Query sort"},
+      {value: 0, text: "Disabled"},
       {value: 1, text: "Alphabetical (asc)"},
       {value: 1, text: "Alphabetical (asc)"},
       {value: 2, text: "Alphabetical (desc)"},
       {value: 2, text: "Alphabetical (desc)"},
       {value: 3, text: "Numerical (asc)"},
       {value: 3, text: "Numerical (asc)"},
@@ -115,6 +115,7 @@ export class VariableEditorCtrl {
         $scope.runQuery().then(function() {
         $scope.runQuery().then(function() {
           $scope.reset();
           $scope.reset();
           $scope.mode = 'list';
           $scope.mode = 'list';
+          templateSrv.updateTemplateData();
         });
         });
       }
       }
     };
     };
@@ -124,18 +125,6 @@ export class VariableEditorCtrl {
       $scope.current = variableSrv.createVariableFromModel({type: 'query'});
       $scope.current = variableSrv.createVariableFromModel({type: 'query'});
     };
     };
 
 
-    $scope.showSelectionOptions = function() {
-      if ($scope.current) {
-        if ($scope.current.type === 'query') {
-          return true;
-        }
-        if ($scope.current.type === 'custom') {
-          return true;
-        }
-      }
-      return false;
-    };
-
     $scope.typeChanged = function() {
     $scope.typeChanged = function() {
       var old = $scope.current;
       var old = $scope.current;
       $scope.current = variableSrv.createVariableFromModel({type: $scope.current.type});
       $scope.current = variableSrv.createVariableFromModel({type: $scope.current.type});
@@ -147,27 +136,6 @@ export class VariableEditorCtrl {
       if (oldIndex !== -1) {
       if (oldIndex !== -1) {
         this.variables[oldIndex] = $scope.current;
         this.variables[oldIndex] = $scope.current;
       }
       }
-
-      // if ($scope.current.type === 'interval') {
-      //   $scope.current.query = '1m,10m,30m,1h,6h,12h,1d,7d,14d,30d';
-      //   $scope.current.refresh = 0;
-      // }
-      //
-      // if ($scope.current.type === 'query') {
-      //   $scope.current.query = '';
-      // }
-      //
-      // if ($scope.current.type === 'constant') {
-      //   $scope.current.query = '';
-      //   $scope.current.refresh = 0;
-      //   $scope.current.hide = 2;
-      // }
-      //
-      // if ($scope.current.type === 'datasource') {
-      //   $scope.current.query = $scope.datasourceTypes[0].value;
-      //   $scope.current.regex = '';
-      //   $scope.current.refresh = 1;
-      // }
     };
     };
 
 
     $scope.removeVariable = function(variable) {
     $scope.removeVariable = function(variable) {

+ 5 - 1
public/app/features/templating/interval_variable.ts

@@ -11,12 +11,14 @@ export class IntervalVariable implements Variable {
   options: any;
   options: any;
   auto: boolean;
   auto: boolean;
   query: string;
   query: string;
+  refresh: number;
 
 
   defaults = {
   defaults = {
     type: 'interval',
     type: 'interval',
     name: '',
     name: '',
     hide: 0,
     hide: 0,
     label: '',
     label: '',
+    refresh: 2,
     options: [],
     options: [],
     current: {text: '', value: ''},
     current: {text: '', value: ''},
     query: '1m,10m,30m,1h,6h,12h,1d,7d,14d,30d',
     query: '1m,10m,30m,1h,6h,12h,1d,7d,14d,30d',
@@ -28,6 +30,7 @@ export class IntervalVariable implements Variable {
   /** @ngInject */
   /** @ngInject */
   constructor(private model, private timeSrv, private templateSrv, private variableSrv) {
   constructor(private model, private timeSrv, private templateSrv, private variableSrv) {
     assignModelProperties(this, model, this.defaults);
     assignModelProperties(this, model, this.defaults);
+    this.refresh = 2;
   }
   }
 
 
   getModel() {
   getModel() {
@@ -37,7 +40,7 @@ export class IntervalVariable implements Variable {
 
 
   setValue(option) {
   setValue(option) {
     this.updateAutoValue();
     this.updateAutoValue();
-    this.variableSrv.setOptionAsCurrent(this, option);
+    return this.variableSrv.setOptionAsCurrent(this, option);
   }
   }
 
 
   updateAutoValue() {
   updateAutoValue() {
@@ -61,6 +64,7 @@ export class IntervalVariable implements Variable {
     });
     });
 
 
     this.updateAutoValue();
     this.updateAutoValue();
+    return this.variableSrv.validateVariableSelectionState(this);
   }
   }
 
 
   dependsOn(variable) {
   dependsOn(variable) {

+ 84 - 84
public/app/features/templating/partials/editor.html

@@ -181,19 +181,8 @@
               <select class="gf-form-input" ng-model="current.refresh" ng-options="f.value as f.text for f in refreshOptions"></select>
               <select class="gf-form-input" ng-model="current.refresh" ng-options="f.value as f.text for f in refreshOptions"></select>
             </div>
             </div>
           </div>
           </div>
-          <div class="gf-form max-width-21">
-            <span class="gf-form-label width-7">
-              Sort
-              <info-popover mode="right-normal">
-                How to sort the values of this variable.
-              </info-popover>
-            </span>
-            <div class="gf-form-select-wrapper max-width-14">
-              <select class="gf-form-input" ng-model="current.sort" ng-options="f.value as f.text for f in sortOptions" ng-change="runQuery()"></select>
-            </div>
-          </div>
-        </div>
-        <div class="gf-form">
+				</div>
+				<div class="gf-form">
           <span class="gf-form-label width-7">Query</span>
           <span class="gf-form-label width-7">Query</span>
           <input type="text" class="gf-form-input" ng-model='current.query' placeholder="metric name or tags query" ng-model-onblur ng-change="runQuery()"></input>
           <input type="text" class="gf-form-input" ng-model='current.query' placeholder="metric name or tags query" ng-model-onblur ng-change="runQuery()"></input>
         </div>
         </div>
@@ -206,35 +195,46 @@
           </span>
           </span>
           <input type="text" class="gf-form-input" ng-model='current.regex' placeholder="/.*-(.*)-.*/" ng-model-onblur ng-change="runQuery()"></input>
           <input type="text" class="gf-form-input" ng-model='current.regex' placeholder="/.*-(.*)-.*/" ng-model-onblur ng-change="runQuery()"></input>
         </div>
         </div>
-      </div>
+				<div class="gf-form max-width-21">
+					<span class="gf-form-label width-7">
+						Sort
+						<info-popover mode="right-normal">
+							How to sort the values of this variable.
+						</info-popover>
+					</span>
+					<div class="gf-form-select-wrapper max-width-14">
+						<select class="gf-form-input" ng-model="current.sort" ng-options="f.value as f.text for f in sortOptions" ng-change="runQuery()"></select>
+					</div>
+				</div>
+			</div>
 
 
-      <div ng-show="current.type === 'datasource'" class="gf-form-group">
-        <h5 class="section-heading">Data source options</h5>
+			<div ng-show="current.type === 'datasource'" class="gf-form-group">
+				<h5 class="section-heading">Data source options</h5>
 
 
-        <div class="gf-form">
-          <label class="gf-form-label width-12">Type</label>
-          <div class="gf-form-select-wrapper max-width-18">
-            <select class="gf-form-input" ng-model="current.query" ng-options="f.value as f.text for f in datasourceTypes" ng-change="runQuery()"></select>
-          </div>
-        </div>
+				<div class="gf-form">
+					<label class="gf-form-label width-12">Type</label>
+					<div class="gf-form-select-wrapper max-width-18">
+						<select class="gf-form-input" ng-model="current.query" ng-options="f.value as f.text for f in datasourceTypes" ng-change="runQuery()"></select>
+					</div>
+				</div>
 
 
-        <div class="gf-form">
-          <label class="gf-form-label width-12">
-            Instance name filter
-            <info-popover mode="right-normal">
-              Regex filter for which data source instances to choose from in
-              the variable value dropdown. Leave empty for all.
-              <br><br>
-              Example: <code>/^prod/</code>
+				<div class="gf-form">
+					<label class="gf-form-label width-12">
+						Instance name filter
+						<info-popover mode="right-normal">
+							Regex filter for which data source instances to choose from in
+							the variable value dropdown. Leave empty for all.
+							<br><br>
+							Example: <code>/^prod/</code>
 
 
-            </info-popover>
-          </label>
-          <input type="text" class="gf-form-input max-width-18" ng-model='current.regex' placeholder="/.*-(.*)-.*/" ng-model-onblur ng-change="runQuery()"></input>
-        </div>
-      </div>
+						</info-popover>
+					</label>
+					<input type="text" class="gf-form-input max-width-18" ng-model='current.regex' placeholder="/.*-(.*)-.*/" ng-model-onblur ng-change="runQuery()"></input>
+				</div>
+			</div>
 
 
 			<div ng-show="current.type === 'adhoc'" class="gf-form-group">
 			<div ng-show="current.type === 'adhoc'" class="gf-form-group">
-        <h5 class="section-heading">Options</h5>
+				<h5 class="section-heading">Options</h5>
 
 
 				<div class="gf-form max-width-21">
 				<div class="gf-form max-width-21">
 					<span class="gf-form-label width-8">Data source</span>
 					<span class="gf-form-label width-8">Data source</span>
@@ -242,58 +242,58 @@
 						<select class="gf-form-input" ng-model="current.datasource" ng-options="f.value as f.name for f in datasources"></select>
 						<select class="gf-form-input" ng-model="current.datasource" ng-options="f.value as f.name for f in datasources"></select>
 					</div>
 					</div>
 				</div>
 				</div>
-      </div>
+			</div>
 
 
-      <div class="section gf-form-group" ng-show="showSelectionOptions()">
-        <h5 class="section-heading">Selection Options</h5>
-        <div class="section">
-          <gf-form-switch class="gf-form"
-                          label="Multi-value"
-                          label-class="width-10"
-                          tooltip="Enables multiple values to be selected at the same time"
-                          checked="current.multi"
-                          on-change="runQuery()">
-          </gf-form-switch>
-          <gf-form-switch class="gf-form"
-                          label="Include All option"
-                          label-class="width-10"
-                          checked="current.includeAll"
-                          on-change="runQuery()">
-          </gf-form-switch>
-        </div>
-        <div class="gf-form" ng-if="current.includeAll">
-          <span class="gf-form-label width-10">Custom all value</span>
-          <input type="text" class="gf-form-input max-width-15" ng-model='current.allValue' placeholder="blank = auto"></input>
-        </div>
-      </div>
+			<div class="section gf-form-group" ng-show="current.supportsMulti">
+				<h5 class="section-heading">Selection Options</h5>
+				<div class="section">
+					<gf-form-switch class="gf-form"
+										 label="Multi-value"
+					 label-class="width-10"
+			tooltip="Enables multiple values to be selected at the same time"
+	 checked="current.multi"
+	on-change="runQuery()">
+					</gf-form-switch>
+					<gf-form-switch class="gf-form"
+										 label="Include All option"
+					 label-class="width-10"
+			checked="current.includeAll"
+	 on-change="runQuery()">
+					</gf-form-switch>
+				</div>
+				<div class="gf-form" ng-if="current.includeAll">
+					<span class="gf-form-label width-10">Custom all value</span>
+					<input type="text" class="gf-form-input max-width-15" ng-model='current.allValue' placeholder="blank = auto"></input>
+				</div>
+			</div>
 
 
-      <div class="gf-form-group" ng-if="current.type === 'query'">
-        <h5>Value groups/tags (Experimental feature)</h5>
-        <div class="gf-form">
-          <editor-checkbox text="Enable" model="current.useTags" change="runQuery()"></editor-checkbox>
-        </div>
-        <div class="gf-form last" ng-if="current.useTags">
-          <span class="gf-form-label width-10">Tags query</span>
-          <input type="text" class="gf-form-input" ng-model='current.tagsQuery' placeholder="metric name or tags query" ng-model-onblur></input>
-        </div>
-        <div class="gf-form" ng-if="current.useTags">
-          <li class="gf-form-label width-10">Tag values query</li>
-          <input type="text" class="gf-form-input" ng-model='current.tagValuesQuery' placeholder="apps.$tag.*" ng-model-onblur></input>
-        </div>
-      </div>
+			<div class="gf-form-group" ng-if="current.type === 'query'">
+				<h5>Value groups/tags (Experimental feature)</h5>
+				<div class="gf-form">
+					<editor-checkbox text="Enable" model="current.useTags" change="runQuery()"></editor-checkbox>
+				</div>
+				<div class="gf-form last" ng-if="current.useTags">
+					<span class="gf-form-label width-10">Tags query</span>
+					<input type="text" class="gf-form-input" ng-model='current.tagsQuery' placeholder="metric name or tags query" ng-model-onblur></input>
+				</div>
+				<div class="gf-form" ng-if="current.useTags">
+					<li class="gf-form-label width-10">Tag values query</li>
+					<input type="text" class="gf-form-input" ng-model='current.tagValuesQuery' placeholder="apps.$tag.*" ng-model-onblur></input>
+				</div>
+			</div>
 
 
-      <div class="gf-form-group">
-        <h5>Preview of values (shows max 20)</h5>
-        <div class="gf-form-inline">
-          <div class="gf-form" ng-repeat="option in current.options | limitTo: 20">
-            <span class="gf-form-label">{{option.text}}</span>
-          </div>
-        </div>
-      </div>
-    </div>
+			<div class="gf-form-group">
+				<h5>Preview of values (shows max 20)</h5>
+				<div class="gf-form-inline">
+					<div class="gf-form" ng-repeat="option in current.options | limitTo: 20">
+						<span class="gf-form-label">{{option.text}}</span>
+					</div>
+				</div>
+			</div>
+		</div>
 
 
-    <div class="gf-form-button-row p-y-0">
-      <button type="button" class="btn btn-success" ng-show="mode === 'edit'" ng-click="update();">Update</button>
+		<div class="gf-form-button-row p-y-0">
+			<button type="button" class="btn btn-success" ng-show="mode === 'edit'" ng-click="update();">Update</button>
       <button type="button" class="btn btn-success" ng-show="mode === 'new'" ng-click="add();">Add</button>
       <button type="button" class="btn btn-success" ng-show="mode === 'new'" ng-click="add();">Add</button>
     </div>
     </div>
   </div>
   </div>

+ 4 - 4
public/app/features/templating/query_variable.ts

@@ -37,6 +37,8 @@ export class QueryVariable implements Variable {
     current: {text: '', value: ''},
     current: {text: '', value: ''},
   };
   };
 
 
+  supportsMulti = true;
+
   constructor(private model, private datasourceSrv, private templateSrv, private variableSrv, private $q)  {
   constructor(private model, private datasourceSrv, private templateSrv, private variableSrv, private $q)  {
     // copy model properties to this instance
     // copy model properties to this instance
     assignModelProperties(this, model, this.defaults);
     assignModelProperties(this, model, this.defaults);
@@ -49,7 +51,7 @@ export class QueryVariable implements Variable {
   }
   }
 
 
   setValue(option){
   setValue(option){
-    this.variableSrv.setOptionAsCurrent(this, option);
+    return this.variableSrv.setOptionAsCurrent(this, option);
   }
   }
 
 
   setValueFromUrl(urlValue) {
   setValueFromUrl(urlValue) {
@@ -59,9 +61,7 @@ export class QueryVariable implements Variable {
   updateOptions() {
   updateOptions() {
     return this.datasourceSrv.get(this.datasource)
     return this.datasourceSrv.get(this.datasource)
     .then(this.updateOptionsFromMetricFindQuery.bind(this))
     .then(this.updateOptionsFromMetricFindQuery.bind(this))
-    .then(() => {
-      this.variableSrv.validateVariableSelectionState(this);
-    });
+    .then(this.variableSrv.validateVariableSelectionState.bind(this.variableSrv, this));
   }
   }
 
 
   updateOptionsFromMetricFindQuery(datasource) {
   updateOptionsFromMetricFindQuery(datasource) {

+ 16 - 17
public/app/features/templating/variable_srv.ts

@@ -15,7 +15,7 @@ export class VariableSrv {
   /** @ngInject */
   /** @ngInject */
   constructor(private $rootScope, private $q, private $location, private $injector, private templateSrv) {
   constructor(private $rootScope, private $q, private $location, private $injector, private templateSrv) {
     // update time variant variables
     // update time variant variables
-    // $rootScope.onAppEvent('refresh', this.onDashboardRefresh.bind(this), $rootScope);
+    $rootScope.$on('refresh', this.onDashboardRefresh.bind(this), $rootScope);
   }
   }
 
 
   init(dashboard) {
   init(dashboard) {
@@ -41,22 +41,21 @@ export class VariableSrv {
   }
   }
 
 
   onDashboardRefresh() {
   onDashboardRefresh() {
-    // var promises = this.variables
-    // .filter(variable => variable.refresh === 2)
-    // .map(variable => {
-    //   var previousOptions = variable.options.slice();
-    //
-    //   return self.updateOptions(variable).then(function () {
-    //     return self.variableUpdated(variable).then(function () {
-    //       // check if current options changed due to refresh
-    //       if (angular.toJson(previousOptions) !== angular.toJson(variable.options)) {
-    //         $rootScope.appEvent('template-variable-value-updated');
-    //       }
-    //     });
-    //   });
-    // });
-    //
-    // return this.$q.all(promises);
+    var promises = this.variables
+    .filter(variable => variable.refresh === 2)
+    .map(variable => {
+      var previousOptions = variable.options.slice();
+
+      return variable.updateOptions()
+      .then(this.variableUpdated.bind(this, variable))
+      .then(() => {
+        if (angular.toJson(previousOptions) !== angular.toJson(variable.options)) {
+          this.$rootScope.$emit('template-variable-value-updated');
+        }
+      });
+    });
+
+    return this.$q.all(promises);
   }
   }
 
 
   processVariable(variable, queryParams) {
   processVariable(variable, queryParams) {

+ 4 - 0
public/app/plugins/datasource/elasticsearch/datasource.js

@@ -257,6 +257,10 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
       esQuery = header + '\n' + esQuery + '\n';
       esQuery = header + '\n' + esQuery + '\n';
 
 
       return this._post('_msearch?search_type=count', esQuery).then(function(res) {
       return this._post('_msearch?search_type=count', esQuery).then(function(res) {
+        if (!res.responses[0].aggregations) {
+          return [];
+        }
+
         var buckets = res.responses[0].aggregations["1"].buckets;
         var buckets = res.responses[0].aggregations["1"].buckets;
         return _.map(buckets, function(bucket) {
         return _.map(buckets, function(bucket) {
           return {text: bucket.key, value: bucket.key};
           return {text: bucket.key, value: bucket.key};