From f4658b72e1d3673af8cd7964b7d8d3199337c9dd Mon Sep 17 00:00:00 2001 From: Joey Tawadrous <90795735+joey-grafana@users.noreply.github.com> Date: Mon, 28 Feb 2022 10:15:33 +0000 Subject: [PATCH] InfluxDB: variables migration to backend (#45512) * Remove check for explore * Remove core app import * applyVariables for select * applyVariables for tags * applyVariables for other query props * applyTemplateVariables for flux * applyTemplateVariables for influx * Backwards compatibility * interpolateVariablesInQueries * Update InfluxQL mode and alias in test * Should interpolate all variables with Flux mode * Variables should be interpolated correctly * Removed unused import * explore in if check * Removing backwards compat copy from classicQuery * Return expandedQuery * Const --- .../plugins/datasource/influxdb/datasource.ts | 130 +++++++++--------- .../influxdb/specs/datasource.test.ts | 122 +++++++++++----- 2 files changed, 151 insertions(+), 101 deletions(-) diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index e9cff1ca03a..ae483f9b057 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -227,18 +227,21 @@ export default class InfluxDatasource extends DataSourceWithBackend { - // this only works in flux-mode, it should not be called in non-flux-mode - if (!this.isFlux) { - return query; - } - // We want to interpolate these variables on backend const { __interval, __interval_ms, ...rest } = scopedVars; - return { - ...query, - query: this.templateSrv.replace(query.query ?? '', rest), // The raw query text - }; + if (this.isFlux) { + return { + ...query, + query: this.templateSrv.replace(query.query ?? '', rest), // The raw query text + }; + } + + if (config.featureToggles.influxdbBackendMigration && this.access === 'proxy') { + query = this.applyVariables(query, scopedVars, rest); + } + + return query; } /** @@ -381,67 +384,68 @@ export default class InfluxDatasource extends DataSourceWithBackend 0) { - expandedQueries = queries.map((query) => { - if (this.isFlux) { - return { - ...query, - datasource: this.getRef(), - query: this.templateSrv.replace(query.query ?? '', scopedVars, 'regex'), - }; - } - - const expandedQuery = { + return queries.map((query) => { + if (this.isFlux) { + return { ...query, datasource: this.getRef(), - measurement: this.templateSrv.replace(query.measurement ?? '', scopedVars, 'regex'), - policy: this.templateSrv.replace(query.policy ?? '', scopedVars, 'regex'), - limit: this.templateSrv.replace(query.limit?.toString() ?? '', scopedVars, 'regex'), - slimit: this.templateSrv.replace(query.slimit?.toString() ?? '', scopedVars, 'regex'), - tz: this.templateSrv.replace(query.tz ?? '', scopedVars), + query: this.templateSrv.replace(query.query ?? '', scopedVars), // The raw query text }; + } - if (query.rawQuery) { - expandedQuery.query = this.templateSrv.replace(query.query ?? '', scopedVars, 'regex'); - } + return { + ...query, + datasource: this.getRef(), + ...this.applyVariables(query, scopedVars, scopedVars), + }; + }); + } - if (query.groupBy) { - expandedQuery.groupBy = query.groupBy.map((groupBy) => { - return { - ...groupBy, - params: groupBy.params?.map((param) => { - return this.templateSrv.replace(param.toString(), undefined, 'regex'); - }), - }; - }); - } - - if (query.select) { - expandedQuery.select = query.select.map((selects) => { - return selects.map((select: any) => { - return { - ...select, - params: select.params?.map((param: any) => { - return this.templateSrv.replace(param.toString(), undefined, 'regex'); - }), - }; - }); - }); - } - - if (query.tags) { - expandedQuery.tags = query.tags.map((tag) => { - return { - ...tag, - value: this.templateSrv.replace(tag.value, undefined, 'regex'), - }; - }); - } - return expandedQuery; + applyVariables(query: InfluxQuery, scopedVars: ScopedVars, rest: ScopedVars) { + const expandedQuery = { ...query }; + if (query.groupBy) { + expandedQuery.groupBy = query.groupBy.map((groupBy) => { + return { + ...groupBy, + params: groupBy.params?.map((param) => { + return this.templateSrv.replace(param.toString(), undefined, 'regex'); + }), + }; }); } - return expandedQueries; + + if (query.select) { + expandedQuery.select = query.select.map((selects) => { + return selects.map((select: any) => { + return { + ...select, + params: select.params?.map((param: any) => { + return this.templateSrv.replace(param.toString(), undefined, 'regex'); + }), + }; + }); + }); + } + + if (query.tags) { + expandedQuery.tags = query.tags.map((tag) => { + return { + ...tag, + value: this.templateSrv.replace(tag.value, undefined, 'regex'), + }; + }); + } + + return { + ...expandedQuery, + query: this.templateSrv.replace(query.query ?? '', rest), // The raw query text + alias: this.templateSrv.replace(query.alias ?? '', scopedVars), + limit: this.templateSrv.replace(query.limit?.toString() ?? '', scopedVars, 'regex'), + measurement: this.templateSrv.replace(query.measurement ?? '', scopedVars, 'regex'), + policy: this.templateSrv.replace(query.policy ?? '', scopedVars, 'regex'), + slimit: this.templateSrv.replace(query.slimit?.toString() ?? '', scopedVars, 'regex'), + tz: this.templateSrv.replace(query.tz ?? '', scopedVars), + }; } async metricFindQuery(query: string, options?: any): Promise { diff --git a/public/app/plugins/datasource/influxdb/specs/datasource.test.ts b/public/app/plugins/datasource/influxdb/specs/datasource.test.ts index c9042cd72ea..eb3fcc2e556 100644 --- a/public/app/plugins/datasource/influxdb/specs/datasource.test.ts +++ b/public/app/plugins/datasource/influxdb/specs/datasource.test.ts @@ -4,6 +4,7 @@ import { FetchResponse } from '@grafana/runtime'; import InfluxDatasource from '../datasource'; import { TemplateSrvStub } from 'test/specs/helpers'; import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__ +import config from 'app/core/config'; //@ts-ignore const templateSrv = new TemplateSrvStub(); @@ -174,54 +175,99 @@ describe('InfluxDataSource', () => { }); }); - describe('Interpolating query variables for dashboard->explore', () => { + describe('Variables should be interpolated correctly', () => { const templateSrv: any = { replace: jest.fn() }; const instanceSettings: any = {}; const ds = new InfluxDatasource(instanceSettings, templateSrv); const text = 'interpolationText'; + templateSrv.replace.mockReturnValue(text); - it('Should interpolate all variables', () => { - const query = { - refId: 'x', - measurement: '$interpolationVar', - policy: '$interpolationVar', - limit: '$interpolationVar', - slimit: '$interpolationVar', - tz: '$interpolationVar', - tags: [ - { - key: 'cpu', - operator: '=~', - value: '/^$interpolationVar$/', - }, - ], - groupBy: [ + const fluxQuery = { + refId: 'x', + query: '$interpolationVar', + }; + + const influxQuery = { + refId: 'x', + alias: '$interpolationVar', + measurement: '$interpolationVar', + policy: '$interpolationVar', + limit: '$interpolationVar', + slimit: '$interpolationVar', + tz: '$interpolationVar', + tags: [ + { + key: 'cpu', + operator: '=~', + value: '/^$interpolationVar$/', + }, + ], + groupBy: [ + { + params: ['$interpolationVar'], + type: 'tag', + }, + ], + select: [ + [ { params: ['$interpolationVar'], - type: 'tag', + type: 'field', }, ], - select: [ - [ - { - params: ['$interpolationVar'], - type: 'field', - }, - ], - ], - }; - templateSrv.replace.mockReturnValue(text); + ], + }; - const queries = ds.interpolateVariablesInQueries([query], { interpolationVar: { text: text, value: text } }); - expect(templateSrv.replace).toBeCalledTimes(8); - expect(queries[0].measurement).toBe(text); - expect(queries[0].policy).toBe(text); - expect(queries[0].limit).toBe(text); - expect(queries[0].slimit).toBe(text); - expect(queries[0].tz).toBe(text); - expect(queries[0].tags![0].value).toBe(text); - expect(queries[0].groupBy![0].params![0]).toBe(text); - expect(queries[0].select![0][0].params![0]).toBe(text); + function fluxChecks(query: any) { + expect(templateSrv.replace).toBeCalledTimes(1); + expect(query).toBe(text); + } + + function influxChecks(query: any) { + expect(templateSrv.replace).toBeCalledTimes(10); + expect(query.alias).toBe(text); + expect(query.measurement).toBe(text); + expect(query.policy).toBe(text); + expect(query.limit).toBe(text); + expect(query.slimit).toBe(text); + expect(query.tz).toBe(text); + expect(query.tags![0].value).toBe(text); + expect(query.groupBy![0].params![0]).toBe(text); + expect(query.select![0][0].params![0]).toBe(text); + } + + describe('when interpolating query variables for dashboard->explore', () => { + it('should interpolate all variables with Flux mode', () => { + ds.isFlux = true; + const queries = ds.interpolateVariablesInQueries([fluxQuery], { + interpolationVar: { text: text, value: text }, + }); + fluxChecks(queries[0].query); + }); + + it('should interpolate all variables with InfluxQL mode', () => { + ds.isFlux = false; + const queries = ds.interpolateVariablesInQueries([influxQuery], { + interpolationVar: { text: text, value: text }, + }); + influxChecks(queries[0]); + }); + }); + + describe('when interpolating template variables', () => { + it('should apply all template variables with Flux mode', () => { + ds.isFlux = true; + const query = ds.applyTemplateVariables(fluxQuery, { interpolationVar: { text: text, value: text } }); + fluxChecks(query.query); + }); + + it('should apply all template variables with InfluxQL mode', () => { + ds.isFlux = false; + ds.access = 'proxy'; + config.featureToggles.influxdbBackendMigration = true; + const query = ds.applyTemplateVariables(influxQuery, { interpolationVar: { text: text, value: text } }); + influxChecks(query); + }); }); }); });