wip: load alert rules via redux

This commit is contained in:
Torkel Ödegaard 2018-09-02 11:36:03 -07:00
parent 7b06800295
commit 2a64d19f5b
10 changed files with 126 additions and 74 deletions

View File

@ -1,5 +1,5 @@
import { navIndexReducer as navIndex } from './navModel'; import { navIndexReducer as navIndex } from './navModel';
import location from './location'; import { locationReducer as location } from './location';
export default { export default {
navIndex, navIndex,

View File

@ -16,7 +16,7 @@ function renderUrl(path: string, query: UrlQueryMap): string {
return path; return path;
} }
const routerReducer = (state = initialState, action: Action): LocationState => { export const locationReducer = (state = initialState, action: Action): LocationState => {
switch (action.type) { switch (action.type) {
case 'UPDATE_LOCATION': { case 'UPDATE_LOCATION': {
const { path, query, routeParams } = action.payload; const { path, query, routeParams } = action.payload;
@ -31,5 +31,3 @@ const routerReducer = (state = initialState, action: Action): LocationState => {
return state; return state;
}; };
export default routerReducer;

View File

@ -15,7 +15,7 @@ function getNotFoundModel(): NavModel {
}; };
} }
export function selectNavNode(navIndex: NavIndex, id: string): NavModel { export function getNavModel(navIndex: NavIndex, id: string): NavModel {
if (navIndex[id]) { if (navIndex[id]) {
const node = navIndex[id]; const node = navIndex[id];
const main = { const main = {

View File

@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader'; import { hot } from 'react-hot-loader';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { NavModel, StoreState } from 'app/types'; import { NavModel, StoreState } from 'app/types';
import { selectNavNode } from 'app/core/selectors/navModel'; import { getNavModel } from 'app/core/selectors/navModel';
import { getServerStats, ServerStat } from '../apis'; import { getServerStats, ServerStat } from '../apis';
import PageHeader from 'app/core/components/PageHeader/PageHeader'; import PageHeader from 'app/core/components/PageHeader/PageHeader';
@ -66,7 +66,7 @@ function StatItem(stat: ServerStat) {
} }
const mapStateToProps = (state: StoreState) => ({ const mapStateToProps = (state: StoreState) => ({
navModel: selectNavNode(state.navIndex, 'server-stats'), navModel: getNavModel(state.navIndex, 'server-stats'),
getServerStats: getServerStats, getServerStats: getServerStats,
}); });

View File

@ -6,13 +6,15 @@ import PageHeader from 'app/core/components/PageHeader/PageHeader';
import appEvents from 'app/core/app_events'; import appEvents from 'app/core/app_events';
import Highlighter from 'react-highlight-words'; import Highlighter from 'react-highlight-words';
import { updateLocation } from 'app/core/actions'; import { updateLocation } from 'app/core/actions';
import { selectNavNode } from 'app/core/selectors/navModel'; import { getNavModel } from 'app/core/selectors/navModel';
import { NavModel, StoreState } from 'app/types'; import { NavModel, StoreState, AlertRule } from 'app/types';
import { getAlertRules, AlertRule } from './state/apis'; import { getAlertRulesAsync } from './state/actions';
interface Props { interface Props {
navModel: NavModel; navModel: NavModel;
alertRules: AlertRule[];
updateLocation: typeof updateLocation; updateLocation: typeof updateLocation;
getAlertRulesAsync: typeof getAlertRulesAsync;
} }
interface State { interface State {
@ -49,16 +51,11 @@ export class AlertRuleList extends PureComponent<Props, State> {
this.props.updateLocation({ this.props.updateLocation({
query: { state: evt.target.value }, query: { state: evt.target.value },
}); });
// this.fetchRules(); this.fetchRules();
}; };
async fetchRules() { async fetchRules() {
try { await this.props.getAlertRulesAsync();
const rules = await getAlertRules();
this.setState({ rules });
} catch (error) {
console.error(error);
}
// this.props.alertList.loadRules({ // this.props.alertList.loadRules({
// state: this.props.view.query.get('state') || 'all', // state: this.props.view.query.get('state') || 'all',
@ -78,8 +75,8 @@ export class AlertRuleList extends PureComponent<Props, State> {
}; };
render() { render() {
const { navModel } = this.props; const { navModel, alertRules } = this.props;
const { rules, search, stateFilter } = this.state; const { search, stateFilter } = this.state;
return ( return (
<div> <div>
@ -117,7 +114,7 @@ export class AlertRuleList extends PureComponent<Props, State> {
<section> <section>
<ol className="alert-rule-list"> <ol className="alert-rule-list">
{rules.map(rule => <AlertRuleItem rule={rule} key={rule.id} search={search} />)} {alertRules.map(rule => <AlertRuleItem rule={rule} key={rule.id} search={search} />)}
</ol> </ol>
</section> </section>
</div> </div>
@ -201,11 +198,13 @@ export class AlertRuleItem extends React.Component<AlertRuleItemProps, any> {
} }
const mapStateToProps = (state: StoreState) => ({ const mapStateToProps = (state: StoreState) => ({
navModel: selectNavNode(state.navIndex, 'alert-list'), navModel: getNavModel(state.navIndex, 'alert-list'),
alertRules: state.alertRules,
}); });
const mapDispatchToProps = { const mapDispatchToProps = {
updateLocation, updateLocation,
getAlertRulesAsync,
}; };
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(AlertRuleList)); export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(AlertRuleList));

View File

@ -0,0 +1,26 @@
import { Dispatch } from 'redux';
import { getBackendSrv } from 'app/core/services/backend_srv';
import { AlertRule } from 'app/types';
export interface LoadAlertRulesAction {
type: 'LOAD_ALERT_RULES';
payload: AlertRule[];
}
export const loadAlertRules = (rules: AlertRule[]): LoadAlertRulesAction => ({
type: 'LOAD_ALERT_RULES',
payload: rules,
});
export type Action = LoadAlertRulesAction;
export const getAlertRulesAsync = () => async (dispatch: Dispatch<Action>): Promise<AlertRule[]> => {
try {
const rules = await getBackendSrv().get('/api/alerts', {});
dispatch(loadAlertRules(rules));
return rules;
} catch (error) {
console.error(error);
throw error;
}
};

View File

@ -1,52 +0,0 @@
import { getBackendSrv } from 'app/core/services/backend_srv';
import alertDef from './alertDef';
import moment from 'moment';
export interface AlertRule {
id: number;
dashboardId: number;
panelId: number;
name: string;
state: string;
stateText: string;
stateIcon: string;
stateClass: string;
stateAge: string;
info?: string;
url: string;
}
export function setStateFields(rule, state) {
const stateModel = alertDef.getStateDisplayModel(state);
rule.state = state;
rule.stateText = stateModel.text;
rule.stateIcon = stateModel.iconClass;
rule.stateClass = stateModel.stateClass;
rule.stateAge = moment(rule.newStateDate)
.fromNow()
.replace(' ago', '');
}
export const getAlertRules = async (): Promise<AlertRule[]> => {
try {
const rules = await getBackendSrv().get('/api/alerts', {});
for (const rule of rules) {
setStateFields(rule, rule.state);
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 rules;
} catch (error) {
console.error(error);
throw error;
}
};

View File

@ -0,0 +1,46 @@
import { Action } from './actions';
import { AlertRule } from 'app/types';
import alertDef from './alertDef';
import moment from 'moment';
export const initialState: AlertRule[] = [];
export function setStateFields(rule, state) {
const stateModel = alertDef.getStateDisplayModel(state);
rule.state = state;
rule.stateText = stateModel.text;
rule.stateIcon = stateModel.iconClass;
rule.stateClass = stateModel.stateClass;
rule.stateAge = moment(rule.newStateDate)
.fromNow()
.replace(' ago', '');
}
export const alertRulesReducer = (state = initialState, action: Action): AlertRule[] => {
switch (action.type) {
case 'LOAD_ALERT_RULES': {
const alertRules = action.payload;
for (const rule of alertRules) {
setStateFields(rule, rule.state);
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 alertRules;
}
}
return state;
};
export default {
alertRules: alertRulesReducer,
};

View File

@ -2,9 +2,11 @@ import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
import thunk from 'redux-thunk'; import thunk from 'redux-thunk';
import { createLogger } from 'redux-logger'; import { createLogger } from 'redux-logger';
import sharedReducers from 'app/core/reducers'; import sharedReducers from 'app/core/reducers';
import alertingReducers from 'app/features/alerting/state/reducers';
const rootReducer = combineReducers({ const rootReducer = combineReducers({
...sharedReducers ...sharedReducers,
...alertingReducers,
}); });
export let store; export let store;

View File

@ -1,3 +1,7 @@
//
// Location
//
export interface LocationUpdate { export interface LocationUpdate {
path?: string; path?: string;
query?: UrlQueryMap; query?: UrlQueryMap;
@ -14,6 +18,30 @@ export interface LocationState {
export type UrlQueryValue = string | number | boolean | string[] | number[] | boolean[]; export type UrlQueryValue = string | number | boolean | string[] | number[] | boolean[];
export type UrlQueryMap = { [s: string]: UrlQueryValue }; export type UrlQueryMap = { [s: string]: UrlQueryValue };
//
// Alerting
//
export interface AlertRule {
id: number;
dashboardId: number;
panelId: number;
name: string;
state: string;
stateText: string;
stateIcon: string;
stateClass: string;
stateAge: string;
info?: string;
url: string;
executionError?: string;
evalData?: { noData: boolean };
}
//
// NavModel
//
export interface NavModelItem { export interface NavModelItem {
text: string; text: string;
url: string; url: string;
@ -37,7 +65,12 @@ export interface NavModel {
export type NavIndex = { [s: string]: NavModelItem }; export type NavIndex = { [s: string]: NavModelItem };
//
// Store
//
export interface StoreState { export interface StoreState {
navIndex: NavIndex; navIndex: NavIndex;
location: LocationState; location: LocationState;
alertRules: AlertRule[];
} }