diff --git a/prometheus/src/model/api-types.ts b/prometheus/src/model/api-types.ts index 0e2d35a2..8c487d06 100644 --- a/prometheus/src/model/api-types.ts +++ b/prometheus/src/model/api-types.ts @@ -139,3 +139,5 @@ export interface ParseQueryRequestParameters { } export type ParseQueryResponse = ApiResponse; + +export type FlagsResponse = ApiResponse>; diff --git a/prometheus/src/model/prometheus-client.codemirror-adapter.ts b/prometheus/src/model/prometheus-client.codemirror-adapter.ts new file mode 100644 index 00000000..4782d78e --- /dev/null +++ b/prometheus/src/model/prometheus-client.codemirror-adapter.ts @@ -0,0 +1,81 @@ +// Copyright 2025 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PrometheusClient as CodeMirrorPrometheusClient } from '@prometheus-io/codemirror-promql'; +import { LabelNamesRequestParameters, LabelValuesRequestParameters, SeriesRequestParameters } from './api-types'; +import { Matcher } from '@prometheus-io/codemirror-promql/dist/esm/types'; +import { PrometheusClient } from './prometheus-client'; +import { MetricMetadata } from '@prometheus-io/codemirror-promql/dist/esm/client/prometheus'; + +export const createCodeMirrorPromClient = ( + prometheusClient?: PrometheusClient +): CodeMirrorPrometheusClient | undefined => { + if (prometheusClient) { + return { + labelNames: async (metricNames: string | undefined) => { + let params: LabelNamesRequestParameters = {}; + + if (metricNames) { + params['match[]'] = metricNames.split(','); + } + const { data } = (await prometheusClient?.labelNames(params)) || {}; + return data || []; + }, + labelValues: async (labelName: string, metricName?: string, matchers?: Matcher[]) => { + let params: LabelValuesRequestParameters = { + labelName, + }; + if (metricName) { + params['match[]'] = [ + `${metricName}{${matchers + ?.reduce((acc: Matcher[], curr: Matcher) => { + if (!curr.matchesEmpty()) { + acc.push(curr); + } + return acc; + }, []) + .map((m) => `${m.name}${m.type}"${m.value}"`) + .join(',')}}`, + ]; + } + + const { data } = (await prometheusClient?.labelValues(params)) || {}; + return data || []; + }, + metricNames: async (prefix?: string) => { + let params: LabelValuesRequestParameters = { + labelName: '__name__', + }; + const { data } = (await prometheusClient?.labelValues(params)) || {}; + return data || []; + }, + metricMetadata: async (): Promise> => { + const { data } = (await prometheusClient?.metricMetadata({})) || {}; + + return data || {}; + }, + series: async (metricName: string, matchers?: Matcher[], labelName?: string): Promise[]> => { + const params: SeriesRequestParameters = { + 'match[]': [], + }; + const { data } = (await prometheusClient?.series(params)) || {}; + return (data || []).map((item: any) => new Map(Object.entries(item.metric))); + }, + flags: async () => { + const { data } = (await prometheusClient?.flags()) || {}; + + return data || {}; + }, + }; + } +}; diff --git a/prometheus/src/model/prometheus-client.ts b/prometheus/src/model/prometheus-client.ts index 86b1aa4f..ff6b47ef 100644 --- a/prometheus/src/model/prometheus-client.ts +++ b/prometheus/src/model/prometheus-client.ts @@ -14,6 +14,7 @@ import { fetch, fetchJson, RequestHeaders } from '@perses-dev/core'; import { DatasourceClient } from '@perses-dev/plugin-system'; import { + FlagsResponse, InstantQueryRequestParameters, InstantQueryResponse, LabelNamesRequestParameters, @@ -45,6 +46,7 @@ export interface PrometheusClient extends DatasourceClient { metricMetadata(params: MetricMetadataRequestParameters, headers?: RequestHeaders): Promise; series(params: SeriesRequestParameters, headers?: RequestHeaders): Promise; parseQuery(params: ParseQueryRequestParameters, headers?: RequestHeaders): Promise; + flags(headers?: RequestHeaders): Promise; } export interface QueryOptions { @@ -149,6 +151,10 @@ export function parseQuery( return fetchWithPost(apiURI, params, queryOptions); } +export function flags(queryOptions: QueryOptions): Promise { + return fetchWithGet('/api/v1/status/flags', {}, queryOptions); +} + function fetchWithGet, TResponse>( apiURI: string, params: T, diff --git a/prometheus/src/plugins/prometheus-datasource.tsx b/prometheus/src/plugins/prometheus-datasource.tsx index 516ba4fb..14bd865b 100644 --- a/prometheus/src/plugins/prometheus-datasource.tsx +++ b/prometheus/src/plugins/prometheus-datasource.tsx @@ -23,6 +23,7 @@ import { metricMetadata, series, parseQuery, + flags, } from '../model'; import { PrometheusDatasourceSpec } from './types'; import { PrometheusDatasourceEditor } from './PrometheusDatasourceEditor'; @@ -55,6 +56,7 @@ const createClient: DatasourcePlugin metricMetadata: (params, headers) => metricMetadata(params, { datasourceUrl, headers: headers ?? specHeaders }), series: (params, headers) => series(params, { datasourceUrl, headers: headers ?? specHeaders }), parseQuery: (params, headers) => parseQuery(params, { datasourceUrl, headers: headers ?? specHeaders }), + flags: (headers) => flags({ datasourceUrl, headers: headers ?? specHeaders }), }; }; diff --git a/prometheus/src/plugins/prometheus-time-series-query/PrometheusTimeSeriesQueryEditor.tsx b/prometheus/src/plugins/prometheus-time-series-query/PrometheusTimeSeriesQueryEditor.tsx index 66e4f39a..32fe24ce 100644 --- a/prometheus/src/plugins/prometheus-time-series-query/PrometheusTimeSeriesQueryEditor.tsx +++ b/prometheus/src/plugins/prometheus-time-series-query/PrometheusTimeSeriesQueryEditor.tsx @@ -31,6 +31,7 @@ import { useFormatState, useMinStepState, } from './query-editor-model'; +import { createCodeMirrorPromClient } from '../../model/prometheus-client.codemirror-adapter'; /** * The options editor component for editing a PrometheusTimeSeriesQuery's spec. @@ -42,7 +43,7 @@ export function PrometheusTimeSeriesQueryEditor(props: PrometheusTimeSeriesQuery const datasourceSelectLabelID = `prom-datasource-label-${selectedDatasource.name || 'default'}`; const { data: client } = useDatasourceClient(selectedDatasource); - const promURL = client?.options.datasourceUrl; + const codeMirrorPromClient = createCodeMirrorPromClient(client); const { data: datasourceResource } = useDatasource(selectedDatasource); const { handleQueryChange, handleQueryBlur } = useQueryState(props); @@ -81,7 +82,9 @@ export function PrometheusTimeSeriesQueryEditor(props: PrometheusTimeSeriesQuery /> @@ -146,7 +147,7 @@ export function PrometheusPromQLVariableEditor( const selectedDatasource = datasource ?? DEFAULT_PROM; const { data: client } = useDatasourceClient(selectedDatasource); - const promURL = client?.options.datasourceUrl; + const codeMirrorPromClient = createCodeMirrorPromClient(client); const handleDatasourceChange: DatasourceSelectProps['onChange'] = (next) => { if (isPrometheusDatasourceSelector(next)) { @@ -176,7 +177,7 @@ export function PrometheusPromQLVariableEditor( /> {