diff --git a/public/app/plugins/datasource/influxdb/__mocks__/datasource.ts b/public/app/plugins/datasource/influxdb/__mocks__/datasource.ts index 6630252bd64..4024021c2f3 100644 --- a/public/app/plugins/datasource/influxdb/__mocks__/datasource.ts +++ b/public/app/plugins/datasource/influxdb/__mocks__/datasource.ts @@ -1,32 +1,23 @@ import { of } from 'rxjs'; -import { AdHocVariableFilter, DataSourceInstanceSettings, PluginType, ScopedVars } from '@grafana/data'; -import { FetchResponse, getBackendSrv, setBackendSrv, VariableInterpolation } from '@grafana/runtime'; +import { DataSourceInstanceSettings, PluginType, ScopedVars } from '@grafana/data'; +import { FetchResponse, getBackendSrv, setBackendSrv, TemplateSrv, VariableInterpolation } from '@grafana/runtime'; -import { TemplateSrv } from '../../../../features/templating/template_srv'; import InfluxDatasource from '../datasource'; import { InfluxOptions, InfluxVersion } from '../types'; -const getAdhocFiltersMock = jest.fn().mockImplementation(() => []); -const replaceMock = jest.fn().mockImplementation((a: string, ...rest: unknown[]) => a); - -export const templateSrvStub = { - getAdhocFilters: getAdhocFiltersMock, - replace: replaceMock, -} as unknown as TemplateSrv; - +export const replaceMock = jest.fn().mockImplementation((a: string, ...rest: unknown[]) => a); +export const templateSrvStub = mockTemplateSrv(replaceMock); export function mockTemplateSrv( - getAdhocFiltersMock: (datasourceName: string) => AdHocVariableFilter[], - replaceMock: ( + rm: ( target?: string, scopedVars?: ScopedVars, format?: string | Function | undefined, interpolations?: VariableInterpolation[] - ) => string + ) => string = replaceMock ): TemplateSrv { return { - getAdhocFilters: getAdhocFiltersMock, - replace: replaceMock, + replace: rm, } as unknown as TemplateSrv; } @@ -42,7 +33,7 @@ export function mockBackendService(response: FetchResponse) { export function getMockInfluxDS( instanceSettings: DataSourceInstanceSettings = getMockDSInstanceSettings(), - templateSrv: TemplateSrv = templateSrvStub + templateSrv: TemplateSrv = mockTemplateSrv(replaceMock) ): InfluxDatasource { return new InfluxDatasource(instanceSettings, templateSrv); } diff --git a/public/app/plugins/datasource/influxdb/__mocks__/request.ts b/public/app/plugins/datasource/influxdb/__mocks__/request.ts index 986c780922d..d646b62a4ef 100644 --- a/public/app/plugins/datasource/influxdb/__mocks__/request.ts +++ b/public/app/plugins/datasource/influxdb/__mocks__/request.ts @@ -19,6 +19,7 @@ export const mockInfluxQueryRequest = (targets?: QueryType[]): DataQueryRequest< from: dateTime(0), to: dateTime(10), }, + filters: [{ key: 'adhoc_key', value: 'adhoc_val', operator: '=' }], requestId: '', scopedVars: {}, startTime: 0, diff --git a/public/app/plugins/datasource/influxdb/datasource.test.ts b/public/app/plugins/datasource/influxdb/datasource.test.ts index b33a6787bbc..5d961dd3866 100644 --- a/public/app/plugins/datasource/influxdb/datasource.test.ts +++ b/public/app/plugins/datasource/influxdb/datasource.test.ts @@ -1,18 +1,15 @@ import { lastValueFrom, of } from 'rxjs'; -import { BackendSrvRequest } from '@grafana/runtime'; +import { BackendSrvRequest, TemplateSrv } from '@grafana/runtime'; import config from 'app/core/config'; -import { TemplateSrv } from '../../../features/templating/template_srv'; import { queryBuilder } from '../../../features/variables/shared/testing/builders'; -import { getMockDSInstanceSettings, getMockInfluxDS, mockBackendService } from './__mocks__/datasource'; -import { queryOptions } from './__mocks__/query'; -import { mockInfluxQueryRequest, mockInfluxQueryWithTemplateVars } from './__mocks__/request'; +import { getMockDSInstanceSettings, getMockInfluxDS, mockBackendService, replaceMock } from './__mocks__/datasource'; +import { mockInfluxQueryRequest } from './__mocks__/request'; import { mockInfluxFetchResponse, mockMetricFindQueryResponse } from './__mocks__/response'; import { BROWSER_MODE_DISABLED_MESSAGE } from './constants'; import InfluxDatasource from './datasource'; -import { InfluxQuery, InfluxVersion } from './types'; const fetchMock = mockBackendService(mockInfluxFetchResponse()); @@ -142,24 +139,11 @@ describe('InfluxDataSource Frontend Mode [influxdbBackendMigration=false]', () = // Update this after starting to use TemplateSrv from @grafana/runtime package describe('adhoc variables', () => { - const adhocFilters = [ - { - key: 'adhoc_key', - operator: '=', - value: 'adhoc_val', - condition: '', - }, - ]; - const mockTemplateService = new TemplateSrv(); - mockTemplateService.getAdhocFilters = jest.fn((_: string) => adhocFilters); - let ds = getMockInfluxDS(getMockDSInstanceSettings(), mockTemplateService); + let ds = getMockInfluxDS(getMockDSInstanceSettings()); it('query should contain the ad-hoc variable', () => { ds.query(mockInfluxQueryRequest()); - const expected = encodeURIComponent( - 'SELECT mean("value") FROM "cpu" WHERE time >= 0ms and time <= 10ms AND "adhoc_key" = \'adhoc_val\' GROUP BY time($__interval) fill(null)' - ); - expect(fetchMock.mock.calls[0][0].data).toBe(`q=${expected}`); + expect(replaceMock.mock.calls[0][0]).toBe('adhoc_val'); }); it('should make the fetch call for adhoc filter keys', () => { @@ -257,125 +241,6 @@ describe('InfluxDataSource Frontend Mode [influxdbBackendMigration=false]', () = expect(ds.database).toBe('fallback'); }); }); - - describe('variable interpolation', () => { - const variablesMock = [ - queryBuilder().withId('var1').withName('var1').withCurrent('var1_value').build(), - queryBuilder().withId('path').withName('path').withCurrent('/etc/hosts').build(), - ]; - const mockTemplateService = new TemplateSrv({ - getVariables: () => variablesMock, - getVariableWithName: (name: string) => variablesMock.filter((v) => v.name === name)[0], - getFilteredVariables: jest.fn(), - }); - // Remove this after start using TemplateSrv from @grafana/runtime - mockTemplateService.getAdhocFilters = jest.fn(); - - describe('when interpolating query variables for dashboard->explore', () => { - it('should interpolate all variables with Flux mode', () => { - const ds = getMockInfluxDS(getMockDSInstanceSettings({ version: InfluxVersion.Flux }), mockTemplateService); - const fluxQuery = { - refId: 'x', - query: 'some query with $var1 and $path', - }; - const queries = ds.interpolateVariablesInQueries([fluxQuery], {}); - expect(queries[0].query).toBe('some query with var1_value and /etc/hosts'); - }); - - it('should interpolate all variables with InfluxQL mode', () => { - const ds = getMockInfluxDS(getMockDSInstanceSettings({ version: InfluxVersion.InfluxQL }), mockTemplateService); - const [query] = ds.interpolateVariablesInQueries([mockInfluxQueryWithTemplateVars([])], {}); - expect(query.alias).toBe('var1_value'); - expect(query.measurement).toBe('var1_value'); - expect(query.policy).toBe('var1_value'); - expect(query.limit).toBe('var1_value'); - expect(query.slimit).toBe('var1_value'); - expect(query.tz).toBe('var1_value'); - expect(query.tags![0].value).toBe(`/^\\/etc\\/hosts$/`); - expect(query.groupBy![0].params![0]).toBe('var1_value'); - expect(query.select![0][0].params![0]).toBe('var1_value'); - }); - }); - - describe('applyTemplateVariables', () => { - it('should apply all template variables with Flux mode', () => { - const ds = getMockInfluxDS(getMockDSInstanceSettings({ version: InfluxVersion.Flux }), mockTemplateService); - const fluxQuery = { - refId: 'x', - query: '$var1', - }; - const query = ds.applyTemplateVariables(fluxQuery, {}); - expect(query.query).toBe('var1_value'); - }); - }); - - describe('variable interpolation with chained variables with frontend mode', () => { - let ds = getMockInfluxDS(getMockDSInstanceSettings(), mockTemplateService); - const fetchMockImpl = () => - of({ - data: { - status: 'success', - results: [ - { - series: [ - { - name: 'measurement', - columns: ['name'], - values: [['cpu']], - }, - ], - }, - ], - }, - }); - - beforeEach(() => { - jest.clearAllMocks(); - fetchMock.mockImplementation(fetchMockImpl); - }); - - it('should render chained regex variables with floating point number', () => { - ds.metricFindQuery(`SELECT sum("piece_count") FROM "rp"."pdata" WHERE diameter <= $maxSED`, { - scopedVars: { maxSED: { text: '8.1', value: '8.1' } }, - }); - const qe = `SELECT sum("piece_count") FROM "rp"."pdata" WHERE diameter <= 8.1`; - const qData = decodeURIComponent(fetchMock.mock.calls[0][0].data.substring(2)); - expect(qData).toBe(qe); - }); - - it('should render chained regex variables with URL', () => { - ds.metricFindQuery('SHOW TAG VALUES WITH KEY = "agent_url" WHERE agent_url =~ /^$var1$/', { - scopedVars: { - var1: { - text: 'https://aaaa-aa-aaa.bbb.ccc.ddd:8443/ggggg', - value: 'https://aaaa-aa-aaa.bbb.ccc.ddd:8443/ggggg', - }, - }, - }); - const qe = `SHOW TAG VALUES WITH KEY = "agent_url" WHERE agent_url =~ /^https:\\/\\/aaaa-aa-aaa\\.bbb\\.ccc\\.ddd:8443\\/ggggg$/`; - const qData = decodeURIComponent(fetchMock.mock.calls[0][0].data.substring(2)); - expect(qData).toBe(qe); - }); - - it('should render chained regex variables with floating point number and url', () => { - ds.metricFindQuery( - 'SELECT sum("piece_count") FROM "rp"."pdata" WHERE diameter <= $maxSED AND agent_url =~ /^$var1$/', - { - scopedVars: { - var1: { - text: 'https://aaaa-aa-aaa.bbb.ccc.ddd:8443/ggggg', - value: 'https://aaaa-aa-aaa.bbb.ccc.ddd:8443/ggggg', - }, - maxSED: { text: '8.1', value: '8.1' }, - }, - } - ); - const qe = `SELECT sum("piece_count") FROM "rp"."pdata" WHERE diameter <= 8.1 AND agent_url =~ /^https:\\/\\/aaaa-aa-aaa\\.bbb\\.ccc\\.ddd:8443\\/ggggg$/`; - const qData = decodeURIComponent(fetchMock.mock.calls[0][0].data.substring(2)); - expect(qData).toBe(qe); - }); - }); - }); }); describe('InfluxDataSource Backend Mode [influxdbBackendMigration=true]', () => { @@ -399,171 +264,10 @@ describe('InfluxDataSource Backend Mode [influxdbBackendMigration=true]', () => expect(values[0].text).toBe('test-t2-1'); }); }); - - describe('variable interpolation with chained variables with backend mode', () => { - const variablesMock = [ - queryBuilder().withId('var1').withName('var1').withCurrent('var1').build(), - queryBuilder().withId('path').withName('path').withCurrent('/etc/hosts').build(), - queryBuilder() - .withId('field_var') - .withName('field_var') - .withMulti(true) - .withOptions( - { - text: `field_1`, - value: `field_1`, - }, - { - text: `field_2`, - value: `field_2`, - }, - { - text: `field_3`, - value: `field_3`, - } - ) - .withCurrent(['field_1', 'field_3']) - .build(), - ]; - const mockTemplateService = new TemplateSrv({ - getVariables: () => variablesMock, - getVariableWithName: (name: string) => variablesMock.filter((v) => v.name === name)[0], - getFilteredVariables: jest.fn(), - }); - mockTemplateService.getAdhocFilters = jest.fn((_: string) => []); - let ds = getMockInfluxDS(getMockDSInstanceSettings(), mockTemplateService); - const fetchMockImpl = () => - of({ - data: { - status: 'success', - results: [ - { - series: [ - { - name: 'measurement', - columns: ['name'], - values: [['cpu']], - }, - ], - }, - ], - }, - }); - - beforeEach(() => { - jest.clearAllMocks(); - fetchMock.mockImplementation(fetchMockImpl); - }); - - it('should render chained regex variables with floating point number', () => { - ds.metricFindQuery(`SELECT sum("piece_count") FROM "rp"."pdata" WHERE diameter <= $maxSED`, { - ...queryOptions, - scopedVars: { maxSED: { text: '8.1', value: '8.1' } }, - }); - const qe = `SELECT sum("piece_count") FROM "rp"."pdata" WHERE diameter <= 8.1`; - const qData = fetchMock.mock.calls[0][0].data.queries[0].query; - expect(qData).toBe(qe); - }); - - it('should render chained regex variables with URL', () => { - ds.metricFindQuery('SHOW TAG VALUES WITH KEY = "agent_url" WHERE agent_url =~ /^$var1$/', { - ...queryOptions, - scopedVars: { - var1: { - text: 'https://aaaa-aa-aaa.bbb.ccc.ddd:8443/ggggg', - value: 'https://aaaa-aa-aaa.bbb.ccc.ddd:8443/ggggg', - }, - }, - }); - const qe = `SHOW TAG VALUES WITH KEY = "agent_url" WHERE agent_url =~ /^https:\\/\\/aaaa-aa-aaa\\.bbb\\.ccc\\.ddd:8443\\/ggggg$/`; - expect(fetchMock).toHaveBeenCalled(); - const qData = fetchMock.mock.calls[0][0].data.queries[0].query; - expect(qData).toBe(qe); - }); - - it('should render chained regex variables with floating point number and url', () => { - ds.metricFindQuery( - 'SELECT sum("piece_count") FROM "rp"."pdata" WHERE diameter <= $maxSED AND agent_url =~ /^$var1$/', - { - ...queryOptions, - scopedVars: { - var1: { - text: 'https://aaaa-aa-aaa.bbb.ccc.ddd:8443/ggggg', - value: 'https://aaaa-aa-aaa.bbb.ccc.ddd:8443/ggggg', - }, - maxSED: { text: '8.1', value: '8.1' }, - }, - } - ); - const qe = `SELECT sum("piece_count") FROM "rp"."pdata" WHERE diameter <= 8.1 AND agent_url =~ /^https:\\/\\/aaaa-aa-aaa\\.bbb\\.ccc\\.ddd:8443\\/ggggg$/`; - const qData = fetchMock.mock.calls[0][0].data.queries[0].query; - expect(qData).toBe(qe); - }); - - it('should interpolate variable inside a regex pattern', () => { - const query: InfluxQuery = { - refId: 'A', - tags: [ - { - key: 'key', - operator: '=~', - value: '/^.*-$var1$/', - }, - ], - }; - const res = ds.applyVariables(query, {}); - const expected = `/^.*-var1$/`; - expect(res.tags?.[0].value).toEqual(expected); - }); - - it('should remove regex wrappers when operator is not a regex operator', () => { - const query: InfluxQuery = { - refId: 'A', - tags: [ - { - key: 'key', - operator: '=', - value: '/^$path$/', - }, - ], - }; - const res = ds.applyVariables(query, {}); - const expected = `/etc/hosts`; - expect(res.tags?.[0].value).toEqual(expected); - }); - - it('should interpolate field keys with given scopedVars', () => { - const query: InfluxQuery = { - refId: 'A', - tags: [ - { - key: 'key', - operator: '=', - value: 'value', - }, - ], - select: [ - [ - { - type: 'field', - params: ['$field_var'], - }, - { - type: 'mean', - params: [], - }, - ], - ], - }; - const res = ds.applyVariables(query, { field_var: { text: 'field_3', value: 'field_3' } }); - const expected = `field_3`; - expect(res.select?.[0][0].params?.[0]).toEqual(expected); - }); - }); }); describe('interpolateQueryExpr', () => { - let ds = getMockInfluxDS(getMockDSInstanceSettings(), new TemplateSrv()); + let ds = getMockInfluxDS(getMockDSInstanceSettings(), {} as TemplateSrv); it('should return the value as it is', () => { const value = 'normalValue'; const variableMock = queryBuilder().withId('tempVar').withName('tempVar').withMulti(false).build(); diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index e1f514efa84..e25441fb557 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -3,6 +3,7 @@ import { lastValueFrom, merge, Observable, of, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { + AdHocVariableFilter, AnnotationEvent, DataFrame, DataQueryError, @@ -31,10 +32,11 @@ import { FetchResponse, frameToMetricFindValue, getBackendSrv, + getTemplateSrv, + TemplateSrv, } from '@grafana/runtime'; import { QueryFormat, SQLQuery } from '@grafana/sql'; import config from 'app/core/config'; -import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; import { AnnotationEditor } from './components/editor/annotation/AnnotationEditor'; import { FluxQueryEditor } from './components/editor/query/flux/FluxQueryEditor'; @@ -170,7 +172,11 @@ export default class InfluxDatasource extends DataSourceWithBackend { const { condition, ...asTag } = af; @@ -238,7 +244,7 @@ export default class InfluxDatasource extends DataSourceWithBackend { @@ -282,7 +288,7 @@ export default class InfluxDatasource extends DataSourceWithBackend target.adhocFilters ?? []); if (adhocFilters?.length || adhocFiltersFromDashboard?.length) { const ahFilters = adhocFilters?.length ? adhocFilters : adhocFiltersFromDashboard; diff --git a/public/app/plugins/datasource/influxdb/datasource_sql.test.ts b/public/app/plugins/datasource/influxdb/datasource_sql.test.ts index bc516995873..72d940d5ad0 100644 --- a/public/app/plugins/datasource/influxdb/datasource_sql.test.ts +++ b/public/app/plugins/datasource/influxdb/datasource_sql.test.ts @@ -14,7 +14,7 @@ mockBackendService(mockInfluxSQLFetchResponse); describe('InfluxDB SQL Support', () => { const replaceMock = jest.fn(); - const templateSrv = mockTemplateSrv(jest.fn(), replaceMock); + const templateSrv = mockTemplateSrv(replaceMock); let sqlQuery: SQLQuery; diff --git a/public/app/plugins/datasource/influxdb/influx_query_model.test.ts b/public/app/plugins/datasource/influxdb/influx_query_model.test.ts index 92a92f41aac..15aa3b72c15 100644 --- a/public/app/plugins/datasource/influxdb/influx_query_model.test.ts +++ b/public/app/plugins/datasource/influxdb/influx_query_model.test.ts @@ -1,4 +1,4 @@ -import { TemplateSrv } from 'app/features/templating/template_srv'; +import { TemplateSrv } from '@grafana/runtime'; import InfluxQueryModel from './influx_query_model'; diff --git a/public/app/plugins/datasource/influxdb/influxql_query_builder.ts b/public/app/plugins/datasource/influxdb/influxql_query_builder.ts index a9f6c37f02a..1ca19b15b9a 100644 --- a/public/app/plugins/datasource/influxdb/influxql_query_builder.ts +++ b/public/app/plugins/datasource/influxdb/influxql_query_builder.ts @@ -1,8 +1,7 @@ import { reduce } from 'lodash'; -import { escapeRegex, ScopedVars } from '@grafana/data/src'; - -import { TemplateSrv } from '../../../features/templating/template_srv'; +import { escapeRegex, ScopedVars } from '@grafana/data'; +import { TemplateSrv } from '@grafana/runtime'; import { DEFAULT_POLICY, InfluxQueryTag, MetadataQueryType } from './types';