mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Show error when operations, but no stream selector (#47890)
This commit is contained in:
parent
992c0604f9
commit
677327ea07
@ -3,7 +3,7 @@ import { render, screen, getAllByRole, waitFor } from '@testing-library/react';
|
|||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { LokiQueryBuilder } from './LokiQueryBuilder';
|
import { LokiQueryBuilder } from './LokiQueryBuilder';
|
||||||
import { LokiDatasource } from '../../datasource';
|
import { LokiDatasource } from '../../datasource';
|
||||||
import { LokiVisualQuery } from '../types';
|
import { LokiOperationId, LokiVisualQuery } from '../types';
|
||||||
import { PanelData } from '@grafana/data';
|
import { PanelData } from '@grafana/data';
|
||||||
|
|
||||||
const defaultQuery: LokiVisualQuery = {
|
const defaultQuery: LokiVisualQuery = {
|
||||||
@ -17,10 +17,20 @@ describe('LokiQueryBuilder', () => {
|
|||||||
datasource.languageProvider.fetchSeriesLabels = jest.fn().mockReturnValue({ job: ['a'], instance: ['b'] });
|
datasource.languageProvider.fetchSeriesLabels = jest.fn().mockReturnValue({ job: ['a'], instance: ['b'] });
|
||||||
userEvent.click(screen.getByLabelText('Add'));
|
userEvent.click(screen.getByLabelText('Add'));
|
||||||
const labels = screen.getByText(/Labels/);
|
const labels = screen.getByText(/Labels/);
|
||||||
const selects = getAllByRole(labels.parentElement!, 'combobox');
|
const selects = getAllByRole(labels.parentElement!.parentElement!.parentElement!, 'combobox');
|
||||||
userEvent.click(selects[3]);
|
userEvent.click(selects[3]);
|
||||||
await waitFor(() => expect(screen.getByText('job')).toBeInTheDocument());
|
await waitFor(() => expect(screen.getByText('job')).toBeInTheDocument());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('shows error for query with operations and no stream selector', async () => {
|
||||||
|
setup({ labels: [], operations: [{ id: LokiOperationId.Logfmt, params: [] }] });
|
||||||
|
expect(screen.getByText('You need to specify at least 1 label filter (stream selector)')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows no error for query with empty __line_contains operation and no stream selector', async () => {
|
||||||
|
setup({ labels: [], operations: [{ id: LokiOperationId.LineContains, params: [''] }] });
|
||||||
|
expect(screen.queryByText('You need to specify at least 1 label filter (stream selector)')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function setup(query: LokiVisualQuery = defaultQuery, data?: PanelData) {
|
function setup(query: LokiVisualQuery = defaultQuery, data?: PanelData) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { LokiVisualQuery } from '../types';
|
import { LokiOperationId, LokiVisualQuery } from '../types';
|
||||||
import { LokiDatasource } from '../../datasource';
|
import { LokiDatasource } from '../../datasource';
|
||||||
import { LabelFilters } from 'app/plugins/datasource/prometheus/querybuilder/shared/LabelFilters';
|
import { LabelFilters } from 'app/plugins/datasource/prometheus/querybuilder/shared/LabelFilters';
|
||||||
import { OperationList } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationList';
|
import { OperationList } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationList';
|
||||||
@ -57,6 +57,18 @@ export const LokiQueryBuilder = React.memo<Props>(({ datasource, query, nested,
|
|||||||
return result[forLabelInterpolated] ?? [];
|
return result[forLabelInterpolated] ?? [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const labelFilterError: string | undefined = useMemo(() => {
|
||||||
|
const { labels, operations: op } = query;
|
||||||
|
if (!labels.length && op.length) {
|
||||||
|
// We don't want to show error for initial state with empty line contains operation
|
||||||
|
if (op.length === 1 && op[0].id === LokiOperationId.LineContains && op[0].params[0] === '') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return 'You need to specify at least 1 label filter (stream selector)';
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}, [query]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<EditorRow>
|
<EditorRow>
|
||||||
@ -69,6 +81,7 @@ export const LokiQueryBuilder = React.memo<Props>(({ datasource, query, nested,
|
|||||||
}
|
}
|
||||||
labelsFilters={query.labels}
|
labelsFilters={query.labels}
|
||||||
onChange={onChangeLabels}
|
onChange={onChangeLabels}
|
||||||
|
error={labelFilterError}
|
||||||
/>
|
/>
|
||||||
</EditorRow>
|
</EditorRow>
|
||||||
<OperationsEditorRow>
|
<OperationsEditorRow>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { SelectableValue } from '@grafana/data';
|
import { SelectableValue } from '@grafana/data';
|
||||||
import { EditorField, EditorFieldGroup, EditorList } from '@grafana/experimental';
|
import { EditorFieldGroup, EditorList } from '@grafana/experimental';
|
||||||
|
import { Field } from '@grafana/ui';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { QueryBuilderLabelFilter } from '../shared/types';
|
import { QueryBuilderLabelFilter } from '../shared/types';
|
||||||
@ -10,9 +11,10 @@ export interface Props {
|
|||||||
onChange: (labelFilters: QueryBuilderLabelFilter[]) => void;
|
onChange: (labelFilters: QueryBuilderLabelFilter[]) => void;
|
||||||
onGetLabelNames: (forLabel: Partial<QueryBuilderLabelFilter>) => Promise<SelectableValue[]>;
|
onGetLabelNames: (forLabel: Partial<QueryBuilderLabelFilter>) => Promise<SelectableValue[]>;
|
||||||
onGetLabelValues: (forLabel: Partial<QueryBuilderLabelFilter>) => Promise<SelectableValue[]>;
|
onGetLabelValues: (forLabel: Partial<QueryBuilderLabelFilter>) => Promise<SelectableValue[]>;
|
||||||
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LabelFilters({ labelsFilters, onChange, onGetLabelNames, onGetLabelValues }: Props) {
|
export function LabelFilters({ labelsFilters, onChange, onGetLabelNames, onGetLabelValues, error }: Props) {
|
||||||
const defaultOp = '=';
|
const defaultOp = '=';
|
||||||
const [items, setItems] = useState<Array<Partial<QueryBuilderLabelFilter>>>([{ op: defaultOp }]);
|
const [items, setItems] = useState<Array<Partial<QueryBuilderLabelFilter>>>([{ op: defaultOp }]);
|
||||||
|
|
||||||
@ -36,7 +38,7 @@ export function LabelFilters({ labelsFilters, onChange, onGetLabelNames, onGetLa
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<EditorFieldGroup>
|
<EditorFieldGroup>
|
||||||
<EditorField label="Labels">
|
<Field label="Labels" error={error} invalid={!!error}>
|
||||||
<EditorList
|
<EditorList
|
||||||
items={items}
|
items={items}
|
||||||
onChange={onLabelsChange}
|
onChange={onLabelsChange}
|
||||||
@ -51,7 +53,7 @@ export function LabelFilters({ labelsFilters, onChange, onGetLabelNames, onGetLa
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</EditorField>
|
</Field>
|
||||||
</EditorFieldGroup>
|
</EditorFieldGroup>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { screen, getAllByRole } from '@testing-library/react';
|
|||||||
|
|
||||||
export function getLabelSelects(index = 0) {
|
export function getLabelSelects(index = 0) {
|
||||||
const labels = screen.getByText(/Labels/);
|
const labels = screen.getByText(/Labels/);
|
||||||
const selects = getAllByRole(labels.parentElement!, 'combobox');
|
const selects = getAllByRole(labels.parentElement!.parentElement!.parentElement!, 'combobox');
|
||||||
return {
|
return {
|
||||||
name: selects[3 * index],
|
name: selects[3 * index],
|
||||||
value: selects[3 * index + 2],
|
value: selects[3 * index + 2],
|
||||||
|
Loading…
Reference in New Issue
Block a user