@ -1,11 +1,5 @@
import _ from 'lodash' ;
import {
alignRange ,
extractRuleMappingFromGroups ,
PrometheusDatasource ,
prometheusRegularEscape ,
prometheusSpecialRegexEscape ,
} from './datasource' ;
import { of , throwError } from 'rxjs' ;
import {
CoreApp ,
DataQueryRequest ,
@ -16,51 +10,46 @@ import {
LoadingState ,
toDataFrame ,
} from '@grafana/data' ;
import {
alignRange ,
extractRuleMappingFromGroups ,
PrometheusDatasource ,
prometheusRegularEscape ,
prometheusSpecialRegexEscape ,
} from './datasource' ;
import { PromOptions , PromQuery } from './types' ;
import templateSrv from 'app/features/templating/template_srv' ;
import { getTimeSrv , TimeSrv } from 'app/features/dashboard/services/TimeSrv' ;
import { VariableHide } from '../../../features/variables/types' ;
import { describe } from '../../../../test/lib/common' ;
import { QueryOptions } from 'app/types' ;
import { of , throwError } from 'rxjs' ;
const fetchMock = jest . fn ( ) . mockReturnValue ( of ( createDefaultPromResponse ( ) ) ) ;
jest . mock ( './metric_find_query' ) ;
jest . mock ( '@grafana/runtime' , ( ) = > ( {
// @ts-ignore
. . . jest . requireActual ( '@grafana/runtime' ) ,
getBackendSrv : ( ) = > ( {
fetch : fetchMock ,
} ) ,
} ) ) ;
jest . mock ( 'app/features/templating/template_srv' , ( ) = > {
return {
getAdhocFilters : jest.fn ( ( ) = > [ ] as any [ ] ) ,
replace : jest.fn ( ( a : string ) = > a ) ,
} ;
} ) ;
jest . mock ( 'app/features/dashboard/services/TimeSrv' , ( ) = > ( {
__esModule : true ,
getTimeSrv : jest.fn ( ) . mockReturnValue ( {
timeRange ( ) : any {
return {
from : dateTime ( 1531468681 ) ,
to : dateTime ( 1531489712 ) ,
} ;
} ,
} ) ,
} ) ) ;
const templateSrvStub = {
getAdhocFilters : jest.fn ( ( ) = > [ ] as any [ ] ) ,
replace : jest.fn ( ( a : string , . . . rest : any ) = > a ) ,
} ;
const getAdhocFiltersMock = ( templateSrv . getAdhocFilters as any ) as jest . Mock < any > ;
const replaceMock = ( templateSrv . replace as any ) as jest . Mock < any > ;
const getTimeSrvMock = ( getTimeSrv as any ) as jest . Mock < TimeSrv > ;
const timeSrvStub = {
timeRange ( ) : any {
return {
from : dateTime ( 1531468681 ) ,
to : dateTime ( 1531489712 ) ,
} ;
} ,
} ;
beforeEach ( ( ) = > {
fetchMock . mockClear ( ) ;
getAdhocFiltersMock . mockClear ( ) ;
replaceMock . mockClear ( ) ;
getTimeSrvMock . mockClear ( ) ;
jest . clearAllMocks ( ) ;
} ) ;
describe ( 'PrometheusDatasource' , ( ) = > {
@ -74,7 +63,7 @@ describe('PrometheusDatasource', () => {
} as unknown ) as DataSourceInstanceSettings < PromOptions > ;
beforeEach ( ( ) = > {
ds = new PrometheusDatasource ( instanceSettings ) ;
ds = new PrometheusDatasource ( instanceSettings , templateSrvStub as any , timeSrvStub as any ) ;
} ) ;
describe ( 'Query' , ( ) = > {
@ -145,7 +134,7 @@ describe('PrometheusDatasource', () => {
it ( 'should still perform a GET request with the DS HTTP method set to POST' , ( ) = > {
const postSettings = _ . cloneDeep ( instanceSettings ) ;
postSettings . jsonData . httpMethod = 'POST' ;
const promDs = new PrometheusDatasource ( postSettings ) ;
const promDs = new PrometheusDatasource ( postSettings , templateSrvStub as any , timeSrvStub as any ) ;
promDs . metadataRequest ( '/foo' ) ;
expect ( fetchMock . mock . calls . length ) . toBe ( 1 ) ;
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . method ) . toBe ( 'GET' ) ;
@ -155,10 +144,10 @@ describe('PrometheusDatasource', () => {
describe ( 'When using adhoc filters' , ( ) = > {
const DEFAULT_QUERY_EXPRESSION = 'metric{job="foo"} - metric' ;
const target = { expr : DEFAULT_QUERY_EXPRESSION } ;
const originalAdhocFiltersMock = getAdhocFiltersMock ( ) ;
const originalAdhocFiltersMock = templateSrvStub . getAdhocFilters ( ) ;
afterAll ( ( ) = > {
getAdhocFiltersMock . mockReturnValue ( originalAdhocFiltersMock ) ;
templateSrvStub . getAdhocFilters . mockReturnValue ( originalAdhocFiltersMock ) ;
} ) ;
it ( 'should not modify expression with no filters' , ( ) = > {
@ -167,7 +156,7 @@ describe('PrometheusDatasource', () => {
} ) ;
it ( 'should add filters to expression' , ( ) = > {
getAdhocFiltersMock . mockReturnValue ( [
templateSrvStub . getAdhocFilters . mockReturnValue ( [
{
key : 'k1' ,
operator : '=' ,
@ -184,7 +173,7 @@ describe('PrometheusDatasource', () => {
} ) ;
it ( 'should add escaping if needed to regex filter expressions' , ( ) = > {
getAdhocFiltersMock . mockReturnValue ( [
templateSrvStub . getAdhocFilters . mockReturnValue ( [
{
key : 'k1' ,
operator : '=~' ,
@ -511,30 +500,30 @@ describe('PrometheusDatasource', () => {
describe ( 'metricFindQuery' , ( ) = > {
beforeEach ( ( ) = > {
const query = 'query_result(topk(5,rate(http_request_duration_microseconds_count[$__interval])))' ;
replaceMock . mockImplementation ( jest . fn ) ;
templateSrvStub . replace = jest . fn ( ) ;
ds . metricFindQuery ( query ) ;
} ) ;
afterAll ( ( ) = > {
replaceMock . mockImplementatio n( ( a : string ) = > a ) ;
templateSrvStub . replace = jest . f n( ( a : string ) = > a ) ;
} ) ;
it ( 'should call templateSrv.replace with scopedVars' , ( ) = > {
expect ( replaceMock . mock . calls [ 0 ] [ 1 ] ) . toBeDefined ( ) ;
expect ( templateSrvStub . replace . mock . calls [ 0 ] [ 1 ] ) . toBeDefined ( ) ;
} ) ;
it ( 'should have the correct range and range_ms' , ( ) = > {
const range = replaceMock . mock . calls [ 0 ] [ 1 ] . __range ;
const rangeMs = replaceMock . mock . calls [ 0 ] [ 1 ] . __range_ms ;
const rangeS = replaceMock . mock . calls [ 0 ] [ 1 ] . __range_s ;
const range = templateSrvStub . replace . mock . calls [ 0 ] [ 1 ] . __range ;
const rangeMs = templateSrvStub . replace . mock . calls [ 0 ] [ 1 ] . __range_ms ;
const rangeS = templateSrvStub . replace . mock . calls [ 0 ] [ 1 ] . __range_s ;
expect ( range ) . toEqual ( { text : '21s' , value : '21s' } ) ;
expect ( rangeMs ) . toEqual ( { text : 21031 , value : 21031 } ) ;
expect ( rangeS ) . toEqual ( { text : 21 , value : 21 } ) ;
} ) ;
it ( 'should pass the default interval value' , ( ) = > {
const interval = replaceMock . mock . calls [ 0 ] [ 1 ] . __interval ;
const intervalMs = replaceMock . mock . calls [ 0 ] [ 1 ] . __interval_ms ;
const interval = templateSrvStub . replace . mock . calls [ 0 ] [ 1 ] . __interval ;
const intervalMs = templateSrvStub . replace . mock . calls [ 0 ] [ 1 ] . __interval_ms ;
expect ( interval ) . toEqual ( { text : '15s' , value : '15s' } ) ;
expect ( intervalMs ) . toEqual ( { text : 15000 , value : 15000 } ) ;
} ) ;
@ -558,7 +547,7 @@ describe('PrometheusDatasource', () => {
let ds : PrometheusDatasource ;
beforeEach ( ( ) = > {
ds = new PrometheusDatasource ( instanceSettings ) ;
ds = new PrometheusDatasource ( instanceSettings , templateSrvStub as any , timeSrvStub as any ) ;
} ) ;
describe ( 'When querying prometheus with one target using query editor target spec' , ( ) = > {
@ -1242,14 +1231,7 @@ describe('PrometheusDatasource', () => {
end -= end % 55 ;
const start = 0 ;
const step = 55 ;
const adjusted = alignRange (
start ,
end ,
step ,
getTimeSrv ( )
. timeRange ( )
. to . utcOffset ( ) * 60
) ;
const adjusted = alignRange ( start , end , step , timeSrvStub . timeRange ( ) . to . utcOffset ( ) * 60 ) ;
const urlExpected =
'proxied/api/v1/query_range?query=test' + '&start=' + adjusted . start + '&end=' + adjusted . end + '&step=' + step ;
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
@ -1293,15 +1275,14 @@ describe('PrometheusDatasource', () => {
encodeURIComponent ( 'rate(test[$__interval])' ) +
'&start=60&end=420&step=10' ;
templateSrv . replace = jest . fn ( str = > str ) as any ;
templateSrvStub . replace = jest . fn ( str = > str ) as any ;
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
ds . query ( query as any ) ;
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
// @ts-ignore
expect ( templateSrv . replace . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
expect ( templateSrvStub . replace . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
__interval : {
text : '10s' ,
value : '10s' ,
@ -1334,14 +1315,13 @@ describe('PrometheusDatasource', () => {
encodeURIComponent ( 'rate(test[$__interval])' ) +
'&start=60&end=420&step=10' ;
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
templateSrv . replace = jest . fn ( str = > str ) as any ;
templateSrvStub . replace = jest . fn ( str = > str ) as any ;
ds . query ( query as any ) ;
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
// @ts-ignore
expect ( templateSrv . replace . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
expect ( templateSrvStub . replace . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
__interval : {
text : '5s' ,
value : '5s' ,
@ -1375,14 +1355,13 @@ describe('PrometheusDatasource', () => {
encodeURIComponent ( 'rate(test[$__interval])' ) +
'&start=0&end=400&step=100' ;
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
templateSrv . replace = jest . fn ( str = > str ) as any ;
templateSrvStub . replace = jest . fn ( str = > str ) as any ;
ds . query ( query as any ) ;
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
// @ts-ignore
expect ( templateSrv . replace . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
expect ( templateSrvStub . replace . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
__interval : {
text : '10s' ,
value : '10s' ,
@ -1421,15 +1400,14 @@ describe('PrometheusDatasource', () => {
encodeURIComponent ( 'rate(test[$__interval])' ) +
'&start=50&end=400&step=50' ;
templateSrv . replace = jest . fn ( str = > str ) as any ;
templateSrvStub . replace = jest . fn ( str = > str ) as any ;
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
ds . query ( query as any ) ;
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
// @ts-ignore
expect ( templateSrv . replace . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
expect ( templateSrvStub . replace . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
__interval : {
text : '5s' ,
value : '5s' ,
@ -1469,8 +1447,7 @@ describe('PrometheusDatasource', () => {
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
// @ts-ignore
expect ( templateSrv . replace . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
expect ( templateSrvStub . replace . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
__interval : {
text : '5s' ,
value : '5s' ,
@ -1502,14 +1479,7 @@ describe('PrometheusDatasource', () => {
end -= end % 55 ;
const start = 0 ;
const step = 55 ;
const adjusted = alignRange (
start ,
end ,
step ,
getTimeSrv ( )
. timeRange ( )
. to . utcOffset ( ) * 60
) ;
const adjusted = alignRange ( start , end , step , timeSrvStub . timeRange ( ) . to . utcOffset ( ) * 60 ) ;
const urlExpected =
'proxied/api/v1/query_range?query=' +
encodeURIComponent ( 'rate(test[$__interval])' ) +
@ -1520,14 +1490,13 @@ describe('PrometheusDatasource', () => {
'&step=' +
step ;
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
templateSrv . replace = jest . fn ( str = > str ) as any ;
templateSrvStub . replace = jest . fn ( str = > str ) as any ;
ds . query ( query as any ) ;
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
// @ts-ignore
expect ( templateSrv . replace . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
expect ( templateSrvStub . replace . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
__interval : {
text : '5s' ,
value : '5s' ,
@ -1570,14 +1539,13 @@ describe('PrometheusDatasource', () => {
query . targets [ 0 ] . expr
) } & start = 0 & end = 3600 & step = 60 ` ;
templateSrv . replace = jest . fn ( str = > str ) as any ;
templateSrvStub . replace = jest . fn ( str = > str ) as any ;
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
ds . query ( query as any ) ;
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
expect ( res . url ) . toBe ( urlExpected ) ;
// @ts-ignore
expect ( templateSrv . replace . mock . calls [ 1 ] [ 1 ] ) . toEqual ( {
expect ( templateSrvStub . replace . mock . calls [ 1 ] [ 1 ] ) . toEqual ( {
__range_s : {
text : expectedRangeSecond ,
value : expectedRangeSecond ,
@ -1602,46 +1570,46 @@ describe('PrometheusDatasource', () => {
const target = { expr : 'rate(process_cpu_seconds_total[$__rate_interval])' , refId : 'A' } ;
beforeEach ( ( ) = > {
( templateSrv . replace as any ) . mockClear ( ) ;
templateSrvStub . replace . mockClear ( ) ;
} ) ;
it ( 'should be 4 times the scrape interval if interval + scrape interval is lower' , ( ) = > {
ds . createQuery ( target , { interval : '15s' } as any , 0 , 300 ) ;
expect ( ( templateSrv . replace as any ) . mock . calls [ 1 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '60s' ) ;
expect ( templateSrvStub . replace . mock . calls [ 1 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '60s' ) ;
} ) ;
it ( 'should be interval + scrape interval if 4 times the scrape interval is lower' , ( ) = > {
ds . createQuery ( target , { interval : '5m' } as any , 0 , 10080 ) ;
expect ( ( templateSrv . replace as any ) . mock . calls [ 1 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '315s' ) ;
expect ( templateSrvStub . replace . mock . calls [ 1 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '315s' ) ;
} ) ;
it ( 'should fall back to a scrape interval of 15s if min step is set to 0, resulting in 4*15s = 60s' , ( ) = > {
ds . createQuery ( { . . . target , interval : '' } , { interval : '15s' } as any , 0 , 300 ) ;
expect ( ( templateSrv . replace as any ) . mock . calls [ 1 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '60s' ) ;
expect ( templateSrvStub . replace . mock . calls [ 1 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '60s' ) ;
} ) ;
it ( 'should be 4 times the scrape interval if min step set to 1m and interval is 15s' , ( ) = > {
// For a 5m graph, $__interval is 15s
ds . createQuery ( { . . . target , interval : '1m' } , { interval : '15s' } as any , 0 , 300 ) ;
expect ( ( templateSrv . replace as any ) . mock . calls [ 2 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '240s' ) ;
expect ( templateSrvStub . replace . mock . calls [ 2 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '240s' ) ;
} ) ;
it ( 'should be interval + scrape interval if min step set to 1m and interval is 5m' , ( ) = > {
// For a 7d graph, $__interval is 5m
ds . createQuery ( { . . . target , interval : '1m' } , { interval : '5m' } as any , 0 , 10080 ) ;
expect ( ( templateSrv . replace as any ) . mock . calls [ 2 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '360s' ) ;
expect ( templateSrvStub . replace . mock . calls [ 2 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '360s' ) ;
} ) ;
it ( 'should be interval + scrape interval if resolution is set to 1/2 and interval is 10m' , ( ) = > {
// For a 7d graph, $__interval is 10m
ds . createQuery ( { . . . target , intervalFactor : 2 } , { interval : '10m' } as any , 0 , 10080 ) ;
expect ( ( templateSrv . replace as any ) . mock . calls [ 1 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '1215s' ) ;
expect ( templateSrvStub . replace . mock . calls [ 1 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '1215s' ) ;
} ) ;
it ( 'should be 4 times the scrape interval if resolution is set to 1/2 and interval is 15s' , ( ) = > {
// For a 5m graph, $__interval is 15s
ds . createQuery ( { . . . target , intervalFactor : 2 } , { interval : '15s' } as any , 0 , 300 ) ;
expect ( ( templateSrv . replace as any ) . mock . calls [ 1 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '60s' ) ;
expect ( templateSrvStub . replace . mock . calls [ 1 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '60s' ) ;
} ) ;
it ( 'should interpolate min step if set' , ( ) = > {
templateSrv . replace = jest . fn ( ( ) = > '15s' ) ;
templateSrvStub . replace = jest . fn ( ( _ : string ) = > '15s' ) ;
ds . createQuery ( { . . . target , interval : '$int' } , { interval : '15s' } as any , 0 , 300 ) ;
expect ( ( templateSrv . replace as any ) . mock . calls ) . toHaveLength ( 3 ) ;
templateSrv . replace = jest . fn ( ( a : string ) = > a ) ;
expect ( templateSrvStub . replace . mock . calls ) . toHaveLength ( 3 ) ;
templateSrvStub . replace = jest . fn ( ( a : string ) = > a ) ;
} ) ;
} ) ;
} ) ;
@ -1658,7 +1626,7 @@ describe('PrometheusDatasource for POST', () => {
let ds : PrometheusDatasource ;
beforeEach ( ( ) = > {
ds = new PrometheusDatasource ( instanceSettings ) ;
ds = new PrometheusDatasource ( instanceSettings , templateSrvStub as any , timeSrvStub as any ) ;
} ) ;
describe ( 'When querying prometheus with one target using query editor target spec' , ( ) = > {
@ -1724,7 +1692,11 @@ describe('PrometheusDatasource for POST', () => {
} ) ;
it ( 'with direct access tracing headers should not be added' , ( ) = > {
const mockDs = new PrometheusDatasource ( { . . . instanceSettings , url : 'http://127.0.0.1:8000' } ) ;
const mockDs = new PrometheusDatasource (
{ . . . instanceSettings , url : 'http://127.0.0.1:8000' } ,
templateSrvStub as any ,
timeSrvStub as any
) ;
mockDs . _addTracingHeaders ( httpOptions as any , options as any ) ;
expect ( httpOptions . headers [ 'X-Dashboard-Id' ] ) . toBe ( undefined ) ;
expect ( httpOptions . headers [ 'X-Panel-Id' ] ) . toBe ( undefined ) ;
@ -1747,7 +1719,7 @@ const getPrepareTargetsContext = (target: PromQuery, app?: CoreApp, queryOptions
PromQuery
> ;
const ds = new PrometheusDatasource ( instanceSettings ) ;
const ds = new PrometheusDatasource ( instanceSettings , templateSrvStub as any , timeSrvStub as any ) ;
const { queries , activeTargets } = ds . prepareTargets ( options , start , end ) ;
return {
@ -1918,7 +1890,7 @@ describe('modifyQuery', () => {
const query : PromQuery = { refId : 'A' , expr : 'go_goroutines' } ;
const action = { key : 'cluster' , value : 'us-cluster' , type : 'ADD_FILTER' } ;
const instanceSettings = ( { jsonData : { } } as unknown ) as DataSourceInstanceSettings < PromOptions > ;
const ds = new PrometheusDatasource ( instanceSettings ) ;
const ds = new PrometheusDatasource ( instanceSettings , templateSrvStub as any , timeSrvStub as any ) ;
const result = ds . modifyQuery ( query , action ) ;
@ -1932,7 +1904,7 @@ describe('modifyQuery', () => {
const query : PromQuery = { refId : 'A' , expr : 'go_goroutines{cluster="us-cluster"}' } ;
const action = { key : 'pod' , value : 'pod-123' , type : 'ADD_FILTER' } ;
const instanceSettings = ( { jsonData : { } } as unknown ) as DataSourceInstanceSettings < PromOptions > ;
const ds = new PrometheusDatasource ( instanceSettings ) ;
const ds = new PrometheusDatasource ( instanceSettings , templateSrvStub as any , timeSrvStub as any ) ;
const result = ds . modifyQuery ( query , action ) ;
@ -1948,7 +1920,7 @@ describe('modifyQuery', () => {
const query : PromQuery = { refId : 'A' , expr : 'go_goroutines' } ;
const action = { key : 'cluster' , value : 'us-cluster' , type : 'ADD_FILTER_OUT' } ;
const instanceSettings = ( { jsonData : { } } as unknown ) as DataSourceInstanceSettings < PromOptions > ;
const ds = new PrometheusDatasource ( instanceSettings ) ;
const ds = new PrometheusDatasource ( instanceSettings , templateSrvStub as any , timeSrvStub as any ) ;
const result = ds . modifyQuery ( query , action ) ;
@ -1962,7 +1934,7 @@ describe('modifyQuery', () => {
const query : PromQuery = { refId : 'A' , expr : 'go_goroutines{cluster="us-cluster"}' } ;
const action = { key : 'pod' , value : 'pod-123' , type : 'ADD_FILTER_OUT' } ;
const instanceSettings = ( { jsonData : { } } as unknown ) as DataSourceInstanceSettings < PromOptions > ;
const ds = new PrometheusDatasource ( instanceSettings ) ;
const ds = new PrometheusDatasource ( instanceSettings , templateSrvStub as any , timeSrvStub as any ) ;
const result = ds . modifyQuery ( query , action ) ;