import { isEqual } from 'lodash'; import React, { SyntheticEvent, useCallback, useEffect, useState } from 'react'; import { usePrevious } from 'react-use'; import { CoreApp, LoadingState } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { EditorHeader, EditorRows, FlexItem, Space, Stack } from '@grafana/experimental'; import { config, reportInteraction } from '@grafana/runtime'; import { Button, ConfirmModal } 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, useFlag } from '../../prometheus/querybuilder/shared/hooks/useFlag'; import { LabelBrowserModal } from '../querybuilder/components/LabelBrowserModal'; import { LokiQueryBuilderContainer } from '../querybuilder/components/LokiQueryBuilderContainer'; import { LokiQueryBuilderOptions } from '../querybuilder/components/LokiQueryBuilderOptions'; import { LokiQueryCodeEditor } from '../querybuilder/components/LokiQueryCodeEditor'; import { QueryPatternsModal } from '../querybuilder/components/QueryPatternsModal'; import { buildVisualQueryFromString } from '../querybuilder/parsing'; import { changeEditorMode, getQueryWithDefaults } from '../querybuilder/state'; import { LokiQuery, QueryStats } from '../types'; import { shouldUpdateStats } from './stats'; import { LokiQueryEditorProps } from './types'; export const testIds = { editor: 'loki-editor', }; export const LokiQueryEditor = React.memo((props) => { const { onChange, onRunQuery, onAddQuery, data, app, queries, datasource } = props; const [parseModalOpen, setParseModalOpen] = useState(false); const [queryPatternsModalOpen, setQueryPatternsModalOpen] = useState(false); const [dataIsStale, setDataIsStale] = useState(false); const [labelBrowserVisible, setLabelBrowserVisible] = useState(false); const [queryStats, setQueryStats] = useState(null); const { flag: explain, setFlag: setExplain } = useFlag(lokiQueryEditorExplainKey); const timerange = datasource.getTimeRange(); const predefinedOperations = datasource.predefinedOperations; const previousTimerange = usePrevious(timerange); const query = getQueryWithDefaults(props.query); if (config.featureToggles.lokiPredefinedOperations && !query.expr && predefinedOperations) { query.expr = `{} ${predefinedOperations}`; } const previousQueryExpr = usePrevious(query.expr); const previousQueryType = usePrevious(query.queryType); // This should be filled in from the defaults by now. const editorMode = query.editorMode!; const onExplainChange = (event: SyntheticEvent) => { 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) => { if (!isEqual(query, props.query)) { setDataIsStale(true); } onChange(query); }; const onClickLabelBrowserButton = () => { reportInteraction('grafana_loki_label_browser_opened', { app: app, }); setLabelBrowserVisible((visible) => !visible); }; useEffect(() => { const update = shouldUpdateStats( query.expr, previousQueryExpr, timerange, previousTimerange, query.queryType, previousQueryType ); if (update) { const makeAsyncRequest = async () => { const stats = await datasource.getStats(query); setQueryStats(stats); }; makeAsyncRequest(); } }, [datasource, timerange, previousTimerange, query, previousQueryExpr, previousQueryType, setQueryStats]); return ( <> { onChange({ ...query, editorMode: QueryEditorMode.Builder }); setParseModalOpen(false); }} onDismiss={() => setParseModalOpen(false)} /> setQueryPatternsModalOpen(false)} query={query} queries={queries} app={app} onChange={onChange} onAddQuery={onAddQuery} /> setLabelBrowserVisible(false)} onChange={onChangeInternal} onRunQuery={onRunQuery} /> {app !== CoreApp.Explore && app !== CoreApp.Correlations && ( )} {editorMode === QueryEditorMode.Code && ( )} {editorMode === QueryEditorMode.Builder && ( )} ); }); LokiQueryEditor.displayName = 'LokiQueryEditor';