Files
grafana/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor.tsx
Erik Sundell bab78a9e64 CloudWatch: Add support for AWS Metric Insights (#42487)
* add support for code editor and builder

* refactor cloudwatch migration

* Add tooltip to editor field (#56)

* add tooltip

* add old tooltips

* Bug bash feedback fixes (#58)

* make ASC the default option

* update sql preview whenever sql changes

* don't allow queries without aggregation

* set default value for aggregation

* use new input field

* cleanup

* pr feedback

* prevent unnecessary rerenders

* use frame error instead of main error

* remove not used snapshot

* Use dimension filter in schema picker  (#63)

* use dimension key filter in group by and schema labels

* add dimension filter also to code editor

* add tests

* fix build error

* fix strict error

* remove debug code

* fix annotation editor (#64)

* fix annotation editor

* fix broken test

* revert annotation backend change

* PR feedback (#67)

* pr feedback

* removed dimension filter from group by

* add spacing between common fields and rest

* do not generate deep link for metric queries (#70)

* update docs (#69)

Co-authored-by: Erik Sundell <erik.sundell87@gmail.com>

* fix lint problem caused by merge conflict

Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com>
2021-11-30 10:53:31 +01:00

188 lines
5.9 KiB
TypeScript

import React, { PureComponent, ChangeEvent } from 'react';
import { QueryEditorProps } from '@grafana/data';
import { Input } from '@grafana/ui';
import {
CloudWatchQuery,
CloudWatchMetricsQuery,
CloudWatchJsonData,
MetricQueryType,
MetricEditorMode,
} from '../types';
import { CloudWatchDatasource } from '../datasource';
import { Alias, MetricStatEditor, MathExpressionQueryField, SQLBuilderEditor, SQLCodeEditor } from './';
import EditorRow from './ui/EditorRow';
import EditorField from './ui/EditorField';
import { Space } from './ui/Space';
import QueryHeader from './QueryHeader';
import { isMetricsQuery } from '../guards';
export type Props = QueryEditorProps<CloudWatchDatasource, CloudWatchQuery, CloudWatchJsonData>;
interface State {
sqlCodeEditorIsDirty: boolean;
}
export const normalizeQuery = ({
namespace,
metricName,
expression,
dimensions,
region,
id,
alias,
statistic,
period,
sqlExpression,
metricQueryType,
metricEditorMode,
...rest
}: CloudWatchMetricsQuery): CloudWatchMetricsQuery => {
const normalizedQuery = {
queryMode: 'Metrics' as const,
namespace: namespace ?? '',
metricName: metricName ?? '',
expression: expression ?? '',
dimensions: dimensions ?? {},
region: region ?? 'default',
id: id ?? '',
alias: alias ?? '',
statistic: statistic ?? 'Average',
period: period ?? '',
metricQueryType: metricQueryType ?? MetricQueryType.Search,
metricEditorMode: metricEditorMode ?? MetricEditorMode.Builder,
sqlExpression: sqlExpression ?? '',
...rest,
};
return !rest.hasOwnProperty('matchExact') ? { ...normalizedQuery, matchExact: true } : normalizedQuery;
};
export class MetricsQueryEditor extends PureComponent<Props, State> {
state = {
sqlCodeEditorIsDirty: false,
};
componentDidMount = () => {
const metricsQuery = this.props.query as CloudWatchMetricsQuery;
const query = normalizeQuery(metricsQuery);
this.props.onChange(query);
};
onChange = (query: CloudWatchQuery) => {
const { onChange, onRunQuery } = this.props;
onChange(query);
onRunQuery();
};
render() {
const { onRunQuery, datasource } = this.props;
const metricsQuery = this.props.query as CloudWatchMetricsQuery;
const query = normalizeQuery(metricsQuery);
return (
<>
<QueryHeader
query={query}
onRunQuery={onRunQuery}
datasource={datasource}
onChange={(newQuery) => {
if (isMetricsQuery(newQuery) && newQuery.metricEditorMode !== query.metricEditorMode) {
this.setState({ sqlCodeEditorIsDirty: false });
}
this.onChange(newQuery);
}}
sqlCodeEditorIsDirty={this.state.sqlCodeEditorIsDirty}
/>
<Space v={0.5} />
{query.metricQueryType === MetricQueryType.Search && (
<>
{query.metricEditorMode === MetricEditorMode.Builder && (
<MetricStatEditor {...{ ...this.props, query }}></MetricStatEditor>
)}
{query.metricEditorMode === MetricEditorMode.Code && (
<MathExpressionQueryField
onRunQuery={onRunQuery}
expression={query.expression ?? ''}
onChange={(expression) => this.props.onChange({ ...query, expression })}
></MathExpressionQueryField>
)}
</>
)}
{query.metricQueryType === MetricQueryType.Query && (
<>
{query.metricEditorMode === MetricEditorMode.Code && (
<SQLCodeEditor
region={query.region}
sql={query.sqlExpression ?? ''}
onChange={(sqlExpression) => {
if (!this.state.sqlCodeEditorIsDirty) {
this.setState({ sqlCodeEditorIsDirty: true });
}
this.props.onChange({ ...metricsQuery, sqlExpression });
}}
onRunQuery={onRunQuery}
datasource={datasource}
/>
)}
{query.metricEditorMode === MetricEditorMode.Builder && (
<>
<SQLBuilderEditor
query={query}
onChange={this.props.onChange}
onRunQuery={onRunQuery}
datasource={datasource}
></SQLBuilderEditor>
</>
)}
</>
)}
<Space v={0.5} />
<EditorRow>
<EditorField
label="ID"
width={26}
optional
tooltip="ID can be used to reference other queries in math expressions. The ID can include numbers, letters, and underscore, and must start with a lowercase letter."
>
<Input
onBlur={onRunQuery}
onChange={(event: ChangeEvent<HTMLInputElement>) =>
this.onChange({ ...metricsQuery, id: event.target.value })
}
type="text"
invalid={!!query.id && !/^$|^[a-z][a-zA-Z0-9_]*$/.test(query.id)}
value={query.id}
/>
</EditorField>
<EditorField label="Period" width={26} tooltip="Minimum interval between points in seconds.">
<Input
value={query.period || ''}
placeholder="auto"
onBlur={onRunQuery}
onChange={(event: ChangeEvent<HTMLInputElement>) =>
this.onChange({ ...metricsQuery, period: event.target.value })
}
/>
</EditorField>
<EditorField
label="Alias"
width={26}
optional
tooltip="Change time series legend name using this field. See documentation for replacement variable formats."
>
<Alias
value={metricsQuery.alias ?? ''}
onChange={(value: string) => this.onChange({ ...metricsQuery, alias: value })}
/>
</EditorField>
</EditorRow>
</>
);
}
}