diff --git a/public/app/plugins/datasource/loki/datasource.test.ts b/public/app/plugins/datasource/loki/datasource.test.ts index ff23e68c88c..6638ce3e326 100644 --- a/public/app/plugins/datasource/loki/datasource.test.ts +++ b/public/app/plugins/datasource/loki/datasource.test.ts @@ -1148,6 +1148,86 @@ describe('applyTemplateVariables', () => { ds.applyTemplateVariables({ expr: '{test}', refId: 'A' }, {}); expect(spy).toHaveBeenCalledWith('{test}'); }); + + describe('with template and built-in variables', () => { + const scopedVars = { + __interval: { text: '1m', value: '1m' }, + __interval_ms: { text: '1000', value: '1000' }, + __range: { text: '1m', value: '1m' }, + __range_ms: { text: '1000', value: '1000' }, + __range_s: { text: '60', value: '60' }, + testVariable: { text: 'foo', value: 'foo' }, + }; + + it('should not interpolate __interval variables', () => { + const templateSrvMock = { + getAdhocFilters: jest.fn().mockImplementation((query: string) => query), + replace: jest.fn((a: string, ...rest: unknown[]) => a), + } as unknown as TemplateSrv; + + const ds = createLokiDatasource(templateSrvMock); + ds.addAdHocFilters = jest.fn().mockImplementation((query: string) => query); + ds.applyTemplateVariables( + { expr: 'rate({job="grafana"}[$__interval]) + rate({job="grafana"}[$__interval_ms])', refId: 'A' }, + scopedVars + ); + expect(templateSrvMock.replace).toHaveBeenCalledTimes(2); + // Interpolated legend + expect(templateSrvMock.replace).toHaveBeenCalledWith( + undefined, + expect.not.objectContaining({ + __interval: { text: '1m', value: '1m' }, + __interval_ms: { text: '1000', value: '1000' }, + }) + ); + // Interpolated expr + expect(templateSrvMock.replace).toHaveBeenCalledWith( + 'rate({job="grafana"}[$__interval]) + rate({job="grafana"}[$__interval_ms])', + expect.not.objectContaining({ + __interval: { text: '1m', value: '1m' }, + __interval_ms: { text: '1000', value: '1000' }, + }), + expect.any(Function) + ); + }); + + it('should not interpolate __range variables', () => { + const templateSrvMock = { + getAdhocFilters: jest.fn().mockImplementation((query: string) => query), + replace: jest.fn((a: string, ...rest: unknown[]) => a), + } as unknown as TemplateSrv; + + const ds = createLokiDatasource(templateSrvMock); + ds.addAdHocFilters = jest.fn().mockImplementation((query: string) => query); + ds.applyTemplateVariables( + { + expr: 'rate({job="grafana"}[$__range]) + rate({job="grafana"}[$__range_ms]) + rate({job="grafana"}[$__range_s])', + refId: 'A', + }, + scopedVars + ); + expect(templateSrvMock.replace).toHaveBeenCalledTimes(2); + // Interpolated legend + expect(templateSrvMock.replace).toHaveBeenCalledWith( + undefined, + expect.not.objectContaining({ + __range: { text: '1m', value: '1m' }, + __range_ms: { text: '1000', value: '1000' }, + __range_s: { text: '60', value: '60' }, + }) + ); + // Interpolated expr + expect(templateSrvMock.replace).toHaveBeenCalledWith( + 'rate({job="grafana"}[$__range]) + rate({job="grafana"}[$__range_ms]) + rate({job="grafana"}[$__range_s])', + expect.not.objectContaining({ + __range: { text: '1m', value: '1m' }, + __range_ms: { text: '1000', value: '1000' }, + __range_s: { text: '60', value: '60' }, + }), + expect.any(Function) + ); + }); + }); }); describe('getTimeRange*()', () => { diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts index 0aa609e7462..d53fc65f6e6 100644 --- a/public/app/plugins/datasource/loki/datasource.ts +++ b/public/app/plugins/datasource/loki/datasource.ts @@ -815,8 +815,9 @@ export class LokiDatasource // Used when running queries through backend applyTemplateVariables(target: LokiQuery, scopedVars: ScopedVars): LokiQuery { - // We want to interpolate these variables on backend - const { __interval, __interval_ms, ...rest } = scopedVars || {}; + // We want to interpolate these variables on backend because we support using them in + // alerting/ML queries and we want to have consistent interpolation for all queries + const { __interval, __interval_ms, __range, __range_s, __range_ms, ...rest } = scopedVars || {}; const exprWithAdHoc = this.addAdHocFilters(target.expr);