mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Loki: Use TemplateSrv from @grafana/runtime (#78196)
Loki: Use TemplateSrv from grafana/runtime
This commit is contained in:
parent
875ea092df
commit
9200e17b18
@ -23,8 +23,8 @@ import {
|
||||
getBackendSrv,
|
||||
reportInteraction,
|
||||
setBackendSrv,
|
||||
TemplateSrv,
|
||||
} from '@grafana/runtime';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
|
||||
import { initialCustomVariableModelState } from '../../../features/variables/custom/reducer';
|
||||
import { CustomVariableModel } from '../../../features/variables/types';
|
||||
@ -46,7 +46,6 @@ jest.mock('@grafana/runtime', () => {
|
||||
jest.mock('./querySplitting');
|
||||
|
||||
const templateSrvStub = {
|
||||
getAdhocFilters: jest.fn(() => [] as unknown[]),
|
||||
replace: jest.fn((a: string, ...rest: unknown[]) => a),
|
||||
} as unknown as TemplateSrv;
|
||||
|
||||
@ -216,7 +215,6 @@ describe('LokiDatasource', () => {
|
||||
describe('When using adhoc filters', () => {
|
||||
const DEFAULT_EXPR = 'rate({bar="baz", job="foo"} |= "bar" [5m])';
|
||||
const query: LokiQuery = { expr: DEFAULT_EXPR, refId: 'A' };
|
||||
const mockedGetAdhocFilters = templateSrvStub.getAdhocFilters as jest.Mock;
|
||||
const ds = createLokiDatasource(templateSrvStub);
|
||||
|
||||
it('should not modify expression with no filters', async () => {
|
||||
@ -224,7 +222,7 @@ describe('LokiDatasource', () => {
|
||||
});
|
||||
|
||||
it('should add filters to expression', async () => {
|
||||
mockedGetAdhocFilters.mockReturnValue([
|
||||
const adhocFilters = [
|
||||
{
|
||||
key: 'k1',
|
||||
operator: '=',
|
||||
@ -235,15 +233,15 @@ describe('LokiDatasource', () => {
|
||||
operator: '!=',
|
||||
value: 'v2',
|
||||
},
|
||||
]);
|
||||
];
|
||||
|
||||
expect(ds.applyTemplateVariables(query, {}).expr).toBe(
|
||||
expect(ds.applyTemplateVariables(query, {}, adhocFilters).expr).toBe(
|
||||
'rate({bar="baz", job="foo", k1="v1", k2!="v2"} |= "bar" [5m])'
|
||||
);
|
||||
});
|
||||
|
||||
it('should add escaping if needed to regex filter expressions', async () => {
|
||||
mockedGetAdhocFilters.mockReturnValue([
|
||||
const adhocFilters = [
|
||||
{
|
||||
key: 'k1',
|
||||
operator: '=~',
|
||||
@ -254,8 +252,8 @@ describe('LokiDatasource', () => {
|
||||
operator: '=~',
|
||||
value: `v'.*`,
|
||||
},
|
||||
]);
|
||||
expect(ds.applyTemplateVariables(query, {}).expr).toBe(
|
||||
];
|
||||
expect(ds.applyTemplateVariables(query, {}, adhocFilters).expr).toBe(
|
||||
'rate({bar="baz", job="foo", k1=~"v.*", k2=~"v\\\\\'.*"} |= "bar" [5m])'
|
||||
);
|
||||
});
|
||||
@ -318,8 +316,8 @@ describe('LokiDatasource', () => {
|
||||
expr,
|
||||
},
|
||||
];
|
||||
ds.interpolateVariablesInQueries(queries, {});
|
||||
expect(ds.addAdHocFilters).toHaveBeenCalledWith(expr);
|
||||
ds.interpolateVariablesInQueries(queries, {}, []);
|
||||
expect(ds.addAdHocFilters).toHaveBeenCalledWith(expr, []);
|
||||
});
|
||||
});
|
||||
|
||||
@ -896,119 +894,123 @@ describe('LokiDatasource', () => {
|
||||
|
||||
describe('addAdHocFilters', () => {
|
||||
let ds: LokiDatasource;
|
||||
const createTemplateSrvMock = (options: { adHocFilters: AdHocFilter[] }) => {
|
||||
return {
|
||||
getAdhocFilters: (): AdHocFilter[] => options.adHocFilters,
|
||||
replace: (a: string) => a,
|
||||
} as unknown as TemplateSrv;
|
||||
};
|
||||
describe('when called with "=" operator', () => {
|
||||
beforeEach(() => {
|
||||
const defaultAdHocFilters: AdHocFilter[] = [
|
||||
{
|
||||
condition: '',
|
||||
key: 'job',
|
||||
operator: '=',
|
||||
value: 'grafana',
|
||||
},
|
||||
];
|
||||
ds = createLokiDatasource(createTemplateSrvMock({ adHocFilters: defaultAdHocFilters }));
|
||||
ds = createLokiDatasource();
|
||||
});
|
||||
const defaultAdHocFilters: AdHocFilter[] = [
|
||||
{
|
||||
condition: '',
|
||||
key: 'job',
|
||||
operator: '=',
|
||||
value: 'grafana',
|
||||
},
|
||||
];
|
||||
describe('and query has no parser', () => {
|
||||
it('then the correct label should be added for logs query', () => {
|
||||
assertAdHocFilters('{bar="baz"}', '{bar="baz", job="grafana"}', ds);
|
||||
assertAdHocFilters('{bar="baz"}', '{bar="baz", job="grafana"}', ds, defaultAdHocFilters);
|
||||
});
|
||||
|
||||
it('then the correct label should be added for metrics query', () => {
|
||||
assertAdHocFilters('rate({bar="baz"}[5m])', 'rate({bar="baz", job="grafana"}[5m])', ds);
|
||||
assertAdHocFilters('rate({bar="baz"}[5m])', 'rate({bar="baz", job="grafana"}[5m])', ds, defaultAdHocFilters);
|
||||
});
|
||||
|
||||
it('then the correct label should be added for metrics query and variable', () => {
|
||||
assertAdHocFilters('rate({bar="baz"}[$__interval])', 'rate({bar="baz", job="grafana"}[$__interval])', ds);
|
||||
assertAdHocFilters(
|
||||
'rate({bar="baz"}[$__interval])',
|
||||
'rate({bar="baz", job="grafana"}[$__interval])',
|
||||
ds,
|
||||
defaultAdHocFilters
|
||||
);
|
||||
});
|
||||
|
||||
it('then the correct label should be added for logs query with empty selector', () => {
|
||||
assertAdHocFilters('{}', '{job="grafana"}', ds);
|
||||
assertAdHocFilters('{}', '{job="grafana"}', ds, defaultAdHocFilters);
|
||||
});
|
||||
|
||||
it('then the correct label should be added for metrics query with empty selector', () => {
|
||||
assertAdHocFilters('rate({}[5m])', 'rate({job="grafana"}[5m])', ds);
|
||||
assertAdHocFilters('rate({}[5m])', 'rate({job="grafana"}[5m])', ds, defaultAdHocFilters);
|
||||
});
|
||||
|
||||
it('then the correct label should be added for metrics query with empty selector and variable', () => {
|
||||
assertAdHocFilters('rate({}[$__interval])', 'rate({job="grafana"}[$__interval])', ds);
|
||||
assertAdHocFilters('rate({}[$__interval])', 'rate({job="grafana"}[$__interval])', ds, defaultAdHocFilters);
|
||||
});
|
||||
it('should correctly escape special characters in ad hoc filter', () => {
|
||||
const ds = createLokiDatasource(
|
||||
createTemplateSrvMock({
|
||||
adHocFilters: [
|
||||
{
|
||||
condition: '',
|
||||
key: 'instance',
|
||||
operator: '=',
|
||||
value: '"test"',
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
assertAdHocFilters('{job="grafana"}', '{job="grafana", instance="\\"test\\""}', ds);
|
||||
assertAdHocFilters('{job="grafana"}', '{job="grafana", instance="\\"test\\""}', ds, [
|
||||
{
|
||||
condition: '',
|
||||
key: 'instance',
|
||||
operator: '=',
|
||||
value: '"test"',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('and query has parser', () => {
|
||||
it('then the correct label should be added for logs query', () => {
|
||||
assertAdHocFilters('{bar="baz"} | logfmt', '{bar="baz"} | logfmt | job=`grafana`', ds);
|
||||
assertAdHocFilters('{bar="baz"} | logfmt', '{bar="baz"} | logfmt | job=`grafana`', ds, defaultAdHocFilters);
|
||||
});
|
||||
it('then the correct label should be added for metrics query', () => {
|
||||
assertAdHocFilters('rate({bar="baz"} | logfmt [5m])', 'rate({bar="baz"} | logfmt | job=`grafana` [5m])', ds);
|
||||
assertAdHocFilters(
|
||||
'rate({bar="baz"} | logfmt [5m])',
|
||||
'rate({bar="baz"} | logfmt | job=`grafana` [5m])',
|
||||
ds,
|
||||
defaultAdHocFilters
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when called with "!=" operator', () => {
|
||||
const defaultAdHocFilters: AdHocFilter[] = [
|
||||
{
|
||||
condition: '',
|
||||
key: 'job',
|
||||
operator: '!=',
|
||||
value: 'grafana',
|
||||
},
|
||||
];
|
||||
beforeEach(() => {
|
||||
const defaultAdHocFilters: AdHocFilter[] = [
|
||||
{
|
||||
condition: '',
|
||||
key: 'job',
|
||||
operator: '!=',
|
||||
value: 'grafana',
|
||||
},
|
||||
];
|
||||
ds = createLokiDatasource(createTemplateSrvMock({ adHocFilters: defaultAdHocFilters }));
|
||||
ds = createLokiDatasource();
|
||||
});
|
||||
describe('and query has no parser', () => {
|
||||
it('then the correct label should be added for logs query', () => {
|
||||
assertAdHocFilters('{bar="baz"}', '{bar="baz", job!="grafana"}', ds);
|
||||
assertAdHocFilters('{bar="baz"}', '{bar="baz", job!="grafana"}', ds, defaultAdHocFilters);
|
||||
});
|
||||
|
||||
it('then the correct label should be added for metrics query', () => {
|
||||
assertAdHocFilters('rate({bar="baz"}[5m])', 'rate({bar="baz", job!="grafana"}[5m])', ds);
|
||||
assertAdHocFilters('rate({bar="baz"}[5m])', 'rate({bar="baz", job!="grafana"}[5m])', ds, defaultAdHocFilters);
|
||||
});
|
||||
});
|
||||
describe('and query has parser', () => {
|
||||
it('then the correct label should be added for logs query', () => {
|
||||
assertAdHocFilters('{bar="baz"} | logfmt', '{bar="baz"} | logfmt | job!=`grafana`', ds);
|
||||
assertAdHocFilters('{bar="baz"} | logfmt', '{bar="baz"} | logfmt | job!=`grafana`', ds, defaultAdHocFilters);
|
||||
});
|
||||
it('then the correct label should be added for metrics query', () => {
|
||||
assertAdHocFilters('rate({bar="baz"} | logfmt [5m])', 'rate({bar="baz"} | logfmt | job!=`grafana` [5m])', ds);
|
||||
assertAdHocFilters(
|
||||
'rate({bar="baz"} | logfmt [5m])',
|
||||
'rate({bar="baz"} | logfmt | job!=`grafana` [5m])',
|
||||
ds,
|
||||
defaultAdHocFilters
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when called with regex operator', () => {
|
||||
const defaultAdHocFilters: AdHocFilter[] = [
|
||||
{
|
||||
condition: '',
|
||||
key: 'instance',
|
||||
operator: '=~',
|
||||
value: '.*',
|
||||
},
|
||||
];
|
||||
beforeEach(() => {
|
||||
const defaultAdHocFilters: AdHocFilter[] = [
|
||||
{
|
||||
condition: '',
|
||||
key: 'instance',
|
||||
operator: '=~',
|
||||
value: '.*',
|
||||
},
|
||||
];
|
||||
ds = createLokiDatasource(createTemplateSrvMock({ adHocFilters: defaultAdHocFilters }));
|
||||
ds = createLokiDatasource();
|
||||
});
|
||||
it('should not escape special characters in ad hoc filter', () => {
|
||||
assertAdHocFilters('{job="grafana"}', '{job="grafana", instance=~".*"}', ds);
|
||||
assertAdHocFilters('{job="grafana"}', '{job="grafana", instance=~".*"}', ds, defaultAdHocFilters);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1403,10 +1405,10 @@ describe('LokiDatasource', () => {
|
||||
|
||||
describe('applyTemplateVariables', () => {
|
||||
it('should add the adhoc filter to the query', () => {
|
||||
const ds = createLokiDatasource(templateSrvStub);
|
||||
const ds = createLokiDatasource();
|
||||
const spy = jest.spyOn(ds, 'addAdHocFilters');
|
||||
ds.applyTemplateVariables({ expr: '{test}', refId: 'A' }, {});
|
||||
expect(spy).toHaveBeenCalledWith('{test}');
|
||||
ds.applyTemplateVariables({ expr: '{test}', refId: 'A' }, {}, []);
|
||||
expect(spy).toHaveBeenCalledWith('{test}', []);
|
||||
});
|
||||
|
||||
describe('with template and built-in variables', () => {
|
||||
@ -1648,9 +1650,9 @@ describe('queryHasFilter()', () => {
|
||||
});
|
||||
});
|
||||
|
||||
function assertAdHocFilters(query: string, expectedResults: string, ds: LokiDatasource) {
|
||||
function assertAdHocFilters(query: string, expectedResults: string, ds: LokiDatasource, adhocFilters?: AdHocFilter[]) {
|
||||
const lokiQuery: LokiQuery = { refId: 'A', expr: query };
|
||||
const result = ds.addAdHocFilters(lokiQuery.expr);
|
||||
const result = ds.addAdHocFilters(lokiQuery.expr, adhocFilters);
|
||||
|
||||
expect(result).toEqual(expectedResults);
|
||||
}
|
||||
|
@ -35,13 +35,13 @@ import {
|
||||
QueryFilterOptions,
|
||||
renderLegendFormat,
|
||||
LegacyMetricFindQueryOptions,
|
||||
AdHocVariableFilter,
|
||||
} from '@grafana/data';
|
||||
import { Duration } from '@grafana/lezer-logql';
|
||||
import { BackendSrvRequest, config, DataSourceWithBackend } from '@grafana/runtime';
|
||||
import { BackendSrvRequest, config, DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime';
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
import { convertToWebSocketUrl } from 'app/core/utils/explore';
|
||||
import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv';
|
||||
|
||||
import { serializeParams } from '../../../core/utils/fetch';
|
||||
import { queryLogsSample, queryLogsVolume } from '../../../features/logs/logsModel';
|
||||
@ -420,13 +420,20 @@ export class LokiDatasource
|
||||
* Implemented as a part of DataSourceApi. Interpolates variables and adds ad hoc filters to a list of Loki queries.
|
||||
* @returns An array of expanded Loki queries with interpolated variables and ad hoc filters.
|
||||
*/
|
||||
interpolateVariablesInQueries(queries: LokiQuery[], scopedVars: ScopedVars): LokiQuery[] {
|
||||
interpolateVariablesInQueries(
|
||||
queries: LokiQuery[],
|
||||
scopedVars: ScopedVars,
|
||||
adhocFilters?: AdHocVariableFilter[]
|
||||
): LokiQuery[] {
|
||||
let expandedQueries = queries;
|
||||
if (queries && queries.length) {
|
||||
expandedQueries = queries.map((query) => ({
|
||||
...query,
|
||||
datasource: this.getRef(),
|
||||
expr: this.addAdHocFilters(this.templateSrv.replace(query.expr, scopedVars, this.interpolateQueryExpr)),
|
||||
expr: this.addAdHocFilters(
|
||||
this.templateSrv.replace(query.expr, scopedVars, this.interpolateQueryExpr),
|
||||
adhocFilters
|
||||
),
|
||||
}));
|
||||
}
|
||||
|
||||
@ -1046,8 +1053,11 @@ export class LokiDatasource
|
||||
* @returns The query expression with ad hoc filters and correctly escaped values.
|
||||
* @todo this.templateSrv.getAdhocFilters() is deprecated
|
||||
*/
|
||||
addAdHocFilters(queryExpr: string) {
|
||||
const adhocFilters = this.templateSrv.getAdhocFilters(this.name);
|
||||
addAdHocFilters(queryExpr: string, adhocFilters?: AdHocVariableFilter[]) {
|
||||
if (!adhocFilters) {
|
||||
return queryExpr;
|
||||
}
|
||||
|
||||
let expr = replaceVariables(queryExpr);
|
||||
|
||||
expr = adhocFilters.reduce((acc: string, filter: { key: string; operator: string; value: string }) => {
|
||||
@ -1085,12 +1095,12 @@ export class LokiDatasource
|
||||
* It is called from DatasourceWithBackend.
|
||||
* @returns A modified Loki query with template variables and ad hoc filters applied.
|
||||
*/
|
||||
applyTemplateVariables(target: LokiQuery, scopedVars: ScopedVars): LokiQuery {
|
||||
applyTemplateVariables(target: LokiQuery, scopedVars: ScopedVars, adhocFilters?: AdHocVariableFilter[]): LokiQuery {
|
||||
// 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 { __auto, __interval, __interval_ms, __range, __range_s, __range_ms, ...rest } = scopedVars || {};
|
||||
|
||||
const exprWithAdHoc = this.addAdHocFilters(target.expr);
|
||||
const exprWithAdHoc = this.addAdHocFilters(target.expr, adhocFilters);
|
||||
|
||||
return {
|
||||
...target,
|
||||
|
@ -35,6 +35,7 @@ const defaultTimeSrvMock = {
|
||||
|
||||
const defaultTemplateSrvMock = {
|
||||
replace: (input: string) => input,
|
||||
getVariables: () => [],
|
||||
};
|
||||
|
||||
export function createLokiDatasource(
|
||||
|
@ -3,10 +3,8 @@ import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { getSelectParent } from 'test/helpers/selectOptionInTest';
|
||||
|
||||
import { DataSourceInstanceSettings, DataSourcePluginMeta } from '@grafana/data';
|
||||
|
||||
import { MISSING_LABEL_FILTER_ERROR_MESSAGE } from '../../../prometheus/querybuilder/shared/LabelFilters';
|
||||
import { LokiDatasource } from '../../datasource';
|
||||
import { createLokiDatasource } from '../../mocks';
|
||||
import { LokiOperationId, LokiVisualQuery } from '../types';
|
||||
|
||||
import { LokiQueryBuilder } from './LokiQueryBuilder';
|
||||
@ -18,15 +16,7 @@ const defaultQuery: LokiVisualQuery = {
|
||||
};
|
||||
|
||||
const createDefaultProps = () => {
|
||||
const datasource = new LokiDatasource(
|
||||
{
|
||||
url: '',
|
||||
jsonData: {},
|
||||
meta: {} as DataSourcePluginMeta,
|
||||
} as DataSourceInstanceSettings,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
const datasource = createLokiDatasource();
|
||||
|
||||
const props = {
|
||||
datasource,
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { DataSourcePluginMeta } from '@grafana/data';
|
||||
import { addOperation } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationList.testUtils';
|
||||
|
||||
import { LokiDatasource } from '../../datasource';
|
||||
import { createLokiDatasource } from '../../mocks';
|
||||
|
||||
import { LokiQueryBuilderContainer } from './LokiQueryBuilderContainer';
|
||||
|
||||
@ -15,21 +14,7 @@ describe('LokiQueryBuilderContainer', () => {
|
||||
expr: '{job="testjob"}',
|
||||
refId: 'A',
|
||||
},
|
||||
datasource: new LokiDatasource(
|
||||
{
|
||||
id: 1,
|
||||
uid: '',
|
||||
type: 'loki',
|
||||
name: 'loki-test',
|
||||
access: 'proxy',
|
||||
url: '',
|
||||
jsonData: {},
|
||||
meta: {} as DataSourcePluginMeta,
|
||||
readOnly: false,
|
||||
},
|
||||
undefined,
|
||||
undefined
|
||||
),
|
||||
datasource: createLokiDatasource(),
|
||||
onChange: jest.fn(),
|
||||
onRunQuery: () => {},
|
||||
showExplain: false,
|
||||
|
@ -2,13 +2,14 @@ import { screen, render } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React, { ComponentProps } from 'react';
|
||||
|
||||
import { DataFrame, DataSourceApi, DataSourcePluginMeta, FieldType, toDataFrame } from '@grafana/data';
|
||||
import { DataFrame, DataSourceApi, FieldType, toDataFrame } from '@grafana/data';
|
||||
import {
|
||||
QueryBuilderOperation,
|
||||
QueryBuilderOperationParamDef,
|
||||
} from 'app/plugins/datasource/prometheus/querybuilder/shared/types';
|
||||
|
||||
import { LokiDatasource } from '../../datasource';
|
||||
import { createLokiDatasource } from '../../mocks';
|
||||
import { LokiOperationId } from '../types';
|
||||
|
||||
import { UnwrapParamEditor } from './UnwrapParamEditor';
|
||||
@ -75,21 +76,7 @@ const createProps = (
|
||||
},
|
||||
paramDef: {} as QueryBuilderOperationParamDef,
|
||||
operation: {} as QueryBuilderOperation,
|
||||
datasource: new LokiDatasource(
|
||||
{
|
||||
id: 1,
|
||||
uid: '',
|
||||
type: 'loki',
|
||||
name: 'loki-test',
|
||||
access: 'proxy',
|
||||
url: '',
|
||||
jsonData: {},
|
||||
meta: {} as DataSourcePluginMeta,
|
||||
readOnly: false,
|
||||
},
|
||||
undefined,
|
||||
undefined
|
||||
) as DataSourceApi,
|
||||
datasource: createLokiDatasource() as DataSourceApi,
|
||||
};
|
||||
const props = { ...propsDefault, ...propsOverrides };
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user