grafana/public/app/features/alerting/components/AlertingQueryEditor.tsx

224 lines
5.9 KiB
TypeScript
Raw Normal View History

import React, { PureComponent } from 'react';
import { css } from '@emotion/css';
import {
DataQuery,
getDefaultRelativeTimeRange,
GrafanaTheme2,
LoadingState,
PanelData,
RelativeTimeRange,
} from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { Button, HorizontalGroup, Icon, stylesFactory, Tooltip } from '@grafana/ui';
import { config } from '@grafana/runtime';
import { AlertingQueryRows } from './AlertingQueryRows';
import { dataSource as expressionDatasource, ExpressionDatasourceUID } from '../../expressions/ExpressionDatasource';
import { getNextRefIdChar } from 'app/core/utils/query';
import { defaultCondition } from '../../expressions/utils/expressionTypes';
import { ExpressionQueryType } from '../../expressions/types';
import { GrafanaQuery } from 'app/types/unified-alerting-dto';
import { AlertingQueryRunner } from '../state/AlertingQueryRunner';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { isExpressionQuery } from 'app/features/expressions/guards';
interface Props {
value?: GrafanaQuery[];
onChange: (queries: GrafanaQuery[]) => void;
}
interface State {
panelDataByRefId: Record<string, PanelData>;
}
export class AlertingQueryEditor extends PureComponent<Props, State> {
private runner: AlertingQueryRunner;
constructor(props: Props) {
super(props);
this.state = { panelDataByRefId: {} };
this.runner = new AlertingQueryRunner();
}
componentDidMount() {
this.runner.get().subscribe((data) => {
this.setState({ panelDataByRefId: data });
});
}
componentWillUnmount() {
this.runner.destroy();
}
onRunQueries = () => {
const { value = [] } = this.props;
this.runner.run(value);
};
onCancelQueries = () => {
this.runner.cancel();
};
onDuplicateQuery = (query: GrafanaQuery) => {
const { onChange, value = [] } = this.props;
onChange(addQuery(value, query));
};
onNewAlertingQuery = () => {
const { onChange, value = [] } = this.props;
const defaultDataSource = getDatasourceSrv().getInstanceSettings('default');
if (!defaultDataSource) {
return;
}
onChange(
addQuery(value, {
datasourceUid: defaultDataSource.uid,
model: {
refId: '',
datasource: defaultDataSource.name,
},
})
);
};
onNewExpressionQuery = () => {
const { onChange, value = [] } = this.props;
onChange(
addQuery(value, {
datasourceUid: ExpressionDatasourceUID,
model: expressionDatasource.newQuery({
type: ExpressionQueryType.classic,
conditions: [defaultCondition],
}),
})
);
};
renderAddQueryRow(styles: ReturnType<typeof getStyles>) {
return (
<HorizontalGroup spacing="md" align="flex-start">
<Button
type="button"
icon="plus"
onClick={this.onNewAlertingQuery}
variant="secondary"
aria-label={selectors.components.QueryTab.addQuery}
>
Query
</Button>
{config.expressionsEnabled && (
<Tooltip content="Experimental feature: queries could stop working in next version" placement="right">
<Button
type="button"
icon="plus"
onClick={this.onNewExpressionQuery}
variant="secondary"
className={styles.expressionButton}
>
<span>Expression&nbsp;</span>
<Icon name="exclamation-triangle" className="muted" size="sm" />
</Button>
</Tooltip>
)}
</HorizontalGroup>
);
}
isRunning() {
const data = Object.values(this.state.panelDataByRefId).find((d) => Boolean(d));
return data?.state === LoadingState.Loading;
}
renderRunQueryButton() {
const isRunning = this.isRunning();
const styles = getStyles(config.theme2);
if (isRunning) {
return (
<div className={styles.runWrapper}>
<Button icon="fa fa-spinner" type="button" variant="destructive" onClick={this.onCancelQueries}>
Cancel
</Button>
</div>
);
}
return (
<div className={styles.runWrapper}>
<Button icon="sync" type="button" onClick={this.onRunQueries}>
Run queries
</Button>
</div>
);
}
render() {
const { value = [] } = this.props;
Alerting: visualizing data when creating/editing alerting rule. (#34191) * VizWrapper to handle some state * alertingqueryrunner first edition * added so we always set name and uid when changing datasource. * wip. * wip * added support for canceling requests. * util for getting time ranges for expression queries * remove logs, store data in state * merge from run branch * incremental commit * viz working * added structure for marble testing. * paddings and move viz picker * less height for viz, less width on rows * change so the expression buttons doesnt submit form. * fixed run button. * replaced mocks with implementation that will set default query + expression. * merge with run queries * fixed so we set a datasource name for the default expression rule. * improving expression guard. * lots of styling fixes for viz * adding placeholder for relative time range. * fixed story. * added basic structure to handle open/close of time range picker. * removed section from TimeOptions since it isn't used any where. * adding mapper and tests * move relativetimepicker to its own dir * added some simple tests. * changed test. * use relativetimerangeinput * redo state management * refactored the tests. * replace timerange with relativetimerange * wip * wip * did some refactoring. * refactored time option formatting. * added proper formatting and display of time range. * add relative time description, slight refactor of height * fixed incorrect import. * added validator and changed formatting. * removed unused dep. * reverted back to internal function. * fixed display of relative time range picker. * fixed failing tests. * fixed parsing issue. * fixed position of time range picker. * some more refactorings. * fixed validation of really big values. * added another test. * restored the step2 check. * fixed merge issue. * Update packages/grafana-ui/src/components/Forms/RadioButtonGroup/RadioButtonGroup.tsx Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com> * reverted change. * fixed merge conflict. * fixed todo. * sort some paddings * replace theme with theme2 Co-authored-by: Peter Holmberg <peter.hlmbrg@gmail.com> Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
2021-05-18 09:16:26 -05:00
const { panelDataByRefId } = this.state;
const styles = getStyles(config.theme2);
return (
<div className={styles.container}>
<AlertingQueryRows
Alerting: visualizing data when creating/editing alerting rule. (#34191) * VizWrapper to handle some state * alertingqueryrunner first edition * added so we always set name and uid when changing datasource. * wip. * wip * added support for canceling requests. * util for getting time ranges for expression queries * remove logs, store data in state * merge from run branch * incremental commit * viz working * added structure for marble testing. * paddings and move viz picker * less height for viz, less width on rows * change so the expression buttons doesnt submit form. * fixed run button. * replaced mocks with implementation that will set default query + expression. * merge with run queries * fixed so we set a datasource name for the default expression rule. * improving expression guard. * lots of styling fixes for viz * adding placeholder for relative time range. * fixed story. * added basic structure to handle open/close of time range picker. * removed section from TimeOptions since it isn't used any where. * adding mapper and tests * move relativetimepicker to its own dir * added some simple tests. * changed test. * use relativetimerangeinput * redo state management * refactored the tests. * replace timerange with relativetimerange * wip * wip * did some refactoring. * refactored time option formatting. * added proper formatting and display of time range. * add relative time description, slight refactor of height * fixed incorrect import. * added validator and changed formatting. * removed unused dep. * reverted back to internal function. * fixed display of relative time range picker. * fixed failing tests. * fixed parsing issue. * fixed position of time range picker. * some more refactorings. * fixed validation of really big values. * added another test. * restored the step2 check. * fixed merge issue. * Update packages/grafana-ui/src/components/Forms/RadioButtonGroup/RadioButtonGroup.tsx Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com> * reverted change. * fixed merge conflict. * fixed todo. * sort some paddings * replace theme with theme2 Co-authored-by: Peter Holmberg <peter.hlmbrg@gmail.com> Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
2021-05-18 09:16:26 -05:00
data={panelDataByRefId}
queries={value}
onQueriesChange={this.props.onChange}
onDuplicateQuery={this.onDuplicateQuery}
onRunQueries={this.onRunQueries}
/>
{this.renderAddQueryRow(styles)}
{this.renderRunQueryButton()}
</div>
);
}
}
const addQuery = (
queries: GrafanaQuery[],
queryToAdd: Pick<GrafanaQuery, 'model' | 'datasourceUid'>
): GrafanaQuery[] => {
const refId = getNextRefIdChar(queries);
const query: GrafanaQuery = {
...queryToAdd,
refId,
queryType: '',
model: {
...queryToAdd.model,
hide: false,
refId,
},
relativeTimeRange: defaultTimeRange(queryToAdd.model),
};
return [...queries, query];
};
const defaultTimeRange = (model: DataQuery): RelativeTimeRange | undefined => {
if (isExpressionQuery(model)) {
return;
}
Alerting: visualizing data when creating/editing alerting rule. (#34191) * VizWrapper to handle some state * alertingqueryrunner first edition * added so we always set name and uid when changing datasource. * wip. * wip * added support for canceling requests. * util for getting time ranges for expression queries * remove logs, store data in state * merge from run branch * incremental commit * viz working * added structure for marble testing. * paddings and move viz picker * less height for viz, less width on rows * change so the expression buttons doesnt submit form. * fixed run button. * replaced mocks with implementation that will set default query + expression. * merge with run queries * fixed so we set a datasource name for the default expression rule. * improving expression guard. * lots of styling fixes for viz * adding placeholder for relative time range. * fixed story. * added basic structure to handle open/close of time range picker. * removed section from TimeOptions since it isn't used any where. * adding mapper and tests * move relativetimepicker to its own dir * added some simple tests. * changed test. * use relativetimerangeinput * redo state management * refactored the tests. * replace timerange with relativetimerange * wip * wip * did some refactoring. * refactored time option formatting. * added proper formatting and display of time range. * add relative time description, slight refactor of height * fixed incorrect import. * added validator and changed formatting. * removed unused dep. * reverted back to internal function. * fixed display of relative time range picker. * fixed failing tests. * fixed parsing issue. * fixed position of time range picker. * some more refactorings. * fixed validation of really big values. * added another test. * restored the step2 check. * fixed merge issue. * Update packages/grafana-ui/src/components/Forms/RadioButtonGroup/RadioButtonGroup.tsx Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com> * reverted change. * fixed merge conflict. * fixed todo. * sort some paddings * replace theme with theme2 Co-authored-by: Peter Holmberg <peter.hlmbrg@gmail.com> Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
2021-05-18 09:16:26 -05:00
return getDefaultRelativeTimeRange();
};
const getStyles = stylesFactory((theme: GrafanaTheme2) => {
return {
container: css`
background-color: ${theme.colors.background.primary};
height: 100%;
`,
runWrapper: css`
margin-top: ${theme.spacing(1)};
`,
editorWrapper: css`
border: 1px solid ${theme.colors.border.medium};
border-radius: ${theme.shape.borderRadius()};
`,
expressionButton: css`
margin-right: ${theme.spacing(0.5)};
`,
};
});