diff --git a/public/app/plugins/datasource/loki/datasource.test.ts b/public/app/plugins/datasource/loki/datasource.test.ts index 11da36c2698..87c2a83433d 100644 --- a/public/app/plugins/datasource/loki/datasource.test.ts +++ b/public/app/plugins/datasource/loki/datasource.test.ts @@ -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)); }); }); diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts index 761de171144..db17e26be79 100644 --- a/public/app/plugins/datasource/loki/datasource.ts +++ b/public/app/plugins/datasource/loki/datasource.ts @@ -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 | AnnotationQueryRequest; @@ -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]); } diff --git a/public/app/plugins/datasource/loki/mocks.ts b/public/app/plugins/datasource/loki/mocks.ts index 5c677de912b..46277fe04e8 100644 --- a/public/app/plugins/datasource/loki/mocks.ts +++ b/public/app/plugins/datasource/loki/mocks.ts @@ -25,8 +25,12 @@ const defaultTimeSrvMock = { }), }; +const defaultTemplateSrvMock = { + replace: (input: string) => input, +}; + export function createLokiDatasource( - templateSrvMock: TemplateSrv, + templateSrvMock: Partial = defaultTemplateSrvMock, settings: Partial> = {}, timeSrvStub = defaultTimeSrvMock ): LokiDatasource { diff --git a/public/app/plugins/datasource/loki/variables.test.ts b/public/app/plugins/datasource/loki/variables.test.ts index 0d98a07b6bb..ecb94e43cae 100644 --- a/public/app/plugins/datasource/loki/variables.test.ts +++ b/public/app/plugins/datasource/loki/variables.test.ts @@ -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( diff --git a/public/app/plugins/datasource/loki/variables.ts b/public/app/plugins/datasource/loki/variables.ts index 60151a23219..39d7c9b1c2c 100644 --- a/public/app/plugins/datasource/loki/variables.ts +++ b/public/app/plugins/datasource/loki/variables.ts @@ -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 { editor = LokiVariableQueryEditor; @@ -16,20 +16,7 @@ export class LokiVariableSupport extends CustomVariableSupport): Observable {