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:
Ivana Huckova
2022-10-20 11:46:48 +02:00
committed by GitHub
parent cadc6088db
commit 3ee450e66b
11 changed files with 358 additions and 650 deletions

View File

@@ -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>
);
});

View File

@@ -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} />}
</>

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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';