grafana/public/app/features/alerting/NextGenAlertingPage.tsx
Peter Holmberg 12dcba5d0b
AlertingNG: Test definition (#30886)
* break out new and edit

* changed model to match new model in backend

* AlertingNG: API modifications (#30683)

* Fix API consistency

* Change eval alert definition to POST request

* Fix eval endpoint to accept custom now parameter

* Change JSON input property for create/update endpoints

* model adjustments

* set mixed datasource, fix put url

* update snapshots

* run test response through converters

* remove edit and add landing page

* remove snapshot tests ans snapshots

* wrap linkbutton in array

* different approaches to massage data

* get instead of post

* use function to return instances data

* hook up test button in view

* test endpoint for not saved definitions

* function that return query options

* Chore: fixes strict error

* hide ng alert button

* typings

* fix setAlertDef error

* better message when you have queries but no data

* NGAlert: Refactoring that handles cleaning up state (#31087)

* Chore: some refactorings of state

* Chore: reduces strict null errors

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
Co-authored-by: Sofia Papagiannaki <sofia@grafana.com>
Co-authored-by: Hugo Häggmark <hugo.haggmark@gmail.com>
Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
2021-02-15 13:56:59 +01:00

196 lines
5.6 KiB
TypeScript

import React, { FormEvent, PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import { css } from 'emotion';
import { GrafanaTheme, SelectableValue } from '@grafana/data';
import { PageToolbar, stylesFactory, ToolbarButton } from '@grafana/ui';
import { config } from 'app/core/config';
import { SplitPaneWrapper } from 'app/core/components/SplitPaneWrapper/SplitPaneWrapper';
import { AlertingQueryEditor } from './components/AlertingQueryEditor';
import { AlertDefinitionOptions } from './components/AlertDefinitionOptions';
import { AlertingQueryPreview } from './components/AlertingQueryPreview';
import {
cleanUpDefinitionState,
createAlertDefinition,
evaluateAlertDefinition,
evaluateNotSavedAlertDefinition,
getAlertDefinition,
onRunQueries,
updateAlertDefinition,
updateAlertDefinitionOption,
updateAlertDefinitionUiState,
} from './state/actions';
import { getRouteParamsId } from 'app/core/selectors/location';
import { StoreState } from 'app/types';
function mapStateToProps(state: StoreState) {
const pageId = getRouteParamsId(state.location);
return {
uiState: state.alertDefinition.uiState,
getQueryOptions: state.alertDefinition.getQueryOptions,
queryRunner: state.alertDefinition.queryRunner,
getInstances: state.alertDefinition.getInstances,
alertDefinition: state.alertDefinition.alertDefinition,
pageId: (pageId as string) ?? '',
};
}
const mapDispatchToProps = {
updateAlertDefinitionUiState,
updateAlertDefinitionOption,
evaluateAlertDefinition,
updateAlertDefinition,
createAlertDefinition,
getAlertDefinition,
evaluateNotSavedAlertDefinition,
onRunQueries,
cleanUpDefinitionState,
};
const connector = connect(mapStateToProps, mapDispatchToProps);
interface OwnProps {
saveDefinition: typeof createAlertDefinition | typeof updateAlertDefinition;
}
type Props = OwnProps & ConnectedProps<typeof connector>;
class NextGenAlertingPageUnconnected extends PureComponent<Props> {
componentDidMount() {
const { getAlertDefinition, pageId } = this.props;
if (pageId) {
getAlertDefinition(pageId);
}
}
componentWillUnmount() {
this.props.cleanUpDefinitionState();
}
onChangeAlertOption = (event: FormEvent<HTMLElement>) => {
const formEvent = event as FormEvent<HTMLFormElement>;
this.props.updateAlertDefinitionOption({ [formEvent.currentTarget.name]: formEvent.currentTarget.value });
};
onChangeInterval = (interval: SelectableValue<number>) => {
this.props.updateAlertDefinitionOption({
intervalSeconds: interval.value,
});
};
onConditionChange = (condition: SelectableValue<string>) => {
this.props.updateAlertDefinitionOption({
condition: condition.value,
});
};
onSaveAlert = () => {
const { alertDefinition, createAlertDefinition, updateAlertDefinition } = this.props;
if (alertDefinition.uid) {
updateAlertDefinition();
} else {
createAlertDefinition();
}
};
onDiscard = () => {};
onTest = () => {
const { alertDefinition, evaluateAlertDefinition, evaluateNotSavedAlertDefinition } = this.props;
if (alertDefinition.uid) {
evaluateAlertDefinition();
} else {
evaluateNotSavedAlertDefinition();
}
};
renderToolbarActions() {
return [
<ToolbarButton variant="destructive" key="discard" onClick={this.onDiscard}>
Discard
</ToolbarButton>,
<ToolbarButton key="test" onClick={this.onTest}>
Test
</ToolbarButton>,
<ToolbarButton variant="primary" key="save" onClick={this.onSaveAlert}>
Save
</ToolbarButton>,
];
}
render() {
const {
alertDefinition,
getInstances,
uiState,
updateAlertDefinitionUiState,
queryRunner,
getQueryOptions,
onRunQueries,
} = this.props;
const styles = getStyles(config.theme);
const queryOptions = getQueryOptions();
return (
<div className={styles.wrapper}>
<PageToolbar title="Alert editor" pageIcon="bell">
{this.renderToolbarActions()}
</PageToolbar>
<div className={styles.splitPanesWrapper}>
<SplitPaneWrapper
leftPaneComponents={[
<AlertingQueryPreview
key="queryPreview"
queryRunner={queryRunner!} // if the queryRunner is undefined here somethings very wrong so it's ok to throw an unhandled error
getInstances={getInstances}
queries={queryOptions.queries}
onTest={this.onTest}
onRunQueries={onRunQueries}
/>,
<AlertingQueryEditor key="queryEditor" />,
]}
uiState={uiState}
updateUiState={updateAlertDefinitionUiState}
rightPaneComponents={
<AlertDefinitionOptions
alertDefinition={alertDefinition}
onChange={this.onChangeAlertOption}
onIntervalChange={this.onChangeInterval}
onConditionChange={this.onConditionChange}
queryOptions={queryOptions}
/>
}
/>
</div>
</div>
);
}
}
export default hot(module)(connector(NextGenAlertingPageUnconnected));
const getStyles = stylesFactory((theme: GrafanaTheme) => ({
wrapper: css`
width: calc(100% - 55px);
height: 100%;
position: fixed;
top: 0;
bottom: 0;
background: ${theme.colors.dashboardBg};
display: flex;
flex-direction: column;
`,
splitPanesWrapper: css`
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
position: relative;
`,
}));