From 86694fcd50ab9d40924dddd6418ee4e848c3baff Mon Sep 17 00:00:00 2001 From: Yury Moladau Date: Tue, 6 Feb 2024 15:02:53 +0100 Subject: [PATCH] feat: add query editor --- .../QueryEditor/QueryCodeEditor.tsx | 59 +----- src/components/QueryEditor/QueryEditor.tsx | 200 ++---------------- .../QueryEditor/QueryEditorByApp.tsx | 21 ++ .../QueryEditor/QueryEditorForAlerting.tsx | 21 +- src/components/QueryEditor/QueryField.tsx | 125 ++++------- src/components/QueryEditor/state.ts | 15 ++ .../monaco-query-field/MonacoQueryField.tsx | 138 +----------- .../MonacoQueryFieldLazy.tsx | 18 -- .../MonacoQueryFieldProps.ts | 28 +-- .../MonacoQueryFieldWrapper.tsx | 18 -- src/configuration/AlertingSettings.tsx | 14 +- src/configuration/ConfigEditor.tsx | 102 +++------ src/configuration/QuerySettings.tsx | 57 +---- 13 files changed, 150 insertions(+), 666 deletions(-) diff --git a/src/components/QueryEditor/QueryCodeEditor.tsx b/src/components/QueryEditor/QueryCodeEditor.tsx index 7e4fcc2..dc90b43 100644 --- a/src/components/QueryEditor/QueryCodeEditor.tsx +++ b/src/components/QueryEditor/QueryCodeEditor.tsx @@ -2,40 +2,23 @@ import { css } from '@emotion/css'; import React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; -import { config } from '@grafana/runtime'; -import { useStyles2, HorizontalGroup, IconButton, Tooltip, Icon } from '@grafana/ui'; -import { getModKey } from 'app/core/utils/browser'; +import { useStyles2 } from '@grafana/ui'; -import { testIds } from '../../components/LokiQueryEditor'; -import { LokiQueryField } from '../../components/LokiQueryField'; -import { LokiQueryEditorProps } from '../../components/types'; -import { formatLogqlQuery } from '../../queryUtils'; +import { VictoriaLogsQueryEditorProps } from "../../types"; -import { LokiQueryBuilderExplained } from './LokiQueryBuilderExplained'; +import QueryField from "./QueryField"; -type Props = LokiQueryEditorProps & { +type Props = VictoriaLogsQueryEditorProps & { showExplain: boolean; }; -export function LokiQueryCodeEditor({ - query, - datasource, - range, - onRunQuery, - onChange, - data, - app, - showExplain, - history, -}: Props) { +const QueryCodeEditor = (props: Props) => { + const { query, datasource, range, onRunQuery, onChange, data, app, history } = props; const styles = useStyles2(getStyles); - const lokiFormatQuery = config.featureToggles.lokiFormatQuery; - const onClickFormatQueryButton = async () => onChange({ ...query, expr: formatLogqlQuery(query.expr, datasource) }); - return (
- - {lokiFormatQuery && ( -
-
- - - - - - -
-
- )} - - } /> - {showExplain && }
); } @@ -76,9 +36,6 @@ const getStyles = (theme: GrafanaTheme2) => { return { wrapper: css` max-width: 100%; - .gf-form { - margin-bottom: 0.5; - } `, buttonGroup: css` border: 1px solid ${theme.colors.border.medium}; @@ -97,3 +54,5 @@ const getStyles = (theme: GrafanaTheme2) => { `, }; }; + +export default QueryCodeEditor diff --git a/src/components/QueryEditor/QueryEditor.tsx b/src/components/QueryEditor/QueryEditor.tsx index 9139a6d..ebf56b1 100644 --- a/src/components/QueryEditor/QueryEditor.tsx +++ b/src/components/QueryEditor/QueryEditor.tsx @@ -1,220 +1,52 @@ import { isEqual } from 'lodash'; -import React, { SyntheticEvent, useCallback, useEffect, useId, useState } from 'react'; -import { usePrevious } from 'react-use'; +import React, { useEffect, useState } from 'react'; 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 { Button } from '@grafana/ui'; -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 { Query, VictoriaLogsQueryEditorProps } from "../../types"; -import { shouldUpdateStats } from './stats'; -import { LokiQueryEditorProps } from './types'; +import QueryCodeEditor from "./QueryCodeEditor"; +import { getQueryWithDefaults } from "./state"; -export const testIds = { - editor: 'loki-editor', -}; - -export const lokiQueryEditorExplainKey = 'LokiQueryEditorExplainDefault'; - -export const LokiQueryEditor = React.memo((props) => { - const id = useId(); - const { onChange, onRunQuery, onAddQuery, data, app, queries, datasource, range: timeRange } = props; - const [parseModalOpen, setParseModalOpen] = useState(false); - const [queryPatternsModalOpen, setQueryPatternsModalOpen] = useState(false); +const QueryEditor = React.memo((props) => { + const { onChange, onRunQuery, data, app, queries } = props; const [dataIsStale, setDataIsStale] = useState(false); - const [labelBrowserVisible, setLabelBrowserVisible] = useState(false); - const [queryStats, setQueryStats] = useState(null); - const [explain, setExplain] = useState(window.localStorage.getItem(lokiQueryEditorExplainKey) === 'true'); - - 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) => { - window.localStorage.setItem(lokiQueryEditorExplainKey, event.currentTarget.checked ? 'true' : 'false'); - 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) => { + const onChangeInternal = (query: Query) => { if (!isEqual(query, props.query)) { setDataIsStale(true); } onChange(query); }; - const onClickLabelBrowserButton = () => { - reportInteraction('grafana_loki_label_browser_opened', { - app: app, - }); - - setLabelBrowserVisible((visible) => !visible); - }; - - useEffect(() => { - const shouldUpdate = shouldUpdateStats( - query.expr, - previousQueryExpr, - timeRange, - previousTimeRange, - query.queryType, - previousQueryType - ); - if (shouldUpdate && timeRange) { - const makeAsyncRequest = async () => { - // overwriting the refId that is later used to cancel inflight queries with the same ID. - const stats = await datasource.getStats({ ...query, refId: `${id}_${query.refId}` }, timeRange); - setQueryStats(stats); - }; - makeAsyncRequest(); - } - }, [datasource, timeRange, previousTimeRange, query, previousQueryExpr, previousQueryType, setQueryStats, id]); - 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} - timeRange={timeRange} - /> - - - - - - - +
{app !== CoreApp.Explore && app !== CoreApp.Correlations && ( )} - - - - - {editorMode === QueryEditorMode.Code && ( - - )} - {editorMode === QueryEditorMode.Builder && ( - - )} - - +
+
+ +
); }); -LokiQueryEditor.displayName = 'LokiQueryEditor'; +QueryEditor.displayName = 'LokiQueryEditor'; +export default QueryEditor diff --git a/src/components/QueryEditor/QueryEditorByApp.tsx b/src/components/QueryEditor/QueryEditorByApp.tsx index e69de29..1cbe155 100644 --- a/src/components/QueryEditor/QueryEditorByApp.tsx +++ b/src/components/QueryEditor/QueryEditorByApp.tsx @@ -0,0 +1,21 @@ +import React, { memo } from 'react'; + +import { CoreApp } from '@grafana/data'; + +import { VictoriaLogsQueryEditorProps } from '../../types'; + +import QueryEditor from './QueryEditor'; +import QueryEditorForAlerting from './QueryEditorForAlerting'; + +const QueryEditorByApp = (props: VictoriaLogsQueryEditorProps) => { + const { app } = props; + + switch (app) { + case CoreApp.CloudAlerting: + return ; + default: + return ; + } +} + +export default memo(QueryEditorByApp); diff --git a/src/components/QueryEditor/QueryEditorForAlerting.tsx b/src/components/QueryEditor/QueryEditorForAlerting.tsx index 7e31f9f..17fbbf7 100644 --- a/src/components/QueryEditor/QueryEditorForAlerting.tsx +++ b/src/components/QueryEditor/QueryEditorForAlerting.tsx @@ -1,25 +1,12 @@ import React from 'react'; -import { LokiQueryField } from './LokiQueryField'; -import { LokiQueryEditorProps } from './types'; +import { VictoriaLogsQueryEditorProps } from "../../types"; -export function LokiQueryEditorForAlerting(props: LokiQueryEditorProps) { - const { query, data, datasource, onChange, onRunQuery, history } = props; +const QueryEditorForAlerting = (props: VictoriaLogsQueryEditorProps) => { return ( - +
QueryEditorForAlerting
); } -export const testIds = { - editor: 'loki-editor-cloud-alerting', -}; +export default QueryEditorForAlerting diff --git a/src/components/QueryEditor/QueryField.tsx b/src/components/QueryEditor/QueryField.tsx index 237e2bf..1cdf1f0 100644 --- a/src/components/QueryEditor/QueryField.tsx +++ b/src/components/QueryEditor/QueryField.tsx @@ -1,93 +1,62 @@ -import React, { ReactNode } from 'react'; +import React from 'react'; import { QueryEditorProps } from '@grafana/data'; -import { LokiDatasource } from '../datasource'; -import { shouldRefreshLabels } from '../languageUtils'; -import { LokiQuery, LokiOptions } from '../types'; +import { VictoriaLogsDatasource } from "../../datasource"; +import { Options, Query } from "../../types"; +import { MonacoQueryFieldWrapper } from "../monaco-query-field/MonacoQueryFieldWrapper"; -import { MonacoQueryFieldWrapper } from './monaco-query-field/MonacoQueryFieldWrapper'; - -export interface LokiQueryFieldProps extends QueryEditorProps { - ExtraFieldElement?: ReactNode; - placeholder?: string; +export interface LokiQueryFieldProps extends QueryEditorProps { + ExtraFieldElement?: React.ReactNode; 'data-testid'?: string; } -interface LokiQueryFieldState { - labelsLoaded: boolean; -} - -export class LokiQueryField extends React.PureComponent { - _isMounted = false; - - constructor(props: LokiQueryFieldProps) { - super(props); - - this.state = { labelsLoaded: false }; - } - - async componentDidMount() { - this._isMounted = true; - await this.props.datasource.languageProvider.start(this.props.range); - if (this._isMounted) { - this.setState({ labelsLoaded: true }); - } - } - - componentWillUnmount() { - this._isMounted = false; - } - - componentDidUpdate(prevProps: LokiQueryFieldProps) { - const { - range, - datasource: { languageProvider }, - } = this.props; - const refreshLabels = shouldRefreshLabels(range, prevProps.range); - // We want to refresh labels when range changes (we round up intervals to a minute) - if (refreshLabels) { - languageProvider.fetchLabels({ timeRange: range }); - } - } - - onChangeQuery = (value: string, override?: boolean) => { - // Send text change to parent - const { query, onChange, onRunQuery } = this.props; +const QueryField: React.FC = (props) => { + const { + ExtraFieldElement, + query, + // datasource, + history, + onRunQuery, + // range, + onChange, + 'data-testid': dataTestId + } = props; + // const [labelsLoaded, setLabelsLoaded] = useState(false); + + // Replace componentDidUpdate logic if needed + + const onChangeQuery = (value: string) => { if (onChange) { const nextQuery = { ...query, expr: value }; onChange(nextQuery); - if (override && onRunQuery) { - onRunQuery(); - } + // if (override && onRunQuery) { + // onRunQuery(); + // } } }; - render() { - const { ExtraFieldElement, query, datasource, history, onRunQuery, range } = this.props; - const placeholder = this.props.placeholder ?? 'Enter a Loki query (run with Shift+Enter)'; - - return ( - <> -
-
- -
+ return ( + <> +
+
+
- {ExtraFieldElement} - - ); - } -} +
+ {ExtraFieldElement} + + ); +}; + +export default QueryField; diff --git a/src/components/QueryEditor/state.ts b/src/components/QueryEditor/state.ts index e69de29..70f55db 100644 --- a/src/components/QueryEditor/state.ts +++ b/src/components/QueryEditor/state.ts @@ -0,0 +1,15 @@ +import { Query, QueryType } from "../../types"; + +export function getQueryWithDefaults(query: Query): Query { + let result = query; + + if (query.expr == null) { + result = { ...result, expr: '' }; + } + + if (query.queryType == null) { + result = { ...result, queryType: QueryType.Range }; + } + + return result; +} diff --git a/src/components/monaco-query-field/MonacoQueryField.tsx b/src/components/monaco-query-field/MonacoQueryField.tsx index 951ff35..ef77d06 100755 --- a/src/components/monaco-query-field/MonacoQueryField.tsx +++ b/src/components/monaco-query-field/MonacoQueryField.tsx @@ -1,33 +1,12 @@ -// Copyright (c) 2022 Grafana Labs -// Modifications Copyright (c) 2022 VictoriaMetrics -// 2022-10-04: remove onChangeRef -// A detailed history of changes can be seen here - https://github.com/VictoriaMetrics/grafana-datasource -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - import { css } from '@emotion/css'; -import { promLanguageDefinition } from 'monaco-promql'; -import React, { useRef, useEffect } from 'react'; +import React, { useRef } from 'react'; import { useLatest } from 'react-use'; import { GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { useTheme2, ReactMonacoEditor, Monaco, monacoTypes } from '@grafana/ui'; +import { useTheme2, ReactMonacoEditor, monacoTypes } from '@grafana/ui'; import { Props } from './MonacoQueryFieldProps'; -import { getOverrideServices } from './getOverrideServices'; -import { getCompletionProvider, getSuggestOptions } from './monaco-completion-provider'; const options: monacoTypes.editor.IStandaloneEditorConstructionOptions = { codeLens: false, @@ -56,7 +35,6 @@ const options: monacoTypes.editor.IStandaloneEditorConstructionOptions = { horizontalScrollbarSize: 0, }, scrollBeyondLastLine: false, - suggest: getSuggestOptions(), suggestFontSize: 12, wordWrap: 'on', }; @@ -70,24 +48,6 @@ const options: monacoTypes.editor.IStandaloneEditorConstructionOptions = { // up & down. this we want to avoid) const EDITOR_HEIGHT_OFFSET = 2; -const PROMQL_LANG_ID = promLanguageDefinition.id; - -// we must only run the promql-setup code once -let PROMQL_SETUP_STARTED = false; - -function ensurePromQL(monaco: Monaco) { - if (!PROMQL_SETUP_STARTED) { - PROMQL_SETUP_STARTED = true; - const { aliases, extensions, mimetypes, loader } = promLanguageDefinition; - monaco.languages.register({ id: PROMQL_LANG_ID, aliases, extensions, mimetypes }); - - loader().then((mod) => { - monaco.languages.setMonarchTokensProvider(PROMQL_LANG_ID, mod.language); - monaco.languages.setLanguageConfiguration(PROMQL_LANG_ID, mod.languageConfiguration); - }); - } -} - const getStyles = (theme: GrafanaTheme2, placeholder: string) => { return { container: css` @@ -106,126 +66,34 @@ const getStyles = (theme: GrafanaTheme2, placeholder: string) => { const MonacoQueryField = (props: Props) => { // we need only one instance of `overrideServices` during the lifetime of the react component - const overrideServicesRef = useRef(getOverrideServices()); const containerRef = useRef(null); - const { languageProvider, history, onBlur, onRunQuery, initialValue, placeholder, readOnly } = props; + const { onBlur, onRunQuery, initialValue, placeholder, readOnly } = props; - const lpRef = useLatest(languageProvider); - const historyRef = useLatest(history); const onRunQueryRef = useLatest(onRunQuery); const onBlurRef = useLatest(onBlur); - const autocompleteDisposeFun = useRef<(() => void) | null>(null); - const theme = useTheme2(); const styles = getStyles(theme, placeholder); - useEffect(() => { - // when we unmount, we unregister the autocomplete-function, if it was registered - return () => { - autocompleteDisposeFun.current?.(); - }; - }, []); - return (
{ - ensurePromQL(monaco); - }} onMount={(editor, monaco) => { // we setup on-blur editor.onDidBlurEditorWidget(() => { onBlurRef.current(editor.getValue()); }); - // we construct a DataProvider object - const getSeries = (selector: string) => lpRef.current.getSeries(selector); - - const getHistory = () => - Promise.resolve(historyRef.current.map((h) => h.query.expr).filter((expr) => expr !== undefined)); - - const getAllMetricNames = () => { - const { metrics, metricsMetadata } = lpRef.current; - const result = metrics.map((m) => { - const metaItem = metricsMetadata?.[m]; - return { - name: m, - help: metaItem?.help ?? '', - type: metaItem?.type ?? '', - }; - }); - - return Promise.resolve(result); - }; - - const getAllWithTemplates = () => { - const { withTemplates } = lpRef.current; - const result = withTemplates.map(t => ({ - name: t.label, - help: t.comment || "", - value: t.value - })) - - return Promise.resolve(result); - }; - - const getAllLabelNames = () => Promise.resolve(lpRef.current.getLabelKeys()); - - const getLabelValues = (labelName: string) => lpRef.current.getLabelValues(labelName); - - const dataProvider = { - getSeries, - getHistory, - getAllMetricNames, - getAllWithTemplates, - getAllLabelNames, - getLabelValues - }; - const completionProvider = getCompletionProvider(monaco, dataProvider); - - // completion-providers in monaco are not registered directly to editor-instances, - // they are registered to languages. this makes it hard for us to have - // separate completion-providers for every query-field-instance - // (but we need that, because they might connect to different datasources). - // the trick we do is, we wrap the callback in a "proxy", - // and in the proxy, the first thing is, we check if we are called from - // "our editor instance", and if not, we just return nothing. if yes, - // we call the completion-provider. - const filteringCompletionProvider: monacoTypes.languages.CompletionItemProvider = { - ...completionProvider, - provideCompletionItems: (model, position, context, token) => { - // if the model-id does not match, then this call is from a different editor-instance, - // not "our instance", so return nothing - if (editor.getModel()?.id !== model.id) { - return { suggestions: [] }; - } - return completionProvider.provideCompletionItems(model, position, context, token); - }, - }; - - const { dispose } = monaco.languages.registerCompletionItemProvider( - PROMQL_LANG_ID, - filteringCompletionProvider - ); - - autocompleteDisposeFun.current = dispose; - // this code makes the editor resize itself so that the content fits - // (it will grow taller when necessary) - // FIXME: maybe move this functionality into CodeEditor, like: - // const updateElementHeight = () => { const containerDiv = containerRef.current; if (containerDiv !== null) { diff --git a/src/components/monaco-query-field/MonacoQueryFieldLazy.tsx b/src/components/monaco-query-field/MonacoQueryFieldLazy.tsx index 853bd18..c69f3e9 100755 --- a/src/components/monaco-query-field/MonacoQueryFieldLazy.tsx +++ b/src/components/monaco-query-field/MonacoQueryFieldLazy.tsx @@ -1,21 +1,3 @@ -// Copyright (c) 2022 Grafana Labs -// Modifications Copyright (c) 2022 VictoriaMetrics -// 2022-10-04: remove lazy Field import -// A detailed history of changes can be seen here - https://github.com/VictoriaMetrics/grafana-datasource -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - import React, { Suspense } from 'react'; import Field from './MonacoQueryField' diff --git a/src/components/monaco-query-field/MonacoQueryFieldProps.ts b/src/components/monaco-query-field/MonacoQueryFieldProps.ts index 9c9225c..5ec6678 100755 --- a/src/components/monaco-query-field/MonacoQueryFieldProps.ts +++ b/src/components/monaco-query-field/MonacoQueryFieldProps.ts @@ -1,34 +1,10 @@ -// Copyright (c) 2022 Grafana Labs -// Modifications Copyright (c) 2022 VictoriaMetrics -// 2022-10-04: remove onChange method -// A detailed history of changes can be seen here - https://github.com/VictoriaMetrics/grafana-datasource -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - import { HistoryItem } from '@grafana/data'; -import type PromQlLanguageProvider from '../../language_provider'; -import { PromQuery } from '../../types'; +import { Query } from '../../types'; -// we need to store this in a separate file, -// because we have an async-wrapper around, -// the react-component, and it needs the same -// props as the sync-component. export type Props = { initialValue: string; - languageProvider: PromQlLanguageProvider; - history: Array>; + history: Array>; placeholder: string; readOnly?: boolean; onRunQuery: (value: string) => void; diff --git a/src/components/monaco-query-field/MonacoQueryFieldWrapper.tsx b/src/components/monaco-query-field/MonacoQueryFieldWrapper.tsx index 3800b49..60295a3 100755 --- a/src/components/monaco-query-field/MonacoQueryFieldWrapper.tsx +++ b/src/components/monaco-query-field/MonacoQueryFieldWrapper.tsx @@ -1,21 +1,3 @@ -// Copyright (c) 2022 Grafana Labs -// Modifications Copyright (c) 2022 VictoriaMetrics -// 2022-10-04: remove handleChange function -// A detailed history of changes can be seen here - https://github.com/VictoriaMetrics/grafana-datasource -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - import React, { useRef } from 'react'; import { MonacoQueryFieldLazy } from './MonacoQueryFieldLazy'; diff --git a/src/configuration/AlertingSettings.tsx b/src/configuration/AlertingSettings.tsx index d9c4c43..3223a86 100644 --- a/src/configuration/AlertingSettings.tsx +++ b/src/configuration/AlertingSettings.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { DataSourcePluginOptionsEditorProps } from '@grafana/data'; -import { ConfigDescriptionLink, ConfigSubSection } from '@grafana/experimental'; import { InlineField, InlineSwitch } from '@grafana/ui'; export function AlertingSettings({ @@ -9,16 +8,7 @@ export function AlertingSettings({ onOptionsChange, }: Pick) { return ( - - } - > +
- +
); } diff --git a/src/configuration/ConfigEditor.tsx b/src/configuration/ConfigEditor.tsx index ff2ad03..36b242c 100644 --- a/src/configuration/ConfigEditor.tsx +++ b/src/configuration/ConfigEditor.tsx @@ -1,95 +1,47 @@ -import React, { useCallback } from 'react'; +import React from 'react'; import { DataSourcePluginOptionsEditorProps, DataSourceSettings } from '@grafana/data'; -import { - ConfigSection, - DataSourceDescription, - ConnectionSettings, - Auth, - convertLegacyAuthProps, - AdvancedHttpSettings, -} from '@grafana/experimental'; -import { config, reportInteraction } from '@grafana/runtime'; -import { SecureSocksProxySettings } from '@grafana/ui'; -import { Divider } from 'app/core/components/Divider'; +import { config } from '@grafana/runtime'; +import { DataSourceHttpSettings } from "@grafana/ui"; -import { LokiOptions } from '../types'; +import { Options } from '../types'; import { AlertingSettings } from './AlertingSettings'; -import { DerivedFields } from './DerivedFields'; +import { HelpfulLinks } from "./HelpfulLinks"; import { QuerySettings } from './QuerySettings'; -export type Props = DataSourcePluginOptionsEditorProps; +export type Props = DataSourcePluginOptionsEditorProps; -const makeJsonUpdater = - (field: keyof LokiOptions) => - (options: DataSourceSettings, value: T): DataSourceSettings => { - return { - ...options, - jsonData: { - ...options.jsonData, - [field]: value, - }, - }; - }; +const makeJsonUpdater = (field: keyof Options) => + (options: DataSourceSettings, value: T): DataSourceSettings => ({ + ...options, + jsonData: { + ...options.jsonData, + [field]: value, + }, + }) const setMaxLines = makeJsonUpdater('maxLines'); -const setPredefinedOperations = makeJsonUpdater('predefinedOperations'); -const setDerivedFields = makeJsonUpdater('derivedFields'); -export const ConfigEditor = (props: Props) => { +const ConfigEditor = (props: Props) => { const { options, onOptionsChange } = props; - const updatePredefinedOperations = useCallback( - (value: string) => { - reportInteraction('grafana_loki_predefined_operations_changed', { value }); - onOptionsChange(setPredefinedOperations(options, value)); - }, - [options, onOptionsChange] - ); - return ( <> - + - - - - + onOptionsChange(setMaxLines(options, value))} /> - - - - - {config.secureSocksDSProxyEnabled && ( - - )} - - - onOptionsChange(setMaxLines(options, value))} - predefinedOperations={options.jsonData.predefinedOperations || ''} - onPredefinedOperationsChange={updatePredefinedOperations} - /> - - onOptionsChange(setDerivedFields(options, value))} - /> - ); }; + +export default ConfigEditor; diff --git a/src/configuration/QuerySettings.tsx b/src/configuration/QuerySettings.tsx index 7cdbc8a..bcc06f6 100644 --- a/src/configuration/QuerySettings.tsx +++ b/src/configuration/QuerySettings.tsx @@ -1,29 +1,16 @@ import React from 'react'; -import { ConfigDescriptionLink, ConfigSubSection } from '@grafana/experimental'; -import { config } from '@grafana/runtime'; -import { Badge, InlineField, InlineFieldRow, Input } from '@grafana/ui'; +import { InlineField, Input } from '@grafana/ui'; type Props = { maxLines: string; onMaxLinedChange: (value: string) => void; - predefinedOperations: string; - onPredefinedOperationsChange: (value: string) => void; }; export const QuerySettings = (props: Props) => { - const { maxLines, onMaxLinedChange, predefinedOperations, onPredefinedOperationsChange } = props; + const { maxLines, onMaxLinedChange } = props; return ( - - } - > +
{ /> - {config.featureToggles.lokiPredefinedOperations && ( - - - { - 'Predefined operations are used as an initial state for your queries. They are useful, if you want to unpack, parse or format all log lines. Currently we support only log operations starting with |. For example: | unpack | line_format "{{.message}}".' - } - - } - > - ) => - onPredefinedOperationsChange(event.currentTarget.value) - } - width={40} - placeholder="| unpack | line_format" - spellCheck={false} - /> - - - - - - )} - +
); };