From 4c42632ab81d9f58b5644de23289a042ec6f8bc4 Mon Sep 17 00:00:00 2001 From: Kevin Yu Date: Mon, 24 Jul 2023 14:33:45 -0700 Subject: [PATCH] CloudWatch Logs: Add Monaco-based query field editor behind feature flag (#71799) --- .betterer.results | 3 - .../cloudwatch/components/LogsQueryEditor.tsx | 15 ++- .../LogsQueryField/LogsQueryField.tsx | 108 ++++++++++++++++++ .../datasource/cloudwatch/datasource.ts | 3 + 4 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 public/app/plugins/datasource/cloudwatch/components/LogsQueryField/LogsQueryField.tsx diff --git a/.betterer.results b/.betterer.results index 514d2c6f5a9..b64c3848efe 100644 --- a/.betterer.results +++ b/.betterer.results @@ -3638,9 +3638,6 @@ exports[`better eslint`] = { "public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "public/app/plugins/datasource/cloudwatch/components/LogsQueryEditor.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] - ], "public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], diff --git a/public/app/plugins/datasource/cloudwatch/components/LogsQueryEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/LogsQueryEditor.tsx index 69f35a5dd64..33422c1fd5b 100644 --- a/public/app/plugins/datasource/cloudwatch/components/LogsQueryEditor.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/LogsQueryEditor.tsx @@ -3,6 +3,7 @@ import { css } from '@emotion/css'; import React, { memo } from 'react'; import { AbsoluteTimeRange, QueryEditorProps } from '@grafana/data'; +import { config } from '@grafana/runtime'; import { InlineFormLabel } from '@grafana/ui'; import { CloudWatchDatasource } from '../datasource'; @@ -10,6 +11,7 @@ import { CloudWatchJsonData, CloudWatchLogsQuery, CloudWatchQuery } from '../typ import { CloudWatchLink } from './CloudWatchLink'; import CloudWatchLogsQueryField from './LogsQueryField'; +import CloudWatchLogsQueryFieldMonaco from './LogsQueryField/LogsQueryField'; type Props = QueryEditorProps & { query: CloudWatchLogsQuery; @@ -37,7 +39,16 @@ export const CloudWatchLogsQueryEditor = memo(function CloudWatchLogsQueryEditor }; } - return ( + return config.featureToggles.cloudWatchLogsMonacoEditor ? ( + + + + } + /> + ) : ( - + } /> diff --git a/public/app/plugins/datasource/cloudwatch/components/LogsQueryField/LogsQueryField.tsx b/public/app/plugins/datasource/cloudwatch/components/LogsQueryField/LogsQueryField.tsx new file mode 100644 index 00000000000..5a1b4c36fc6 --- /dev/null +++ b/public/app/plugins/datasource/cloudwatch/components/LogsQueryField/LogsQueryField.tsx @@ -0,0 +1,108 @@ +import type * as monacoType from 'monaco-editor/esm/vs/editor/editor.api'; +import React, { ReactNode, useCallback } from 'react'; + +import { QueryEditorProps } from '@grafana/data'; +import { CodeEditor, Monaco, Themeable2, withTheme2 } from '@grafana/ui'; + +import { CloudWatchDatasource } from '../../datasource'; +import language from '../../language/logs/definition'; +import { TRIGGER_SUGGEST } from '../../language/monarch/commands'; +import { registerLanguage } from '../../language/monarch/register'; +import { CloudWatchJsonData, CloudWatchLogsQuery, CloudWatchQuery } from '../../types'; +import { getStatsGroups } from '../../utils/query/getStatsGroups'; + +import { LogGroupsField } from './../LogGroups/LogGroupsField'; + +export interface CloudWatchLogsQueryFieldProps + extends QueryEditorProps, + Themeable2 { + ExtraFieldElement?: ReactNode; + query: CloudWatchLogsQuery; +} +export const CloudWatchLogsQueryFieldMonaco = (props: CloudWatchLogsQueryFieldProps) => { + const { query, datasource, onChange, ExtraFieldElement, data } = props; + + const showError = data?.error?.refId === query.refId; + + const onChangeQuery = useCallback( + (value: string) => { + const nextQuery = { + ...query, + expression: value, + statsGroups: getStatsGroups(value), + }; + onChange(nextQuery); + }, + [onChange, query] + ); + const onEditorMount = useCallback( + (editor: monacoType.editor.IStandaloneCodeEditor, monaco: Monaco) => { + editor.onDidFocusEditorText(() => editor.trigger(TRIGGER_SUGGEST.id, TRIGGER_SUGGEST.id, {})); + editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Enter, () => { + const text = editor.getValue(); + onChangeQuery(text); + }); + }, + [onChangeQuery] + ); + + return ( + <> + { + onChange({ ...query, logGroups, logGroupNames: undefined }); + }} + /> +
+
+ { + if (value !== query.expression) { + onChangeQuery(value); + } + }} + onBeforeEditorMount={(monaco: Monaco) => + registerLanguage(monaco, language, datasource.logsCompletionItemProvider) + } + onEditorDidMount={onEditorMount} + /> +
+ {ExtraFieldElement} +
+ {showError ? ( +
+
{data?.error?.message}
+
+ ) : null} + + ); +}; + +export default withTheme2(CloudWatchLogsQueryFieldMonaco); diff --git a/public/app/plugins/datasource/cloudwatch/datasource.ts b/public/app/plugins/datasource/cloudwatch/datasource.ts index 1ee0ebd28e9..34b95790a3b 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.ts +++ b/public/app/plugins/datasource/cloudwatch/datasource.ts @@ -22,6 +22,7 @@ import { DEFAULT_METRICS_QUERY, getDefaultLogsQuery } from './defaultQueries'; import { isCloudWatchAnnotationQuery, isCloudWatchLogsQuery, isCloudWatchMetricsQuery } from './guards'; import { CloudWatchLogsLanguageProvider } from './language/cloudwatch-logs/CloudWatchLogsLanguageProvider'; import { SQLCompletionItemProvider } from './language/cloudwatch-sql/completion/CompletionItemProvider'; +import { LogsCompletionItemProvider } from './language/logs/completion/CompletionItemProvider'; import { MetricMathCompletionItemProvider } from './language/metric-math/completion/CompletionItemProvider'; import { CloudWatchAnnotationQueryRunner } from './query-runner/CloudWatchAnnotationQueryRunner'; import { CloudWatchLogsQueryRunner } from './query-runner/CloudWatchLogsQueryRunner'; @@ -44,6 +45,7 @@ export class CloudWatchDatasource languageProvider: CloudWatchLogsLanguageProvider; sqlCompletionItemProvider: SQLCompletionItemProvider; metricMathCompletionItemProvider: MetricMathCompletionItemProvider; + logsCompletionItemProvider: LogsCompletionItemProvider; defaultLogGroups?: string[]; type = 'cloudwatch'; @@ -65,6 +67,7 @@ export class CloudWatchDatasource this.sqlCompletionItemProvider = new SQLCompletionItemProvider(this.resources, this.templateSrv); this.metricMathCompletionItemProvider = new MetricMathCompletionItemProvider(this.resources, this.templateSrv); this.metricsQueryRunner = new CloudWatchMetricsQueryRunner(instanceSettings, templateSrv); + this.logsCompletionItemProvider = new LogsCompletionItemProvider(this.resources, this.templateSrv); this.logsQueryRunner = new CloudWatchLogsQueryRunner(instanceSettings, templateSrv, timeSrv); this.annotationQueryRunner = new CloudWatchAnnotationQueryRunner(instanceSettings, templateSrv); this.variables = new CloudWatchVariableSupport(this.resources);