diff --git a/.betterer.results b/.betterer.results index 7fa954579a6..c4004ec1d7a 100644 --- a/.betterer.results +++ b/.betterer.results @@ -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"] diff --git a/public/app/plugins/datasource/loki/variables.test.ts b/public/app/plugins/datasource/loki/LokiVariableSupport.test.ts similarity index 96% rename from public/app/plugins/datasource/loki/variables.test.ts rename to public/app/plugins/datasource/loki/LokiVariableSupport.test.ts index 99a65507af1..4c73b7bef08 100644 --- a/public/app/plugins/datasource/loki/variables.test.ts +++ b/public/app/plugins/datasource/loki/LokiVariableSupport.test.ts @@ -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; diff --git a/public/app/plugins/datasource/loki/variables.ts b/public/app/plugins/datasource/loki/LokiVariableSupport.ts similarity index 100% rename from public/app/plugins/datasource/loki/variables.ts rename to public/app/plugins/datasource/loki/LokiVariableSupport.ts diff --git a/public/app/plugins/datasource/loki/components/VariableQueryEditor.test.tsx b/public/app/plugins/datasource/loki/components/VariableQueryEditor.test.tsx index c8f6298e6f8..1e7c803a96b 100644 --- a/public/app/plugins/datasource/loki/components/VariableQueryEditor.test.tsx +++ b/public/app/plugins/datasource/loki/components/VariableQueryEditor.test.tsx @@ -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 () => { diff --git a/public/app/plugins/datasource/loki/components/VariableQueryEditor.tsx b/public/app/plugins/datasource/loki/components/VariableQueryEditor.tsx index 4b46324d75d..749b564b80a 100644 --- a/public/app/plugins/datasource/loki/components/VariableQueryEditor.tsx +++ b/public/app/plugins/datasource/loki/components/VariableQueryEditor.tsx @@ -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]); diff --git a/public/app/plugins/datasource/loki/datasource.test.ts b/public/app/plugins/datasource/loki/datasource.test.ts index 93426033ba2..9b9bc74ced6 100644 --- a/public/app/plugins/datasource/loki/datasource.test.ts +++ b/public/app/plugins/datasource/loki/datasource.test.ts @@ -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 { diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts index 456694d46ba..88aaedb77b2 100644 --- a/public/app/plugins/datasource/loki/datasource.ts +++ b/public/app/plugins/datasource/loki/datasource.ts @@ -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 | AnnotationQueryRequest; 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 { if (!query) { return Promise.resolve([]); } + let interpolatedVariableQuery: LokiVariableQuery | undefined; + if (typeof query === 'string') { - const interpolated = this.interpolateString(query, options?.scopedVars); - return await this.legacyProcessMetricFindQuery(interpolated); + 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), + }; } - const interpolatedQuery = { - ...query, - label: this.interpolateString(query.label || '', options?.scopedVars), - stream: this.interpolateString(query.stream || '', options?.scopedVars), - }; + if (interpolatedVariableQuery) { + return await this.processMetricFindQuery(interpolatedVariableQuery, options?.range); + } - 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. */ - private async processMetricFindQuery(query: LokiVariableQuery) { + private async processMetricFindQuery(query: LokiVariableQuery, timeRange?: TimeRange): Promise { 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 { + type: LokiVariableQueryType.LabelValues, + label: labelValues[2], + stream: labelValues[1], + refId, + }; } - - 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); + 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 { + 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 { + const result = await this.languageProvider.fetchLabelValues(options.key, { timeRange: options.timeRange }); + return result.map((value: string) => ({ text: value })); } /**