diff --git a/packages/grafana-prometheus/src/metric_find_query.test.ts b/packages/grafana-prometheus/src/metric_find_query.test.ts index 347f2b30be3..8a0aabe6480 100644 --- a/packages/grafana-prometheus/src/metric_find_query.test.ts +++ b/packages/grafana-prometheus/src/metric_find_query.test.ts @@ -8,6 +8,7 @@ import { PrometheusDatasource } from './datasource'; import { getPrometheusTime } from './language_utils'; import { PrometheusMetricFindQuery } from './metric_find_query'; import { PromApplication, PromOptions } from './types'; +import { escapeForUtf8Support } from './utf8_support'; const fetchMock = jest.fn((options: BackendSrvRequest): Observable> => { return of({} as unknown as FetchResponse); @@ -413,5 +414,51 @@ describe('PrometheusMetricFindQuery', () => { }); }); // + describe('utf8 metric and label support', () => { + it('utf8 label - label_values(a_utf8_http_requests_total,instance.test) should generate label values query', () => { + const metricName = 'a_utf8_http_requests_total'; + const label = 'instance.test'; + const query = `label_values(${metricName},${label})`; + const metricFindQuery = new PrometheusMetricFindQuery(prometheusDatasource, query); + metricFindQuery.process(raw); + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(fetchMock).toHaveBeenCalledWith({ + method: 'GET', + url: `/api/datasources/uid/ABCDEF/resources/api/v1/label/${escapeForUtf8Support(label)}/values?match%5B%5D=${metricName}&start=1524650400&end=1524654000`, + hideFromInspector: true, + headers: {}, + }); + }); + + it('utf8 metric - label_values(utf8.http_requests_total,instance_test) should generate label values query', () => { + const metricName = 'utf8.http_requests_total'; + const label = 'instance_test'; + const query = `label_values(${metricName},${label})`; + const metricFindQuery = new PrometheusMetricFindQuery(prometheusDatasource, query); + metricFindQuery.process(raw); + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(fetchMock).toHaveBeenCalledWith({ + method: 'GET', + url: `/api/datasources/uid/ABCDEF/resources/api/v1/label/${label}/values?match%5B%5D=${metricName}&start=1524650400&end=1524654000`, + hideFromInspector: true, + headers: {}, + }); + }); + + it('utf8 metric and label - label_values(utf8.http_requests_total,instance.test) should generate label values query', () => { + const metricName = 'utf8.http_requests_total'; + const label = 'instance.test'; + const query = `label_values(${metricName},${label})`; + const metricFindQuery = new PrometheusMetricFindQuery(prometheusDatasource, query); + metricFindQuery.process(raw); + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(fetchMock).toHaveBeenCalledWith({ + method: 'GET', + url: `/api/datasources/uid/ABCDEF/resources/api/v1/label/${escapeForUtf8Support(label)}/values?match%5B%5D=${metricName}&start=1524650400&end=1524654000`, + hideFromInspector: true, + headers: {}, + }); + }); + }); }); }); diff --git a/packages/grafana-prometheus/src/metric_find_query.ts b/packages/grafana-prometheus/src/metric_find_query.ts index 8be41c06364..69494f0f7fc 100644 --- a/packages/grafana-prometheus/src/metric_find_query.ts +++ b/packages/grafana-prometheus/src/metric_find_query.ts @@ -8,9 +8,11 @@ import { getPrometheusTime } from './language_utils'; import { PrometheusLabelNamesRegex, PrometheusLabelNamesRegexWithMatch, + PrometheusLabelValuesRegex, PrometheusMetricNamesRegex, PrometheusQueryResultRegex, } from './migrations/variableMigration'; +import { escapeForUtf8Support, isValidLegacyName } from './utf8_support'; export class PrometheusMetricFindQuery { range: TimeRange; @@ -28,7 +30,7 @@ export class PrometheusMetricFindQuery { this.range = timeRange; const labelNamesRegex = PrometheusLabelNamesRegex; const labelNamesRegexWithMatch = PrometheusLabelNamesRegexWithMatch; - const labelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]*)\)\s*$/; + const labelValuesRegex = PrometheusLabelValuesRegex; const metricNamesRegex = PrometheusMetricNamesRegex; const queryResultRegex = PrometheusQueryResultRegex; const labelNamesQuery = this.query.match(labelNamesRegex); @@ -83,8 +85,13 @@ export class PrometheusMetricFindQuery { const end = getPrometheusTime(this.range.to, true); const params = { ...(metric && { 'match[]': metric }), start: start.toString(), end: end.toString() }; + let escapedLabel = label; + if (!isValidLegacyName(label)) { + escapedLabel = escapeForUtf8Support(label); + } + if (!metric || this.datasource.hasLabelsMatchAPISupport()) { - const url = `/api/v1/label/${label}/values`; + const url = `/api/v1/label/${escapedLabel}/values`; return this.datasource.metadataRequest(url, params).then((result) => { return _map(result.data.data, (value) => { diff --git a/packages/grafana-prometheus/src/migrations/variableMigration.ts b/packages/grafana-prometheus/src/migrations/variableMigration.ts index 08476c2f4b6..35c4c483180 100644 --- a/packages/grafana-prometheus/src/migrations/variableMigration.ts +++ b/packages/grafana-prometheus/src/migrations/variableMigration.ts @@ -4,8 +4,7 @@ import { buildVisualQueryFromString } from '../querybuilder/parsing'; import { PromVariableQuery, PromVariableQueryType as QueryType } from '../types'; export const PrometheusLabelNamesRegex = /^label_names\(\)\s*$/; -// Note that this regex is different from the one in metric_find_query.ts because this is used pre-interpolation -export const PrometheusLabelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_$][a-zA-Z0-9_]*)\)\s*$/; +export const PrometheusLabelValuesRegex = /^label_values\((?:(.+),\s*)?(.+)\)\s*$/; export const PrometheusMetricNamesRegex = /^metrics\((.+)\)\s*$/; export const PrometheusQueryResultRegex = /^query_result\((.+)\)\s*$/; export const PrometheusLabelNamesRegexWithMatch = /^label_names\((.+)\)\s*$/; @@ -97,7 +96,7 @@ export function migrateVariableQueryToEditor(rawQuery: string | PromVariableQuer return queryBase; } -// migrate it back to a string with the correct varialbes in place +// migrate it back to a string with the correct variables in place export function migrateVariableEditorBackToVariableSupport(QueryVariable: PromVariableQuery): string { switch (QueryVariable.qryType) { case QueryType.LabelNames: