mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
8120306fea
commit
8a7eb4b484
@ -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.", "2"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
[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.", "4"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
|
|
||||||
],
|
],
|
||||||
"public/app/plugins/datasource/loki/queryUtils.ts:5381": [
|
"public/app/plugins/datasource/loki/queryUtils.ts:5381": [
|
||||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
import { LokiVariableSupport } from './LokiVariableSupport';
|
||||||
import { createLokiDatasource, createMetadataRequest } from './mocks';
|
import { createLokiDatasource, createMetadataRequest } from './mocks';
|
||||||
import { LokiVariableQueryType } from './types';
|
import { LokiVariableQueryType } from './types';
|
||||||
import { LokiVariableSupport } from './variables';
|
|
||||||
|
|
||||||
describe('LokiVariableSupport', () => {
|
describe('LokiVariableSupport', () => {
|
||||||
let lokiVariableSupport: LokiVariableSupport;
|
let lokiVariableSupport: LokiVariableSupport;
|
@ -26,14 +26,7 @@ describe('LokiVariableQueryEditor', () => {
|
|||||||
onChange: () => {},
|
onChange: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.spyOn(props.datasource, 'labelNamesQuery').mockResolvedValue([
|
jest.spyOn(props.datasource.languageProvider, 'fetchLabels').mockResolvedValue(['luna', 'moon']);
|
||||||
{
|
|
||||||
text: 'moon',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'luna',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Allows to create a Label names variable', async () => {
|
test('Allows to create a Label names variable', async () => {
|
||||||
|
@ -38,8 +38,8 @@ export const LokiVariableQueryEditor = ({ onChange, query, datasource }: Props)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource.labelNamesQuery().then((labelNames: Array<{ text: string }>) => {
|
datasource.languageProvider.fetchLabels().then((labelNames: string[]) => {
|
||||||
setLabelOptions(labelNames.map(({ text }) => ({ label: text, value: text })));
|
setLabelOptions(labelNames.map((labelName) => ({ label: labelName, value: labelName })));
|
||||||
});
|
});
|
||||||
}, [datasource, type]);
|
}, [datasource, type]);
|
||||||
|
|
||||||
|
@ -28,12 +28,12 @@ import {
|
|||||||
TemplateSrv,
|
TemplateSrv,
|
||||||
} from '@grafana/runtime';
|
} from '@grafana/runtime';
|
||||||
|
|
||||||
|
import { LokiVariableSupport } from './LokiVariableSupport';
|
||||||
import { LokiDatasource, REF_ID_DATA_SAMPLES } from './datasource';
|
import { LokiDatasource, REF_ID_DATA_SAMPLES } from './datasource';
|
||||||
import { createLokiDatasource, createMetadataRequest } from './mocks';
|
import { createLokiDatasource, createMetadataRequest } from './mocks';
|
||||||
import { runSplitQuery } from './querySplitting';
|
import { runSplitQuery } from './querySplitting';
|
||||||
import { parseToNodeNamesArray } from './queryUtils';
|
import { parseToNodeNamesArray } from './queryUtils';
|
||||||
import { LokiOptions, LokiQuery, LokiQueryType, LokiVariableQueryType, SupportingQueryType } from './types';
|
import { LokiOptions, LokiQuery, LokiQueryType, LokiVariableQueryType, SupportingQueryType } from './types';
|
||||||
import { LokiVariableSupport } from './variables';
|
|
||||||
|
|
||||||
jest.mock('@grafana/runtime', () => {
|
jest.mock('@grafana/runtime', () => {
|
||||||
return {
|
return {
|
||||||
|
@ -37,6 +37,9 @@ import {
|
|||||||
LegacyMetricFindQueryOptions,
|
LegacyMetricFindQueryOptions,
|
||||||
AdHocVariableFilter,
|
AdHocVariableFilter,
|
||||||
urlUtil,
|
urlUtil,
|
||||||
|
MetricFindValue,
|
||||||
|
DataSourceGetTagValuesOptions,
|
||||||
|
DataSourceGetTagKeysOptions,
|
||||||
DataSourceWithQueryModificationSupport,
|
DataSourceWithQueryModificationSupport,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { Duration } from '@grafana/lezer-logql';
|
import { Duration } from '@grafana/lezer-logql';
|
||||||
@ -51,6 +54,7 @@ import { replaceVariables, returnVariables } from '../prometheus/querybuilder/sh
|
|||||||
import LanguageProvider from './LanguageProvider';
|
import LanguageProvider from './LanguageProvider';
|
||||||
import { LiveStreams, LokiLiveTarget } from './LiveStreams';
|
import { LiveStreams, LokiLiveTarget } from './LiveStreams';
|
||||||
import { LogContextProvider } from './LogContextProvider';
|
import { LogContextProvider } from './LogContextProvider';
|
||||||
|
import { LokiVariableSupport } from './LokiVariableSupport';
|
||||||
import { transformBackendResult } from './backendResultTransformer';
|
import { transformBackendResult } from './backendResultTransformer';
|
||||||
import { LokiAnnotationsQueryEditor } from './components/AnnotationsQueryEditor';
|
import { LokiAnnotationsQueryEditor } from './components/AnnotationsQueryEditor';
|
||||||
import { placeHolderScopedVars } from './components/monaco-query-field/monaco-completion-provider/validation';
|
import { placeHolderScopedVars } from './components/monaco-query-field/monaco-completion-provider/validation';
|
||||||
@ -94,7 +98,6 @@ import {
|
|||||||
QueryStats,
|
QueryStats,
|
||||||
SupportingQueryType,
|
SupportingQueryType,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { LokiVariableSupport } from './variables';
|
|
||||||
|
|
||||||
export type RangeQueryOptions = DataQueryRequest<LokiQuery> | AnnotationQueryRequest<LokiQuery>;
|
export type RangeQueryOptions = DataQueryRequest<LokiQuery> | AnnotationQueryRequest<LokiQuery>;
|
||||||
export const DEFAULT_MAX_LINES = 1000;
|
export const DEFAULT_MAX_LINES = 1000;
|
||||||
@ -646,23 +649,31 @@ export class LokiDatasource
|
|||||||
* Implemented as part of DataSourceAPI and used for template variable queries.
|
* 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.
|
* @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) {
|
if (!query) {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let interpolatedVariableQuery: LokiVariableQuery | undefined;
|
||||||
|
|
||||||
if (typeof query === 'string') {
|
if (typeof query === 'string') {
|
||||||
const interpolated = this.interpolateString(query, options?.scopedVars);
|
interpolatedVariableQuery = this.parseStringToVariableQuery(this.interpolateString(query, options?.scopedVars));
|
||||||
return await this.legacyProcessMetricFindQuery(interpolated);
|
} else {
|
||||||
|
interpolatedVariableQuery = {
|
||||||
|
...query,
|
||||||
|
label: this.interpolateString(query.label || '', options?.scopedVars),
|
||||||
|
stream: this.interpolateString(query.stream || '', options?.scopedVars),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const interpolatedQuery = {
|
if (interpolatedVariableQuery) {
|
||||||
...query,
|
return await this.processMetricFindQuery(interpolatedVariableQuery, options?.range);
|
||||||
label: this.interpolateString(query.label || '', options?.scopedVars),
|
}
|
||||||
stream: this.interpolateString(query.stream || '', options?.scopedVars),
|
|
||||||
};
|
|
||||||
|
|
||||||
return await this.processMetricFindQuery(interpolatedQuery);
|
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.
|
* @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) {
|
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) {
|
if (!query.label) {
|
||||||
@ -681,81 +693,38 @@ export class LokiDatasource
|
|||||||
|
|
||||||
// If we have stream selector, use /series endpoint
|
// If we have stream selector, use /series endpoint
|
||||||
if (query.stream) {
|
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.
|
* Used in `metricFindQuery` to process legacy query strings (label_name() and label_values()) to variable query objects.
|
||||||
* @returns A Promise that resolves to an array of variables based on the legacy query string.
|
* @returns LokiVariableQuery object based on the provided query string, or undefined if string can't be parsed.
|
||||||
* @todo It can be refactored in the future to return a LokiVariableQuery and be used in `processMetricFindQuery`
|
|
||||||
* to not duplicate querying logic.
|
|
||||||
*/
|
*/
|
||||||
async legacyProcessMetricFindQuery(query: string) {
|
private parseStringToVariableQuery(query: string): LokiVariableQuery | undefined {
|
||||||
|
const refId = 'LokiVariableQueryEditor-VariableQuery';
|
||||||
const labelNames = query.match(labelNamesRegex);
|
const labelNames = query.match(labelNamesRegex);
|
||||||
if (labelNames) {
|
if (labelNames) {
|
||||||
return await this.labelNamesQuery();
|
return {
|
||||||
|
type: LokiVariableQueryType.LabelNames,
|
||||||
|
refId,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const labelValues = query.match(labelValuesRegex);
|
const labelValues = query.match(labelValuesRegex);
|
||||||
if (labelValues) {
|
if (labelValues) {
|
||||||
// If we have stream selector, use /series endpoint
|
return {
|
||||||
if (labelValues[1]) {
|
type: LokiVariableQueryType.LabelValues,
|
||||||
return await this.labelValuesSeriesQuery(labelValues[1], labelValues[2]);
|
label: labelValues[2],
|
||||||
}
|
stream: labelValues[1],
|
||||||
return await this.labelValuesQuery(labelValues[2]);
|
refId,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
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,
|
|
||||||
};
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -784,18 +753,20 @@ export class LokiDatasource
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented as part of the DataSourceAPI. Retrieves tag keys that can be used for ad-hoc filtering.
|
* 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() {
|
async getTagKeys(options?: DataSourceGetTagKeysOptions): Promise<MetricFindValue[]> {
|
||||||
return await this.labelNamesQuery();
|
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.
|
* 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 = {}) {
|
async getTagValues(options: DataSourceGetTagValuesOptions): Promise<MetricFindValue[]> {
|
||||||
return await this.labelValuesQuery(options.key);
|
const result = await this.languageProvider.fetchLabelValues(options.key, { timeRange: options.timeRange });
|
||||||
|
return result.map((value: string) => ({ text: value }));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user