mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
CloudWatch: Annotation Editor rewrite (#20765)
* wip: react rewrite * Cleanup * Break out non annontations specific fields * Cleanup. Make annontations editor a functional component * Remove redundant classnames * Add paneldata to props * Cleanup * Fix rebase merge problem * Updates after pr feedback * Fix conflict with master
This commit is contained in:
parent
29687903f8
commit
a35b2ac463
@ -54,16 +54,20 @@ func (e *CloudWatchExecutor) executeAnnotationQuery(ctx context.Context, queryCo
|
|||||||
alarmNames = filterAlarms(resp, namespace, metricName, dimensions, statistics, period)
|
alarmNames = filterAlarms(resp, namespace, metricName, dimensions, statistics, period)
|
||||||
} else {
|
} else {
|
||||||
if region == "" || namespace == "" || metricName == "" || len(statistics) == 0 {
|
if region == "" || namespace == "" || metricName == "" || len(statistics) == 0 {
|
||||||
return result, nil
|
return result, errors.New("Invalid annotations query")
|
||||||
}
|
}
|
||||||
|
|
||||||
var qd []*cloudwatch.Dimension
|
var qd []*cloudwatch.Dimension
|
||||||
for k, v := range dimensions {
|
for k, v := range dimensions {
|
||||||
if vv, ok := v.(string); ok {
|
if vv, ok := v.([]interface{}); ok {
|
||||||
qd = append(qd, &cloudwatch.Dimension{
|
for _, vvv := range vv {
|
||||||
Name: aws.String(k),
|
if vvvv, ok := vvv.(string); ok {
|
||||||
Value: aws.String(vv),
|
qd = append(qd, &cloudwatch.Dimension{
|
||||||
})
|
Name: aws.String(k),
|
||||||
|
Value: aws.String(vvvv),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, s := range statistics {
|
for _, s := range statistics {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { react2AngularDirective } from 'app/core/utils/react2angular';
|
import { react2AngularDirective } from 'app/core/utils/react2angular';
|
||||||
import { QueryEditor as StackdriverQueryEditor } from 'app/plugins/datasource/stackdriver/components/QueryEditor';
|
import { QueryEditor as StackdriverQueryEditor } from 'app/plugins/datasource/stackdriver/components/QueryEditor';
|
||||||
import { AnnotationQueryEditor as StackdriverAnnotationQueryEditor } from 'app/plugins/datasource/stackdriver/components/AnnotationQueryEditor';
|
import { AnnotationQueryEditor as StackdriverAnnotationQueryEditor } from 'app/plugins/datasource/stackdriver/components/AnnotationQueryEditor';
|
||||||
|
import { AnnotationQueryEditor as CloudWatchAnnotationQueryEditor } from 'app/plugins/datasource/cloudwatch/components/AnnotationQueryEditor';
|
||||||
import PageHeader from './components/PageHeader/PageHeader';
|
import PageHeader from './components/PageHeader/PageHeader';
|
||||||
import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA';
|
import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA';
|
||||||
import { TagFilter } from './components/TagFilter/TagFilter';
|
import { TagFilter } from './components/TagFilter/TagFilter';
|
||||||
@ -93,6 +94,11 @@ export function registerAngularDirectives() {
|
|||||||
['datasource', { watchDepth: 'reference' }],
|
['datasource', { watchDepth: 'reference' }],
|
||||||
['templateSrv', { watchDepth: 'reference' }],
|
['templateSrv', { watchDepth: 'reference' }],
|
||||||
]);
|
]);
|
||||||
|
react2AngularDirective('cloudwatchAnnotationQueryEditor', CloudWatchAnnotationQueryEditor, [
|
||||||
|
'query',
|
||||||
|
'onChange',
|
||||||
|
['datasource', { watchDepth: 'reference' }],
|
||||||
|
]);
|
||||||
react2AngularDirective('secretFormField', SecretFormField, [
|
react2AngularDirective('secretFormField', SecretFormField, [
|
||||||
'value',
|
'value',
|
||||||
'isConfigured',
|
'isConfigured',
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import { AnnotationQuery } from './types';
|
||||||
|
|
||||||
|
export class CloudWatchAnnotationsQueryCtrl {
|
||||||
|
static templateUrl = 'partials/annotations.editor.html';
|
||||||
|
annotation: any;
|
||||||
|
|
||||||
|
/** @ngInject */
|
||||||
|
constructor() {
|
||||||
|
_.defaultsDeep(this.annotation, {
|
||||||
|
namespace: '',
|
||||||
|
metricName: '',
|
||||||
|
expression: '',
|
||||||
|
dimensions: {},
|
||||||
|
region: 'default',
|
||||||
|
id: '',
|
||||||
|
alias: '',
|
||||||
|
statistics: ['Average'],
|
||||||
|
matchExact: true,
|
||||||
|
prefixMatching: false,
|
||||||
|
actionPrefix: '',
|
||||||
|
alarmNamePrefix: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
this.onChange = this.onChange.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(query: AnnotationQuery) {
|
||||||
|
Object.assign(this.annotation, query);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
import React, { ChangeEvent } from 'react';
|
||||||
|
import { Switch } from '@grafana/ui';
|
||||||
|
import { PanelData } from '@grafana/data';
|
||||||
|
import { CloudWatchQuery, AnnotationQuery } from '../types';
|
||||||
|
import CloudWatchDatasource from '../datasource';
|
||||||
|
import { QueryField, QueryFieldsEditor } from './';
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
query: AnnotationQuery;
|
||||||
|
datasource: CloudWatchDatasource;
|
||||||
|
onChange: (value: AnnotationQuery) => void;
|
||||||
|
data?: PanelData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function AnnotationQueryEditor(props: React.PropsWithChildren<Props>) {
|
||||||
|
const { query, onChange } = props;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<QueryFieldsEditor
|
||||||
|
{...props}
|
||||||
|
onChange={(editorQuery: CloudWatchQuery) => onChange({ ...query, ...editorQuery })}
|
||||||
|
hideWilcard
|
||||||
|
></QueryFieldsEditor>
|
||||||
|
<div className="gf-form-inline">
|
||||||
|
<Switch
|
||||||
|
label="Enable Prefix Matching"
|
||||||
|
labelClass="query-keyword"
|
||||||
|
checked={query.prefixMatching}
|
||||||
|
onChange={() => onChange({ ...query, prefixMatching: !query.prefixMatching })}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="gf-form gf-form--grow">
|
||||||
|
<QueryField label="Action">
|
||||||
|
<input
|
||||||
|
disabled={!query.prefixMatching}
|
||||||
|
className="gf-form-input width-12"
|
||||||
|
value={query.actionPrefix || ''}
|
||||||
|
onChange={(event: ChangeEvent<HTMLInputElement>) =>
|
||||||
|
onChange({ ...query, actionPrefix: event.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</QueryField>
|
||||||
|
<QueryField label="Alarm Name">
|
||||||
|
<input
|
||||||
|
disabled={!query.prefixMatching}
|
||||||
|
className="gf-form-input width-12"
|
||||||
|
value={query.alarmNamePrefix || ''}
|
||||||
|
onChange={(event: ChangeEvent<HTMLInputElement>) =>
|
||||||
|
onChange({ ...query, alarmNamePrefix: event.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</QueryField>
|
||||||
|
<div className="gf-form gf-form--grow">
|
||||||
|
<div className="gf-form-label gf-form-label--grow" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -1,18 +1,13 @@
|
|||||||
import React, { PureComponent, ChangeEvent } from 'react';
|
import React, { PureComponent, ChangeEvent } from 'react';
|
||||||
import { SelectableValue, ExploreQueryFieldProps } from '@grafana/data';
|
import { ExploreQueryFieldProps } from '@grafana/data';
|
||||||
import { Input, Segment, SegmentAsync, ValidationEvents, EventsWithValidation, Switch } from '@grafana/ui';
|
import { Input, ValidationEvents, EventsWithValidation, Switch } from '@grafana/ui';
|
||||||
import { CloudWatchQuery } from '../types';
|
import { CloudWatchQuery } from '../types';
|
||||||
import CloudWatchDatasource from '../datasource';
|
import CloudWatchDatasource from '../datasource';
|
||||||
import { SelectableStrings } from '../types';
|
import { QueryField, Alias, QueryFieldsEditor } from './';
|
||||||
import { Stats, Dimensions, QueryInlineField, QueryField, Alias } from './';
|
|
||||||
|
|
||||||
export type Props = ExploreQueryFieldProps<CloudWatchDatasource, CloudWatchQuery>;
|
export type Props = ExploreQueryFieldProps<CloudWatchDatasource, CloudWatchQuery>;
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
regions: SelectableStrings;
|
|
||||||
namespaces: SelectableStrings;
|
|
||||||
metricNames: SelectableStrings;
|
|
||||||
variableOptionGroup: SelectableValue<string>;
|
|
||||||
showMeta: boolean;
|
showMeta: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +21,7 @@ const idValidationEvents: ValidationEvents = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class QueryEditor extends PureComponent<Props, State> {
|
export class QueryEditor extends PureComponent<Props, State> {
|
||||||
state: State = { regions: [], namespaces: [], metricNames: [], variableOptionGroup: {}, showMeta: false };
|
state: State = { showMeta: false };
|
||||||
|
|
||||||
static getDerivedStateFromProps(props: Props, state: State) {
|
static getDerivedStateFromProps(props: Props, state: State) {
|
||||||
const { query } = props;
|
const { query } = props;
|
||||||
@ -70,128 +65,32 @@ export class QueryEditor extends PureComponent<Props, State> {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { datasource } = this.props;
|
|
||||||
const variableOptionGroup = {
|
|
||||||
label: 'Template Variables',
|
|
||||||
options: this.props.datasource.variables.map(this.toOption),
|
|
||||||
};
|
|
||||||
Promise.all([datasource.metricFindQuery('regions()'), datasource.metricFindQuery('namespaces()')]).then(
|
|
||||||
([regions, namespaces]) => {
|
|
||||||
this.setState({
|
|
||||||
...this.state,
|
|
||||||
regions: [...regions, variableOptionGroup],
|
|
||||||
namespaces: [...namespaces, variableOptionGroup],
|
|
||||||
variableOptionGroup,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadMetricNames = async () => {
|
|
||||||
const { namespace, region } = this.props.query;
|
|
||||||
return this.props.datasource.metricFindQuery(`metrics(${namespace},${region})`).then(this.appendTemplateVariables);
|
|
||||||
};
|
|
||||||
|
|
||||||
appendTemplateVariables = (values: SelectableValue[]) => [
|
|
||||||
...values,
|
|
||||||
{ label: 'Template Variables', options: this.props.datasource.variables.map(this.toOption) },
|
|
||||||
];
|
|
||||||
|
|
||||||
toOption = (value: any) => ({ label: value, value });
|
|
||||||
|
|
||||||
onChange(query: CloudWatchQuery) {
|
onChange(query: CloudWatchQuery) {
|
||||||
const { onChange, onRunQuery } = this.props;
|
const { onChange, onRunQuery } = this.props;
|
||||||
onChange(query);
|
onChange(query);
|
||||||
onRunQuery();
|
onRunQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load dimension values based on current selected dimensions.
|
|
||||||
// Remove the new dimension key and all dimensions that has a wildcard as selected value
|
|
||||||
loadDimensionValues = (newKey: string) => {
|
|
||||||
const { datasource, query } = this.props;
|
|
||||||
const { [newKey]: value, ...dim } = query.dimensions;
|
|
||||||
const newDimensions = Object.entries(dim).reduce(
|
|
||||||
(result, [key, value]) => (value === '*' ? result : { ...result, [key]: value }),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
return datasource
|
|
||||||
.getDimensionValues(query.region, query.namespace, query.metricName, newKey, newDimensions)
|
|
||||||
.then(values => (values.length ? [{ value: '*', text: '*', label: '*' }, ...values] : values))
|
|
||||||
.then(this.appendTemplateVariables);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { query, datasource, onChange, onRunQuery, data } = this.props;
|
const { data, query, onRunQuery } = this.props;
|
||||||
const { regions, namespaces, variableOptionGroup: variableOptionGroup, showMeta } = this.state;
|
const { showMeta } = this.state;
|
||||||
const metaDataExist = data && Object.values(data).length && data.state === 'Done';
|
const metaDataExist = data && Object.values(data).length && data.state === 'Done';
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<QueryInlineField label="Region">
|
<QueryFieldsEditor {...this.props}></QueryFieldsEditor>
|
||||||
<Segment
|
|
||||||
value={query.region}
|
|
||||||
placeholder="Select region"
|
|
||||||
options={regions}
|
|
||||||
allowCustomValue
|
|
||||||
onChange={({ value: region }) => this.onChange({ ...query, region })}
|
|
||||||
/>
|
|
||||||
</QueryInlineField>
|
|
||||||
|
|
||||||
{query.expression.length === 0 && (
|
|
||||||
<>
|
|
||||||
<QueryInlineField label="Namespace">
|
|
||||||
<Segment
|
|
||||||
value={query.namespace}
|
|
||||||
placeholder="Select namespace"
|
|
||||||
allowCustomValue
|
|
||||||
options={namespaces}
|
|
||||||
onChange={({ value: namespace }) => this.onChange({ ...query, namespace })}
|
|
||||||
/>
|
|
||||||
</QueryInlineField>
|
|
||||||
|
|
||||||
<QueryInlineField label="Metric Name">
|
|
||||||
<SegmentAsync
|
|
||||||
value={query.metricName}
|
|
||||||
placeholder="Select metric name"
|
|
||||||
allowCustomValue
|
|
||||||
loadOptions={this.loadMetricNames}
|
|
||||||
onChange={({ value: metricName }) => this.onChange({ ...query, metricName })}
|
|
||||||
/>
|
|
||||||
</QueryInlineField>
|
|
||||||
|
|
||||||
<QueryInlineField label="Stats">
|
|
||||||
<Stats
|
|
||||||
stats={datasource.standardStatistics.map(this.toOption)}
|
|
||||||
values={query.statistics}
|
|
||||||
onChange={statistics => this.onChange({ ...query, statistics })}
|
|
||||||
variableOptionGroup={variableOptionGroup}
|
|
||||||
/>
|
|
||||||
</QueryInlineField>
|
|
||||||
|
|
||||||
<QueryInlineField label="Dimensions">
|
|
||||||
<Dimensions
|
|
||||||
dimensions={query.dimensions}
|
|
||||||
onChange={dimensions => this.onChange({ ...query, dimensions })}
|
|
||||||
loadKeys={() =>
|
|
||||||
datasource.getDimensionKeys(query.namespace, query.region).then(this.appendTemplateVariables)
|
|
||||||
}
|
|
||||||
loadValues={this.loadDimensionValues}
|
|
||||||
/>
|
|
||||||
</QueryInlineField>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{query.statistics.length <= 1 && (
|
{query.statistics.length <= 1 && (
|
||||||
<div className="gf-form-inline">
|
<div className="gf-form-inline">
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
<QueryField
|
<QueryField
|
||||||
className="query-keyword"
|
|
||||||
label="Id"
|
label="Id"
|
||||||
tooltip="Id can include numbers, letters, and underscore, and must start with a lowercase letter."
|
tooltip="Id can include numbers, letters, and underscore, and must start with a lowercase letter."
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
className="gf-form-input width-8"
|
className="gf-form-input width-8"
|
||||||
onBlur={onRunQuery}
|
onBlur={onRunQuery}
|
||||||
onChange={(event: ChangeEvent<HTMLInputElement>) => onChange({ ...query, id: event.target.value })}
|
onChange={(event: ChangeEvent<HTMLInputElement>) =>
|
||||||
|
this.onChange({ ...query, id: event.target.value })
|
||||||
|
}
|
||||||
validationEvents={idValidationEvents}
|
validationEvents={idValidationEvents}
|
||||||
value={query.id || ''}
|
value={query.id || ''}
|
||||||
/>
|
/>
|
||||||
@ -208,7 +107,7 @@ export class QueryEditor extends PureComponent<Props, State> {
|
|||||||
onBlur={onRunQuery}
|
onBlur={onRunQuery}
|
||||||
value={query.expression || ''}
|
value={query.expression || ''}
|
||||||
onChange={(event: ChangeEvent<HTMLInputElement>) =>
|
onChange={(event: ChangeEvent<HTMLInputElement>) =>
|
||||||
onChange({ ...query, expression: event.target.value })
|
this.onChange({ ...query, expression: event.target.value })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</QueryField>
|
</QueryField>
|
||||||
@ -217,19 +116,20 @@ export class QueryEditor extends PureComponent<Props, State> {
|
|||||||
)}
|
)}
|
||||||
<div className="gf-form-inline">
|
<div className="gf-form-inline">
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
<QueryField className="query-keyword" label="Period" tooltip="Minimum interval between points in seconds">
|
<QueryField label="Period" tooltip="Minimum interval between points in seconds">
|
||||||
<Input
|
<Input
|
||||||
className="gf-form-input width-8"
|
className="gf-form-input width-8"
|
||||||
value={query.period || ''}
|
value={query.period || ''}
|
||||||
placeholder="auto"
|
placeholder="auto"
|
||||||
onBlur={onRunQuery}
|
onBlur={onRunQuery}
|
||||||
onChange={(event: ChangeEvent<HTMLInputElement>) => onChange({ ...query, period: event.target.value })}
|
onChange={(event: ChangeEvent<HTMLInputElement>) =>
|
||||||
|
this.onChange({ ...query, period: event.target.value })
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</QueryField>
|
</QueryField>
|
||||||
</div>
|
</div>
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
<QueryField
|
<QueryField
|
||||||
className="query-keyword"
|
|
||||||
label="Alias"
|
label="Alias"
|
||||||
tooltip="Alias replacement variables: {{metric}}, {{stat}}, {{namespace}}, {{region}}, {{period}}, {{label}}, {{YOUR_DIMENSION_NAME}}"
|
tooltip="Alias replacement variables: {{metric}}, {{stat}}, {{namespace}}, {{region}}, {{period}}, {{label}}, {{YOUR_DIMENSION_NAME}}"
|
||||||
>
|
>
|
||||||
@ -247,7 +147,6 @@ export class QueryEditor extends PureComponent<Props, State> {
|
|||||||
onClick={() =>
|
onClick={() =>
|
||||||
metaDataExist &&
|
metaDataExist &&
|
||||||
this.setState({
|
this.setState({
|
||||||
...this.state,
|
|
||||||
showMeta: !showMeta,
|
showMeta: !showMeta,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,144 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { SelectableValue } from '@grafana/data';
|
||||||
|
import { Segment, SegmentAsync } from '@grafana/ui';
|
||||||
|
import { CloudWatchQuery, SelectableStrings } from '../types';
|
||||||
|
import CloudWatchDatasource from '../datasource';
|
||||||
|
import { Stats, Dimensions, QueryInlineField } from './';
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
query: CloudWatchQuery;
|
||||||
|
datasource: CloudWatchDatasource;
|
||||||
|
onRunQuery?: () => void;
|
||||||
|
onChange: (value: CloudWatchQuery) => void;
|
||||||
|
hideWilcard?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
regions: SelectableStrings;
|
||||||
|
namespaces: SelectableStrings;
|
||||||
|
metricNames: SelectableStrings;
|
||||||
|
variableOptionGroup: SelectableValue<string>;
|
||||||
|
showMeta: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function QueryFieldsEditor({
|
||||||
|
query,
|
||||||
|
datasource,
|
||||||
|
onChange,
|
||||||
|
onRunQuery = () => {},
|
||||||
|
hideWilcard = false,
|
||||||
|
}: React.PropsWithChildren<Props>) {
|
||||||
|
const [state, setState] = useState<State>({
|
||||||
|
regions: [],
|
||||||
|
namespaces: [],
|
||||||
|
metricNames: [],
|
||||||
|
variableOptionGroup: {},
|
||||||
|
showMeta: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const variableOptionGroup = {
|
||||||
|
label: 'Template Variables',
|
||||||
|
options: datasource.variables.map(toOption),
|
||||||
|
};
|
||||||
|
|
||||||
|
Promise.all([datasource.metricFindQuery('regions()'), datasource.metricFindQuery('namespaces()')]).then(
|
||||||
|
([regions, namespaces]) => {
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
regions: [...regions, variableOptionGroup],
|
||||||
|
namespaces: [...namespaces, variableOptionGroup],
|
||||||
|
variableOptionGroup,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const loadMetricNames = async () => {
|
||||||
|
const { namespace, region } = query;
|
||||||
|
return datasource.metricFindQuery(`metrics(${namespace},${region})`).then(appendTemplateVariables);
|
||||||
|
};
|
||||||
|
|
||||||
|
const appendTemplateVariables = (values: SelectableValue[]) => [
|
||||||
|
...values,
|
||||||
|
{ label: 'Template Variables', options: datasource.variables.map(toOption) },
|
||||||
|
];
|
||||||
|
|
||||||
|
const toOption = (value: any) => ({ label: value, value });
|
||||||
|
|
||||||
|
const onQueryChange = (query: CloudWatchQuery) => {
|
||||||
|
onChange(query);
|
||||||
|
onRunQuery();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load dimension values based on current selected dimensions.
|
||||||
|
// Remove the new dimension key and all dimensions that has a wildcard as selected value
|
||||||
|
const loadDimensionValues = (newKey: string) => {
|
||||||
|
const { [newKey]: value, ...dim } = query.dimensions;
|
||||||
|
const newDimensions = Object.entries(dim).reduce(
|
||||||
|
(result, [key, value]) => (value === '*' ? result : { ...result, [key]: value }),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
return datasource
|
||||||
|
.getDimensionValues(query.region, query.namespace, query.metricName, newKey, newDimensions)
|
||||||
|
.then(values => (values.length ? [{ value: '*', text: '*', label: '*' }, ...values] : values))
|
||||||
|
.then(appendTemplateVariables);
|
||||||
|
};
|
||||||
|
|
||||||
|
const { regions, namespaces, variableOptionGroup } = state;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<QueryInlineField label="Region">
|
||||||
|
<Segment
|
||||||
|
value={query.region}
|
||||||
|
placeholder="Select region"
|
||||||
|
options={regions}
|
||||||
|
allowCustomValue
|
||||||
|
onChange={({ value: region }) => onChange({ ...query, region })}
|
||||||
|
/>
|
||||||
|
</QueryInlineField>
|
||||||
|
|
||||||
|
{query.expression.length === 0 && (
|
||||||
|
<>
|
||||||
|
<QueryInlineField label="Namespace">
|
||||||
|
<Segment
|
||||||
|
value={query.namespace}
|
||||||
|
placeholder="Select namespace"
|
||||||
|
allowCustomValue
|
||||||
|
options={namespaces}
|
||||||
|
onChange={({ value: namespace }) => onChange({ ...query, namespace })}
|
||||||
|
/>
|
||||||
|
</QueryInlineField>
|
||||||
|
|
||||||
|
<QueryInlineField label="Metric Name">
|
||||||
|
<SegmentAsync
|
||||||
|
value={query.metricName}
|
||||||
|
placeholder="Select metric name"
|
||||||
|
allowCustomValue
|
||||||
|
loadOptions={loadMetricNames}
|
||||||
|
onChange={({ value: metricName }) => onChange({ ...query, metricName })}
|
||||||
|
/>
|
||||||
|
</QueryInlineField>
|
||||||
|
|
||||||
|
<QueryInlineField label="Stats">
|
||||||
|
<Stats
|
||||||
|
stats={datasource.standardStatistics.map(toOption)}
|
||||||
|
values={query.statistics}
|
||||||
|
onChange={statistics => onQueryChange({ ...query, statistics })}
|
||||||
|
variableOptionGroup={variableOptionGroup}
|
||||||
|
/>
|
||||||
|
</QueryInlineField>
|
||||||
|
|
||||||
|
<QueryInlineField label="Dimensions">
|
||||||
|
<Dimensions
|
||||||
|
dimensions={query.dimensions}
|
||||||
|
onChange={dimensions => onQueryChange({ ...query, dimensions })}
|
||||||
|
loadKeys={() => datasource.getDimensionKeys(query.namespace, query.region).then(appendTemplateVariables)}
|
||||||
|
loadValues={loadDimensionValues}
|
||||||
|
/>
|
||||||
|
</QueryInlineField>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -2,3 +2,4 @@ export { Stats } from './Stats';
|
|||||||
export { Dimensions } from './Dimensions';
|
export { Dimensions } from './Dimensions';
|
||||||
export { QueryInlineField, QueryField } from './Forms';
|
export { QueryInlineField, QueryField } from './Forms';
|
||||||
export { Alias } from './Alias';
|
export { Alias } from './Alias';
|
||||||
|
export { QueryFieldsEditor } from './QueryFieldsEditor';
|
||||||
|
@ -3,12 +3,9 @@ import { DataSourcePlugin } from '@grafana/data';
|
|||||||
import { ConfigEditor } from './components/ConfigEditor';
|
import { ConfigEditor } from './components/ConfigEditor';
|
||||||
import { QueryEditor } from './components/QueryEditor';
|
import { QueryEditor } from './components/QueryEditor';
|
||||||
import CloudWatchDatasource from './datasource';
|
import CloudWatchDatasource from './datasource';
|
||||||
|
import { CloudWatchAnnotationsQueryCtrl } from './annotations_query_ctrl';
|
||||||
import { CloudWatchJsonData, CloudWatchQuery } from './types';
|
import { CloudWatchJsonData, CloudWatchQuery } from './types';
|
||||||
|
|
||||||
class CloudWatchAnnotationsQueryCtrl {
|
|
||||||
static templateUrl = 'partials/annotations.editor.html';
|
|
||||||
}
|
|
||||||
|
|
||||||
export const plugin = new DataSourcePlugin<CloudWatchDatasource, CloudWatchQuery, CloudWatchJsonData>(
|
export const plugin = new DataSourcePlugin<CloudWatchDatasource, CloudWatchQuery, CloudWatchJsonData>(
|
||||||
CloudWatchDatasource
|
CloudWatchDatasource
|
||||||
)
|
)
|
||||||
|
@ -1,18 +1,5 @@
|
|||||||
<cloudwatch-query-parameter target="ctrl.annotation" datasource="ctrl.datasource"></cloudwatch-query-parameter>
|
<cloudwatch-annotation-query-editor
|
||||||
|
datasource="ctrl.datasource"
|
||||||
<div class="editor-row" style="padding: 2rem 0">
|
on-change="ctrl.onChange"
|
||||||
<div class="section">
|
query="ctrl.annotation"
|
||||||
<h5>Prefix matching</h5>
|
></cloudwatch-annotation-query-editor>
|
||||||
<div class="gf-form-inline">
|
|
||||||
<gf-form-switch class="gf-form" label="Enable" checked="ctrl.annotation.prefixMatching" switch-class="max-width-6"></gf-form-switch>
|
|
||||||
<div class="gf-form" ng-if="ctrl.annotation.prefixMatching">
|
|
||||||
<span class="gf-form-label">Action</span>
|
|
||||||
<input type="text" class="gf-form-input" ng-model='ctrl.annotation.actionPrefix'></input>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form" ng-if="ctrl.annotation.prefixMatching">
|
|
||||||
<span class="gf-form-label">Alarm Name</span>
|
|
||||||
<input type="text" class="gf-form-input" ng-model='ctrl.annotation.alarmNamePrefix'></input>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
@ -13,6 +13,12 @@ export interface CloudWatchQuery extends DataQuery {
|
|||||||
matchExact: boolean;
|
matchExact: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AnnotationQuery extends CloudWatchQuery {
|
||||||
|
prefixMatching: boolean;
|
||||||
|
actionPrefix: string;
|
||||||
|
alarmNamePrefix: string;
|
||||||
|
}
|
||||||
|
|
||||||
export type SelectableStrings = Array<SelectableValue<string>>;
|
export type SelectableStrings = Array<SelectableValue<string>>;
|
||||||
|
|
||||||
export interface CloudWatchJsonData extends DataSourceJsonData {
|
export interface CloudWatchJsonData extends DataSourceJsonData {
|
||||||
|
Loading…
Reference in New Issue
Block a user