Browse Source

stackdriver: break out aggretation logic into its own directive and controller. also adds tests for new dropdown population logic

Erik Sundell 7 years ago
parent
commit
7666e7bfe3

+ 42 - 0
public/app/plugins/datasource/stackdriver/partials/query.aggregation.html

@@ -0,0 +1,42 @@
+<div class="gf-form-inline">
+  <div class="gf-form">
+    <label class="gf-form-label query-keyword width-9">Aggregation</label>
+    <div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
+      <select class="gf-form-input width-12" ng-model="target.aggregation.crossSeriesReducer" ng-options="f.value as f.text for f in getAggOptions()"
+        ng-change="onAggregationChange(target.aggregation.crossSeriesReducer)"></select>
+    </div>
+  </div>
+  <div class="gf-form gf-form--grow">
+    <label class="gf-form-label gf-form-label--grow">
+      <a ng-click="target.showAggregationOptions = !target.showAggregationOptions">
+        <i class="fa fa-caret-down" ng-show="target.showAggregationOptions"></i>
+        <i class="fa fa-caret-right" ng-hide="target.showAggregationOptions"></i>
+        Advanced Options
+      </a>
+    </label>
+  </div>
+</div>
+<div class="gf-form-group" ng-if="target.showAggregationOptions">
+  <div class="gf-form offset-width-9">
+    <label class="gf-form-label query-keyword width-12">Aligner</label>
+    <div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
+      <select class="gf-form-input width-14" ng-model="target.aggregation.perSeriesAligner" ng-options="f.value as f.text for f in getAlignOptions()"
+        ng-change="onAlignmentChange(target.aggregation.perSeriesAligner)"></select>
+    </div>
+
+    <div class="gf-form gf-form--grow">
+      <div class="gf-form-label gf-form-label--grow"></div>
+    </div>
+  </div>
+  <div class="gf-form offset-width-9">
+    <label class="gf-form-label query-keyword width-12">Alignment Period</label>
+    <div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
+      <select class="gf-form-input width-14" ng-model="target.aggregation.alignmentPeriod" ng-options="f.value as f.text for f in alignmentPeriods"
+        ng-change="refresh()"></select>
+    </div>
+
+    <div class="gf-form gf-form--grow">
+      <div class="gf-form-label gf-form-label--grow"></div>
+    </div>
+  </div>
+</div>

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

@@ -31,48 +31,7 @@
       <div class="gf-form-label gf-form-label--grow"></div>
     </div>
   </div>
-  <div class="gf-form-inline">
-    <div class="gf-form">
-      <label class="gf-form-label query-keyword width-9">Aggregation</label>
-      <div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
-        <select class="gf-form-input width-12" ng-model="ctrl.target.aggregation.crossSeriesReducer" ng-options="f.value as f.text for f in ctrl.getAggOptions()"
-          ng-change="ctrl.refresh()"></select>
-      </div>
-    </div>
-    <div class="gf-form gf-form--grow">
-      <label class="gf-form-label gf-form-label--grow">
-        <a ng-click="ctrl.target.showAggregationOptions = !ctrl.target.showAggregationOptions">
-          <i class="fa fa-caret-down" ng-show="ctrl.target.showAggregationOptions"></i>
-          <i class="fa fa-caret-right" ng-hide="ctrl.target.showAggregationOptions"></i>
-          Advanced Options
-        </a>
-      </label>
-    </div>
-  </div>
-  <div class="gf-form-group" ng-if="ctrl.target.showAggregationOptions">
-    <div class="gf-form offset-width-9">
-      <label class="gf-form-label query-keyword width-12">Aligner</label>
-      <div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
-        <select class="gf-form-input width-14" ng-model="ctrl.target.aggregation.perSeriesAligner" ng-options="f.value as f.text for f in ctrl.getAlignOptions()"
-          ng-change="ctrl.refresh()"></select>
-      </div>
-
-      <div class="gf-form gf-form--grow">
-        <div class="gf-form-label gf-form-label--grow"></div>
-      </div>
-    </div>
-    <div class="gf-form offset-width-9">
-      <label class="gf-form-label query-keyword width-12">Alignment Period</label>
-      <div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
-        <select class="gf-form-input width-14" ng-model="ctrl.target.aggregation.alignmentPeriod" ng-options="f.value as f.text for f in ctrl.stackdriverConstants.alignmentPeriods"
-          ng-change="ctrl.refresh()"></select>
-      </div>
-
-      <div class="gf-form gf-form--grow">
-        <div class="gf-form-label gf-form-label--grow"></div>
-      </div>
-    </div>
-  </div>
+  <stackdriver-aggregation target="ctrl.target" refresh="ctrl.refresh()"></stackdriver-aggregation>
   <div class="gf-form-inline">
     <div class="gf-form">
       <span class="gf-form-label query-keyword width-9">Alias By</span>

+ 73 - 0
public/app/plugins/datasource/stackdriver/query_aggregation_ctrl.ts

@@ -0,0 +1,73 @@
+import angular from 'angular';
+import _ from 'lodash';
+import * as options from './constants';
+
+export class StackdriverAggregation {
+  constructor() {
+    return {
+      templateUrl: 'public/app/plugins/datasource/stackdriver/partials/query.aggregation.html',
+      controller: 'StackdriverAggregationCtrl',
+      restrict: 'E',
+      scope: {
+        target: '=',
+        refresh: '&',
+      },
+    };
+  }
+}
+
+export class StackdriverAggregationCtrl {
+  target: any;
+  alignOptions: any[];
+  aggOptions: any[];
+  refresh: () => void;
+
+  constructor(private $scope) {
+    this.aggOptions = options.aggOptions;
+    this.alignOptions = options.alignOptions;
+    $scope.alignmentPeriods = options.alignmentPeriods;
+    $scope.getAlignOptions = this.getAlignOptions;
+    $scope.getAggOptions = this.getAggOptions;
+    $scope.onAlignmentChange = this.onAlignmentChange;
+    $scope.onAggregationChange = this.onAggregationChange;
+    this.refresh = $scope.refresh;
+  }
+
+  onAlignmentChange(newVal) {
+    if (newVal === 'ALIGN_NONE') {
+      this.target.aggregation.crossSeriesReducer = 'REDUCE_NONE';
+    }
+    this.refresh();
+  }
+
+  onAggregationChange(newVal) {
+    if (newVal !== 'REDUCE_NONE') {
+      const newAlignmentOption = options.alignOptions.find(o => o.value !== 'ALIGN_NONE');
+      this.target.aggregation.perSeriesAligner = newAlignmentOption ? newAlignmentOption.value : '';
+    }
+    this.refresh();
+  }
+
+  getAlignOptions() {
+    return !this.target.valueType
+      ? options.alignOptions
+      : options.alignOptions.filter(i => {
+          return (
+            i.valueTypes.indexOf(this.target.valueType) !== -1 && i.metricKinds.indexOf(this.target.metricKind) !== -1
+          );
+        });
+  }
+
+  getAggOptions() {
+    return !this.target.metricKind
+      ? options.aggOptions
+      : options.aggOptions.filter(i => {
+          return (
+            i.valueTypes.indexOf(this.target.valueType) !== -1 && i.metricKinds.indexOf(this.target.metricKind) !== -1
+          );
+        });
+  }
+}
+
+angular.module('grafana.controllers').directive('stackdriverAggregation', StackdriverAggregation);
+angular.module('grafana.controllers').controller('StackdriverAggregationCtrl', StackdriverAggregationCtrl);

+ 4 - 33
public/app/plugins/datasource/stackdriver/query_ctrl.ts

@@ -1,8 +1,8 @@
 import _ from 'lodash';
 import { QueryCtrl } from 'app/plugins/sdk';
 import appEvents from 'app/core/app_events';
-import * as options from './constants';
 import { FilterSegments, DefaultRemoveFilterValue } from './filter_segments';
+import './query_aggregation_ctrl';
 
 export interface QueryMeta {
   rawQuery: string;
@@ -55,8 +55,6 @@ export class StackdriverQueryCtrl extends QueryCtrl {
     valueType: '',
   };
 
-  alignOptions: any[];
-  aggOptions: any[];
   groupBySegments: any[];
   removeSegment: any;
   showHelp: boolean;
@@ -74,9 +72,6 @@ export class StackdriverQueryCtrl extends QueryCtrl {
 
     this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope);
     this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope);
-    this.stackdriverConstants = options;
-    this.aggOptions = options.aggOptions;
-    this.alignOptions = options.alignOptions;
 
     this.getCurrentProject()
       .then(this.getMetricTypes.bind(this))
@@ -155,6 +150,9 @@ export class StackdriverQueryCtrl extends QueryCtrl {
 
         this.metricLabels = data.results[this.target.refId].meta.metricLabels;
         this.resourceLabels = data.results[this.target.refId].meta.resourceLabels;
+
+        this.target.valueType = data.results[this.target.refId].meta.valueType;
+        this.target.metricKind = data.results[this.target.refId].meta.metricKind;
         resolve();
       } catch (error) {
         resolve();
@@ -264,31 +262,6 @@ export class StackdriverQueryCtrl extends QueryCtrl {
     }
   }
 
-  getAlignOptions() {
-    return !this.target.valueType
-      ? options.alignOptions
-      : options.alignOptions.filter(i => {
-          return (
-            i.valueTypes.indexOf(this.target.valueType) !== -1 && i.metricKinds.indexOf(this.target.metricKind) !== -1
-          );
-        });
-  }
-
-  getAggOptions() {
-    if (this.target.aggregation.perSeriesAligner === 'ALIGN_NONE') {
-      this.target.aggregation.crossSeriesReducer = options.aggOptions[0].value;
-      return options.aggOptions.slice(0, 1);
-    }
-
-    return !this.target.metricKind
-      ? options.aggOptions
-      : options.aggOptions.filter(i => {
-          return (
-            i.valueTypes.indexOf(this.target.valueType) !== -1 && i.metricKinds.indexOf(this.target.metricKind) !== -1
-          );
-        });
-  }
-
   onDataReceived(dataList) {
     this.lastQueryError = null;
     this.lastQueryMeta = null;
@@ -297,8 +270,6 @@ export class StackdriverQueryCtrl extends QueryCtrl {
     if (anySeriesFromQuery) {
       this.lastQueryMeta = anySeriesFromQuery.meta;
       this.lastQueryMeta.rawQueryString = decodeURIComponent(this.lastQueryMeta.rawQuery);
-      this.target.valueType = anySeriesFromQuery.meta.valueType;
-      this.target.metricKind = anySeriesFromQuery.meta.metricKind;
     }
   }
 

+ 81 - 0
public/app/plugins/datasource/stackdriver/specs/query_aggregation_ctrl.test.ts

@@ -0,0 +1,81 @@
+import { StackdriverAggregationCtrl } from '../query_aggregation_ctrl';
+
+describe('StackdriverAggregationCtrl', () => {
+  let ctrl;
+  describe('aggregation and alignment options', () => {
+    beforeEach(() => {
+      ctrl = createCtrlWithFakes();
+    });
+    describe('when new query result is returned from the server', () => {
+      describe('and result is double and gauge', () => {
+        beforeEach(async () => {
+          ctrl.target.valueType = 'DOUBLE';
+          ctrl.target.metricKind = 'GAUGE';
+        });
+
+        it('should populate all aggregate options except two', () => {
+          const result = ctrl.getAggOptions();
+          expect(result.length).toBe(11);
+          expect(result.map(o => o.value)).toEqual(
+            expect.not.arrayContaining(['REDUCE_COUNT_TRUE', 'REDUCE_COUNT_FALSE'])
+          );
+        });
+
+        it('should populate all alignment options except two', () => {
+          const result = ctrl.getAlignOptions();
+          console.log(result.map(o => o.value));
+          expect(result.length).toBe(10);
+          expect(result.map(o => o.value)).toEqual(
+            expect.not.arrayContaining(['REDUCE_COUNT_TRUE', 'REDUCE_COUNT_FALSE'])
+          );
+        });
+      });
+    });
+
+    describe('when a user a user select ALIGN_NONE and a reducer is selected', () => {
+      beforeEach(async () => {
+        ctrl.target.aggregation.crossSeriesReducer = 'RANDOM_REDUCER';
+        ctrl.onAlignmentChange('ALIGN_NONE');
+      });
+      it('should set REDUCE_NONE as selected aggregation', () => {
+        expect(ctrl.target.aggregation.crossSeriesReducer).toBe('REDUCE_NONE');
+      });
+    });
+
+    describe('when a user a user select a reducer and no alignment is selected', () => {
+      beforeEach(async () => {
+        ctrl.target.aggregation.crossSeriesReducer = 'REDUCE_NONE';
+        ctrl.target.aggregation.perSeriesAligner = 'ALIGN_NONE';
+        ctrl.onAggregationChange('ALIGN_NONE');
+      });
+
+      it('should set an alignment', () => {
+        expect(ctrl.target.aggregation.perSeriesAligner).not.toBe('ALIGN_NONE');
+      });
+    });
+  });
+});
+
+function createCtrlWithFakes() {
+  StackdriverAggregationCtrl.prototype.target = createTarget();
+  return new StackdriverAggregationCtrl({ refresh: () => {} });
+}
+
+function createTarget(existingFilters?: string[]) {
+  return {
+    project: {
+      id: '',
+      name: '',
+    },
+    metricType: 'ametric',
+    refId: 'A',
+    aggregation: {
+      crossSeriesReducer: '',
+      alignmentPeriod: '',
+      perSeriesAligner: '',
+      groupBys: [],
+    },
+    filters: existingFilters || [],
+    aliasBy: '',
+  };
+}