From d0a565d85359df3b8d6fa80bcb2f0c89dbe5f995 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 13 Sep 2018 23:51:45 +0200 Subject: [PATCH] stackdriver: improve segments for group bys in query editor --- pkg/tsdb/stackdriver/stackdriver_test.go | 54 +++++----- .../datasource/stackdriver/datasource.ts | 4 +- .../datasource/stackdriver/query_ctrl.ts | 54 +++++----- .../stackdriver/specs/query_ctrl.test.ts | 99 +++++++++++++++++++ 4 files changed, 162 insertions(+), 49 deletions(-) create mode 100644 public/app/plugins/datasource/stackdriver/specs/query_ctrl.test.ts diff --git a/pkg/tsdb/stackdriver/stackdriver_test.go b/pkg/tsdb/stackdriver/stackdriver_test.go index 503dbec4163..d08553c7daf 100644 --- a/pkg/tsdb/stackdriver/stackdriver_test.go +++ b/pkg/tsdb/stackdriver/stackdriver_test.go @@ -29,6 +29,7 @@ func TestStackdriver(t *testing.T) { Model: simplejson.NewFromAny(map[string]interface{}{ "target": "target", "metricType": "a/metric/type", + "view": "FULL", }), RefId: "A", }, @@ -42,11 +43,12 @@ func TestStackdriver(t *testing.T) { So(len(queries), ShouldEqual, 1) So(queries[0].RefID, ShouldEqual, "A") So(queries[0].Target, ShouldEqual, "target") - So(len(queries[0].Params), ShouldEqual, 4) + So(len(queries[0].Params), ShouldEqual, 5) So(queries[0].Params["interval.startTime"][0], ShouldEqual, "2018-03-15T13:00:00Z") So(queries[0].Params["interval.endTime"][0], ShouldEqual, "2018-03-15T13:34:00Z") So(queries[0].Params["aggregation.perSeriesAligner"][0], ShouldEqual, "ALIGN_NONE") So(queries[0].Params["filter"][0], ShouldEqual, "metric.type=\"a/metric/type\"") + So(queries[0].Params["view"][0], ShouldEqual, "FULL") }) Convey("and query has aggregation mean set", func() { @@ -54,6 +56,32 @@ func TestStackdriver(t *testing.T) { "target": "target", "metricType": "a/metric/type", "primaryAggregation": "REDUCE_MEAN", + "view": "FULL", + }) + + queries, err := executor.buildQueries(tsdbQuery) + So(err, ShouldBeNil) + + So(len(queries), ShouldEqual, 1) + So(queries[0].RefID, ShouldEqual, "A") + So(queries[0].Target, ShouldEqual, "target") + So(len(queries[0].Params), ShouldEqual, 7) + So(queries[0].Params["interval.startTime"][0], ShouldEqual, "2018-03-15T13:00:00Z") + So(queries[0].Params["interval.endTime"][0], ShouldEqual, "2018-03-15T13:34:00Z") + So(queries[0].Params["aggregation.crossSeriesReducer"][0], ShouldEqual, "REDUCE_MEAN") + So(queries[0].Params["aggregation.perSeriesAligner"][0], ShouldEqual, "ALIGN_MEAN") + So(queries[0].Params["aggregation.alignmentPeriod"][0], ShouldEqual, "+60s") + So(queries[0].Params["filter"][0], ShouldEqual, "metric.type=\"a/metric/type\"") + So(queries[0].Params["view"][0], ShouldEqual, "FULL") + }) + + Convey("and query has group bys", func() { + tsdbQuery.Queries[0].Model = simplejson.NewFromAny(map[string]interface{}{ + "target": "target", + "metricType": "a/metric/type", + "primaryAggregation": "REDUCE_NONE", + "groupBys": []interface{}{"metric.label.group1", "metric.label.group2"}, + "view": "FULL", }) queries, err := executor.buildQueries(tsdbQuery) @@ -65,33 +93,11 @@ func TestStackdriver(t *testing.T) { So(len(queries[0].Params), ShouldEqual, 6) So(queries[0].Params["interval.startTime"][0], ShouldEqual, "2018-03-15T13:00:00Z") So(queries[0].Params["interval.endTime"][0], ShouldEqual, "2018-03-15T13:34:00Z") - So(queries[0].Params["aggregation.crossSeriesReducer"][0], ShouldEqual, "REDUCE_MEAN") - So(queries[0].Params["aggregation.perSeriesAligner"][0], ShouldEqual, "ALIGN_MEAN") - So(queries[0].Params["aggregation.alignmentPeriod"][0], ShouldEqual, "+60s") - So(queries[0].Params["filter"][0], ShouldEqual, "metric.type=\"a/metric/type\"") - }) - - Convey("and query has group bys", func() { - tsdbQuery.Queries[0].Model = simplejson.NewFromAny(map[string]interface{}{ - "target": "target", - "metricType": "a/metric/type", - "primaryAggregation": "REDUCE_NONE", - "groupBys": []interface{}{"metric.label.group1", "metric.label.group2"}, - }) - - queries, err := executor.buildQueries(tsdbQuery) - So(err, ShouldBeNil) - - So(len(queries), ShouldEqual, 1) - So(queries[0].RefID, ShouldEqual, "A") - So(queries[0].Target, ShouldEqual, "target") - So(len(queries[0].Params), ShouldEqual, 5) - So(queries[0].Params["interval.startTime"][0], ShouldEqual, "2018-03-15T13:00:00Z") - So(queries[0].Params["interval.endTime"][0], ShouldEqual, "2018-03-15T13:34:00Z") So(queries[0].Params["aggregation.perSeriesAligner"][0], ShouldEqual, "ALIGN_NONE") So(queries[0].Params["aggregation.groupByFields"][0], ShouldEqual, "metric.label.group1") So(queries[0].Params["aggregation.groupByFields"][1], ShouldEqual, "metric.label.group2") So(queries[0].Params["filter"][0], ShouldEqual, "metric.type=\"a/metric/type\"") + So(queries[0].Params["view"][0], ShouldEqual, "FULL") }) }) diff --git a/public/app/plugins/datasource/stackdriver/datasource.ts b/public/app/plugins/datasource/stackdriver/datasource.ts index a65154739d4..30052e32d5d 100644 --- a/public/app/plugins/datasource/stackdriver/datasource.ts +++ b/public/app/plugins/datasource/stackdriver/datasource.ts @@ -16,8 +16,8 @@ export default class StackdriverDatasource { refId: t.refId, datasourceId: this.id, metricType: t.metricType, - primaryAggregation: 'REDUCE_MEAN', //t.aggregation.crossSeriesReducer, - // groupBys: t.aggregation.groupBys, + primaryAggregation: t.aggregation.crossSeriesReducer, + groupBys: t.aggregation.groupBys, view: t.view || 'FULL', })); diff --git a/public/app/plugins/datasource/stackdriver/query_ctrl.ts b/public/app/plugins/datasource/stackdriver/query_ctrl.ts index f4b13254d45..5d40c32a003 100644 --- a/public/app/plugins/datasource/stackdriver/query_ctrl.ts +++ b/public/app/plugins/datasource/stackdriver/query_ctrl.ts @@ -10,8 +10,8 @@ export interface LabelType { export interface QueryMeta { rawQuery: string; rawQueryString: string; - metricLabels: LabelType[]; - resourceLabels: LabelType[]; + metricLabels: { [key: string]: string[] }; + resourceLabels: { [key: string]: string[] }; } export class StackdriverQueryCtrl extends QueryCtrl { static templateUrl = 'partials/query.editor.html'; @@ -152,34 +152,42 @@ export class StackdriverQueryCtrl extends QueryCtrl { } getGroupBys() { - const metricLabels = Object.keys(this.metricLabels).map(l => { - return this.uiSegmentSrv.newSegment({ - value: `metric.label.${l}`, - expandable: false, + const metricLabels = Object.keys(this.metricLabels) + .filter(ml => { + return this.target.aggregation.groupBys.indexOf('metric.label.' + ml) === -1; + }) + .map(l => { + return this.uiSegmentSrv.newSegment({ + value: `metric.label.${l}`, + expandable: false, + }); }); - }); - const resourceLabels = Object.keys(this.resourceLabels).map(l => { - return this.uiSegmentSrv.newSegment({ - value: `resource.label.${l}`, - expandable: false, + const resourceLabels = Object.keys(this.resourceLabels) + .filter(ml => { + return this.target.aggregation.groupBys.indexOf('resource.label.' + ml) === -1; + }) + .map(l => { + return this.uiSegmentSrv.newSegment({ + value: `resource.label.${l}`, + expandable: false, + }); }); - }); return Promise.resolve([...metricLabels, ...resourceLabels]); } - groupByChanged(segment, index) { - this.target.aggregation.groupBys = _.reduce( - this.groupBySegments, - function(memo, seg) { - if (!seg.fake) { - memo.push(seg.value); - } - return memo; - }, - [] - ); + groupByChanged(segment) { + segment.type = 'value'; + + const reducer = (memo, seg) => { + if (!seg.fake) { + memo.push(seg.value); + } + return memo; + }; + + this.target.aggregation.groupBys = this.groupBySegments.reduce(reducer, []); this.ensurePlusButton(this.groupBySegments); this.refresh(); } diff --git a/public/app/plugins/datasource/stackdriver/specs/query_ctrl.test.ts b/public/app/plugins/datasource/stackdriver/specs/query_ctrl.test.ts new file mode 100644 index 00000000000..6f1abcab1a3 --- /dev/null +++ b/public/app/plugins/datasource/stackdriver/specs/query_ctrl.test.ts @@ -0,0 +1,99 @@ +import { StackdriverQueryCtrl } from '../query_ctrl'; + +describe('StackdriverQueryCtrl', () => { + let ctrl; + let result; + + beforeEach(() => { + ctrl = createCtrlWithFakes(); + }); + + describe('when labels are fetched', () => { + beforeEach(async () => { + ctrl.metricLabels = { 'metric-key-1': ['metric-value-1'] }; + ctrl.resourceLabels = { 'resource-key-1': ['resource-value-1'] }; + + result = await ctrl.getGroupBys(); + }); + + it('should populate group bys segments', () => { + expect(result.length).toBe(2); + expect(result[0].value).toBe('metric.label.metric-key-1'); + expect(result[1].value).toBe('resource.label.resource-key-1'); + }); + }); + + describe('when a group by label is selected', () => { + beforeEach(async () => { + ctrl.metricLabels = { + 'metric-key-1': ['metric-value-1'], + 'metric-key-2': ['metric-value-2'], + }; + ctrl.resourceLabels = { + 'resource-key-1': ['resource-value-1'], + 'resource-key-2': ['resource-value-2'], + }; + ctrl.target.aggregation.groupBys = ['metric.label.metric-key-1', 'resource.label.resource-key-1']; + + result = await ctrl.getGroupBys(); + }); + + it('should not be used to populate group bys segments', () => { + expect(result.length).toBe(2); + expect(result[0].value).toBe('metric.label.metric-key-2'); + expect(result[1].value).toBe('resource.label.resource-key-2'); + }); + }); + + describe('when a group by is selected', () => { + beforeEach(() => { + const segment = { value: 'groupby1' }; + ctrl.groupBySegments = [segment]; + ctrl.groupByChanged(segment); + }); + + it('should be added to group bys list', () => { + expect(ctrl.target.aggregation.groupBys.length).toBe(1); + }); + }); +}); + +function createCtrlWithFakes() { + StackdriverQueryCtrl.prototype.panelCtrl = { + events: { on: () => {} }, + panel: { scopedVars: [], targets: [] }, + refresh: () => {}, + }; + StackdriverQueryCtrl.prototype.target = createTarget(); + StackdriverQueryCtrl.prototype.getMetricTypes = () => { + return Promise.resolve(); + }; + StackdriverQueryCtrl.prototype.getLabels = () => { + return Promise.resolve(); + }; + + const fakeSegmentServer = { + newSegment: obj => { + return { value: obj.value }; + }, + newPlusButton: () => {}, + }; + return new StackdriverQueryCtrl(null, null, fakeSegmentServer, null); +} + +function createTarget() { + return { + project: { + id: '', + name: '', + }, + metricType: 'ametric', + refId: 'A', + aggregation: { + crossSeriesReducer: '', + alignmentPeriod: '', + perSeriesAligner: '', + groupBys: [], + }, + }; +}