Loki: add missing interpolation to metric find query requests (#54979)
* feat(loki-variable-query-support): refactor datasource to support legacy and new variable query format * Chore: fix comment * Chore: use internal method to interpolate strings * Chore: revert removed changes * Chore: update test
This commit is contained in:
@@ -23,7 +23,7 @@ import { CustomVariableModel } from '../../../features/variables/types';
|
||||
|
||||
import { LokiDatasource } from './datasource';
|
||||
import { createMetadataRequest, createLokiDatasource } from './mocks';
|
||||
import { LokiOptions, LokiQuery, LokiQueryType } from './types';
|
||||
import { LokiOptions, LokiQuery, LokiQueryType, LokiVariableQueryType } from './types';
|
||||
import { LokiVariableSupport } from './variables';
|
||||
|
||||
const templateSrvStub = {
|
||||
@@ -464,36 +464,71 @@ describe('LokiDatasource', () => {
|
||||
return { ds };
|
||||
};
|
||||
|
||||
it(`should return label names for Loki`, async () => {
|
||||
it('should return label names for Loki', async () => {
|
||||
const { ds } = getTestContext();
|
||||
|
||||
const res = await ds.metricFindQuery('label_names()');
|
||||
const legacyResult = await ds.metricFindQuery('label_names()');
|
||||
const result = await ds.metricFindQuery({ refId: 'test', type: LokiVariableQueryType.LabelNames });
|
||||
|
||||
expect(res).toEqual([{ text: 'label1' }, { text: 'label2' }]);
|
||||
expect(legacyResult).toEqual(result);
|
||||
expect(result).toEqual([{ text: 'label1' }, { text: 'label2' }]);
|
||||
});
|
||||
|
||||
it(`should return label values for Loki when no matcher`, async () => {
|
||||
it('should return label values for Loki when no matcher', async () => {
|
||||
const { ds } = getTestContext();
|
||||
|
||||
const res = await ds.metricFindQuery('label_values(label1)');
|
||||
const legacyResult = await ds.metricFindQuery('label_values(label1)');
|
||||
const result = await ds.metricFindQuery({
|
||||
refId: 'test',
|
||||
type: LokiVariableQueryType.LabelValues,
|
||||
label: 'label1',
|
||||
});
|
||||
|
||||
expect(res).toEqual([{ text: 'value1' }, { text: 'value2' }]);
|
||||
expect(legacyResult).toEqual(result);
|
||||
expect(result).toEqual([{ text: 'value1' }, { text: 'value2' }]);
|
||||
});
|
||||
|
||||
it(`should return label values for Loki with matcher`, async () => {
|
||||
it('should return label values for Loki with matcher', async () => {
|
||||
const { ds } = getTestContext();
|
||||
|
||||
const res = await ds.metricFindQuery('label_values({label1="value1", label2="value2"},label5)');
|
||||
const legacyResult = await ds.metricFindQuery('label_values({label1="value1", label2="value2"},label5)');
|
||||
const result = await ds.metricFindQuery({
|
||||
refId: 'test',
|
||||
type: LokiVariableQueryType.LabelValues,
|
||||
stream: '{label1="value1", label2="value2"}',
|
||||
label: 'label5',
|
||||
});
|
||||
|
||||
expect(res).toEqual([{ text: 'value5' }]);
|
||||
expect(legacyResult).toEqual(result);
|
||||
expect(result).toEqual([{ text: 'value5' }]);
|
||||
});
|
||||
|
||||
it(`should return empty array when incorrect query for Loki`, async () => {
|
||||
it('should return empty array when incorrect query for Loki', async () => {
|
||||
const { ds } = getTestContext();
|
||||
|
||||
const res = await ds.metricFindQuery('incorrect_query');
|
||||
const result = await ds.metricFindQuery('incorrect_query');
|
||||
|
||||
expect(res).toEqual([]);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should interpolate strings in the query', async () => {
|
||||
const { ds } = getTestContext();
|
||||
|
||||
await ds.metricFindQuery('label_names()');
|
||||
await ds.metricFindQuery({
|
||||
refId: 'test',
|
||||
type: LokiVariableQueryType.LabelValues,
|
||||
stream: '{label1="value1", label2="value2"}',
|
||||
label: 'label5',
|
||||
});
|
||||
|
||||
expect(templateSrvStub.replace).toHaveBeenCalledWith('label_names()', undefined, expect.any(Function));
|
||||
expect(templateSrvStub.replace).toHaveBeenCalledWith(
|
||||
'{label1="value1", label2="value2"}',
|
||||
undefined,
|
||||
expect.any(Function)
|
||||
);
|
||||
expect(templateSrvStub.replace).toHaveBeenCalledWith('label5', undefined, expect.any(Function));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -62,7 +62,14 @@ import { getQueryHints } from './queryHints';
|
||||
import { getNormalizedLokiQuery, isLogsQuery, isValidQuery } from './query_utils';
|
||||
import { sortDataFrameByTime } from './sortDataFrame';
|
||||
import { doLokiChannelStream } from './streaming';
|
||||
import { LokiOptions, LokiQuery, LokiQueryDirection, LokiQueryType } from './types';
|
||||
import {
|
||||
LokiOptions,
|
||||
LokiQuery,
|
||||
LokiQueryDirection,
|
||||
LokiQueryType,
|
||||
LokiVariableQuery,
|
||||
LokiVariableQueryType,
|
||||
} from './types';
|
||||
import { LokiVariableSupport } from './variables';
|
||||
|
||||
export type RangeQueryOptions = DataQueryRequest<LokiQuery> | AnnotationQueryRequest<LokiQuery>;
|
||||
@@ -305,16 +312,43 @@ export class LokiDatasource
|
||||
return res.data || [];
|
||||
}
|
||||
|
||||
async metricFindQuery(query: string) {
|
||||
async metricFindQuery(query: LokiVariableQuery | string) {
|
||||
if (!query) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
const interpolated = this.templateSrv.replace(query, {}, this.interpolateQueryExpr);
|
||||
return await this.processMetricFindQuery(interpolated);
|
||||
if (typeof query === 'string') {
|
||||
const interpolated = this.interpolateString(query);
|
||||
return await this.legacyProcessMetricFindQuery(interpolated);
|
||||
}
|
||||
|
||||
const interpolatedQuery = {
|
||||
...query,
|
||||
label: this.interpolateString(query.label || ''),
|
||||
stream: this.interpolateString(query.stream || ''),
|
||||
};
|
||||
|
||||
return await this.processMetricFindQuery(interpolatedQuery);
|
||||
}
|
||||
|
||||
async processMetricFindQuery(query: string) {
|
||||
async processMetricFindQuery(query: LokiVariableQuery) {
|
||||
if (query.type === LokiVariableQueryType.LabelNames) {
|
||||
return this.labelNamesQuery();
|
||||
}
|
||||
|
||||
if (!query.label) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// If we have stream selector, use /series endpoint
|
||||
if (query.stream) {
|
||||
return this.labelValuesSeriesQuery(query.stream, query.label);
|
||||
}
|
||||
|
||||
return this.labelValuesQuery(query.label);
|
||||
}
|
||||
|
||||
async legacyProcessMetricFindQuery(query: string) {
|
||||
const labelNames = query.match(labelNamesRegex);
|
||||
if (labelNames) {
|
||||
return await this.labelNamesQuery();
|
||||
@@ -322,7 +356,7 @@ export class LokiDatasource
|
||||
|
||||
const labelValues = query.match(labelValuesRegex);
|
||||
if (labelValues) {
|
||||
// If we have query expr, use /series endpoint
|
||||
// If we have stream selector, use /series endpoint
|
||||
if (labelValues[1]) {
|
||||
return await this.labelValuesSeriesQuery(labelValues[1], labelValues[2]);
|
||||
}
|
||||
|
||||
@@ -25,8 +25,12 @@ const defaultTimeSrvMock = {
|
||||
}),
|
||||
};
|
||||
|
||||
const defaultTemplateSrvMock = {
|
||||
replace: (input: string) => input,
|
||||
};
|
||||
|
||||
export function createLokiDatasource(
|
||||
templateSrvMock: TemplateSrv,
|
||||
templateSrvMock: Partial<TemplateSrv> = defaultTemplateSrvMock,
|
||||
settings: Partial<DataSourceInstanceSettings<LokiOptions>> = {},
|
||||
timeSrvStub = defaultTimeSrvMock
|
||||
): LokiDatasource {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
|
||||
import { createLokiDatasource, createMetadataRequest } from './mocks';
|
||||
import { LokiVariableQueryType } from './types';
|
||||
import { LokiVariableSupport } from './variables';
|
||||
@@ -8,7 +6,7 @@ describe('LokiVariableSupport', () => {
|
||||
let lokiVariableSupport: LokiVariableSupport;
|
||||
|
||||
beforeEach(() => {
|
||||
const datasource = createLokiDatasource({} as unknown as TemplateSrv);
|
||||
const datasource = createLokiDatasource();
|
||||
jest
|
||||
.spyOn(datasource, 'metadataRequest')
|
||||
.mockImplementation(
|
||||
|
||||
@@ -5,7 +5,7 @@ import { CustomVariableSupport, DataQueryRequest, DataQueryResponse } from '@gra
|
||||
|
||||
import { LokiVariableQueryEditor } from './components/VariableQueryEditor';
|
||||
import { LokiDatasource } from './datasource';
|
||||
import { LokiVariableQuery, LokiVariableQueryType } from './types';
|
||||
import { LokiVariableQuery } from './types';
|
||||
|
||||
export class LokiVariableSupport extends CustomVariableSupport<LokiDatasource, LokiVariableQuery> {
|
||||
editor = LokiVariableQueryEditor;
|
||||
@@ -16,20 +16,7 @@ export class LokiVariableSupport extends CustomVariableSupport<LokiDatasource, L
|
||||
}
|
||||
|
||||
async execute(query: LokiVariableQuery) {
|
||||
if (query.type === LokiVariableQueryType.LabelNames) {
|
||||
return this.datasource.labelNamesQuery();
|
||||
}
|
||||
|
||||
if (!query.label) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// If we have query expr, use /series endpoint
|
||||
if (query.stream) {
|
||||
return this.datasource.labelValuesSeriesQuery(query.stream, query.label);
|
||||
}
|
||||
|
||||
return this.datasource.labelValuesQuery(query.label);
|
||||
return this.datasource.metricFindQuery(query);
|
||||
}
|
||||
|
||||
query(request: DataQueryRequest<LokiVariableQuery>): Observable<DataQueryResponse> {
|
||||
|
||||
Reference in New Issue
Block a user