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
This commit is contained in:
Joey Tawadrous
2022-02-28 10:15:33 +00:00
committed by GitHub
parent 4ab191b612
commit f4658b72e1
2 changed files with 151 additions and 101 deletions

View File

@@ -227,18 +227,21 @@ export default class InfluxDatasource extends DataSourceWithBackend<InfluxQuery,
} }
applyTemplateVariables(query: InfluxQuery, scopedVars: ScopedVars): Record<string, any> { applyTemplateVariables(query: InfluxQuery, scopedVars: ScopedVars): Record<string, any> {
// 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 // We want to interpolate these variables on backend
const { __interval, __interval_ms, ...rest } = scopedVars; const { __interval, __interval_ms, ...rest } = scopedVars;
return { if (this.isFlux) {
...query, return {
query: this.templateSrv.replace(query.query ?? '', rest), // The raw query text ...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<InfluxQuery,
return []; return [];
} }
let expandedQueries = queries; return queries.map((query) => {
if (queries && queries.length > 0) { if (this.isFlux) {
expandedQueries = queries.map((query) => { return {
if (this.isFlux) {
return {
...query,
datasource: this.getRef(),
query: this.templateSrv.replace(query.query ?? '', scopedVars, 'regex'),
};
}
const expandedQuery = {
...query, ...query,
datasource: this.getRef(), datasource: this.getRef(),
measurement: this.templateSrv.replace(query.measurement ?? '', scopedVars, 'regex'), query: this.templateSrv.replace(query.query ?? '', scopedVars), // The raw query text
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),
}; };
}
if (query.rawQuery) { return {
expandedQuery.query = this.templateSrv.replace(query.query ?? '', scopedVars, 'regex'); ...query,
} datasource: this.getRef(),
...this.applyVariables(query, scopedVars, scopedVars),
};
});
}
if (query.groupBy) { applyVariables(query: InfluxQuery, scopedVars: ScopedVars, rest: ScopedVars) {
expandedQuery.groupBy = query.groupBy.map((groupBy) => { const expandedQuery = { ...query };
return { if (query.groupBy) {
...groupBy, expandedQuery.groupBy = query.groupBy.map((groupBy) => {
params: groupBy.params?.map((param) => { return {
return this.templateSrv.replace(param.toString(), undefined, 'regex'); ...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;
}); });
} }
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<MetricFindValue[]> { async metricFindQuery(query: string, options?: any): Promise<MetricFindValue[]> {

View File

@@ -4,6 +4,7 @@ import { FetchResponse } from '@grafana/runtime';
import InfluxDatasource from '../datasource'; import InfluxDatasource from '../datasource';
import { TemplateSrvStub } from 'test/specs/helpers'; import { TemplateSrvStub } from 'test/specs/helpers';
import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__ import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
import config from 'app/core/config';
//@ts-ignore //@ts-ignore
const templateSrv = new TemplateSrvStub(); 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 templateSrv: any = { replace: jest.fn() };
const instanceSettings: any = {}; const instanceSettings: any = {};
const ds = new InfluxDatasource(instanceSettings, templateSrv); const ds = new InfluxDatasource(instanceSettings, templateSrv);
const text = 'interpolationText'; const text = 'interpolationText';
templateSrv.replace.mockReturnValue(text);
it('Should interpolate all variables', () => { const fluxQuery = {
const query = { refId: 'x',
refId: 'x', query: '$interpolationVar',
measurement: '$interpolationVar', };
policy: '$interpolationVar',
limit: '$interpolationVar', const influxQuery = {
slimit: '$interpolationVar', refId: 'x',
tz: '$interpolationVar', alias: '$interpolationVar',
tags: [ measurement: '$interpolationVar',
{ policy: '$interpolationVar',
key: 'cpu', limit: '$interpolationVar',
operator: '=~', slimit: '$interpolationVar',
value: '/^$interpolationVar$/', tz: '$interpolationVar',
}, tags: [
], {
groupBy: [ key: 'cpu',
operator: '=~',
value: '/^$interpolationVar$/',
},
],
groupBy: [
{
params: ['$interpolationVar'],
type: 'tag',
},
],
select: [
[
{ {
params: ['$interpolationVar'], 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 } }); function fluxChecks(query: any) {
expect(templateSrv.replace).toBeCalledTimes(8); expect(templateSrv.replace).toBeCalledTimes(1);
expect(queries[0].measurement).toBe(text); expect(query).toBe(text);
expect(queries[0].policy).toBe(text); }
expect(queries[0].limit).toBe(text);
expect(queries[0].slimit).toBe(text); function influxChecks(query: any) {
expect(queries[0].tz).toBe(text); expect(templateSrv.replace).toBeCalledTimes(10);
expect(queries[0].tags![0].value).toBe(text); expect(query.alias).toBe(text);
expect(queries[0].groupBy![0].params![0]).toBe(text); expect(query.measurement).toBe(text);
expect(queries[0].select![0][0].params![0]).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);
});
}); });
}); });
}); });