use new methods in metrics browser and query field
This commit is contained in:
@@ -30,7 +30,7 @@ function setup(app: CoreApp): { onRunQuery: jest.Mock } {
|
||||
start: () => Promise.resolve([]),
|
||||
syntax: () => {},
|
||||
getLabelKeys: () => [],
|
||||
metrics: [],
|
||||
retrieveMetrics: () => [],
|
||||
},
|
||||
} as unknown as PrometheusDatasource;
|
||||
const onRunQuery = jest.fn();
|
||||
|
||||
@@ -29,7 +29,7 @@ const defaultProps = {
|
||||
start: () => Promise.resolve([]),
|
||||
syntax: () => {},
|
||||
getLabelKeys: () => [],
|
||||
metrics: [],
|
||||
retrieveMetrics: () => [],
|
||||
},
|
||||
} as unknown as PrometheusDatasource,
|
||||
query: {
|
||||
|
||||
@@ -44,7 +44,7 @@ export const PromQueryField = (props: PromQueryFieldProps) => {
|
||||
const [labelBrowserVisible, setLabelBrowserVisible] = useState(false);
|
||||
|
||||
const updateLanguage = useCallback(() => {
|
||||
if (languageProvider.metrics) {
|
||||
if (languageProvider.retrieveMetrics()) {
|
||||
setSyntaxLoaded(true);
|
||||
}
|
||||
}, [languageProvider]);
|
||||
|
||||
+36
-26
@@ -32,20 +32,14 @@ Object.defineProperty(window, 'localStorage', { value: localStorageMock });
|
||||
const setupLanguageProviderMock = () => {
|
||||
const mockTimeRange = getMockTimeRange();
|
||||
const mockLanguageProvider = {
|
||||
metrics: ['metric1', 'metric2', 'metric3'],
|
||||
labelKeys: ['__name__', 'instance', 'job', 'service'],
|
||||
metricsMetadata: {
|
||||
retrieveMetrics: () => ['metric1', 'metric2', 'metric3'],
|
||||
retrieveLabelKeys: () => ['__name__', 'instance', 'job', 'service'],
|
||||
retrieveMetricsMetadata: () => ({
|
||||
metric1: { type: 'counter', help: 'Test metric 1' },
|
||||
metric2: { type: 'gauge', help: 'Test metric 2' },
|
||||
},
|
||||
fetchLabels: jest.fn().mockResolvedValue(['__name__', 'instance', 'job', 'service']),
|
||||
fetchSeriesLabelsMatch: jest.fn().mockResolvedValue({
|
||||
__name__: ['metric1', 'metric2'],
|
||||
instance: ['instance1', 'instance2'],
|
||||
job: ['job1', 'job2'],
|
||||
service: ['service1', 'service2'],
|
||||
}),
|
||||
fetchSeriesValuesWithMatch: jest.fn().mockImplementation((_timeRange: TimeRange, label: string) => {
|
||||
queryLabelKeys: jest.fn().mockResolvedValue(['__name__', 'instance', 'job', 'service']),
|
||||
queryLabelValues: jest.fn().mockImplementation((_timeRange: TimeRange, label: string) => {
|
||||
if (label === 'job') {
|
||||
return Promise.resolve(['grafana', 'prometheus']);
|
||||
}
|
||||
@@ -57,10 +51,6 @@ const setupLanguageProviderMock = () => {
|
||||
}
|
||||
return Promise.resolve([]);
|
||||
}),
|
||||
fetchLabelsWithMatch: jest.fn().mockResolvedValue({
|
||||
job: ['job1', 'job2'],
|
||||
instance: ['instance1', 'instance2'],
|
||||
}),
|
||||
} as unknown as PrometheusLanguageProviderInterface;
|
||||
|
||||
return { mockTimeRange, mockLanguageProvider };
|
||||
@@ -298,34 +288,54 @@ describe('MetricsBrowserContext', () => {
|
||||
describe('selector operations', () => {
|
||||
it('should clear all selections', async () => {
|
||||
const user = userEvent.setup();
|
||||
const { renderWithProvider } = setupTest();
|
||||
const { renderWithProvider, mockLanguageProvider } = setupTest();
|
||||
renderWithProvider(<TestComponent />);
|
||||
|
||||
// Wait for component to be ready
|
||||
// Wait for initial data load
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('metrics-count').textContent).toBe('3');
|
||||
});
|
||||
|
||||
// Make selections
|
||||
// Step 1: Select a metric
|
||||
await user.click(screen.getByTestId('select-metric'));
|
||||
await user.click(screen.getByTestId('select-label'));
|
||||
await user.click(screen.getByTestId('select-label-value'));
|
||||
|
||||
// Verify selections
|
||||
await waitFor(() => {
|
||||
expect(mockLanguageProvider.queryLabelKeys).toHaveBeenCalled();
|
||||
expect(screen.getByTestId('selected-metric').textContent).toBe('metric1');
|
||||
expect(screen.getByTestId('selected-label-keys').textContent).toBe('job');
|
||||
expect(screen.getByTestId('selector').textContent).not.toBe('{}');
|
||||
});
|
||||
|
||||
// Clear all selections
|
||||
// Step 2: Select a label
|
||||
await user.click(screen.getByTestId('select-label'));
|
||||
await waitFor(() => {
|
||||
expect(mockLanguageProvider.queryLabelValues).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
'job',
|
||||
expect.anything(),
|
||||
expect.anything()
|
||||
);
|
||||
expect(screen.getByTestId('selected-label-keys').textContent).toBe('job');
|
||||
});
|
||||
|
||||
// Step 3: Select a label value
|
||||
await user.click(screen.getByTestId('select-label-value'));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('selector').textContent).toContain('job="grafana"');
|
||||
});
|
||||
|
||||
// Step 4: Clear all selections
|
||||
await user.click(screen.getByTestId('clear'));
|
||||
|
||||
// Verify all fields are cleared
|
||||
// Verify everything is cleared
|
||||
await waitFor(() => {
|
||||
// Check that all selections are cleared
|
||||
expect(screen.getByTestId('selected-metric').textContent).toBe('');
|
||||
expect(screen.getByTestId('selected-label-keys').textContent).toBe('');
|
||||
expect(screen.getByTestId('selector').textContent).toBe('{}');
|
||||
|
||||
// Verify localStorage was cleared
|
||||
const mockCalls = localStorageMock.setItem.mock.calls;
|
||||
const lastCall = mockCalls[mockCalls.length - 1];
|
||||
expect(lastCall[0]).toBe(LAST_USED_LABELS_KEY);
|
||||
expect(JSON.parse(lastCall[1])).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+268
-1042
File diff suppressed because it is too large
Load Diff
+13
-14
@@ -41,7 +41,7 @@ export const useMetricsLabelsValues = (timeRange: TimeRange, languageProvider: P
|
||||
}
|
||||
}, [timeRange]);
|
||||
|
||||
//Handler for error processing - logs the error and updates UI state
|
||||
// Handler for error processing - logs the error and updates UI state
|
||||
const handleError = useCallback((e: unknown, msg: string) => {
|
||||
if (e instanceof Error) {
|
||||
setErr(`${msg}: ${e.message}`);
|
||||
@@ -54,10 +54,10 @@ export const useMetricsLabelsValues = (timeRange: TimeRange, languageProvider: P
|
||||
// Get metadata details for a metric if available
|
||||
const getMetricDetails = useCallback(
|
||||
(metricName: string) => {
|
||||
const meta = languageProvider.metricsMetadata;
|
||||
const meta = languageProvider.retrieveMetricsMetadata();
|
||||
return meta && meta[metricName] ? `(${meta[metricName].type}) ${meta[metricName].help}` : undefined;
|
||||
},
|
||||
[languageProvider.metricsMetadata]
|
||||
[languageProvider]
|
||||
);
|
||||
|
||||
// Builds a safe selector string from metric name and label values
|
||||
@@ -89,11 +89,10 @@ export const useMetricsLabelsValues = (timeRange: TimeRange, languageProvider: P
|
||||
const fetchMetrics = useCallback(
|
||||
async (safeSelector?: string) => {
|
||||
try {
|
||||
const fetchedMetrics = await languageProvider.fetchSeriesValuesWithMatch(
|
||||
const fetchedMetrics = await languageProvider.queryLabelValues(
|
||||
timeRangeRef.current,
|
||||
METRIC_LABEL,
|
||||
safeSelector,
|
||||
'MetricsBrowser_M',
|
||||
effectiveLimit
|
||||
);
|
||||
return fetchedMetrics.map((m) => ({
|
||||
@@ -114,11 +113,9 @@ export const useMetricsLabelsValues = (timeRange: TimeRange, languageProvider: P
|
||||
async (safeSelector?: string) => {
|
||||
try {
|
||||
if (safeSelector) {
|
||||
return Object.keys(
|
||||
await languageProvider.fetchSeriesLabelsMatch(timeRangeRef.current, safeSelector, effectiveLimit)
|
||||
);
|
||||
return Object.keys(await languageProvider.queryLabelKeys(timeRangeRef.current, safeSelector, effectiveLimit));
|
||||
} else {
|
||||
return (await languageProvider.fetchLabels(timeRangeRef.current, undefined, effectiveLimit)) || [];
|
||||
return (await languageProvider.queryLabelKeys(timeRangeRef.current, undefined, effectiveLimit)) || [];
|
||||
}
|
||||
} catch (e) {
|
||||
handleError(e, 'Error fetching labels');
|
||||
@@ -135,17 +132,18 @@ export const useMetricsLabelsValues = (timeRange: TimeRange, languageProvider: P
|
||||
const newSelectedLabelValues: Record<string, string[]> = {};
|
||||
for (const lk of labelKeys) {
|
||||
try {
|
||||
const values = await languageProvider.fetchSeriesValuesWithMatch(
|
||||
const values = await languageProvider.queryLabelValues(
|
||||
timeRangeRef.current,
|
||||
lk,
|
||||
safeSelector,
|
||||
`MetricsBrowser_LV_${lk}`,
|
||||
effectiveLimit
|
||||
);
|
||||
transformedLabelValues[lk] = values;
|
||||
if (selectedLabelValues[lk]) {
|
||||
newSelectedLabelValues[lk] = [...selectedLabelValues[lk]];
|
||||
}
|
||||
|
||||
setErr('');
|
||||
} catch (e) {
|
||||
handleError(e, 'Error fetching label values');
|
||||
}
|
||||
@@ -294,11 +292,10 @@ export const useMetricsLabelsValues = (timeRange: TimeRange, languageProvider: P
|
||||
if (selectedLabelKeys.length !== 0) {
|
||||
for (const lk of selectedLabelKeys) {
|
||||
try {
|
||||
const fetchedLabelValues = await languageProvider.fetchSeriesValuesWithMatch(
|
||||
const fetchedLabelValues = await languageProvider.queryLabelValues(
|
||||
timeRange,
|
||||
lk,
|
||||
safeSelector,
|
||||
`MetricsBrowser_LV_${lk}`,
|
||||
effectiveLimit
|
||||
);
|
||||
|
||||
@@ -314,6 +311,8 @@ export const useMetricsLabelsValues = (timeRange: TimeRange, languageProvider: P
|
||||
fetchedLabelValues.includes(item)
|
||||
);
|
||||
}
|
||||
|
||||
setErr('');
|
||||
} catch (e: unknown) {
|
||||
handleError(e, 'Error fetching label values');
|
||||
}
|
||||
@@ -352,7 +351,7 @@ export const useMetricsLabelsValues = (timeRange: TimeRange, languageProvider: P
|
||||
setErr('');
|
||||
|
||||
try {
|
||||
const results = await languageProvider.fetchSeriesLabelsMatch(timeRangeRef.current, selector, effectiveLimit);
|
||||
const results = await languageProvider.queryLabelKeys(timeRangeRef.current, selector, effectiveLimit);
|
||||
setValidationStatus(`Selector is valid (${Object.keys(results).length} labels found)`);
|
||||
} catch (e) {
|
||||
handleError(e, 'Validation failed');
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useMetricsState } from './useMetricsState';
|
||||
// Mock implementations
|
||||
const createMockLanguageProvider = (metrics: string[] = []): PrometheusLanguageProviderInterface =>
|
||||
({
|
||||
metrics,
|
||||
retrieveMetrics: () => metrics,
|
||||
}) as unknown as PrometheusLanguageProviderInterface;
|
||||
|
||||
const createMockDatasource = (lookupsDisabled = false): PrometheusDatasource =>
|
||||
|
||||
@@ -25,7 +25,7 @@ export function useMetricsState(
|
||||
syntaxLoaded: boolean
|
||||
) {
|
||||
return useMemo(() => {
|
||||
const hasMetrics = languageProvider.metrics.length > 0;
|
||||
const hasMetrics = languageProvider.retrieveMetrics().length > 0;
|
||||
const chooserText = getChooserText(datasource.lookupsDisabled, syntaxLoaded, hasMetrics);
|
||||
const buttonDisabled = !(syntaxLoaded && hasMetrics);
|
||||
|
||||
@@ -34,5 +34,5 @@ export function useMetricsState(
|
||||
chooserText,
|
||||
buttonDisabled,
|
||||
};
|
||||
}, [languageProvider.metrics, datasource.lookupsDisabled, syntaxLoaded]);
|
||||
}, [languageProvider, datasource.lookupsDisabled, syntaxLoaded]);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/language_provider.mock.ts
|
||||
export class EmptyLanguageProviderMock {
|
||||
metrics = [];
|
||||
|
||||
constructor() {}
|
||||
|
||||
start() {
|
||||
return new Promise((resolve) => {
|
||||
resolve('');
|
||||
});
|
||||
}
|
||||
|
||||
getLabelKeys = jest.fn().mockReturnValue([]);
|
||||
getLabelValues = jest.fn().mockReturnValue([]);
|
||||
getSeries = jest.fn().mockReturnValue({ __name__: [] });
|
||||
@@ -17,4 +20,5 @@ export class EmptyLanguageProviderMock {
|
||||
fetchLabelsWithMatch = jest.fn().mockReturnValue([]);
|
||||
fetchLabels = jest.fn();
|
||||
loadMetricsMetadata = jest.fn();
|
||||
retrieveMetrics = jest.fn().mockReturnValue(['metric']);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user