Alerting: adding query editor when creating threshold rule. (#33123)

* fix viz

* add datasource picker on query rows in mixed mode

* add timerange, handle add/remove queryrunners

* multiqueryrunner test

* trying things out.

* adding another test to verify running a induvidual query runner will update multirunner.

* cleaned up tests a bit.

* draft version working ok.

* fixing so we base the refId from request targets.

* reenable adding expression

* layout fixes for alerting page

* some cleanup

* cleaning up code that we won't use

* changed so we don't display the time range if params not passed.

* remove unused things in querygroup

* changed button to type button.

* removed timerange from dataQuery and removed multiquery runner.

* minor refactoring.

* renamed callback function to make it more clear what it does.

* renamed droppable area.

* changed so we only display the query editor when selecting threshold.

* removed the refresh picker.

* revert

* wip

* extending with data query.

* timerange fixes

* it is now possible to add grafana queries.

* removed unused type.

* removed expect import.

* added docs.

* moved range converting methods to rangeUtil.

* clean up some typings, remove file

* making sure we don't blow up on component being unmounted.

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
This commit is contained in:
Peter Holmberg
2021-04-21 13:57:17 +02:00
committed by GitHub
parent a151dfaa04
commit 569fb3f112
27 changed files with 512 additions and 315 deletions

View File

@@ -4,7 +4,7 @@ import classNames from 'classnames';
import { has, cloneDeep } from 'lodash';
// Utils & Services
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { AngularComponent, getAngularLoader, getTemplateSrv } from '@grafana/runtime';
import { AngularComponent, getAngularLoader } from '@grafana/runtime';
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { ErrorBoundaryAlert, HorizontalGroup, InfoBox } from '@grafana/ui';
import {
@@ -36,6 +36,8 @@ interface Props {
dsSettings: DataSourceInstanceSettings;
id: string;
index: number;
timeRange?: TimeRange;
onChangeTimeRange?: (timeRange: TimeRange) => void;
onAddQuery: (query?: DataQuery) => void;
onRemoveQuery: (query: DataQuery) => void;
onChange: (query: DataQuery) => void;
@@ -301,17 +303,18 @@ export class QueryEditorRow extends PureComponent<Props, State> {
};
renderTitle = (props: QueryOperationRowRenderProps) => {
const { query, dsSettings, onChange, queries } = this.props;
const dataSourceName = dsSettings.meta.mixed
? getTemplateSrv().replace(this.getQueryDataSourceIdentifier() ?? '')
: undefined;
const { query, dsSettings, onChange, queries, onChangeTimeRange, timeRange } = this.props;
const { datasource } = this.state;
const isDisabled = query.hide;
return (
<QueryEditorRowTitle
query={query}
queries={queries}
dataSourceName={dataSourceName}
onTimeRangeChange={onChangeTimeRange}
timeRange={timeRange}
inMixedMode={dsSettings.meta.mixed}
dataSourceName={datasource!.name}
disabled={isDisabled}
onClick={(e) => this.onToggleEditMode(e, props)}
onChange={onChange}

View File

@@ -1,14 +1,19 @@
import React, { useState } from 'react';
import { css, cx } from '@emotion/css';
import { DataQuery, GrafanaTheme } from '@grafana/data';
import { FieldValidationMessage, Icon, Input, stylesFactory, useTheme } from '@grafana/ui';
import { DataQuery, DataSourceInstanceSettings, GrafanaTheme, TimeRange } from '@grafana/data';
import { DataSourcePicker } from '@grafana/runtime';
import { Icon, Input, stylesFactory, useTheme, FieldValidationMessage, TimeRangeInput } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import { ExpressionDatasourceID } from '../../expressions/ExpressionDatasource';
export interface Props {
query: DataQuery;
queries: DataQuery[];
dataSourceName?: string;
dataSourceName: string;
inMixedMode?: boolean;
disabled?: boolean;
timeRange?: TimeRange;
onTimeRangeChange?: (timeRange: TimeRange) => void;
onChange: (query: DataQuery) => void;
onClick: (e: React.MouseEvent) => void;
collapsedText: string | null;
@@ -16,11 +21,14 @@ export interface Props {
export const QueryEditorRowTitle: React.FC<Props> = ({
dataSourceName,
inMixedMode,
disabled,
query,
queries,
onClick,
onChange,
onTimeRangeChange,
timeRange,
collapsedText,
}) => {
const theme = useTheme();
@@ -83,6 +91,10 @@ export const QueryEditorRowTitle: React.FC<Props> = ({
event.target.select();
};
const onDataSourceChange = (dataSource: DataSourceInstanceSettings) => {
onChange({ ...query, datasource: dataSource.name });
};
return (
<div className={styles.wrapper}>
{!isEditing && (
@@ -114,7 +126,17 @@ export const QueryEditorRowTitle: React.FC<Props> = ({
{validationError && <FieldValidationMessage horizontal>{validationError}</FieldValidationMessage>}
</>
)}
{dataSourceName && <em className={styles.contextInfo}> ({dataSourceName})</em>}
{inMixedMode && (
<div style={{ display: 'flex', marginLeft: '8px' }}>
{query.datasource !== ExpressionDatasourceID && (
<>
<DataSourcePicker current={dataSourceName} onChange={onDataSourceChange} />
{onTimeRangeChange && timeRange && <TimeRangeInput onChange={onTimeRangeChange} value={timeRange} />}
</>
)}
</div>
)}
{dataSourceName && !inMixedMode && <em className={styles.contextInfo}> ({dataSourceName})</em>}
{disabled && <em className={styles.contextInfo}> Disabled</em>}
{collapsedText && (

View File

@@ -28,12 +28,6 @@ export class QueryEditorRows extends PureComponent<Props> {
onChangeQuery(query: DataQuery, index: number) {
const { queries, onQueriesChange } = this.props;
const old = queries[index];
if (old.datasource) {
query.datasource = old.datasource;
}
// update query in array
onQueriesChange(
queries.map((item, itemIndex) => {

View File

@@ -45,6 +45,7 @@ interface State {
scrollTop: number;
data: PanelData;
isHelpOpen: boolean;
defaultDataSource?: DataSourceApi;
}
export class QueryGroup extends PureComponent<Props, State> {
@@ -76,7 +77,8 @@ export class QueryGroup extends PureComponent<Props, State> {
try {
const ds = await this.dataSourceSrv.get(options.dataSource.name);
const dsSettings = this.dataSourceSrv.getInstanceSettings(options.dataSource.name);
this.setState({ dataSource: ds, dsSettings });
const defaultDataSource = await this.dataSourceSrv.get();
this.setState({ dataSource: ds, dsSettings, defaultDataSource });
} catch (error) {
console.log('failed to load data source', error);
}
@@ -140,15 +142,23 @@ export class QueryGroup extends PureComponent<Props, State> {
};
onAddQueryClick = () => {
if (this.state.dsSettings?.meta.mixed) {
this.setState({ isAddingMixed: true });
return;
}
this.onChange({ queries: addQuery(this.props.options.queries) });
const { options } = this.props;
this.onChange({ queries: addQuery(options.queries, this.newQuery()) });
this.onScrollBottom();
};
newQuery(): Partial<DataQuery> {
const { dsSettings, defaultDataSource } = this.state;
if (!dsSettings?.meta.mixed) {
return {};
}
return {
datasource: defaultDataSource?.name,
};
}
onChange(changedProps: Partial<QueryGroupOptions>) {
this.props.onOptionsChange({
...this.props.options,
@@ -320,7 +330,6 @@ export class QueryGroup extends PureComponent<Props, State> {
Query
</Button>
)}
{isAddingMixed && this.renderMixedPicker()}
{config.expressionsEnabled && this.isExpressionsSupported(dsSettings) && (
<Tooltip content="Experimental feature: queries could stop working in next version" placement="right">
<Button