Chore: Refactor some of the prometheus data source types (#107031)

* refactor the types

* more refactoring

* fix import
This commit is contained in:
ismail simsek
2025-06-20 12:02:41 +00:00
committed by GitHub
parent 0b7e2f5601
commit 18cb131a2a
19 changed files with 61 additions and 57 deletions
@@ -4,9 +4,10 @@ import { selectors } from '@grafana/e2e-selectors';
import { Trans, t } from '@grafana/i18n';
import { BrowserLabel as PromLabel, Input, Label, useStyles2 } from '@grafana/ui';
import { METRIC_LABEL } from '../../constants';
import { useMetricsBrowser } from './MetricsBrowserContext';
import { getStylesLabelSelector } from './styles';
import { METRIC_LABEL } from './types';
export function LabelSelector() {
const styles = useStyles2(getStylesLabelSelector);
@@ -5,9 +5,10 @@ import { selectors } from '@grafana/e2e-selectors';
import { Trans, t } from '@grafana/i18n';
import { BrowserLabel as PromLabel, Input, Label, useStyles2 } from '@grafana/ui';
import { LIST_ITEM_SIZE } from '../../constants';
import { useMetricsBrowser } from './MetricsBrowserContext';
import { getStylesMetricSelector } from './styles';
import { LIST_ITEM_SIZE } from './types';
export function MetricSelector() {
const styles = useStyles2(getStylesMetricSelector);
@@ -4,11 +4,11 @@ import { ReactNode } from 'react';
import { TimeRange } from '@grafana/data';
import { LAST_USED_LABELS_KEY, METRIC_LABEL } from '../../constants';
import { PrometheusLanguageProviderInterface } from '../../language_provider';
import { getMockTimeRange } from '../../test/__mocks__/datasource';
import { MetricsBrowserProvider, useMetricsBrowser } from './MetricsBrowserContext';
import { LAST_USED_LABELS_KEY, METRIC_LABEL } from './types';
const setupLocalStorageMock = () => {
let store: Record<string, string> = {};
@@ -5,9 +5,13 @@ import { TimeRange } from '@grafana/data';
import { PrometheusLanguageProviderInterface } from '../../language_provider';
import { buildSelector } from './selectorBuilder';
import { Metric } from './types';
import { useMetricsLabelsValues } from './useMetricsLabelsValues';
export interface Metric {
name: string;
details?: string;
}
/**
* Context for the Metrics Browser component
* Provides state and handlers for browsing and selecting Prometheus metrics and labels
@@ -5,9 +5,10 @@ import { selectors } from '@grafana/e2e-selectors';
import { Trans, t } from '@grafana/i18n';
import { Button, Label, Stack, useStyles2 } from '@grafana/ui';
import { EMPTY_SELECTOR } from '../../constants';
import { useMetricsBrowser } from './MetricsBrowserContext';
import { getStylesSelectorActions } from './styles';
import { EMPTY_SELECTOR } from './types';
export function SelectorActions() {
const styles = useStyles2(getStylesSelectorActions);
@@ -5,9 +5,10 @@ import { selectors } from '@grafana/e2e-selectors';
import { t, Trans } from '@grafana/i18n';
import { BrowserLabel as PromLabel, Input, Label, useStyles2 } from '@grafana/ui';
import { LIST_ITEM_SIZE } from '../../constants';
import { useMetricsBrowser } from './MetricsBrowserContext';
import { getStylesValueSelector } from './styles';
import { LIST_ITEM_SIZE } from './types';
export function ValueSelector() {
const styles = useStyles2(getStylesValueSelector);
@@ -1,11 +0,0 @@
export interface Metric {
name: string;
details?: string;
}
export const DEFAULT_SERIES_LIMIT = '40000';
export const REMOVE_SERIES_LIMIT = 'none';
export const EMPTY_SELECTOR = '{}';
export const METRIC_LABEL = '__name__';
export const LIST_ITEM_SIZE = 25;
export const LAST_USED_LABELS_KEY = 'grafana.datasources.prometheus.browser.labels';
@@ -2,11 +2,11 @@ import { act, renderHook, waitFor } from '@testing-library/react';
import { TimeRange } from '@grafana/data';
import { DEFAULT_SERIES_LIMIT, EMPTY_SELECTOR, LAST_USED_LABELS_KEY, METRIC_LABEL } from '../../constants';
import { PrometheusLanguageProviderInterface } from '../../language_provider';
import { getMockTimeRange } from '../../test/__mocks__/datasource';
import * as selectorBuilderModule from './selectorBuilder';
import { DEFAULT_SERIES_LIMIT, EMPTY_SELECTOR, LAST_USED_LABELS_KEY, METRIC_LABEL } from './types';
import { useMetricsLabelsValues } from './useMetricsLabelsValues';
// Test utilities to reduce boilerplate
@@ -3,10 +3,11 @@ import { useDebounce } from 'react-use';
import { TimeRange } from '@grafana/data';
import { DEFAULT_SERIES_LIMIT, EMPTY_SELECTOR, LAST_USED_LABELS_KEY, METRIC_LABEL } from '../../constants';
import { PrometheusLanguageProviderInterface } from '../../language_provider';
import { Metric } from './MetricsBrowserContext';
import { buildSelector } from './selectorBuilder';
import { DEFAULT_SERIES_LIMIT, EMPTY_SELECTOR, LAST_USED_LABELS_KEY, Metric, METRIC_LABEL } from './types';
export const useMetricsLabelsValues = (timeRange: TimeRange, languageProvider: PrometheusLanguageProviderInterface) => {
const timeRangeRef = useRef<TimeRange>(timeRange);
@@ -6,9 +6,10 @@ import { SelectableValue } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { config } from '@grafana/runtime';
import { countError } from '../constants';
import { createDefaultConfigOptions } from '../test/__mocks__/datasource';
import { countError, getValueFromEventItem, PromSettings } from './PromSettings';
import { getValueFromEventItem, PromSettings } from './PromSettings';
beforeEach(() => {
jest.replaceProperty(config, 'featureToggles', {
@@ -15,7 +15,9 @@ import { config } from '@grafana/runtime';
import { InlineField, Input, Select, Switch, TextLink, useTheme2 } from '@grafana/ui';
import {
countError,
DURATION_REGEX,
durationError,
MULTIPLE_DURATION_REGEX,
NON_NEGATIVE_INTEGER_REGEX,
PROM_CONFIG_LABEL_WIDTH,
@@ -29,6 +31,8 @@ import { ExemplarsSettings } from './ExemplarsSettings';
import { PromFlavorVersions } from './PromFlavorVersions';
import { docsTip, overhaulStyles, validateInput } from './shared/utils';
type Props = Pick<DataSourcePluginOptionsEditorProps<PromOptions>, 'options' | 'onOptionsChange'>;
const httpOptions = [
{ value: 'POST', label: 'POST' },
{ value: 'GET', label: 'GET' },
@@ -48,6 +52,16 @@ const cacheValueOptions = [
type PrometheusSelectItemsType = Array<{ value: PromApplication; label: PromApplication }>;
type ValidDuration = {
timeInterval: string;
queryTimeout: string;
incrementalQueryOverlapWindow: string;
};
type ValidCount = {
codeModeMetricNamesSuggestionLimit: string;
};
const prometheusFlavorSelectItems: PrometheusSelectItemsType = [
{ value: PromApplication.Prometheus, label: PromApplication.Prometheus },
{ value: PromApplication.Cortex, label: PromApplication.Cortex },
@@ -55,11 +69,6 @@ const prometheusFlavorSelectItems: PrometheusSelectItemsType = [
{ value: PromApplication.Thanos, label: PromApplication.Thanos },
];
type Props = Pick<DataSourcePluginOptionsEditorProps<PromOptions>, 'options' | 'onOptionsChange'>;
const durationError = 'Value is not valid, you can use number with time unit specifier: y, M, w, d, h, m, s';
export const countError = 'Value is not valid, you can use non-negative integers, including scientific notation';
const getOptionsWithDefaults = (options: DataSourceSettings<PromOptions>) => {
if (options.jsonData.httpMethod) {
return options;
@@ -71,28 +80,17 @@ const getOptionsWithDefaults = (options: DataSourceSettings<PromOptions>) => {
};
export const PromSettings = (props: Props) => {
const { onOptionsChange } = props;
const optionsWithDefaults = getOptionsWithDefaults(props.options);
const theme = useTheme2();
const styles = overhaulStyles(theme);
const { onOptionsChange } = props;
type ValidDuration = {
timeInterval: string;
queryTimeout: string;
incrementalQueryOverlapWindow: string;
};
const optionsWithDefaults = getOptionsWithDefaults(props.options);
const [validDuration, updateValidDuration] = useState<ValidDuration>({
timeInterval: '',
queryTimeout: '',
incrementalQueryOverlapWindow: '',
});
type ValidCount = {
codeModeMetricNamesSuggestionLimit: string;
};
const [validCount, updateValidCount] = useState<ValidCount>({
codeModeMetricNamesSuggestionLimit: '',
});
@@ -5,6 +5,9 @@ export const PROMETHEUS_QUERY_BUILDER_MAX_RESULTS = 1000;
export const PROM_CONFIG_LABEL_WIDTH = 30;
export const LIST_ITEM_SIZE = 25;
export const LAST_USED_LABELS_KEY = 'grafana.datasources.prometheus.browser.labels';
// single duration input
export const DURATION_REGEX = /^$|^\d+(ms|[Mwdhmsy])$/;
@@ -13,5 +16,16 @@ export const MULTIPLE_DURATION_REGEX = /(\d+)(.+)/;
export const NON_NEGATIVE_INTEGER_REGEX = /^(0|[1-9]\d*)(\.\d+)?(e\+?\d+)?$/; // non-negative integers, including scientific notation
export const EMPTY_SELECTOR = '{}';
export const DEFAULT_SERIES_LIMIT = '40000';
export const MATCH_ALL_LABELS_STR = '__name__!=""';
export const MATCH_ALL_LABELS = '{__name__!=""}';
export const METRIC_LABEL = '__name__';
/**
* @deprecated
*/
export const REMOVE_SERIES_LIMIT = 'none';
export const durationError = 'Value is not valid, you can use number with time unit specifier: y, M, w, d, h, m, s';
export const countError = 'Value is not valid, you can use non-negative integers, including scientific notation';
@@ -3,14 +3,13 @@ import { AbstractLabelOperator, dateTime, TimeRange } from '@grafana/data';
jest.mock('./language_utils', () => ({
...jest.requireActual('./language_utils'),
processHistogramMetrics: (metrics: string[]) => metrics,
getPrometheusTime: jest.requireActual('./language_utils').getPrometheusTime,
getRangeSnapInterval: jest.requireActual('./language_utils').getRangeSnapInterval,
}));
import { getCacheDurationInMinutes } from './caching';
import { DEFAULT_SERIES_LIMIT } from './components/metrics-browser/types';
import { Label } from './components/monaco-query-field/monaco-completion-provider/situation';
import { DEFAULT_SERIES_LIMIT } from './constants';
import { PrometheusDatasource } from './datasource';
import {
exportToAbstractQuery,
@@ -308,7 +307,7 @@ describe('Prometheus Language Provider', () => {
const fetchSeriesLabels = languageProvider.fetchSeriesLabels;
const requestSpy = jest.spyOn(languageProvider, 'request');
fetchSeriesLabels(getMockTimeRange(), '$metric');
fetchSeriesLabels(getMockTimeRange(), '$metric', undefined, DEFAULT_SERIES_LIMIT);
verifyRequestParams(requestSpy, '/api/v1/series', {
end: toPrometheusTimeString,
@@ -17,8 +17,8 @@ import {
import { BackendSrvRequest } from '@grafana/runtime';
import { buildCacheHeaders, getDaysToCacheMetadata, getDefaultCacheHeaders } from './caching';
import { DEFAULT_SERIES_LIMIT, REMOVE_SERIES_LIMIT } from './components/metrics-browser/types';
import { Label } from './components/monaco-query-field/monaco-completion-provider/situation';
import { DEFAULT_SERIES_LIMIT, MATCH_ALL_LABELS_STR, EMPTY_SELECTOR, REMOVE_SERIES_LIMIT } from './constants';
import { PrometheusDatasource } from './datasource';
import {
extractLabelMatchers,
@@ -30,11 +30,10 @@ import {
import PromqlSyntax from './promql';
import { buildVisualQueryFromString } from './querybuilder/parsing';
import { LabelsApiClient, ResourceApiClient, SeriesApiClient } from './resource_clients';
import { MATCH_ALL_LABELS_STR, PromMetricsMetadata, PromQuery } from './types';
import { PromMetricsMetadata, PromQuery } from './types';
import { escapeForUtf8Support, isValidLegacyName } from './utf8_support';
const DEFAULT_KEYS = ['job', 'instance'];
const EMPTY_SELECTOR = '{}';
/**
* Prometheus API endpoints for fetching resources
@@ -3,6 +3,7 @@ import { map as _map } from 'lodash';
import { MetricFindValue, TimeRange } from '@grafana/data';
import { METRIC_LABEL } from './constants';
import { PrometheusDatasource } from './datasource';
import { getPrometheusTime } from './language_utils';
import {
@@ -13,7 +14,6 @@ import {
PrometheusQueryResultRegex,
} from './migrations/variableMigration';
import { getOriginalMetricName } from './result_transformer';
import { METRIC_LABEL } from './types';
export class PrometheusMetricFindQuery {
constructor(
@@ -6,7 +6,7 @@ import { Trans, t } from '@grafana/i18n';
import { EditorField, EditorFieldGroup, InputGroup } from '@grafana/plugin-ui';
import { Button, InlineField, InlineFieldRow, Combobox, ComboboxOption } from '@grafana/ui';
import { METRIC_LABEL } from '../../components/metrics-browser/types';
import { METRIC_LABEL } from '../../constants';
import { PrometheusDatasource } from '../../datasource';
import { regexifyLabelValuesQueryString } from '../parsingUtils';
import { QueryBuilderLabelFilter } from '../shared/types';
@@ -20,7 +20,7 @@ import {
} from '@grafana/ui';
import { getDebounceTimeInMilliseconds } from '../../../caching';
import { METRIC_LABEL } from '../../../components/metrics-browser/types';
import { METRIC_LABEL } from '../../../constants';
import { regexifyLabelValuesQueryString } from '../../parsingUtils';
import { formatPrometheusLabelFilters } from '../MetricCombobox';
@@ -2,12 +2,12 @@ import { TimeRange } from '@grafana/data';
import { BackendSrvRequest } from '@grafana/runtime';
import { getDefaultCacheHeaders } from './caching';
import { DEFAULT_SERIES_LIMIT } from './components/metrics-browser/types';
import { DEFAULT_SERIES_LIMIT, EMPTY_SELECTOR, MATCH_ALL_LABELS, METRIC_LABEL } from './constants';
import { PrometheusDatasource } from './datasource';
import { removeQuotesIfExist } from './language_provider';
import { getRangeSnapInterval, processHistogramMetrics } from './language_utils';
import { buildVisualQueryFromString } from './querybuilder/parsing';
import { EMPTY_MATCHER, MATCH_ALL_LABELS, METRIC_LABEL, PrometheusCacheLevel } from './types';
import { PrometheusCacheLevel } from './types';
import { escapeForUtf8Support, utf8Support } from './utf8_support';
type PrometheusSeriesResponse = Array<{ [key: string]: string }>;
@@ -66,7 +66,7 @@ export abstract class BaseResourceClient {
* @param {string} limit - Maximum number of series to return
*/
public querySeries = async (timeRange: TimeRange, match: string, limit: string = DEFAULT_SERIES_LIMIT) => {
const effectiveMatch = !match || match === EMPTY_MATCHER ? MATCH_ALL_LABELS : match;
const effectiveMatch = !match || match === EMPTY_SELECTOR ? MATCH_ALL_LABELS : match;
const timeParams = this.datasource.getTimeRangeParams(timeRange);
const searchParams = { ...timeParams, 'match[]': effectiveMatch, limit };
return await this.requestSeries('/api/v1/series', searchParams, getDefaultCacheHeaders(this.datasource.cacheLevel));
@@ -186,7 +186,7 @@ export class SeriesApiClient extends BaseResourceClient implements ResourceApiCl
match?: string,
limit: string = DEFAULT_SERIES_LIMIT
): Promise<string[]> => {
const effectiveMatch = !match || match === EMPTY_MATCHER ? MATCH_ALL_LABELS : match;
const effectiveMatch = !match || match === EMPTY_SELECTOR ? MATCH_ALL_LABELS : match;
const maybeCachedKeys = this._cache.getLabelKeys(timeRange, effectiveMatch, limit);
if (maybeCachedKeys) {
return maybeCachedKeys;
@@ -205,7 +205,7 @@ export class SeriesApiClient extends BaseResourceClient implements ResourceApiCl
limit: string = DEFAULT_SERIES_LIMIT
): Promise<string[]> => {
let effectiveMatch = '';
if (!match || match === EMPTY_MATCHER) {
if (!match || match === EMPTY_SELECTOR) {
// Just and empty matcher {} or no matcher
effectiveMatch = `{${utf8Support(removeQuotesIfExist(labelKey))}!=""}`;
} else {
-5
View File
@@ -189,8 +189,3 @@ export type RecordingRuleIdentifier = {
identifier?: string;
identifierValue?: string;
};
export const EMPTY_MATCHER = '{}';
export const MATCH_ALL_LABELS_STR = '__name__!=""';
export const MATCH_ALL_LABELS = '{__name__!=""}';
export const METRIC_LABEL = '__name__';