mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
tests
This commit is contained in:
parent
5ac5a08e9e
commit
22510be450
@ -3,7 +3,7 @@ import { shallow } from 'enzyme';
|
|||||||
import AlertRuleItem, { Props } from './AlertRuleItem';
|
import AlertRuleItem, { Props } from './AlertRuleItem';
|
||||||
|
|
||||||
jest.mock('react-redux', () => ({
|
jest.mock('react-redux', () => ({
|
||||||
connect: params => params,
|
connect: () => params => params,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const setup = (propOverrides?: object) => {
|
const setup = (propOverrides?: object) => {
|
||||||
@ -23,6 +23,7 @@ const setup = (propOverrides?: object) => {
|
|||||||
search: '',
|
search: '',
|
||||||
togglePauseAlertRule: jest.fn(),
|
togglePauseAlertRule: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.assign(props, propOverrides);
|
Object.assign(props, propOverrides);
|
||||||
|
|
||||||
return shallow(<AlertRuleItem {...props} />);
|
return shallow(<AlertRuleItem {...props} />);
|
||||||
|
@ -15,7 +15,7 @@ class AlertRuleItem extends PureComponent<Props, any> {
|
|||||||
togglePaused = () => {
|
togglePaused = () => {
|
||||||
const { rule } = this.props;
|
const { rule } = this.props;
|
||||||
|
|
||||||
this.props.togglePauseAlertRule(rule.id, { paused: rule.state === 'paused' });
|
this.props.togglePauseAlertRule(rule.id, { paused: rule.state !== 'paused' });
|
||||||
};
|
};
|
||||||
|
|
||||||
renderText(text: string) {
|
renderText(text: string) {
|
||||||
|
@ -1,69 +1,159 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import moment from 'moment';
|
import { shallow } from 'enzyme';
|
||||||
import { AlertRuleList } from './AlertRuleList';
|
import AlertRuleList, { Props } from './AlertRuleList';
|
||||||
import { RootStore } from 'app/stores/RootStore/RootStore';
|
import { AlertRule, NavModel } from '../../types';
|
||||||
import { backendSrv, createNavTree } from 'test/mocks/common';
|
import appEvents from '../../core/app_events';
|
||||||
import { mount } from 'enzyme';
|
|
||||||
import toJson from 'enzyme-to-json';
|
|
||||||
|
|
||||||
describe('AlertRuleList', () => {
|
jest.mock('react-redux', () => ({
|
||||||
let page, store;
|
connect: () => params => params,
|
||||||
|
}));
|
||||||
|
|
||||||
beforeAll(() => {
|
jest.mock('../../core/app_events', () => ({
|
||||||
backendSrv.get.mockReturnValue(
|
emit: jest.fn(),
|
||||||
Promise.resolve([
|
}));
|
||||||
|
|
||||||
|
const setup = (propOverrides?: object) => {
|
||||||
|
const props: Props = {
|
||||||
|
navModel: {} as NavModel,
|
||||||
|
alertRules: [] as AlertRule[],
|
||||||
|
updateLocation: jest.fn(),
|
||||||
|
getAlertRulesAsync: jest.fn(),
|
||||||
|
setSearchQuery: jest.fn(),
|
||||||
|
stateFilter: '',
|
||||||
|
search: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.assign(props, propOverrides);
|
||||||
|
|
||||||
|
const wrapper = shallow(<AlertRuleList {...props} />);
|
||||||
|
|
||||||
|
return {
|
||||||
|
wrapper,
|
||||||
|
instance: wrapper.instance() as AlertRuleList,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Render', () => {
|
||||||
|
it('should render component', () => {
|
||||||
|
const { wrapper } = setup();
|
||||||
|
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render alert rules', () => {
|
||||||
|
const { wrapper } = setup({
|
||||||
|
alertRules: [
|
||||||
{
|
{
|
||||||
id: 11,
|
id: 1,
|
||||||
dashboardId: 58,
|
dashboardId: 7,
|
||||||
|
dashboardUid: 'ggHbN42mk',
|
||||||
|
dashboardSlug: 'alerting-with-testdata',
|
||||||
panelId: 3,
|
panelId: 3,
|
||||||
name: 'Panel Title alert',
|
name: 'TestData - Always OK',
|
||||||
state: 'ok',
|
state: 'ok',
|
||||||
newStateDate: moment()
|
newStateDate: '2018-09-04T10:01:01+02:00',
|
||||||
.subtract(5, 'minutes')
|
evalDate: '0001-01-01T00:00:00Z',
|
||||||
.format(),
|
|
||||||
evalData: {},
|
evalData: {},
|
||||||
executionError: '',
|
executionError: '',
|
||||||
url: 'd/ufkcofof/my-goal',
|
url: '/d/ggHbN42mk/alerting-with-testdata',
|
||||||
canEdit: true,
|
|
||||||
},
|
},
|
||||||
])
|
{
|
||||||
);
|
id: 3,
|
||||||
|
dashboardId: 7,
|
||||||
|
dashboardUid: 'ggHbN42mk',
|
||||||
|
dashboardSlug: 'alerting-with-testdata',
|
||||||
|
panelId: 3,
|
||||||
|
name: 'TestData - ok',
|
||||||
|
state: 'ok',
|
||||||
|
newStateDate: '2018-09-04T10:01:01+02:00',
|
||||||
|
evalDate: '0001-01-01T00:00:00Z',
|
||||||
|
evalData: {},
|
||||||
|
executionError: 'error',
|
||||||
|
url: '/d/ggHbN42mk/alerting-with-testdata',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
store = RootStore.create(
|
expect(wrapper).toMatchSnapshot();
|
||||||
{},
|
});
|
||||||
{
|
});
|
||||||
backendSrv: backendSrv,
|
|
||||||
navTree: createNavTree('alerting', 'alert-list'),
|
describe('Life cycle', () => {
|
||||||
}
|
describe('component did mount', () => {
|
||||||
);
|
it('should call fetchrules', () => {
|
||||||
|
const { instance } = setup();
|
||||||
page = mount(<AlertRuleList {...store} />);
|
instance.fetchRules = jest.fn();
|
||||||
});
|
instance.componentDidMount();
|
||||||
|
expect(instance.fetchRules).toHaveBeenCalled();
|
||||||
it('should call api to get rules', () => {
|
});
|
||||||
expect(backendSrv.get.mock.calls[0][0]).toEqual('/api/alerts');
|
});
|
||||||
});
|
|
||||||
|
describe('component did update', () => {
|
||||||
it('should render 1 rule', () => {
|
it('should call fetchrules if props differ', () => {
|
||||||
page.update();
|
const { instance } = setup();
|
||||||
const ruleNode = page.find('.alert-rule-item');
|
instance.fetchRules = jest.fn();
|
||||||
expect(toJson(ruleNode)).toMatchSnapshot();
|
|
||||||
});
|
instance.componentDidUpdate({ stateFilter: 'ok' });
|
||||||
|
|
||||||
it('toggle state should change pause rule if not paused', async () => {
|
expect(instance.fetchRules).toHaveBeenCalled();
|
||||||
backendSrv.post.mockReturnValue(
|
});
|
||||||
Promise.resolve({
|
});
|
||||||
state: 'paused',
|
});
|
||||||
})
|
|
||||||
);
|
describe('Functions', () => {
|
||||||
|
describe('Get state filter', () => {
|
||||||
page.find('.fa-pause').simulate('click');
|
it('should get all if prop is not set', () => {
|
||||||
|
const { instance } = setup();
|
||||||
// wait for api call to resolve
|
|
||||||
await Promise.resolve();
|
const stateFilter = instance.getStateFilter();
|
||||||
page.update();
|
|
||||||
|
expect(stateFilter).toEqual('all');
|
||||||
expect(store.alertList.rules[0].state).toBe('paused');
|
});
|
||||||
expect(page.find('.fa-play')).toHaveLength(1);
|
|
||||||
|
it('should return state filter if set', () => {
|
||||||
|
const { instance } = setup({
|
||||||
|
stateFilter: 'ok',
|
||||||
|
});
|
||||||
|
|
||||||
|
const stateFilter = instance.getStateFilter();
|
||||||
|
|
||||||
|
expect(stateFilter).toEqual('ok');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('State filter changed', () => {
|
||||||
|
it('should update location', () => {
|
||||||
|
const { instance } = setup();
|
||||||
|
const mockEvent = { target: { value: 'alerting' } };
|
||||||
|
|
||||||
|
instance.onStateFilterChanged(mockEvent);
|
||||||
|
|
||||||
|
expect(instance.props.updateLocation).toHaveBeenCalledWith({ query: { state: 'alerting' } });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Open how to', () => {
|
||||||
|
it('should emit show-modal event', () => {
|
||||||
|
const { instance } = setup();
|
||||||
|
|
||||||
|
instance.onOpenHowTo();
|
||||||
|
|
||||||
|
expect(appEvents.emit).toHaveBeenCalledWith('show-modal', {
|
||||||
|
src: 'public/app/features/alerting/partials/alert_howto.html',
|
||||||
|
modalClass: 'confirm-modal',
|
||||||
|
model: {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Search query change', () => {
|
||||||
|
it('should set search query', () => {
|
||||||
|
const { instance } = setup();
|
||||||
|
const mockEvent = { target: { value: 'dashboard' } };
|
||||||
|
|
||||||
|
instance.onSearchQueryChange(mockEvent);
|
||||||
|
|
||||||
|
expect(instance.props.setSearchQuery).toHaveBeenCalledWith('dashboard');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -10,7 +10,7 @@ import { NavModel, StoreState, AlertRule } from 'app/types';
|
|||||||
import { getAlertRulesAsync, setSearchQuery } from './state/actions';
|
import { getAlertRulesAsync, setSearchQuery } from './state/actions';
|
||||||
import { getAlertRuleItems, getSearchQuery } from './state/selectors';
|
import { getAlertRuleItems, getSearchQuery } from './state/selectors';
|
||||||
|
|
||||||
interface Props {
|
export interface Props {
|
||||||
navModel: NavModel;
|
navModel: NavModel;
|
||||||
alertRules: AlertRule[];
|
alertRules: AlertRule[];
|
||||||
updateLocation: typeof updateLocation;
|
updateLocation: typeof updateLocation;
|
||||||
@ -20,11 +20,7 @@ interface Props {
|
|||||||
search: string;
|
search: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
class AlertRuleList extends PureComponent<Props, any> {
|
||||||
search: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AlertRuleList extends PureComponent<Props, State> {
|
|
||||||
stateFilters = [
|
stateFilters = [
|
||||||
{ text: 'All', value: 'all' },
|
{ text: 'All', value: 'all' },
|
||||||
{ text: 'OK', value: 'ok' },
|
{ text: 'OK', value: 'ok' },
|
||||||
@ -44,11 +40,9 @@ export class AlertRuleList extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onStateFilterChanged = evt => {
|
async fetchRules() {
|
||||||
this.props.updateLocation({
|
await this.props.getAlertRulesAsync({ state: this.getStateFilter() });
|
||||||
query: { state: evt.target.value },
|
}
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
getStateFilter(): string {
|
getStateFilter(): string {
|
||||||
const { stateFilter } = this.props;
|
const { stateFilter } = this.props;
|
||||||
@ -58,9 +52,11 @@ export class AlertRuleList extends PureComponent<Props, State> {
|
|||||||
return 'all';
|
return 'all';
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchRules() {
|
onStateFilterChanged = event => {
|
||||||
await this.props.getAlertRulesAsync({ state: this.getStateFilter() });
|
this.props.updateLocation({
|
||||||
}
|
query: { state: event.target.value },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
onOpenHowTo = () => {
|
onOpenHowTo = () => {
|
||||||
appEvents.emit('show-modal', {
|
appEvents.emit('show-modal', {
|
||||||
@ -75,13 +71,13 @@ export class AlertRuleList extends PureComponent<Props, State> {
|
|||||||
this.props.setSearchQuery(value);
|
this.props.setSearchQuery(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
alertStateFilterOption({ text, value }) {
|
alertStateFilterOption = ({ text, value }) => {
|
||||||
return (
|
return (
|
||||||
<option key={value} value={value}>
|
<option key={value} value={value}>
|
||||||
{text}
|
{text}
|
||||||
</option>
|
</option>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { navModel, alertRules, search } = this.props;
|
const { navModel, alertRules, search } = this.props;
|
||||||
@ -112,14 +108,11 @@ export class AlertRuleList extends PureComponent<Props, State> {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="page-action-bar__spacer" />
|
<div className="page-action-bar__spacer" />
|
||||||
|
|
||||||
<a className="btn btn-secondary" onClick={this.onOpenHowTo}>
|
<a className="btn btn-secondary" onClick={this.onOpenHowTo}>
|
||||||
<i className="fa fa-info-circle" /> How to add an alert
|
<i className="fa fa-info-circle" /> How to add an alert
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<ol className="alert-rule-list">
|
<ol className="alert-rule-list">
|
||||||
{alertRules.map(rule => <AlertRuleItem rule={rule} key={rule.id} search={search} />)}
|
{alertRules.map(rule => <AlertRuleItem rule={rule} key={rule.id} search={search} />)}
|
||||||
|
@ -1,103 +1,254 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`AlertRuleList should render 1 rule 1`] = `
|
exports[`Render should render alert rules 1`] = `
|
||||||
<li
|
<div>
|
||||||
className="alert-rule-item"
|
<PageHeader
|
||||||
>
|
model={Object {}}
|
||||||
<span
|
/>
|
||||||
className="alert-rule-item__icon alert-state-ok"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="icon-gf icon-gf-online"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<div
|
<div
|
||||||
className="alert-rule-item__body"
|
className="page-container page-body"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="alert-rule-item__header"
|
className="page-action-bar"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="alert-rule-item__name"
|
className="gf-form gf-form--grow"
|
||||||
>
|
>
|
||||||
<a
|
<label
|
||||||
href="d/ufkcofof/my-goal?panelId=3&fullscreen=true&edit=true&tab=alert"
|
className="gf-form--has-input-icon gf-form--grow"
|
||||||
>
|
>
|
||||||
<Highlighter
|
<input
|
||||||
highlightClassName="highlight-search-match"
|
className="gf-form-input"
|
||||||
searchWords={
|
onChange={[Function]}
|
||||||
Array [
|
placeholder="Search alerts"
|
||||||
"",
|
type="text"
|
||||||
]
|
value=""
|
||||||
}
|
/>
|
||||||
textToHighlight="Panel Title alert"
|
<i
|
||||||
>
|
className="gf-form-input-icon fa fa-search"
|
||||||
<span>
|
/>
|
||||||
<span
|
</label>
|
||||||
className=""
|
|
||||||
key="0"
|
|
||||||
>
|
|
||||||
Panel Title alert
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</Highlighter>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="alert-rule-item__text"
|
className="gf-form"
|
||||||
>
|
>
|
||||||
<span
|
<label
|
||||||
className="alert-state-ok"
|
className="gf-form-label"
|
||||||
>
|
>
|
||||||
<Highlighter
|
States
|
||||||
highlightClassName="highlight-search-match"
|
</label>
|
||||||
searchWords={
|
<div
|
||||||
Array [
|
className="gf-form-select-wrapper width-13"
|
||||||
"",
|
>
|
||||||
]
|
<select
|
||||||
}
|
className="gf-form-input"
|
||||||
textToHighlight="OK"
|
onChange={[Function]}
|
||||||
|
value="all"
|
||||||
>
|
>
|
||||||
<span>
|
<option
|
||||||
<span
|
key="all"
|
||||||
className=""
|
value="all"
|
||||||
key="0"
|
>
|
||||||
>
|
All
|
||||||
OK
|
</option>
|
||||||
</span>
|
<option
|
||||||
</span>
|
key="ok"
|
||||||
</Highlighter>
|
value="ok"
|
||||||
</span>
|
>
|
||||||
<span
|
OK
|
||||||
className="alert-rule-item__time"
|
</option>
|
||||||
>
|
<option
|
||||||
for
|
key="not_ok"
|
||||||
5 minutes
|
value="not_ok"
|
||||||
</span>
|
>
|
||||||
|
Not OK
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
key="alerting"
|
||||||
|
value="alerting"
|
||||||
|
>
|
||||||
|
Alerting
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
key="no_data"
|
||||||
|
value="no_data"
|
||||||
|
>
|
||||||
|
No Data
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
key="paused"
|
||||||
|
value="paused"
|
||||||
|
>
|
||||||
|
Paused
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className="page-action-bar__spacer"
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
className="btn btn-secondary"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fa fa-info-circle"
|
||||||
|
/>
|
||||||
|
How to add an alert
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<section>
|
||||||
|
<ol
|
||||||
|
className="alert-rule-list"
|
||||||
|
>
|
||||||
|
<AlertRuleItem
|
||||||
|
key="1"
|
||||||
|
rule={
|
||||||
|
Object {
|
||||||
|
"dashboardId": 7,
|
||||||
|
"dashboardSlug": "alerting-with-testdata",
|
||||||
|
"dashboardUid": "ggHbN42mk",
|
||||||
|
"evalData": Object {},
|
||||||
|
"evalDate": "0001-01-01T00:00:00Z",
|
||||||
|
"executionError": "",
|
||||||
|
"id": 1,
|
||||||
|
"name": "TestData - Always OK",
|
||||||
|
"newStateDate": "2018-09-04T10:01:01+02:00",
|
||||||
|
"panelId": 3,
|
||||||
|
"state": "ok",
|
||||||
|
"url": "/d/ggHbN42mk/alerting-with-testdata",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
search=""
|
||||||
|
/>
|
||||||
|
<AlertRuleItem
|
||||||
|
key="3"
|
||||||
|
rule={
|
||||||
|
Object {
|
||||||
|
"dashboardId": 7,
|
||||||
|
"dashboardSlug": "alerting-with-testdata",
|
||||||
|
"dashboardUid": "ggHbN42mk",
|
||||||
|
"evalData": Object {},
|
||||||
|
"evalDate": "0001-01-01T00:00:00Z",
|
||||||
|
"executionError": "error",
|
||||||
|
"id": 3,
|
||||||
|
"name": "TestData - ok",
|
||||||
|
"newStateDate": "2018-09-04T10:01:01+02:00",
|
||||||
|
"panelId": 3,
|
||||||
|
"state": "ok",
|
||||||
|
"url": "/d/ggHbN42mk/alerting-with-testdata",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
search=""
|
||||||
|
/>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div
|
</div>
|
||||||
className="alert-rule-item__actions"
|
`;
|
||||||
>
|
|
||||||
<button
|
exports[`Render should render component 1`] = `
|
||||||
className="btn btn-small btn-inverse alert-list__btn width-2"
|
<div>
|
||||||
onClick={[Function]}
|
<PageHeader
|
||||||
title="Pausing an alert rule prevents it from executing"
|
model={Object {}}
|
||||||
>
|
/>
|
||||||
<i
|
<div
|
||||||
className="fa fa-pause"
|
className="page-container page-body"
|
||||||
/>
|
>
|
||||||
</button>
|
<div
|
||||||
<a
|
className="page-action-bar"
|
||||||
className="btn btn-small btn-inverse alert-list__btn width-2"
|
>
|
||||||
href="d/ufkcofof/my-goal?panelId=3&fullscreen=true&edit=true&tab=alert"
|
<div
|
||||||
title="Edit alert rule"
|
className="gf-form gf-form--grow"
|
||||||
>
|
>
|
||||||
<i
|
<label
|
||||||
className="icon-gf icon-gf-settings"
|
className="gf-form--has-input-icon gf-form--grow"
|
||||||
/>
|
>
|
||||||
</a>
|
<input
|
||||||
</div>
|
className="gf-form-input"
|
||||||
</li>
|
onChange={[Function]}
|
||||||
|
placeholder="Search alerts"
|
||||||
|
type="text"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
<i
|
||||||
|
className="gf-form-input-icon fa fa-search"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
className="gf-form-label"
|
||||||
|
>
|
||||||
|
States
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
className="gf-form-select-wrapper width-13"
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
className="gf-form-input"
|
||||||
|
onChange={[Function]}
|
||||||
|
value="all"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
key="all"
|
||||||
|
value="all"
|
||||||
|
>
|
||||||
|
All
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
key="ok"
|
||||||
|
value="ok"
|
||||||
|
>
|
||||||
|
OK
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
key="not_ok"
|
||||||
|
value="not_ok"
|
||||||
|
>
|
||||||
|
Not OK
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
key="alerting"
|
||||||
|
value="alerting"
|
||||||
|
>
|
||||||
|
Alerting
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
key="no_data"
|
||||||
|
value="no_data"
|
||||||
|
>
|
||||||
|
No Data
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
key="paused"
|
||||||
|
value="paused"
|
||||||
|
>
|
||||||
|
Paused
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="page-action-bar__spacer"
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
className="btn btn-secondary"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fa fa-info-circle"
|
||||||
|
/>
|
||||||
|
How to add an alert
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<section>
|
||||||
|
<ol
|
||||||
|
className="alert-rule-list"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||||
import { AlertRule, StoreState } from 'app/types';
|
import { AlertRuleApi, StoreState } from 'app/types';
|
||||||
|
|
||||||
export enum ActionTypes {
|
export enum ActionTypes {
|
||||||
LoadAlertRules = 'LOAD_ALERT_RULES',
|
LoadAlertRules = 'LOAD_ALERT_RULES',
|
||||||
@ -9,7 +9,7 @@ export enum ActionTypes {
|
|||||||
|
|
||||||
export interface LoadAlertRulesAction {
|
export interface LoadAlertRulesAction {
|
||||||
type: ActionTypes.LoadAlertRules;
|
type: ActionTypes.LoadAlertRules;
|
||||||
payload: AlertRule[];
|
payload: AlertRuleApi[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SetSearchQueryAction {
|
export interface SetSearchQueryAction {
|
||||||
@ -17,7 +17,7 @@ export interface SetSearchQueryAction {
|
|||||||
payload: string;
|
payload: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadAlertRules = (rules: AlertRule[]): LoadAlertRulesAction => ({
|
export const loadAlertRules = (rules: AlertRuleApi[]): LoadAlertRulesAction => ({
|
||||||
type: ActionTypes.LoadAlertRules,
|
type: ActionTypes.LoadAlertRules,
|
||||||
payload: rules,
|
payload: rules,
|
||||||
});
|
});
|
||||||
@ -31,7 +31,7 @@ export type Action = LoadAlertRulesAction | SetSearchQueryAction;
|
|||||||
|
|
||||||
export const getAlertRulesAsync = (options: { state: string }) => async (
|
export const getAlertRulesAsync = (options: { state: string }) => async (
|
||||||
dispatch: Dispatch<Action>
|
dispatch: Dispatch<Action>
|
||||||
): Promise<AlertRule[]> => {
|
): Promise<AlertRuleApi[]> => {
|
||||||
try {
|
try {
|
||||||
const rules = await getBackendSrv().get('/api/alerts', options);
|
const rules = await getBackendSrv().get('/api/alerts', options);
|
||||||
dispatch(loadAlertRules(rules));
|
dispatch(loadAlertRules(rules));
|
||||||
|
91
public/app/features/alerting/state/reducers.test.ts
Normal file
91
public/app/features/alerting/state/reducers.test.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { ActionTypes, Action } from './actions';
|
||||||
|
import { alertRulesReducer, initialState } from './reducers';
|
||||||
|
import { AlertRuleApi } from '../../../types';
|
||||||
|
|
||||||
|
describe('Alert rules', () => {
|
||||||
|
const payload: AlertRuleApi[] = [
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
dashboardId: 7,
|
||||||
|
dashboardUid: 'ggHbN42mk',
|
||||||
|
dashboardSlug: 'alerting-with-testdata',
|
||||||
|
panelId: 4,
|
||||||
|
name: 'TestData - Always Alerting',
|
||||||
|
state: 'alerting',
|
||||||
|
newStateDate: '2018-09-04T10:00:30+02:00',
|
||||||
|
evalDate: '0001-01-01T00:00:00Z',
|
||||||
|
evalData: { evalMatches: [{ metric: 'A-series', tags: null, value: 215 }] },
|
||||||
|
executionError: '',
|
||||||
|
url: '/d/ggHbN42mk/alerting-with-testdata',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
dashboardId: 7,
|
||||||
|
dashboardUid: 'ggHbN42mk',
|
||||||
|
dashboardSlug: 'alerting-with-testdata',
|
||||||
|
panelId: 3,
|
||||||
|
name: 'TestData - Always OK',
|
||||||
|
state: 'ok',
|
||||||
|
newStateDate: '2018-09-04T10:01:01+02:00',
|
||||||
|
evalDate: '0001-01-01T00:00:00Z',
|
||||||
|
evalData: {},
|
||||||
|
executionError: '',
|
||||||
|
url: '/d/ggHbN42mk/alerting-with-testdata',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
dashboardId: 7,
|
||||||
|
dashboardUid: 'ggHbN42mk',
|
||||||
|
dashboardSlug: 'alerting-with-testdata',
|
||||||
|
panelId: 3,
|
||||||
|
name: 'TestData - ok',
|
||||||
|
state: 'ok',
|
||||||
|
newStateDate: '2018-09-04T10:01:01+02:00',
|
||||||
|
evalDate: '0001-01-01T00:00:00Z',
|
||||||
|
evalData: {},
|
||||||
|
executionError: 'error',
|
||||||
|
url: '/d/ggHbN42mk/alerting-with-testdata',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
dashboardId: 7,
|
||||||
|
dashboardUid: 'ggHbN42mk',
|
||||||
|
dashboardSlug: 'alerting-with-testdata',
|
||||||
|
panelId: 3,
|
||||||
|
name: 'TestData - Paused',
|
||||||
|
state: 'paused',
|
||||||
|
newStateDate: '2018-09-04T10:01:01+02:00',
|
||||||
|
evalDate: '0001-01-01T00:00:00Z',
|
||||||
|
evalData: {},
|
||||||
|
executionError: 'error',
|
||||||
|
url: '/d/ggHbN42mk/alerting-with-testdata',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
dashboardId: 7,
|
||||||
|
dashboardUid: 'ggHbN42mk',
|
||||||
|
dashboardSlug: 'alerting-with-testdata',
|
||||||
|
panelId: 3,
|
||||||
|
name: 'TestData - Ok',
|
||||||
|
state: 'ok',
|
||||||
|
newStateDate: '2018-09-04T10:01:01+02:00',
|
||||||
|
evalDate: '0001-01-01T00:00:00Z',
|
||||||
|
evalData: {
|
||||||
|
noData: true,
|
||||||
|
},
|
||||||
|
executionError: 'error',
|
||||||
|
url: '/d/ggHbN42mk/alerting-with-testdata',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
it('should set alert rules', () => {
|
||||||
|
const action: Action = {
|
||||||
|
type: ActionTypes.LoadAlertRules,
|
||||||
|
payload: payload,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = alertRulesReducer(initialState, action);
|
||||||
|
|
||||||
|
expect(result.items).toEqual(payload);
|
||||||
|
});
|
||||||
|
});
|
@ -1,40 +1,41 @@
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { AlertRulesState } from 'app/types';
|
import { AlertRuleApi, AlertRule, AlertRulesState } from 'app/types';
|
||||||
import { Action, ActionTypes } from './actions';
|
import { Action, ActionTypes } from './actions';
|
||||||
import alertDef from './alertDef';
|
import alertDef from './alertDef';
|
||||||
|
|
||||||
export const initialState: AlertRulesState = { items: [], searchQuery: '' };
|
export const initialState: AlertRulesState = { items: [], searchQuery: '' };
|
||||||
|
|
||||||
export function setStateFields(rule, state) {
|
function convertToAlertRule(rule, state): AlertRule {
|
||||||
const stateModel = alertDef.getStateDisplayModel(state);
|
const stateModel = alertDef.getStateDisplayModel(state);
|
||||||
rule.state = state;
|
|
||||||
rule.stateText = stateModel.text;
|
rule.stateText = stateModel.text;
|
||||||
rule.stateIcon = stateModel.iconClass;
|
rule.stateIcon = stateModel.iconClass;
|
||||||
rule.stateClass = stateModel.stateClass;
|
rule.stateClass = stateModel.stateClass;
|
||||||
rule.stateAge = moment(rule.newStateDate)
|
rule.stateAge = moment(rule.newStateDate)
|
||||||
.fromNow()
|
.fromNow()
|
||||||
.replace(' ago', '');
|
.replace(' ago', '');
|
||||||
|
|
||||||
|
if (rule.state !== 'paused') {
|
||||||
|
if (rule.executionError) {
|
||||||
|
rule.info = 'Execution Error: ' + rule.executionError;
|
||||||
|
}
|
||||||
|
if (rule.evalData && rule.evalData.noData) {
|
||||||
|
rule.info = 'Query returned no data';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const alertRulesReducer = (state = initialState, action: Action): AlertRulesState => {
|
export const alertRulesReducer = (state = initialState, action: Action): AlertRulesState => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionTypes.LoadAlertRules: {
|
case ActionTypes.LoadAlertRules: {
|
||||||
const alertRules = action.payload;
|
const alertRules: AlertRuleApi[] = action.payload;
|
||||||
|
|
||||||
for (const rule of alertRules) {
|
const alertRulesViewModel: AlertRule[] = alertRules.map(rule => {
|
||||||
setStateFields(rule, rule.state);
|
return convertToAlertRule(rule, rule.state);
|
||||||
|
});
|
||||||
|
|
||||||
if (rule.state !== 'paused') {
|
return { items: alertRulesViewModel, searchQuery: state.searchQuery };
|
||||||
if (rule.executionError) {
|
|
||||||
rule.info = 'Execution Error: ' + rule.executionError;
|
|
||||||
}
|
|
||||||
if (rule.evalData && rule.evalData.noData) {
|
|
||||||
rule.info = 'Query returned no data';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { items: alertRules, searchQuery: state.searchQuery };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case ActionTypes.SetSearchQuery:
|
case ActionTypes.SetSearchQuery:
|
||||||
|
@ -22,6 +22,21 @@ export type UrlQueryMap = { [s: string]: UrlQueryValue };
|
|||||||
// Alerting
|
// Alerting
|
||||||
//
|
//
|
||||||
|
|
||||||
|
export interface AlertRuleApi {
|
||||||
|
id: number;
|
||||||
|
dashboardId: number;
|
||||||
|
dashboardUid: string;
|
||||||
|
dashboardSlug: string;
|
||||||
|
panelId: number;
|
||||||
|
name: string;
|
||||||
|
state: string;
|
||||||
|
newStateDate: string;
|
||||||
|
evalDate: string;
|
||||||
|
evalData?: object;
|
||||||
|
executionError: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AlertRule {
|
export interface AlertRule {
|
||||||
id: number;
|
id: number;
|
||||||
dashboardId: number;
|
dashboardId: number;
|
||||||
|
Loading…
Reference in New Issue
Block a user