mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Loki: Remove unused query editors (#57192)
* Loki: Remove not used query editors * Move Loki editor to components and rename * Update public/app/plugins/datasource/loki/components/LokiQueryEditorByApp.test.tsx Co-authored-by: Matias Chomicki <matyax@gmail.com> * Fix test Co-authored-by: Matias Chomicki <matyax@gmail.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
QueryBuilderOperation,
|
||||
} from 'app/plugins/datasource/prometheus/querybuilder/shared/types';
|
||||
|
||||
import { testIds } from '../../components/LokiQueryEditor';
|
||||
import { LokiDatasource } from '../../datasource';
|
||||
import { escapeLabelValueInSelector } from '../../languageUtils';
|
||||
import logqlGrammar from '../../syntax';
|
||||
@@ -107,7 +108,7 @@ export const LokiQueryBuilder = React.memo<Props>(({ datasource, query, onChange
|
||||
|
||||
const lang = { grammar: logqlGrammar, name: 'logql' };
|
||||
return (
|
||||
<>
|
||||
<div data-testid={testIds.editor}>
|
||||
<EditorRow>
|
||||
<LabelFilters
|
||||
onGetLabelNames={(forLabel: Partial<QueryBuilderLabelFilter>) =>
|
||||
@@ -170,7 +171,7 @@ export const LokiQueryBuilder = React.memo<Props>(({ datasource, query, onChange
|
||||
showExplain={showExplain}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import React, { useEffect, useReducer } from 'react';
|
||||
|
||||
import { testIds } from '../../components/LokiQueryEditor';
|
||||
import { LokiDatasource } from '../../datasource';
|
||||
import { LokiQuery } from '../../types';
|
||||
import { lokiQueryModeller } from '../LokiQueryModeller';
|
||||
@@ -64,6 +65,7 @@ export function LokiQueryBuilderContainer(props: Props) {
|
||||
onChange={onVisQueryChange}
|
||||
onRunQuery={onRunQuery}
|
||||
showExplain={showExplain}
|
||||
data-testid={testIds.editor}
|
||||
/>
|
||||
{showRawQuery && <QueryPreview query={query.expr} />}
|
||||
</>
|
||||
|
||||
@@ -37,8 +37,8 @@ export function LokiQueryCodeEditor({ query, datasource, range, onRunQuery, onCh
|
||||
onBlur={onBlur}
|
||||
history={[]}
|
||||
data={data}
|
||||
data-testid={testIds.editor}
|
||||
app={app}
|
||||
data-testid={testIds.editor}
|
||||
/>
|
||||
{showExplain && <LokiQueryBuilderExplained query={query.expr} />}
|
||||
</div>
|
||||
|
||||
@@ -1,194 +0,0 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { cloneDeep, defaultsDeep } from 'lodash';
|
||||
import React from 'react';
|
||||
|
||||
import { DataSourcePluginMeta } from '@grafana/data';
|
||||
import { QueryEditorMode } from 'app/plugins/datasource/prometheus/querybuilder/shared/types';
|
||||
|
||||
import { LokiDatasource } from '../../datasource';
|
||||
import { LokiQuery, LokiQueryType } from '../../types';
|
||||
|
||||
import { EXPLAIN_LABEL_FILTER_CONTENT } from './LokiQueryBuilderExplained';
|
||||
import { LokiQueryEditorSelector } from './LokiQueryEditorSelector';
|
||||
|
||||
jest.mock('@grafana/runtime', () => {
|
||||
return {
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
reportInteraction: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('app/core/store', () => {
|
||||
return {
|
||||
get() {
|
||||
return undefined;
|
||||
},
|
||||
set() {},
|
||||
getObject(key: string, defaultValue: unknown) {
|
||||
return defaultValue;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const defaultQuery = {
|
||||
refId: 'A',
|
||||
expr: '{label1="foo", label2="bar"}',
|
||||
};
|
||||
|
||||
const datasource = new LokiDatasource(
|
||||
{
|
||||
id: 1,
|
||||
uid: '',
|
||||
type: 'loki',
|
||||
name: 'loki-test',
|
||||
access: 'proxy',
|
||||
url: '',
|
||||
jsonData: {},
|
||||
meta: {} as DataSourcePluginMeta,
|
||||
readOnly: false,
|
||||
},
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
|
||||
datasource.languageProvider.fetchLabels = jest.fn().mockResolvedValue([]);
|
||||
datasource.getDataSamples = jest.fn().mockResolvedValue([]);
|
||||
|
||||
const defaultProps = {
|
||||
datasource,
|
||||
query: defaultQuery,
|
||||
onRunQuery: () => {},
|
||||
onChange: () => {},
|
||||
};
|
||||
|
||||
describe('LokiQueryEditorSelector', () => {
|
||||
it('shows code editor if expr and nothing else', async () => {
|
||||
// We opt for showing code editor for queries created before this feature was added
|
||||
render(<LokiQueryEditorSelector {...defaultProps} />);
|
||||
expectCodeEditor();
|
||||
});
|
||||
|
||||
it('shows builder if new query', async () => {
|
||||
render(
|
||||
<LokiQueryEditorSelector
|
||||
{...defaultProps}
|
||||
query={{
|
||||
refId: 'A',
|
||||
expr: '',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
await expectBuilder();
|
||||
});
|
||||
|
||||
it('shows code editor when code mode is set', async () => {
|
||||
renderWithMode(QueryEditorMode.Code);
|
||||
expectCodeEditor();
|
||||
});
|
||||
|
||||
it('shows builder when builder mode is set', async () => {
|
||||
renderWithMode(QueryEditorMode.Builder);
|
||||
await expectBuilder();
|
||||
});
|
||||
|
||||
it('changes to builder mode', async () => {
|
||||
const { onChange } = renderWithMode(QueryEditorMode.Code);
|
||||
await switchToMode(QueryEditorMode.Builder);
|
||||
expect(onChange).toBeCalledWith({
|
||||
refId: 'A',
|
||||
expr: defaultQuery.expr,
|
||||
queryType: LokiQueryType.Range,
|
||||
editorMode: QueryEditorMode.Builder,
|
||||
});
|
||||
});
|
||||
|
||||
it('Can enable raw query', async () => {
|
||||
renderWithMode(QueryEditorMode.Builder);
|
||||
expect(await screen.findByLabelText('selector')).toBeInTheDocument();
|
||||
screen.getByLabelText('Raw query').click();
|
||||
expect(screen.queryByLabelText('selector')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should show raw query by default', async () => {
|
||||
renderWithProps({
|
||||
editorMode: QueryEditorMode.Builder,
|
||||
expr: '{job="grafana"}',
|
||||
});
|
||||
const selector = await screen.findByLabelText('selector');
|
||||
expect(selector).toBeInTheDocument();
|
||||
expect(selector.textContent).toBe('{job="grafana"}');
|
||||
});
|
||||
|
||||
it('Can enable explain', async () => {
|
||||
renderWithMode(QueryEditorMode.Builder);
|
||||
expect(screen.queryByText(EXPLAIN_LABEL_FILTER_CONTENT)).not.toBeInTheDocument();
|
||||
screen.getByLabelText('Explain').click();
|
||||
expect(await screen.findByText(EXPLAIN_LABEL_FILTER_CONTENT)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('changes to code mode', async () => {
|
||||
const { onChange } = renderWithMode(QueryEditorMode.Builder);
|
||||
await switchToMode(QueryEditorMode.Code);
|
||||
expect(onChange).toBeCalledWith({
|
||||
refId: 'A',
|
||||
expr: defaultQuery.expr,
|
||||
queryType: LokiQueryType.Range,
|
||||
editorMode: QueryEditorMode.Code,
|
||||
});
|
||||
});
|
||||
|
||||
it('parses query when changing to builder mode', async () => {
|
||||
const { rerender } = renderWithProps({
|
||||
refId: 'A',
|
||||
expr: 'rate({instance="host.docker.internal:3000"}[$__interval])',
|
||||
editorMode: QueryEditorMode.Code,
|
||||
});
|
||||
await switchToMode(QueryEditorMode.Builder);
|
||||
rerender(
|
||||
<LokiQueryEditorSelector
|
||||
{...defaultProps}
|
||||
query={{
|
||||
refId: 'A',
|
||||
expr: 'rate({instance="host.docker.internal:3000"}[$__interval])',
|
||||
editorMode: QueryEditorMode.Builder,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
await screen.findByText('host.docker.internal:3000');
|
||||
expect(screen.getByText('Rate')).toBeInTheDocument();
|
||||
expect(screen.getByText('$__interval')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
function renderWithMode(mode: QueryEditorMode) {
|
||||
return renderWithProps({ editorMode: mode });
|
||||
}
|
||||
|
||||
function renderWithProps(overrides?: Partial<LokiQuery>) {
|
||||
const query = defaultsDeep(overrides ?? {}, cloneDeep(defaultQuery));
|
||||
const onChange = jest.fn();
|
||||
|
||||
const stuff = render(<LokiQueryEditorSelector {...defaultProps} query={query} onChange={onChange} />);
|
||||
return { onChange, ...stuff };
|
||||
}
|
||||
|
||||
function expectCodeEditor() {
|
||||
// Log browser shows this until log labels are loaded.
|
||||
expect(screen.getByText('Loading labels...')).toBeInTheDocument();
|
||||
}
|
||||
|
||||
async function expectBuilder() {
|
||||
expect(await screen.findByText('Label filters')).toBeInTheDocument();
|
||||
}
|
||||
|
||||
async function switchToMode(mode: QueryEditorMode) {
|
||||
const label = {
|
||||
[QueryEditorMode.Code]: /Code/,
|
||||
[QueryEditorMode.Builder]: /Builder/,
|
||||
}[mode];
|
||||
|
||||
const switchEl = screen.getByLabelText(label);
|
||||
await userEvent.click(switchEl);
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
import React, { SyntheticEvent, useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { CoreApp, LoadingState } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { Button, ConfirmModal, EditorHeader, EditorRows, FlexItem, Space } from '@grafana/ui';
|
||||
import { QueryEditorModeToggle } from 'app/plugins/datasource/prometheus/querybuilder/shared/QueryEditorModeToggle';
|
||||
import { QueryHeaderSwitch } from 'app/plugins/datasource/prometheus/querybuilder/shared/QueryHeaderSwitch';
|
||||
import { QueryEditorMode } from 'app/plugins/datasource/prometheus/querybuilder/shared/types';
|
||||
|
||||
import {
|
||||
lokiQueryEditorExplainKey,
|
||||
lokiQueryEditorRawQueryKey,
|
||||
useFlag,
|
||||
} from '../../../prometheus/querybuilder/shared/hooks/useFlag';
|
||||
import { LokiQueryEditorProps } from '../../components/types';
|
||||
import { LokiQuery } from '../../types';
|
||||
import { buildVisualQueryFromString } from '../parsing';
|
||||
import { changeEditorMode, getQueryWithDefaults } from '../state';
|
||||
|
||||
import { LokiQueryBuilderContainer } from './LokiQueryBuilderContainer';
|
||||
import { LokiQueryBuilderOptions } from './LokiQueryBuilderOptions';
|
||||
import { LokiQueryCodeEditor } from './LokiQueryCodeEditor';
|
||||
import { QueryPatternsModal } from './QueryPatternsModal';
|
||||
|
||||
export const LokiQueryEditorSelector = React.memo<LokiQueryEditorProps>((props) => {
|
||||
const { onChange, onRunQuery, onAddQuery, data, app, queries } = props;
|
||||
const [parseModalOpen, setParseModalOpen] = useState(false);
|
||||
const [queryPatternsModalOpen, setQueryPatternsModalOpen] = useState(false);
|
||||
const [dataIsStale, setDataIsStale] = useState(false);
|
||||
const { flag: explain, setFlag: setExplain } = useFlag(lokiQueryEditorExplainKey);
|
||||
const { flag: rawQuery, setFlag: setRawQuery } = useFlag(lokiQueryEditorRawQueryKey, true);
|
||||
|
||||
const query = getQueryWithDefaults(props.query);
|
||||
// This should be filled in from the defaults by now.
|
||||
const editorMode = query.editorMode!;
|
||||
|
||||
const onExplainChange = (event: SyntheticEvent<HTMLInputElement>) => {
|
||||
setExplain(event.currentTarget.checked);
|
||||
};
|
||||
|
||||
const onEditorModeChange = useCallback(
|
||||
(newEditorMode: QueryEditorMode) => {
|
||||
reportInteraction('grafana_loki_editor_mode_clicked', {
|
||||
newEditor: newEditorMode,
|
||||
previousEditor: query.editorMode ?? '',
|
||||
newQuery: !query.expr,
|
||||
app: app ?? '',
|
||||
});
|
||||
|
||||
if (newEditorMode === QueryEditorMode.Builder) {
|
||||
const result = buildVisualQueryFromString(query.expr || '');
|
||||
// If there are errors, give user a chance to decide if they want to go to builder as that can lose some data.
|
||||
if (result.errors.length) {
|
||||
setParseModalOpen(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
changeEditorMode(query, newEditorMode, onChange);
|
||||
},
|
||||
[onChange, query, app]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setDataIsStale(false);
|
||||
}, [data]);
|
||||
|
||||
const onChangeInternal = (query: LokiQuery) => {
|
||||
setDataIsStale(true);
|
||||
onChange(query);
|
||||
};
|
||||
|
||||
const onQueryPreviewChange = (event: SyntheticEvent<HTMLInputElement>) => {
|
||||
const isEnabled = event.currentTarget.checked;
|
||||
setRawQuery(isEnabled);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfirmModal
|
||||
isOpen={parseModalOpen}
|
||||
title="Query parsing"
|
||||
body="There were errors while trying to parse the query. Continuing to visual builder may lose some parts of the query."
|
||||
confirmText="Continue"
|
||||
onConfirm={() => {
|
||||
onChange({ ...query, editorMode: QueryEditorMode.Builder });
|
||||
setParseModalOpen(false);
|
||||
}}
|
||||
onDismiss={() => setParseModalOpen(false)}
|
||||
/>
|
||||
<QueryPatternsModal
|
||||
isOpen={queryPatternsModalOpen}
|
||||
onClose={() => setQueryPatternsModalOpen(false)}
|
||||
query={query}
|
||||
queries={queries}
|
||||
app={app}
|
||||
onChange={onChange}
|
||||
onAddQuery={onAddQuery}
|
||||
/>
|
||||
<EditorHeader>
|
||||
<Button
|
||||
aria-label={selectors.components.QueryBuilder.queryPatterns}
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setQueryPatternsModalOpen((prevValue) => !prevValue);
|
||||
|
||||
const visualQuery = buildVisualQueryFromString(query.expr || '');
|
||||
reportInteraction('grafana_loki_query_patterns_opened', {
|
||||
version: 'v2',
|
||||
app: app ?? '',
|
||||
editorMode: query.editorMode,
|
||||
preSelectedOperationsCount: visualQuery.query.operations.length,
|
||||
preSelectedLabelsCount: visualQuery.query.labels.length,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Kick start your query
|
||||
</Button>
|
||||
<QueryHeaderSwitch label="Explain" value={explain} onChange={onExplainChange} />
|
||||
{editorMode === QueryEditorMode.Builder && (
|
||||
<>
|
||||
<QueryHeaderSwitch label="Raw query" value={rawQuery} onChange={onQueryPreviewChange} />
|
||||
</>
|
||||
)}
|
||||
<FlexItem grow={1} />
|
||||
{app !== CoreApp.Explore && (
|
||||
<Button
|
||||
variant={dataIsStale ? 'primary' : 'secondary'}
|
||||
size="sm"
|
||||
onClick={onRunQuery}
|
||||
icon={data?.state === LoadingState.Loading ? 'fa fa-spinner' : undefined}
|
||||
disabled={data?.state === LoadingState.Loading}
|
||||
>
|
||||
Run queries
|
||||
</Button>
|
||||
)}
|
||||
<QueryEditorModeToggle mode={editorMode!} onChange={onEditorModeChange} />
|
||||
</EditorHeader>
|
||||
<Space v={0.5} />
|
||||
<EditorRows>
|
||||
{editorMode === QueryEditorMode.Code && (
|
||||
<LokiQueryCodeEditor {...props} query={query} onChange={onChangeInternal} showExplain={explain} />
|
||||
)}
|
||||
{editorMode === QueryEditorMode.Builder && (
|
||||
<LokiQueryBuilderContainer
|
||||
datasource={props.datasource}
|
||||
query={query}
|
||||
onChange={onChangeInternal}
|
||||
onRunQuery={props.onRunQuery}
|
||||
showRawQuery={rawQuery}
|
||||
showExplain={explain}
|
||||
/>
|
||||
)}
|
||||
<LokiQueryBuilderOptions query={query} onChange={onChange} onRunQuery={onRunQuery} app={app} />
|
||||
</EditorRows>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
LokiQueryEditorSelector.displayName = 'LokiQueryEditorSelector';
|
||||
Reference in New Issue
Block a user