Prometheus plugin: Use new labels endpoint, for LabelEditor (#80774)

Prometheus plugin: use new labels endpoint, if supported, for LabelEditor

Before this change LabelEditor always use old '/series' endpoint to get labels.
Now new '/labels' endpoint would be used in case datasource supports it.
This commit is contained in:
Yuri Kotov 2024-01-24 04:00:43 +07:00 committed by GitHub
parent 0173755446
commit a7b58a7cdb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 34 additions and 41 deletions

View File

@ -114,7 +114,7 @@ describe('PrometheusMetricsBrowser', () => {
} }
return []; return [];
}, },
fetchSeriesLabels: (selector: string) => { fetchLabelsWithMatch: (selector: string) => {
switch (selector) { switch (selector) {
case '{label1="value1-1"}': case '{label1="value1-1"}':
return { label1: ['value1-1'], label2: ['value2-1'], label3: ['value3-1'] }; return { label1: ['value1-1'], label2: ['value2-1'], label3: ['value3-1'] };

View File

@ -415,7 +415,7 @@ export class UnthemedPrometheusMetricsBrowser extends React.Component<BrowserPro
this.updateLabelState(lastFacetted, { loading: true }, `Facetting labels for ${selector}`); this.updateLabelState(lastFacetted, { loading: true }, `Facetting labels for ${selector}`);
} }
try { try {
const possibleLabels = await languageProvider.fetchSeriesLabels(selector, true); const possibleLabels = await languageProvider.fetchLabelsWithMatch(selector, true);
// If selector changed, clear loading state and discard result by returning early // If selector changed, clear loading state and discard result by returning early
if (selector !== buildSelector(this.state.labels)) { if (selector !== buildSelector(this.state.labels)) {
if (lastFacetted) { if (lastFacetted) {

View File

@ -142,7 +142,7 @@ describe('PromVariableQueryEditor', () => {
metrics: [], metrics: [],
metricsMetadata: {}, metricsMetadata: {},
getLabelValues: jest.fn().mockImplementation(() => ['that']), getLabelValues: jest.fn().mockImplementation(() => ['that']),
fetchSeriesLabelsMatch: jest.fn().mockImplementation(() => Promise.resolve({ those: 'those' })), fetchLabelsWithMatch: jest.fn().mockImplementation(() => Promise.resolve({ those: 'those' })),
} as Partial<PrometheusLanguageProvider> as PrometheusLanguageProvider, } as Partial<PrometheusLanguageProvider> as PrometheusLanguageProvider,
getInitHints: () => [], getInitHints: () => [],
getDebounceTimeInMilliseconds: jest.fn(), getDebounceTimeInMilliseconds: jest.fn(),

View File

@ -106,19 +106,11 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource, range }:
const labelToConsider = [{ label: '__name__', op: '=', value: metric }]; const labelToConsider = [{ label: '__name__', op: '=', value: metric }];
const expr = promQueryModeller.renderLabels(labelToConsider); const expr = promQueryModeller.renderLabels(labelToConsider);
if (datasource.hasLabelsMatchAPISupport()) { datasource.languageProvider.fetchLabelsWithMatch(expr).then((labelsIndex: Record<string, string[]>) => {
datasource.languageProvider.fetchSeriesLabelsMatch(expr).then((labelsIndex: Record<string, string[]>) => { const labelNames = Object.keys(labelsIndex);
const labelNames = Object.keys(labelsIndex); const names = labelNames.map((value) => ({ label: value, value: value }));
const names = labelNames.map((value) => ({ label: value, value: value })); setLabelOptions([...variables, ...names]);
setLabelOptions([...variables, ...names]); });
});
} else {
datasource.languageProvider.fetchSeriesLabels(expr).then((labelsIndex: Record<string, string[]>) => {
const labelNames = Object.keys(labelsIndex);
const names = labelNames.map((value) => ({ label: value, value: value }));
setLabelOptions([...variables, ...names]);
});
}
} }
}, [datasource, qryType, metric]); }, [datasource, qryType, metric]);

View File

@ -681,13 +681,7 @@ export class PrometheusDatasource
})); }));
const expr = promQueryModeller.renderLabels(labelFilters); const expr = promQueryModeller.renderLabels(labelFilters);
let labelsIndex: Record<string, string[]>; let labelsIndex: Record<string, string[]> = await this.languageProvider.fetchLabelsWithMatch(expr);
if (this.hasLabelsMatchAPISupport()) {
labelsIndex = await this.languageProvider.fetchSeriesLabelsMatch(expr);
} else {
labelsIndex = await this.languageProvider.fetchSeriesLabels(expr);
}
// filter out already used labels // filter out already used labels
return Object.keys(labelsIndex) return Object.keys(labelsIndex)

View File

@ -12,6 +12,7 @@ export class EmptyLanguageProviderMock {
fetchSeries = jest.fn().mockReturnValue([]); fetchSeries = jest.fn().mockReturnValue([]);
fetchSeriesLabels = jest.fn().mockReturnValue([]); fetchSeriesLabels = jest.fn().mockReturnValue([]);
fetchSeriesLabelsMatch = jest.fn().mockReturnValue([]); fetchSeriesLabelsMatch = jest.fn().mockReturnValue([]);
fetchLabelsWithMatch = jest.fn().mockReturnValue([]);
fetchLabels = jest.fn(); fetchLabels = jest.fn();
loadMetricsMetadata = jest.fn(); loadMetricsMetadata = jest.fn();
} }

View File

@ -289,6 +289,20 @@ export default class PromQlLanguageProvider extends LanguageProvider {
return possibleLabelNames.filter((l) => !usedLabelNames.has(l)); return possibleLabelNames.filter((l) => !usedLabelNames.has(l));
}; };
/**
* Fetch labels using the best endpoint that datasource supports.
* This is cached by its args but also by the global timeRange currently selected as they can change over requested time.
* @param name
* @param withName
*/
fetchLabelsWithMatch = async (name: string, withName?: boolean): Promise<Record<string, string[]>> => {
if (this.datasource.hasLabelsMatchAPISupport()) {
return this.fetchSeriesLabelsMatch(name, withName);
} else {
return this.fetchSeriesLabels(name, withName);
}
};
/** /**
* Fetch labels for a series using /series endpoint. This is cached by its args but also by the global timeRange currently selected as * Fetch labels for a series using /series endpoint. This is cached by its args but also by the global timeRange currently selected as
* they can change over requested time. * they can change over requested time.

View File

@ -51,7 +51,7 @@ async function loadGroupByLabels(query: PromVisualQuery, datasource: DataSourceA
} }
const expr = promQueryModeller.renderLabels(labels); const expr = promQueryModeller.renderLabels(labels);
const result = await datasource.languageProvider.fetchSeriesLabels(expr); const result = await datasource.languageProvider.fetchLabelsWithMatch(expr);
return Object.keys(result).map((x) => ({ return Object.keys(result).map((x) => ({
label: x, label: x,

View File

@ -68,12 +68,7 @@ export function MetricsLabelsSection({
labelsToConsider.push({ label: '__name__', op: '=', value: query.metric }); labelsToConsider.push({ label: '__name__', op: '=', value: query.metric });
const expr = promQueryModeller.renderLabels(labelsToConsider); const expr = promQueryModeller.renderLabels(labelsToConsider);
let labelsIndex: Record<string, string[]>; let labelsIndex: Record<string, string[]> = await datasource.languageProvider.fetchLabelsWithMatch(expr);
if (datasource.hasLabelsMatchAPISupport()) {
labelsIndex = await datasource.languageProvider.fetchSeriesLabelsMatch(expr);
} else {
labelsIndex = await datasource.languageProvider.fetchSeriesLabels(expr);
}
// filter out already used labels // filter out already used labels
return Object.keys(labelsIndex) return Object.keys(labelsIndex)

View File

@ -108,7 +108,7 @@ describe('PromQueryBuilder', () => {
it('tries to load labels when metric selected', async () => { it('tries to load labels when metric selected', async () => {
const { languageProvider } = setup(); const { languageProvider } = setup();
await openLabelNameSelect(); await openLabelNameSelect();
await waitFor(() => expect(languageProvider.fetchSeriesLabels).toBeCalledWith('{__name__="random_metric"}')); await waitFor(() => expect(languageProvider.fetchLabelsWithMatch).toBeCalledWith('{__name__="random_metric"}'));
}); });
it('tries to load variables in label field', async () => { it('tries to load variables in label field', async () => {
@ -128,7 +128,9 @@ describe('PromQueryBuilder', () => {
}); });
await openLabelNameSelect(1); await openLabelNameSelect(1);
await waitFor(() => await waitFor(() =>
expect(languageProvider.fetchSeriesLabels).toBeCalledWith('{label_name="label_value", __name__="random_metric"}') expect(languageProvider.fetchLabelsWithMatch).toBeCalledWith(
'{label_name="label_value", __name__="random_metric"}'
)
); );
}); });
//</LegacyPrometheus> //</LegacyPrometheus>
@ -275,7 +277,7 @@ describe('PromQueryBuilder', () => {
jsonData: { prometheusVersion: '2.38.1', prometheusType: PromApplication.Prometheus }, jsonData: { prometheusVersion: '2.38.1', prometheusType: PromApplication.Prometheus },
}); });
await openLabelNameSelect(); await openLabelNameSelect();
await waitFor(() => expect(languageProvider.fetchSeriesLabelsMatch).toBeCalledWith('{__name__="random_metric"}')); await waitFor(() => expect(languageProvider.fetchLabelsWithMatch).toBeCalledWith('{__name__="random_metric"}'));
}); });
it('tries to load variables in label field modern prom', async () => { it('tries to load variables in label field modern prom', async () => {
@ -301,7 +303,7 @@ describe('PromQueryBuilder', () => {
); );
await openLabelNameSelect(1); await openLabelNameSelect(1);
await waitFor(() => await waitFor(() =>
expect(languageProvider.fetchSeriesLabelsMatch).toBeCalledWith( expect(languageProvider.fetchLabelsWithMatch).toBeCalledWith(
'{label_name="label_value", __name__="random_metric"}' '{label_name="label_value", __name__="random_metric"}'
) )
); );

View File

@ -54,12 +54,7 @@ export const PromQail = (props: PromQailProps) => {
useEffect(() => { useEffect(() => {
const fetchLabels = async () => { const fetchLabels = async () => {
let labelsIndex: Record<string, string[]>; let labelsIndex: Record<string, string[]> = await datasource.languageProvider.fetchLabelsWithMatch(query.metric);
if (datasource.hasLabelsMatchAPISupport()) {
labelsIndex = await datasource.languageProvider.fetchSeriesLabelsMatch(query.metric);
} else {
labelsIndex = await datasource.languageProvider.fetchSeriesLabels(query.metric);
}
setLabelNames(Object.keys(labelsIndex)); setLabelNames(Object.keys(labelsIndex));
}; };
fetchLabels(); fetchLabels();

View File

@ -346,7 +346,7 @@ export async function promQailSuggest(
}; };
// get all available labels // get all available labels
const metricLabels = await datasource.languageProvider.fetchSeriesLabelsMatch(query.metric); const metricLabels = await datasource.languageProvider.fetchLabelsWithMatch(query.metric);
let feedTheAI: SuggestionBody = { let feedTheAI: SuggestionBody = {
metric: query.metric, metric: query.metric,