Loki: Refactor template variable logic and remove reliance on timeSrv (#78586)

* Loki: Refactor variables support to reuse loic

* Remove redundant methods

* Make parseStringToVariableQuery private
This commit is contained in:
Ivana Huckova 2023-11-24 12:52:29 +01:00 committed by GitHub
parent 8120306fea
commit 8a7eb4b484
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 57 additions and 94 deletions

View File

@ -5947,8 +5947,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
],
"public/app/plugins/datasource/loki/queryUtils.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]

View File

@ -1,6 +1,6 @@
import { LokiVariableSupport } from './LokiVariableSupport';
import { createLokiDatasource, createMetadataRequest } from './mocks';
import { LokiVariableQueryType } from './types';
import { LokiVariableSupport } from './variables';
describe('LokiVariableSupport', () => {
let lokiVariableSupport: LokiVariableSupport;

View File

@ -26,14 +26,7 @@ describe('LokiVariableQueryEditor', () => {
onChange: () => {},
};
jest.spyOn(props.datasource, 'labelNamesQuery').mockResolvedValue([
{
text: 'moon',
},
{
text: 'luna',
},
]);
jest.spyOn(props.datasource.languageProvider, 'fetchLabels').mockResolvedValue(['luna', 'moon']);
});
test('Allows to create a Label names variable', async () => {

View File

@ -38,8 +38,8 @@ export const LokiVariableQueryEditor = ({ onChange, query, datasource }: Props)
return;
}
datasource.labelNamesQuery().then((labelNames: Array<{ text: string }>) => {
setLabelOptions(labelNames.map(({ text }) => ({ label: text, value: text })));
datasource.languageProvider.fetchLabels().then((labelNames: string[]) => {
setLabelOptions(labelNames.map((labelName) => ({ label: labelName, value: labelName })));
});
}, [datasource, type]);

View File

@ -28,12 +28,12 @@ import {
TemplateSrv,
} from '@grafana/runtime';
import { LokiVariableSupport } from './LokiVariableSupport';
import { LokiDatasource, REF_ID_DATA_SAMPLES } from './datasource';
import { createLokiDatasource, createMetadataRequest } from './mocks';
import { runSplitQuery } from './querySplitting';
import { parseToNodeNamesArray } from './queryUtils';
import { LokiOptions, LokiQuery, LokiQueryType, LokiVariableQueryType, SupportingQueryType } from './types';
import { LokiVariableSupport } from './variables';
jest.mock('@grafana/runtime', () => {
return {

View File

@ -37,6 +37,9 @@ import {
LegacyMetricFindQueryOptions,
AdHocVariableFilter,
urlUtil,
MetricFindValue,
DataSourceGetTagValuesOptions,
DataSourceGetTagKeysOptions,
DataSourceWithQueryModificationSupport,
} from '@grafana/data';
import { Duration } from '@grafana/lezer-logql';
@ -51,6 +54,7 @@ import { replaceVariables, returnVariables } from '../prometheus/querybuilder/sh
import LanguageProvider from './LanguageProvider';
import { LiveStreams, LokiLiveTarget } from './LiveStreams';
import { LogContextProvider } from './LogContextProvider';
import { LokiVariableSupport } from './LokiVariableSupport';
import { transformBackendResult } from './backendResultTransformer';
import { LokiAnnotationsQueryEditor } from './components/AnnotationsQueryEditor';
import { placeHolderScopedVars } from './components/monaco-query-field/monaco-completion-provider/validation';
@ -94,7 +98,6 @@ import {
QueryStats,
SupportingQueryType,
} from './types';
import { LokiVariableSupport } from './variables';
export type RangeQueryOptions = DataQueryRequest<LokiQuery> | AnnotationQueryRequest<LokiQuery>;
export const DEFAULT_MAX_LINES = 1000;
@ -646,23 +649,31 @@ export class LokiDatasource
* Implemented as part of DataSourceAPI and used for template variable queries.
* @returns A Promise that resolves to an array of results from the metric find query.
*/
async metricFindQuery(query: LokiVariableQuery | string, options?: LegacyMetricFindQueryOptions) {
async metricFindQuery(
query: LokiVariableQuery | string,
options?: LegacyMetricFindQueryOptions
): Promise<MetricFindValue[]> {
if (!query) {
return Promise.resolve([]);
}
if (typeof query === 'string') {
const interpolated = this.interpolateString(query, options?.scopedVars);
return await this.legacyProcessMetricFindQuery(interpolated);
}
let interpolatedVariableQuery: LokiVariableQuery | undefined;
const interpolatedQuery = {
if (typeof query === 'string') {
interpolatedVariableQuery = this.parseStringToVariableQuery(this.interpolateString(query, options?.scopedVars));
} else {
interpolatedVariableQuery = {
...query,
label: this.interpolateString(query.label || '', options?.scopedVars),
stream: this.interpolateString(query.stream || '', options?.scopedVars),
};
}
return await this.processMetricFindQuery(interpolatedQuery);
if (interpolatedVariableQuery) {
return await this.processMetricFindQuery(interpolatedVariableQuery, options?.range);
}
return Promise.resolve([]);
}
/**
@ -670,9 +681,10 @@ export class LokiDatasource
* @returns A Promise that resolves to an array of variable results based on the query type and parameters.
*/
private async processMetricFindQuery(query: LokiVariableQuery) {
private async processMetricFindQuery(query: LokiVariableQuery, timeRange?: TimeRange): Promise<MetricFindValue[]> {
if (query.type === LokiVariableQueryType.LabelNames) {
return this.labelNamesQuery();
const result = await this.languageProvider.fetchLabels({ timeRange });
return result.map((value: string) => ({ text: value }));
}
if (!query.label) {
@ -681,81 +693,38 @@ export class LokiDatasource
// If we have stream selector, use /series endpoint
if (query.stream) {
return this.labelValuesSeriesQuery(query.stream, query.label);
const result = await this.languageProvider.fetchSeriesLabels(query.stream, { timeRange });
return result[query.label].map((value: string) => ({ text: value }));
}
return this.labelValuesQuery(query.label);
const result = await this.languageProvider.fetchLabelValues(query.label, { timeRange });
return result.map((value: string) => ({ text: value }));
}
/**
* Used in `metricFindQuery` to process legacy query strings (label_name() and label_values()) and return variable results.
* @returns A Promise that resolves to an array of variables based on the legacy query string.
* @todo It can be refactored in the future to return a LokiVariableQuery and be used in `processMetricFindQuery`
* to not duplicate querying logic.
* Used in `metricFindQuery` to process legacy query strings (label_name() and label_values()) to variable query objects.
* @returns LokiVariableQuery object based on the provided query string, or undefined if string can't be parsed.
*/
async legacyProcessMetricFindQuery(query: string) {
private parseStringToVariableQuery(query: string): LokiVariableQuery | undefined {
const refId = 'LokiVariableQueryEditor-VariableQuery';
const labelNames = query.match(labelNamesRegex);
if (labelNames) {
return await this.labelNamesQuery();
return {
type: LokiVariableQueryType.LabelNames,
refId,
};
}
const labelValues = query.match(labelValuesRegex);
if (labelValues) {
// If we have stream selector, use /series endpoint
if (labelValues[1]) {
return await this.labelValuesSeriesQuery(labelValues[1], labelValues[2]);
}
return await this.labelValuesQuery(labelValues[2]);
}
return Promise.resolve([]);
}
/**
* Private method used in `processMetricFindQuery`, `legacyProcessMetricFindQuery` and `getTagKeys` to fetch label names.
* @returns A Promise that resolves to an array of label names as text values.
* @todo Future exploration may involve using the `languageProvider.fetchLabels()` to avoid duplicating logic.
*/
async labelNamesQuery() {
const url = 'labels';
const params = this.getTimeRangeParams();
const result = await this.metadataRequest(url, params);
return result.map((value: string) => ({ text: value }));
}
/**
* Private method used in `processMetricFindQuery`, `legacyProcessMetricFindQuery` `getTagValues` to fetch label values.
* @returns A Promise that resolves to an array of label values as text values.
* @todo Future exploration may involve using the `languageProvider.fetchLabelValues()` method to avoid duplicating logic.
*/
private async labelValuesQuery(label: string) {
const params = this.getTimeRangeParams();
const url = `label/${label}/values`;
const result = await this.metadataRequest(url, params);
return result.map((value: string) => ({ text: value }));
}
/**
* Private method used in `processMetricFindQuery` and `legacyProcessMetricFindQuery` to fetch label values for specified stream.
* @returns A Promise that resolves to an array of label values as text values.
* @todo Future exploration may involve using the `languageProvider.fetchLabelValues()` or `languageProvider.fetchSeriesLabels()` method to avoid duplicating logic.
*/
private async labelValuesSeriesQuery(expr: string, label: string) {
const timeParams = this.getTimeRangeParams();
const params = {
...timeParams,
'match[]': expr,
return {
type: LokiVariableQueryType.LabelValues,
label: labelValues[2],
stream: labelValues[1],
refId,
};
const url = 'series';
const streams = new Set();
const result = await this.metadataRequest(url, params);
result.forEach((stream: { [key: string]: string }) => {
if (stream[label]) {
streams.add({ text: stream[label] });
}
});
return Array.from(streams);
return undefined;
}
/**
@ -784,18 +753,20 @@ export class LokiDatasource
/**
* Implemented as part of the DataSourceAPI. Retrieves tag keys that can be used for ad-hoc filtering.
* @returns A Promise that resolves to an array of label names.
* @returns A Promise that resolves to an array of label names represented as MetricFindValue objects.
*/
async getTagKeys() {
return await this.labelNamesQuery();
async getTagKeys(options?: DataSourceGetTagKeysOptions): Promise<MetricFindValue[]> {
const result = await this.languageProvider.fetchLabels({ timeRange: options?.timeRange });
return result.map((value: string) => ({ text: value }));
}
/**
* Implemented as part of the DataSourceAPI. Retrieves tag values that can be used for ad-hoc filtering.
* @returns A Promise that resolves to an array of label values.
* @returns A Promise that resolves to an array of label values represented as MetricFindValue objects
*/
async getTagValues(options: any = {}) {
return await this.labelValuesQuery(options.key);
async getTagValues(options: DataSourceGetTagValuesOptions): Promise<MetricFindValue[]> {
const result = await this.languageProvider.fetchLabelValues(options.key, { timeRange: options.timeRange });
return result.map((value: string) => ({ text: value }));
}
/**