From 6687409efba0efb2e6b71625b9782370b19ff111 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 25 Apr 2018 15:36:00 +0200 Subject: [PATCH 1/2] prometheus: fix variable query to fallback correctly to series query Using a query of for example up or up{job=job1} --- public/app/plugins/datasource/prometheus/datasource.ts | 4 ++++ public/app/plugins/datasource/prometheus/metric_find_query.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index cbf701a0abe..2a8b3069a53 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -329,4 +329,8 @@ export class PrometheusDatasource { } return Math.ceil(date.valueOf() / 1000); } + + getOriginalMetricName(labelData) { + return this.resultTransformer.getOriginalMetricName(labelData); + } } diff --git a/public/app/plugins/datasource/prometheus/metric_find_query.ts b/public/app/plugins/datasource/prometheus/metric_find_query.ts index 337cd74c14c..13b6d7df8e3 100644 --- a/public/app/plugins/datasource/prometheus/metric_find_query.ts +++ b/public/app/plugins/datasource/prometheus/metric_find_query.ts @@ -121,7 +121,7 @@ export default class PrometheusMetricFindQuery { var self = this; return this.datasource.metadataRequest(url).then(function(result) { - return _.map(result.data.data, function(metric) { + return _.map(result.data.data, metric => { return { text: self.datasource.getOriginalMetricName(metric), expandable: true, From f112e38266a4cbb96c170fc213e2533d0c06c814 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 25 Apr 2018 15:36:47 +0200 Subject: [PATCH 2/2] prometheus: convert metric find query tests to jest --- .../prometheus/specs/datasource.jest.ts | 20 ++ .../specs/metric_find_query.jest.ts | 205 ++++++++++++++++++ .../specs/metric_find_query_specs.ts | 181 ---------------- 3 files changed, 225 insertions(+), 181 deletions(-) create mode 100644 public/app/plugins/datasource/prometheus/specs/metric_find_query.jest.ts delete mode 100644 public/app/plugins/datasource/prometheus/specs/metric_find_query_specs.ts diff --git a/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts b/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts index a997a2d8233..2ab2895d731 100644 --- a/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts +++ b/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts @@ -43,6 +43,26 @@ describe('PrometheusDatasource', () => { }); }); + describe('When performing performSuggestQuery', () => { + it('should cache response', async () => { + ctx.backendSrvMock.datasourceRequest.mockReturnValue( + Promise.resolve({ + status: 'success', + data: { data: ['value1', 'value2', 'value3'] }, + }) + ); + + let results = await ctx.ds.performSuggestQuery('value', true); + + expect(results).toHaveLength(3); + + ctx.backendSrvMock.datasourceRequest.mockReset(); + results = await ctx.ds.performSuggestQuery('value', true); + + expect(results).toHaveLength(3); + }); + }); + describe('When converting prometheus histogram to heatmap format', () => { beforeEach(() => { ctx.query = { diff --git a/public/app/plugins/datasource/prometheus/specs/metric_find_query.jest.ts b/public/app/plugins/datasource/prometheus/specs/metric_find_query.jest.ts new file mode 100644 index 00000000000..88f6830cd31 --- /dev/null +++ b/public/app/plugins/datasource/prometheus/specs/metric_find_query.jest.ts @@ -0,0 +1,205 @@ +import moment from 'moment'; +import { PrometheusDatasource } from '../datasource'; +import PrometheusMetricFindQuery from '../metric_find_query'; +import q from 'q'; + +describe('PrometheusMetricFindQuery', function() { + let instanceSettings = { + url: 'proxied', + directUrl: 'direct', + user: 'test', + password: 'mupp', + jsonData: { httpMethod: 'GET' }, + }; + const raw = { + from: moment.utc('2018-04-25 10:00'), + to: moment.utc('2018-04-25 11:00'), + }; + let ctx: any = { + backendSrvMock: { + datasourceRequest: jest.fn(() => Promise.resolve({})), + }, + templateSrvMock: { + replace: a => a, + }, + timeSrvMock: { + timeRange: () => ({ + from: raw.from, + to: raw.to, + raw: raw, + }), + }, + }; + + ctx.setupMetricFindQuery = (data: any) => { + ctx.backendSrvMock.datasourceRequest.mockReturnValue(Promise.resolve({ status: 'success', data: data.response })); + return new PrometheusMetricFindQuery(ctx.ds, data.query, ctx.timeSrvMock); + }; + + beforeEach(() => { + ctx.backendSrvMock.datasourceRequest.mockReset(); + ctx.ds = new PrometheusDatasource(instanceSettings, q, ctx.backendSrvMock, ctx.templateSrvMock, ctx.timeSrvMock); + }); + + describe('When performing metricFindQuery', () => { + it('label_values(resource) should generate label search query', async () => { + const query = ctx.setupMetricFindQuery({ + query: 'label_values(resource)', + response: { + data: ['value1', 'value2', 'value3'], + }, + }); + const results = await query.process(); + + expect(results).toHaveLength(3); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({ + method: 'GET', + url: 'proxied/api/v1/label/resource/values', + silent: true, + }); + }); + + it('label_values(metric, resource) should generate series query with correct time', async () => { + const query = ctx.setupMetricFindQuery({ + query: 'label_values(metric, resource)', + response: { + data: [ + { __name__: 'metric', resource: 'value1' }, + { __name__: 'metric', resource: 'value2' }, + { __name__: 'metric', resource: 'value3' }, + ], + }, + }); + const results = await query.process(); + + expect(results).toHaveLength(3); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({ + method: 'GET', + url: `proxied/api/v1/series?match[]=metric&start=${raw.from.unix()}&end=${raw.to.unix()}`, + silent: true, + }); + }); + + it('label_values(metric{label1="foo", label2="bar", label3="baz"}, resource) should generate series query with correct time', async () => { + const query = ctx.setupMetricFindQuery({ + query: 'label_values(metric{label1="foo", label2="bar", label3="baz"}, resource)', + response: { + data: [ + { __name__: 'metric', resource: 'value1' }, + { __name__: 'metric', resource: 'value2' }, + { __name__: 'metric', resource: 'value3' }, + ], + }, + }); + const results = await query.process(); + + expect(results).toHaveLength(3); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({ + method: 'GET', + url: `proxied/api/v1/series?match[]=${encodeURIComponent( + 'metric{label1="foo", label2="bar", label3="baz"}' + )}&start=${raw.from.unix()}&end=${raw.to.unix()}`, + silent: true, + }); + }); + + it('label_values(metric, resource) result should not contain empty string', async () => { + const query = ctx.setupMetricFindQuery({ + query: 'label_values(metric, resource)', + response: { + data: [ + { __name__: 'metric', resource: 'value1' }, + { __name__: 'metric', resource: 'value2' }, + { __name__: 'metric', resource: '' }, + ], + }, + }); + const results = await query.process(); + + expect(results).toHaveLength(2); + expect(results[0].text).toBe('value1'); + expect(results[1].text).toBe('value2'); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({ + method: 'GET', + url: `proxied/api/v1/series?match[]=metric&start=${raw.from.unix()}&end=${raw.to.unix()}`, + silent: true, + }); + }); + + it('metrics(metric.*) should generate metric name query', async () => { + const query = ctx.setupMetricFindQuery({ + query: 'metrics(metric.*)', + response: { + data: ['metric1', 'metric2', 'metric3', 'nomatch'], + }, + }); + const results = await query.process(); + + expect(results).toHaveLength(3); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({ + method: 'GET', + url: 'proxied/api/v1/label/__name__/values', + silent: true, + }); + }); + + it('query_result(metric) should generate metric name query', async () => { + const query = ctx.setupMetricFindQuery({ + query: 'query_result(metric)', + response: { + data: { + resultType: 'vector', + result: [ + { + metric: { __name__: 'metric', job: 'testjob' }, + value: [1443454528.0, '3846'], + }, + ], + }, + }, + }); + const results = await query.process(); + + expect(results).toHaveLength(1); + expect(results[0].text).toBe('metric{job="testjob"} 3846 1443454528000'); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({ + method: 'GET', + url: `proxied/api/v1/query?query=metric&time=${raw.to.unix()}`, + requestId: undefined, + }); + }); + + it('up{job="job1"} should fallback using generate series query', async () => { + const query = ctx.setupMetricFindQuery({ + query: 'up{job="job1"}', + response: { + data: [ + { __name__: 'up', instance: '127.0.0.1:1234', job: 'job1' }, + { __name__: 'up', instance: '127.0.0.1:5678', job: 'job1' }, + { __name__: 'up', instance: '127.0.0.1:9102', job: 'job1' }, + ], + }, + }); + const results = await query.process(); + + expect(results).toHaveLength(3); + expect(results[0].text).toBe('up{instance="127.0.0.1:1234",job="job1"}'); + expect(results[1].text).toBe('up{instance="127.0.0.1:5678",job="job1"}'); + expect(results[2].text).toBe('up{instance="127.0.0.1:9102",job="job1"}'); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({ + method: 'GET', + url: `proxied/api/v1/series?match[]=${encodeURIComponent( + 'up{job="job1"}' + )}&start=${raw.from.unix()}&end=${raw.to.unix()}`, + silent: true, + }); + }); + }); +}); diff --git a/public/app/plugins/datasource/prometheus/specs/metric_find_query_specs.ts b/public/app/plugins/datasource/prometheus/specs/metric_find_query_specs.ts deleted file mode 100644 index e5d7aa81210..00000000000 --- a/public/app/plugins/datasource/prometheus/specs/metric_find_query_specs.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { describe, beforeEach, it, expect, angularMocks } from 'test/lib/common'; - -import moment from 'moment'; -import helpers from 'test/specs/helpers'; -import { PrometheusDatasource } from '../datasource'; -import PrometheusMetricFindQuery from '../metric_find_query'; - -describe('PrometheusMetricFindQuery', function() { - var ctx = new helpers.ServiceTestContext(); - var instanceSettings = { - url: 'proxied', - directUrl: 'direct', - user: 'test', - password: 'mupp', - jsonData: { httpMethod: 'GET' }, - }; - - beforeEach(angularMocks.module('grafana.core')); - beforeEach(angularMocks.module('grafana.services')); - beforeEach( - angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) { - ctx.$q = $q; - ctx.$httpBackend = $httpBackend; - ctx.$rootScope = $rootScope; - ctx.ds = $injector.instantiate(PrometheusDatasource, { - instanceSettings: instanceSettings, - }); - $httpBackend.when('GET', /\.html$/).respond(''); - }) - ); - - describe('When performing metricFindQuery', function() { - var results; - var response; - it('label_values(resource) should generate label search query', function() { - response = { - status: 'success', - data: ['value1', 'value2', 'value3'], - }; - ctx.$httpBackend.expect('GET', 'proxied/api/v1/label/resource/values').respond(response); - var pm = new PrometheusMetricFindQuery(ctx.ds, 'label_values(resource)', ctx.timeSrv); - pm.process().then(function(data) { - results = data; - }); - ctx.$httpBackend.flush(); - ctx.$rootScope.$apply(); - expect(results.length).to.be(3); - }); - it('label_values(metric, resource) should generate series query', function() { - response = { - status: 'success', - data: [ - { __name__: 'metric', resource: 'value1' }, - { __name__: 'metric', resource: 'value2' }, - { __name__: 'metric', resource: 'value3' }, - ], - }; - ctx.$httpBackend.expect('GET', /proxied\/api\/v1\/series\?match\[\]=metric&start=.*&end=.*/).respond(response); - var pm = new PrometheusMetricFindQuery(ctx.ds, 'label_values(metric, resource)', ctx.timeSrv); - pm.process().then(function(data) { - results = data; - }); - ctx.$httpBackend.flush(); - ctx.$rootScope.$apply(); - expect(results.length).to.be(3); - }); - it('label_values(metric, resource) should pass correct time', function() { - ctx.timeSrv.setTime({ - from: moment.utc('2011-01-01'), - to: moment.utc('2015-01-01'), - }); - ctx.$httpBackend - .expect('GET', /proxied\/api\/v1\/series\?match\[\]=metric&start=1293840000&end=1420070400/) - .respond(response); - var pm = new PrometheusMetricFindQuery(ctx.ds, 'label_values(metric, resource)', ctx.timeSrv); - pm.process().then(function(data) { - results = data; - }); - ctx.$httpBackend.flush(); - ctx.$rootScope.$apply(); - }); - it('label_values(metric{label1="foo", label2="bar", label3="baz"}, resource) should generate series query', function() { - response = { - status: 'success', - data: [ - { __name__: 'metric', resource: 'value1' }, - { __name__: 'metric', resource: 'value2' }, - { __name__: 'metric', resource: 'value3' }, - ], - }; - ctx.$httpBackend.expect('GET', /proxied\/api\/v1\/series\?match\[\]=metric&start=.*&end=.*/).respond(response); - var pm = new PrometheusMetricFindQuery(ctx.ds, 'label_values(metric, resource)', ctx.timeSrv); - pm.process().then(function(data) { - results = data; - }); - ctx.$httpBackend.flush(); - ctx.$rootScope.$apply(); - expect(results.length).to.be(3); - }); - it('label_values(metric, resource) result should not contain empty string', function() { - response = { - status: 'success', - data: [ - { __name__: 'metric', resource: 'value1' }, - { __name__: 'metric', resource: 'value2' }, - { __name__: 'metric', resource: '' }, - ], - }; - ctx.$httpBackend.expect('GET', /proxied\/api\/v1\/series\?match\[\]=metric&start=.*&end=.*/).respond(response); - var pm = new PrometheusMetricFindQuery(ctx.ds, 'label_values(metric, resource)', ctx.timeSrv); - pm.process().then(function(data) { - results = data; - }); - ctx.$httpBackend.flush(); - ctx.$rootScope.$apply(); - expect(results.length).to.be(2); - expect(results[0].text).to.be('value1'); - expect(results[1].text).to.be('value2'); - }); - it('metrics(metric.*) should generate metric name query', function() { - response = { - status: 'success', - data: ['metric1', 'metric2', 'metric3', 'nomatch'], - }; - ctx.$httpBackend.expect('GET', 'proxied/api/v1/label/__name__/values').respond(response); - var pm = new PrometheusMetricFindQuery(ctx.ds, 'metrics(metric.*)', ctx.timeSrv); - pm.process().then(function(data) { - results = data; - }); - ctx.$httpBackend.flush(); - ctx.$rootScope.$apply(); - expect(results.length).to.be(3); - }); - it('query_result(metric) should generate metric name query', function() { - response = { - status: 'success', - data: { - resultType: 'vector', - result: [ - { - metric: { __name__: 'metric', job: 'testjob' }, - value: [1443454528.0, '3846'], - }, - ], - }, - }; - ctx.$httpBackend.expect('GET', /proxied\/api\/v1\/query\?query=metric&time=.*/).respond(response); - var pm = new PrometheusMetricFindQuery(ctx.ds, 'query_result(metric)', ctx.timeSrv); - pm.process().then(function(data) { - results = data; - }); - ctx.$httpBackend.flush(); - ctx.$rootScope.$apply(); - expect(results.length).to.be(1); - expect(results[0].text).to.be('metric{job="testjob"} 3846 1443454528000'); - }); - }); - - describe('When performing performSuggestQuery', function() { - var results; - var response; - it('cache response', function() { - response = { - status: 'success', - data: ['value1', 'value2', 'value3'], - }; - ctx.$httpBackend.expect('GET', 'proxied/api/v1/label/__name__/values').respond(response); - ctx.ds.performSuggestQuery('value', true).then(function(data) { - results = data; - }); - ctx.$httpBackend.flush(); - ctx.$rootScope.$apply(); - expect(results.length).to.be(3); - ctx.ds.performSuggestQuery('value', true).then(function(data) { - // get from cache, no need to flush - results = data; - expect(results.length).to.be(3); - }); - }); - }); -});