Prometheus: Fallback to fetch metric names when metadata returns nothing (#115369)

fallback to fetch metric names when metadata returns nothing
This commit is contained in:
ismail simsek
2025-12-29 19:11:44 +01:00
committed by GitHub
parent 4c79775b57
commit 0c6b97bee2
4 changed files with 40 additions and 10 deletions
@@ -48,7 +48,7 @@ describe('MetricsModal', () => {
operations: [],
};
setup(query, ['with-labels'], true);
setup(query, ['with-labels']);
await waitFor(() => {
expect(screen.getByText('with-labels')).toBeInTheDocument();
});
@@ -220,6 +220,10 @@ function createDatasource(withLabels?: boolean) {
// display different results if their labels are selected in the PromVisualQuery
if (withLabels) {
languageProvider.queryMetricsMetadata = jest.fn().mockResolvedValue({
ALERTS: {
type: 'gauge',
help: 'alerts help text',
},
'with-labels': {
type: 'with-labels-type',
help: 'with-labels-help',
@@ -297,7 +301,7 @@ function createProps(query: PromVisualQuery, datasource: PrometheusDatasource, m
};
}
function setup(query: PromVisualQuery, metrics: string[], withlabels?: boolean) {
function setup(query: PromVisualQuery, metrics: string[]) {
const withLabels: boolean = query.labels.length > 0;
const datasource = createDatasource(withLabels);
const props = createProps(query, datasource, metrics);
@@ -138,7 +138,7 @@ const MetricsModalContent = (props: MetricsModalProps) => {
export const MetricsModal = (props: MetricsModalProps) => {
return (
<MetricsModalContextProvider languageProvider={props.datasource.languageProvider}>
<MetricsModalContextProvider languageProvider={props.datasource.languageProvider} timeRange={props.timeRange}>
<MetricsModalContent {...props} />
</MetricsModalContextProvider>
);
@@ -4,6 +4,7 @@ import { ReactNode } from 'react';
import { TimeRange } from '@grafana/data';
import { PrometheusLanguageProviderInterface } from '../../../language_provider';
import { getMockTimeRange } from '../../../test/mocks/datasource';
import { DEFAULT_RESULTS_PER_PAGE, MetricsModalContextProvider, useMetricsModal } from './MetricsModalContext';
import { generateMetricData } from './helpers';
@@ -25,7 +26,9 @@ const mockLanguageProvider: PrometheusLanguageProviderInterface = {
// Helper to create wrapper component
const createWrapper = (languageProvider = mockLanguageProvider) => {
return ({ children }: { children: ReactNode }) => (
<MetricsModalContextProvider languageProvider={languageProvider}>{children}</MetricsModalContextProvider>
<MetricsModalContextProvider languageProvider={languageProvider} timeRange={getMockTimeRange()}>
{children}
</MetricsModalContextProvider>
);
};
@@ -167,6 +170,7 @@ describe('MetricsModalContext', () => {
it('should handle empty metadata response', async () => {
(mockLanguageProvider.queryMetricsMetadata as jest.Mock).mockResolvedValue({});
(mockLanguageProvider.queryLabelValues as jest.Mock).mockResolvedValue(['metric1', 'metric2']);
const { result } = renderHook(() => useMetricsModal(), {
wrapper: createWrapper(),
@@ -176,7 +180,18 @@ describe('MetricsModalContext', () => {
expect(result.current.isLoading).toBe(false);
});
expect(result.current.filteredMetricsData).toEqual([]);
expect(result.current.filteredMetricsData).toEqual([
{
value: 'metric1',
type: 'counter',
description: 'Test metric',
},
{
value: 'metric2',
type: 'counter',
description: 'Test metric',
},
]);
});
it('should handle metadata fetch error', async () => {
@@ -239,6 +254,7 @@ describe('MetricsModalContext', () => {
}));
(mockLanguageProvider.queryMetricsMetadata as jest.Mock).mockResolvedValue({
ALERTS: { type: 'gauge', help: 'Test alerts help' },
test_metric: { type: 'counter', help: 'Test metric' },
});
@@ -250,7 +266,7 @@ describe('MetricsModalContext', () => {
expect(result.current.isLoading).toBe(false);
});
expect(result.current.filteredMetricsData).toHaveLength(1);
expect(result.current.filteredMetricsData).toHaveLength(2);
expect(result.current.selectedTypes).toEqual([]);
});
@@ -318,7 +334,7 @@ describe('MetricsModalContext', () => {
};
const { getByTestId } = render(
<MetricsModalContextProvider languageProvider={mockLanguageProvider}>
<MetricsModalContextProvider languageProvider={mockLanguageProvider} timeRange={getMockTimeRange()}>
<TestComponent />
</MetricsModalContextProvider>
);
@@ -52,11 +52,13 @@ const MetricsModalContext = createContext<MetricsModalContextValue | undefined>(
type MetricsModalContextProviderProps = {
languageProvider: PrometheusLanguageProviderInterface;
timeRange: TimeRange;
};
export const MetricsModalContextProvider: FC<PropsWithChildren<MetricsModalContextProviderProps>> = ({
children,
languageProvider,
timeRange,
}) => {
const [isLoading, setIsLoading] = useState(true);
const [metricsData, setMetricsData] = useState<MetricsData>([]);
@@ -111,8 +113,16 @@ export const MetricsModalContextProvider: FC<PropsWithChildren<MetricsModalConte
setIsLoading(true);
const metadata = await languageProvider.queryMetricsMetadata(PROMETHEUS_QUERY_BUILDER_MAX_RESULTS);
if (Object.keys(metadata).length === 0) {
setMetricsData([]);
// We receive ALERTS metadata in any case
if (Object.keys(metadata).length <= 1) {
const fetchedMetrics = await languageProvider.queryLabelValues(
timeRange,
METRIC_LABEL,
undefined,
PROMETHEUS_QUERY_BUILDER_MAX_RESULTS
);
const processedData = fetchedMetrics.map((m) => generateMetricData(m, languageProvider));
setMetricsData(processedData);
} else {
const processedData = Object.keys(metadata).map((m) => generateMetricData(m, languageProvider));
setMetricsData(processedData);
@@ -122,7 +132,7 @@ export const MetricsModalContextProvider: FC<PropsWithChildren<MetricsModalConte
} finally {
setIsLoading(false);
}
}, [languageProvider]);
}, [languageProvider, timeRange]);
const debouncedBackendSearch = useMemo(
() =>