Prometheus: Add Metrics Explorer button next to MetricCombobox (#95647)

This commit is contained in:
Josh Hunt
2024-11-01 15:32:17 +00:00
committed by GitHub
parent 5f4944117c
commit eb781dd46d
3 changed files with 89 additions and 13 deletions
@@ -2,7 +2,9 @@ import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';
import { DataSourceInstanceSettings, MetricFindValue } from '@grafana/data';
import { config } from '@grafana/runtime';
import { PrometheusDatasource } from '../../datasource';
import { PromOptions } from '../../types';
@@ -124,4 +126,30 @@ describe('MetricCombobox', () => {
expect(mockOnChange).toHaveBeenCalledWith({ metric: 'random_metric', labels: [], operations: [] });
});
it("doesn't show the metrics explorer button by default", () => {
render(<MetricCombobox {...defaultProps} />);
expect(screen.queryByRole('button', { name: /open metrics explorer/i })).not.toBeInTheDocument();
});
describe('when metrics explorer toggle is enabled', () => {
beforeAll(() => {
jest.replaceProperty(config, 'featureToggles', {
prometheusMetricEncyclopedia: true,
});
});
afterAll(() => {
jest.restoreAllMocks();
});
it('opens the metrics explorer when the button is clicked', async () => {
render(<MetricCombobox {...defaultProps} onGetMetrics={() => Promise.resolve([])} />);
const button = screen.getByRole('button', { name: /open metrics explorer/i });
await userEvent.click(button);
expect(screen.getByText('Metrics explorer')).toBeInTheDocument();
});
});
});
@@ -1,14 +1,18 @@
import { useCallback } from 'react';
import { useCallback, useState } from 'react';
import { SelectableValue } from '@grafana/data';
import { EditorField, EditorFieldGroup } from '@grafana/experimental';
import { InlineField, InlineFieldRow, Combobox, ComboboxOption } from '@grafana/ui';
import { EditorField, EditorFieldGroup, InputGroup } from '@grafana/experimental';
import { config } from '@grafana/runtime';
import { Button, InlineField, InlineFieldRow } from '@grafana/ui';
import { Combobox, ComboboxOption } from '@grafana/ui/src/components/Combobox/Combobox';
import { PrometheusDatasource } from '../../datasource';
import { regexifyLabelValuesQueryString } from '../parsingUtils';
import { QueryBuilderLabelFilter } from '../shared/types';
import { PromVisualQuery } from '../types';
import { MetricsModal } from './metrics-modal';
export interface MetricComboboxProps {
metricLookupDisabled: boolean;
query: PromVisualQuery;
@@ -28,6 +32,8 @@ export function MetricCombobox({
labelsFilters,
variableEditor,
}: Readonly<MetricComboboxProps>) {
const [metricsModalOpen, setMetricsModalOpen] = useState(false);
/**
* Gets label_values response from prometheus API for current autocomplete query string and any existing labels filters
*/
@@ -65,21 +71,59 @@ export function MetricCombobox({
[getMetricLabels, onGetMetrics]
);
const loadMetricsExplorerMetrics = useCallback(async () => {
const allMetrics = await onGetMetrics();
const metrics: string[] = [];
for (const metric of allMetrics) {
if (metric.value) {
metrics.push(metric.value);
}
}
return metrics;
}, [onGetMetrics]);
const metricsExplorerEnabled = config.featureToggles.prometheusMetricEncyclopedia;
const asyncSelect = () => {
return (
<Combobox
placeholder="Select metric"
width="auto"
minWidth={25}
options={loadOptions}
value={query.metric}
onChange={onComboboxChange}
/>
<InputGroup>
<Combobox
placeholder="Select metric"
width="auto"
minWidth={25}
options={loadOptions}
value={query.metric}
onChange={onComboboxChange}
/>
{metricsExplorerEnabled ? (
<Button
tooltip="Open metrics explorer"
aria-label="Open metrics explorer"
variant="secondary"
icon="book-open"
onClick={() => setMetricsModalOpen(true)}
/>
) : (
<></>
)}
</InputGroup>
);
};
return (
<>
{metricsExplorerEnabled && !datasource.lookupsDisabled && metricsModalOpen && (
<MetricsModal
datasource={datasource}
isOpen={metricsModalOpen}
onClose={() => setMetricsModalOpen(false)}
query={query}
onChange={onChange}
initialMetrics={loadMetricsExplorerMetrics}
/>
)}
{variableEditor ? (
<InlineFieldRow>
<InlineField
@@ -52,7 +52,7 @@ export type MetricsModalProps = {
query: PromVisualQuery;
onClose: () => void;
onChange: (query: PromVisualQuery) => void;
initialMetrics: string[];
initialMetrics: string[] | (() => Promise<string[]>);
};
export const MetricsModal = (props: MetricsModalProps) => {
@@ -70,7 +70,11 @@ export const MetricsModal = (props: MetricsModalProps) => {
// *** Loading Gif
dispatch(setIsLoading(true));
const data: MetricsModalMetadata = await setMetrics(datasource, query, initialMetrics);
// Because Combobox in MetricsCombobox doesn't use the same lifecycle as Select to open the Metrics Explorer
// it might not have loaded any metrics yet, so it instead passes in an async function to get the metrics
const metrics = typeof initialMetrics === 'function' ? await initialMetrics() : initialMetrics;
const data: MetricsModalMetadata = await setMetrics(datasource, query, metrics);
dispatch(
buildMetrics({
isLoading: false,