Cloud Monitoring: Use new annotation API (#49026)

* remove angular code

* format annotation on backend

* format time with time type instead of string

* update annotation query tests

* update get alignment data function

* update annotation query editor

* add annotation query editor test

* update struct

* add tests

* remove extracted function

* remove non-null assertion

* remove stray commented out console.log

* fix jest haste map warning

* add alignment period

* add AnnotationMetricQuery type
This commit is contained in:
Kevin Yu
2022-05-19 13:52:52 -07:00
committed by GitHub
parent 26e98a6f1b
commit 0a95d493e3
18 changed files with 455 additions and 268 deletions

View File

@ -1,34 +1,29 @@
import React from 'react';
import React, { useState } from 'react';
import { useDebounce } from 'react-use';
import { SelectableValue, toOption } from '@grafana/data';
import { TemplateSrv } from '@grafana/runtime';
import { LegacyForms } from '@grafana/ui';
import { QueryEditorProps, toOption } from '@grafana/data';
import { Input } from '@grafana/ui';
import { INPUT_WIDTH } from '../constants';
import CloudMonitoringDatasource from '../datasource';
import { AnnotationTarget, EditorMode, MetricDescriptor, MetricKind } from '../types';
import {
EditorMode,
MetricKind,
AnnotationMetricQuery,
CloudMonitoringOptions,
CloudMonitoringQuery,
AlignmentTypes,
} from '../types';
import { AnnotationsHelp, LabelFilter, Metrics, Project, QueryEditorRow } from './';
import { MetricQueryEditor } from './MetricQueryEditor';
const { Input } = LegacyForms;
import { AnnotationsHelp, QueryEditorRow } from './';
export interface Props {
refId: string;
onQueryChange: (target: AnnotationTarget) => void;
target: AnnotationTarget;
datasource: CloudMonitoringDatasource;
templateSrv: TemplateSrv;
}
export type Props = QueryEditorProps<CloudMonitoringDatasource, CloudMonitoringQuery, CloudMonitoringOptions>;
interface State extends AnnotationTarget {
variableOptionGroup: SelectableValue<string>;
variableOptions: Array<SelectableValue<string>>;
labels: any;
[key: string]: any;
}
const DefaultTarget: State = {
export const defaultQuery: (datasource: CloudMonitoringDatasource) => AnnotationMetricQuery = (datasource) => ({
editorMode: EditorMode.Visual,
projectName: '',
projectName: datasource.getDefaultProject(),
projects: [],
metricType: '',
filters: [],
@ -40,112 +35,68 @@ const DefaultTarget: State = {
labels: {},
variableOptionGroup: {},
variableOptions: [],
};
query: '',
crossSeriesReducer: 'REDUCE_NONE',
perSeriesAligner: AlignmentTypes.ALIGN_NONE,
alignmentPeriod: 'grafana-auto',
});
export class AnnotationQueryEditor extends React.Component<Props, State> {
state: State = DefaultTarget;
async UNSAFE_componentWillMount() {
// Unfortunately, migrations like this need to go UNSAFE_componentWillMount. As soon as there's
// migration hook for this module.ts, we can do the migrations there instead.
const { target, datasource } = this.props;
if (!target.projectName) {
target.projectName = datasource.getDefaultProject();
}
const variableOptionGroup = {
label: 'Template Variables',
options: datasource.getVariables().map(toOption),
};
const projects = await datasource.getProjects();
this.setState({
variableOptionGroup,
variableOptions: variableOptionGroup.options,
...target,
projects,
});
datasource
.getLabels(target.metricType, target.projectName, target.refId)
.then((labels) => this.setState({ labels }));
}
onMetricTypeChange = ({ valueType, metricKind, type, unit }: MetricDescriptor) => {
const { onQueryChange, datasource } = this.props;
this.setState(
{
metricType: type,
unit,
valueType,
metricKind,
},
() => {
onQueryChange(this.state);
}
);
datasource.getLabels(type, this.state.refId, this.state.projectName).then((labels) => this.setState({ labels }));
export const AnnotationQueryEditor = (props: Props) => {
const { datasource, query, onRunQuery, data, onChange } = props;
const meta = data?.series.length ? data?.series[0].meta : {};
const customMetaData = meta?.custom ?? {};
const metricQuery = { ...defaultQuery(datasource), ...query.metricQuery };
const [title, setTitle] = useState(metricQuery.title || '');
const [text, setText] = useState(metricQuery.text || '');
const variableOptionGroup = {
label: 'Template Variables',
options: datasource.getVariables().map(toOption),
};
onChange(prop: string, value: string | string[]) {
this.setState({ [prop]: value }, () => {
this.props.onQueryChange(this.state);
});
}
const handleQueryChange = (metricQuery: AnnotationMetricQuery) => onChange({ ...query, metricQuery });
const handleTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setTitle(e.target.value);
};
const handleTextChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setText(e.target.value);
};
render() {
const { metricType, projectName, filters, title, text, variableOptionGroup, labels, variableOptions } = this.state;
const { datasource } = this.props;
useDebounce(
() => {
onChange({ ...query, metricQuery: { ...metricQuery, title } });
},
1000,
[title, onChange]
);
useDebounce(
() => {
onChange({ ...query, metricQuery: { ...metricQuery, text } });
},
1000,
[text, onChange]
);
return (
<>
<Project
refId={this.props.refId}
templateVariableOptions={variableOptions}
datasource={datasource}
projectName={projectName || datasource.getDefaultProject()}
onChange={(value) => this.onChange('projectName', value)}
/>
<Metrics
refId={this.props.refId}
projectName={projectName}
metricType={metricType}
templateSrv={datasource.templateSrv}
datasource={datasource}
templateVariableOptions={variableOptions}
onChange={(metric) => this.onMetricTypeChange(metric)}
>
{(metric) => (
<>
<LabelFilter
labels={labels}
filters={filters}
onChange={(value) => this.onChange('filters', value)}
variableOptionGroup={variableOptionGroup}
/>
</>
)}
</Metrics>
return (
<>
<MetricQueryEditor
refId={query.refId}
variableOptionGroup={variableOptionGroup}
customMetaData={customMetaData}
onChange={handleQueryChange}
onRunQuery={onRunQuery}
datasource={datasource}
query={metricQuery}
/>
<QueryEditorRow label="Title">
<Input
type="text"
className="gf-form-input width-20"
value={title}
onChange={(e) => this.onChange('title', e.target.value)}
/>
</QueryEditorRow>
<QueryEditorRow label="Text">
<Input
type="text"
className="gf-form-input width-20"
value={text}
onChange={(e) => this.onChange('text', e.target.value)}
/>
</QueryEditorRow>
<QueryEditorRow label="Title" htmlFor="annotation-query-title">
<Input id="annotation-query-title" value={title} width={INPUT_WIDTH} onChange={handleTitleChange} />
</QueryEditorRow>
<AnnotationsHelp />
</>
);
}
}
<QueryEditorRow label="Text" htmlFor="annotation-query-text">
<Input id="annotation-query-text" value={text} width={INPUT_WIDTH} onChange={handleTextChange} />
</QueryEditorRow>
<AnnotationsHelp />
</>
);
};