From 4bed59efb32bf75589bf3a3492cc6078fd8aef2c Mon Sep 17 00:00:00 2001 From: Matias Chomicki Date: Fri, 9 Sep 2022 14:59:07 +0200 Subject: [PATCH] Loki Query Variables: Add support to select from existing labels (#54625) * feat(loki-variable-editor): replace input with select with datasource labels * feat(loki-variable-editor): update test * feat(loki-variable-editor): allow the editor to receive an existing query instance and edit it * feat(loki-variable-editor): allow custom values in the label select * feat(loki-variable-editor): mark stream field as optional * feat(loki-variable-editor): add placeholder to stream selector and extend tooltip info --- .../components/VariableQueryEditor.test.tsx | 122 +++++++++++++----- .../loki/components/VariableQueryEditor.tsx | 51 ++++++-- 2 files changed, 135 insertions(+), 38 deletions(-) diff --git a/public/app/plugins/datasource/loki/components/VariableQueryEditor.test.tsx b/public/app/plugins/datasource/loki/components/VariableQueryEditor.test.tsx index c55b0185999..0f66035ed62 100644 --- a/public/app/plugins/datasource/loki/components/VariableQueryEditor.test.tsx +++ b/public/app/plugins/datasource/loki/components/VariableQueryEditor.test.tsx @@ -10,43 +10,57 @@ import { LokiVariableQueryType } from '../types'; import { LokiVariableQueryEditor, Props } from './VariableQueryEditor'; -const props: Props = { - datasource: createLokiDatasource({} as unknown as TemplateSrv), - query: { - refId: 'test', - type: LokiVariableQueryType.LabelNames, - }, - onRunQuery: () => {}, - onChange: () => {}, -}; +const refId = 'LokiVariableQueryEditor-VariableQuery'; describe('LokiVariableQueryEditor', () => { - test('Allows to create a Label names variable', async () => { - const onChange = jest.fn(); + let props: Props; - render(); + beforeEach(() => { + props = { + datasource: createLokiDatasource({} as unknown as TemplateSrv), + query: { + refId: 'test', + type: LokiVariableQueryType.LabelNames, + }, + onRunQuery: () => {}, + onChange: () => {}, + }; - expect(onChange).not.toHaveBeenCalled(); - - await selectOptionInTest(screen.getByLabelText('Query type'), 'Label names'); - - expect(onChange).toHaveBeenCalledWith({ - type: LokiVariableQueryType.LabelNames, - label: '', - stream: '', - refId: 'LokiVariableQueryEditor-VariableQuery', - }); + jest.spyOn(props.datasource, 'labelNamesQuery').mockResolvedValue([]); }); - test('Allows to create a Label values variable', async () => { + test('Allows to create a Label names variable', async () => { const onChange = jest.fn(); - render(); expect(onChange).not.toHaveBeenCalled(); await selectOptionInTest(screen.getByLabelText('Query type'), 'Label values'); - await userEvent.type(screen.getByLabelText('Label'), 'label'); + + expect(onChange).toHaveBeenCalledWith({ + type: LokiVariableQueryType.LabelValues, + label: '', + stream: '', + refId, + }); + }); + + test('Allows to create a Label values variable', async () => { + const onChange = jest.fn(); + jest.spyOn(props.datasource, 'labelNamesQuery').mockResolvedValue([ + { + text: 'moon', + }, + { + text: 'luna', + }, + ]); + render(); + + expect(onChange).not.toHaveBeenCalled(); + + await selectOptionInTest(screen.getByLabelText('Query type'), 'Label values'); + await selectOptionInTest(screen.getByLabelText('Label'), 'luna'); await userEvent.type(screen.getByLabelText('Stream selector'), 'stream'); await waitFor(() => expect(screen.getByDisplayValue('stream')).toBeInTheDocument()); @@ -55,20 +69,68 @@ describe('LokiVariableQueryEditor', () => { expect(onChange).toHaveBeenCalledWith({ type: LokiVariableQueryType.LabelValues, - label: 'label', + label: 'luna', stream: 'stream', - refId: 'LokiVariableQueryEditor-VariableQuery', + refId, + }); + }); + + test('Allows to create a Label values variable with custom label', async () => { + const onChange = jest.fn(); + jest.spyOn(props.datasource, 'labelNamesQuery').mockResolvedValue([ + { + text: 'moon', + }, + { + text: 'luna', + }, + ]); + render(); + + expect(onChange).not.toHaveBeenCalled(); + + await selectOptionInTest(screen.getByLabelText('Query type'), 'Label values'); + await userEvent.type(screen.getByLabelText('Label'), 'sol{enter}'); + await userEvent.type(screen.getByLabelText('Stream selector'), 'stream'); + + await waitFor(() => expect(screen.getByDisplayValue('stream')).toBeInTheDocument()); + + await userEvent.click(document.body); + + expect(onChange).toHaveBeenCalledWith({ + type: LokiVariableQueryType.LabelValues, + label: 'sol', + stream: 'stream', + refId, }); }); test('Migrates legacy string queries to LokiVariableQuery instances', async () => { - const query = 'label_values(log stream selector, label)'; - + const query = 'label_values(log stream selector, label_selector)'; // @ts-expect-error render( {}} query={query} />); await waitFor(() => expect(screen.getByText('Label values')).toBeInTheDocument()); - await waitFor(() => expect(screen.getByDisplayValue('label')).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText('label_selector')).toBeInTheDocument()); + await waitFor(() => expect(screen.getByDisplayValue('log stream selector')).toBeInTheDocument()); + }); + + test('Receives a query instance and assigns its values when editing', async () => { + render( + {}} + query={{ + type: LokiVariableQueryType.LabelValues, + label: 'label_selector', + stream: 'log stream selector', + refId, + }} + /> + ); + + await waitFor(() => expect(screen.getByText('Label values')).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText('label_selector')).toBeInTheDocument()); await waitFor(() => expect(screen.getByDisplayValue('log stream selector')).toBeInTheDocument()); }); }); diff --git a/public/app/plugins/datasource/loki/components/VariableQueryEditor.tsx b/public/app/plugins/datasource/loki/components/VariableQueryEditor.tsx index 770d6380cac..22898babca4 100644 --- a/public/app/plugins/datasource/loki/components/VariableQueryEditor.tsx +++ b/public/app/plugins/datasource/loki/components/VariableQueryEditor.tsx @@ -14,22 +14,39 @@ const variableOptions = [ export type Props = QueryEditorProps; -export const LokiVariableQueryEditor: FC = ({ onChange, query }) => { +const refId = 'LokiVariableQueryEditor-VariableQuery'; + +export const LokiVariableQueryEditor: FC = ({ onChange, query, datasource }) => { const [type, setType] = useState(undefined); const [label, setLabel] = useState(''); + const [labelOptions, setLabelOptions] = useState>>([]); const [stream, setStream] = useState(''); useEffect(() => { - if (!query || typeof query !== 'string') { + if (!query) { return; } - const variableQuery = migrateVariableQuery(query); + const variableQuery = typeof query === 'string' ? migrateVariableQuery(query) : query; setType(variableQuery.type); setLabel(variableQuery.label || ''); setStream(variableQuery.stream || ''); + + if (variableQuery.label) { + setLabelOptions([{ label: variableQuery.label, value: variableQuery.label }]); + } }, [query]); + useEffect(() => { + if (type !== QueryType.LabelValues) { + return; + } + + datasource.labelNamesQuery().then((labelNames: Array<{ text: string }>) => { + setLabelOptions(labelNames.map(({ text }) => ({ label: text, value: text }))); + }); + }, [datasource, type]); + const onQueryTypeChange = (newType: SelectableValue) => { setType(newType.value); if (newType.value !== undefined) { @@ -37,13 +54,13 @@ export const LokiVariableQueryEditor: FC = ({ onChange, query }) => { type: newType.value, label, stream, - refId: 'LokiVariableQueryEditor-VariableQuery', + refId, }); } }; - const onLabelChange = (e: FormEvent) => { - setLabel(e.currentTarget.value); + const onLabelChange = (newLabel: SelectableValue) => { + setLabel(newLabel.value || ''); }; const onStreamChange = (e: FormEvent) => { @@ -71,15 +88,33 @@ export const LokiVariableQueryEditor: FC = ({ onChange, query }) => { {type === QueryType.LabelValues && ( <> - +