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 [];
},
fetchSeriesLabels: (selector: string) => {
fetchLabelsWithMatch: (selector: string) => {
switch (selector) {
case '{label1="value1-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}`);
}
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 !== buildSelector(this.state.labels)) {
if (lastFacetted) {

View File

@ -142,7 +142,7 @@ describe('PromVariableQueryEditor', () => {
metrics: [],
metricsMetadata: {},
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,
getInitHints: () => [],
getDebounceTimeInMilliseconds: jest.fn(),

View File

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

View File

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

View File

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

View File

@ -289,6 +289,20 @@ export default class PromQlLanguageProvider extends LanguageProvider {
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
* 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 result = await datasource.languageProvider.fetchSeriesLabels(expr);
const result = await datasource.languageProvider.fetchLabelsWithMatch(expr);
return Object.keys(result).map((x) => ({
label: x,

View File

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

View File

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

View File

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

View File

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