diff --git a/pkg/tsdb/stackdriver/annotation_query.go b/pkg/tsdb/stackdriver/annotation_query.go new file mode 100644 index 00000000000..37e68a6d3bd --- /dev/null +++ b/pkg/tsdb/stackdriver/annotation_query.go @@ -0,0 +1,24 @@ +package stackdriver + +import ( + "context" + + "github.com/grafana/grafana/pkg/tsdb" +) + +func (e *StackdriverExecutor) executeAnnotationQuery(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) { + result := &tsdb.Response{ + Results: make(map[string]*tsdb.QueryResult), + } + + _, err := e.buildAnnotationQuery(tsdbQuery) + if err != nil { + return nil, err + } + + return result, nil +} + +func (e *StackdriverExecutor) buildAnnotationQuery(tsdbQuery *tsdb.TsdbQuery) (*StackdriverQuery, error) { + return &StackdriverQuery{}, nil +} diff --git a/pkg/tsdb/stackdriver/annotation_query_test.go b/pkg/tsdb/stackdriver/annotation_query_test.go new file mode 100644 index 00000000000..0fb47fbe453 --- /dev/null +++ b/pkg/tsdb/stackdriver/annotation_query_test.go @@ -0,0 +1,41 @@ +package stackdriver + +import ( + "fmt" + "testing" + "time" + + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/tsdb" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestStackdriverAnnotationQuery(t *testing.T) { + Convey("Stackdriver Annotation Query Executor", t, func() { + executor := &StackdriverExecutor{} + Convey("Parse queries from frontend and build Stackdriver API queries", func() { + fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC).In(time.Local) + tsdbQuery := &tsdb.TsdbQuery{ + TimeRange: &tsdb.TimeRange{ + From: fmt.Sprintf("%v", fromStart.Unix()*1000), + To: fmt.Sprintf("%v", fromStart.Add(34*time.Minute).Unix()*1000), + }, + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "metricType": "a/metric/type", + "view": "FULL", + "type": "annotationQuery", + }), + RefId: "annotationQuery", + }, + }, + } + query, err := executor.buildAnnotationQuery(tsdbQuery) + So(err, ShouldBeNil) + + So(query, ShouldNotBeNil) + }) + }) +} diff --git a/pkg/tsdb/stackdriver/stackdriver.go b/pkg/tsdb/stackdriver/stackdriver.go index d9bc396038e..ffd065d6a18 100644 --- a/pkg/tsdb/stackdriver/stackdriver.go +++ b/pkg/tsdb/stackdriver/stackdriver.go @@ -66,6 +66,23 @@ func init() { // executes the queries against the Stackdriver API and parses the response into // the time series or table format func (e *StackdriverExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) { + var result *tsdb.Response + var err error + queryType := tsdbQuery.Queries[0].Model.Get("type").MustString("") + + switch queryType { + case "annotationQuery": + result, err = e.executeAnnotationQuery(ctx, tsdbQuery) + case "timeSeriesQuery": + fallthrough + default: + result, err = e.executeTimeSeriesQuery(ctx, tsdbQuery) + } + + return result, err +} + +func (e *StackdriverExecutor) executeTimeSeriesQuery(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) { result := &tsdb.Response{ Results: make(map[string]*tsdb.QueryResult), } diff --git a/pkg/tsdb/stackdriver/stackdriver_test.go b/pkg/tsdb/stackdriver/stackdriver_test.go index 8d52cd25c91..9ec47fee4ea 100644 --- a/pkg/tsdb/stackdriver/stackdriver_test.go +++ b/pkg/tsdb/stackdriver/stackdriver_test.go @@ -31,6 +31,7 @@ func TestStackdriver(t *testing.T) { "metricType": "a/metric/type", "view": "FULL", "aliasBy": "testalias", + "type": "timeSeriesQuery", }), RefId: "A", }, diff --git a/public/app/plugins/datasource/stackdriver/datasource.ts b/public/app/plugins/datasource/stackdriver/datasource.ts index 7b8d6ab0b5e..7d7b1e1cee1 100644 --- a/public/app/plugins/datasource/stackdriver/datasource.ts +++ b/public/app/plugins/datasource/stackdriver/datasource.ts @@ -5,7 +5,7 @@ export default class StackdriverDatasource { baseUrl: string; projectName: string; - constructor(instanceSettings, private backendSrv, private templateSrv) { + constructor(instanceSettings, private backendSrv, private templateSrv, private timeSrv) { this.baseUrl = `/stackdriver/`; this.url = instanceSettings.url; this.doRequest = this.doRequest; @@ -54,6 +54,23 @@ export default class StackdriverDatasource { return data; } + async getLabels(metricType, refId) { + return await this.getTimeSeries({ + targets: [ + { + refId: refId, + datasourceId: this.id, + metricType: this.templateSrv.replace(metricType), + aggregation: { + crossSeriesReducer: 'REDUCE_NONE', + }, + view: 'HEADERS', + }, + ], + range: this.timeSrv.timeRange(), + }); + } + interpolateGroupBys(groupBys: string[], scopedVars): string[] { let interpolatedGroupBys = []; (groupBys || []).forEach(gb => { diff --git a/public/app/plugins/datasource/stackdriver/query_ctrl.ts b/public/app/plugins/datasource/stackdriver/query_ctrl.ts index be98e9bf3ec..3f9cda2a875 100644 --- a/public/app/plugins/datasource/stackdriver/query_ctrl.ts +++ b/public/app/plugins/datasource/stackdriver/query_ctrl.ts @@ -66,7 +66,7 @@ export class StackdriverQueryCtrl extends QueryCtrl { filterSegments: any; /** @ngInject */ - constructor($scope, $injector, private uiSegmentSrv, private timeSrv, private templateSrv) { + constructor($scope, $injector, private uiSegmentSrv, private templateSrv) { super($scope, $injector); _.defaultsDeep(this.target, this.defaults); @@ -133,20 +133,7 @@ export class StackdriverQueryCtrl extends QueryCtrl { async getLabels() { this.loadLabelsPromise = new Promise(async resolve => { try { - const data = await this.datasource.getTimeSeries({ - targets: [ - { - refId: this.target.refId, - datasourceId: this.datasource.id, - metricType: this.templateSrv.replace(this.target.metricType), - aggregation: { - crossSeriesReducer: 'REDUCE_NONE', - }, - view: 'HEADERS', - }, - ], - range: this.timeSrv.timeRange(), - }); + const data = await this.datasource.getLabels(this.target.metricType, this.target.refId); this.metricLabels = data.results[this.target.refId].meta.metricLabels; this.resourceLabels = data.results[this.target.refId].meta.resourceLabels; @@ -155,6 +142,8 @@ export class StackdriverQueryCtrl extends QueryCtrl { this.target.metricKind = data.results[this.target.refId].meta.metricKind; resolve(); } catch (error) { + console.log(error.data.message); + appEvents.emit('alert-error', ['Error', 'Error loading metric labels for ' + this.target.metricType]); resolve(); } }); @@ -167,7 +156,8 @@ export class StackdriverQueryCtrl extends QueryCtrl { async getGroupBys(segment, index, removeText?: string, removeUsed = true) { await this.loadLabelsPromise; - const metricLabels = Object.keys(this.metricLabels) + + const metricLabels = Object.keys(this.metricLabels || {}) .filter(ml => { if (!removeUsed) { return true; @@ -181,7 +171,7 @@ export class StackdriverQueryCtrl extends QueryCtrl { }); }); - const resourceLabels = Object.keys(this.resourceLabels) + const resourceLabels = Object.keys(this.resourceLabels || {}) .filter(ml => { if (!removeUsed) { return true; diff --git a/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts b/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts index 5832ecf7304..541dbfaa7e8 100644 --- a/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts +++ b/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts @@ -10,6 +10,7 @@ describe('StackdriverDataSource', () => { }, }; const templateSrv = new TemplateSrvStub(); + const timeSrv = {}; describe('when performing testDataSource', () => { describe('and call to stackdriver api succeeds', () => { @@ -21,7 +22,7 @@ describe('StackdriverDataSource', () => { return Promise.resolve({ status: 200 }); }, }; - ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv); + ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv, timeSrv); result = await ds.testDatasource(); }); it('should return successfully', () => { @@ -36,7 +37,7 @@ describe('StackdriverDataSource', () => { const backendSrv = { datasourceRequest: async () => Promise.resolve({ status: 200, data: metricDescriptors }), }; - ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv); + ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv, timeSrv); result = await ds.testDatasource(); }); it('should return status success', () => { @@ -55,7 +56,7 @@ describe('StackdriverDataSource', () => { data: { error: { code: 400, message: 'Field interval.endTime had an invalid value' } }, }), }; - ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv); + ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv, timeSrv); result = await ds.testDatasource(); }); @@ -91,7 +92,7 @@ describe('StackdriverDataSource', () => { return Promise.resolve({ status: 200, data: response }); }, }; - ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv); + ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv, timeSrv); result = await ds.getProjects(); }); @@ -140,7 +141,7 @@ describe('StackdriverDataSource', () => { const backendSrv = { datasourceRequest: async () => Promise.resolve({ status: 200, data: response }), }; - ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv); + ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv, timeSrv); }); it('should return a list of datapoints', () => { @@ -174,7 +175,7 @@ describe('StackdriverDataSource', () => { }); }, }; - ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv); + ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv, timeSrv); result = await ds.getMetricTypes(); }); it('should return successfully', () => { @@ -192,7 +193,7 @@ describe('StackdriverDataSource', () => { templateSrv.data = { test: 'groupby1', }; - const ds = new StackdriverDataSource(instanceSettings, {}, templateSrv); + const ds = new StackdriverDataSource(instanceSettings, {}, templateSrv, timeSrv); interpolated = ds.interpolateGroupBys(['[[test]]'], {}); }); @@ -207,7 +208,7 @@ describe('StackdriverDataSource', () => { templateSrv.data = { test: 'groupby1,groupby2', }; - const ds = new StackdriverDataSource(instanceSettings, {}, templateSrv); + const ds = new StackdriverDataSource(instanceSettings, {}, templateSrv, timeSrv); interpolated = ds.interpolateGroupBys(['[[test]]'], {}); }); diff --git a/public/app/plugins/datasource/stackdriver/specs/query_ctrl.test.ts b/public/app/plugins/datasource/stackdriver/specs/query_ctrl.test.ts index c7e167c7c35..590dea22601 100644 --- a/public/app/plugins/datasource/stackdriver/specs/query_ctrl.test.ts +++ b/public/app/plugins/datasource/stackdriver/specs/query_ctrl.test.ts @@ -408,7 +408,7 @@ function createCtrlWithFakes(existingFilters?: string[]) { return { type: 'condition', value: val }; }, }; - return new StackdriverQueryCtrl(null, null, fakeSegmentServer, null, new TemplateSrvStub()); + return new StackdriverQueryCtrl(null, null, fakeSegmentServer, new TemplateSrvStub()); } function createTarget(existingFilters?: string[]) {