Create EndpointTabs SFC and fix actions and reducers for adding and removing new endpoints

pull/2252/head
deniz kusefoglu 2017-11-07 16:25:29 -08:00
parent 3487055fd0
commit 3d859a095f
5 changed files with 162 additions and 120 deletions

View File

@ -163,16 +163,22 @@ export function updateAlerts(ruleID, alerts) {
} }
} }
export function updateAlertNodes(ruleID, alertNodeName, alertNodesText) { export function updateAlertNodes(ruleID, alerts) {
return { return {
type: 'UPDATE_RULE_ALERT_NODES', type: 'UPDATE_RULE_ALERT_NODES',
payload: { payload: {ruleID, alerts},
ruleID,
alertNodeName,
alertNodesText,
},
} }
} }
// export function updateAlertNodes(ruleID, alertNodeName, alertNodesText) {
// return {
// type: 'UPDATE_RULE_ALERT_NODES',
// payload: {
// ruleID,
// alertNodeName,
// alertNodesText,
// },
// }
// }
export function updateRuleName(ruleID, name) { export function updateRuleName(ruleID, name) {
return { return {

View File

@ -0,0 +1,46 @@
import React, {PropTypes} from 'react'
import _ from 'lodash'
import classnames from 'classnames'
import uuid from 'node-uuid'
import {RULE_ALERT_OPTIONS} from 'src/kapacitor/constants'
const EndpointTabs = ({
endpointsOnThisAlert,
selectedEndpoint,
handleChooseAlert,
handleRemoveEndpoint,
}) => {
return endpointsOnThisAlert.length
? <ul className="nav nav-tablist nav-tablist-sm nav-tablist-malachite">
{endpointsOnThisAlert
.filter(alert => _.get(RULE_ALERT_OPTIONS, alert.type, false))
.map(alert =>
<li
key={uuid.v4()}
className={classnames({
active:
alert.alias === (selectedEndpoint && selectedEndpoint.alias),
})}
onClick={handleChooseAlert(alert)}
>
{alert.alias}
<div
className="nav-tab--delete"
onClick={handleRemoveEndpoint(alert)}
/>
</li>
)}
</ul>
: null
}
const {shape, func, array} = PropTypes
EndpointTabs.propTypes = {
endpointsOnThisAlert: array,
selectedEndpoint: shape({}),
handleChooseAlert: func.isRequired,
handleRemoveEndpoint: func.isRequired,
}
export default EndpointTabs

View File

@ -1,14 +1,13 @@
import React, {Component, PropTypes} from 'react' import React, {Component, PropTypes} from 'react'
import _ from 'lodash' import _ from 'lodash'
import classnames from 'classnames'
import uuid from 'node-uuid'
import RuleMessageOptions from 'src/kapacitor/components/RuleMessageOptions' import RuleMessageOptions from 'src/kapacitor/components/RuleMessageOptions'
import RuleMessageText from 'src/kapacitor/components/RuleMessageText' import RuleMessageText from 'src/kapacitor/components/RuleMessageText'
import RuleMessageTemplates from 'src/kapacitor/components/RuleMessageTemplates' import RuleMessageTemplates from 'src/kapacitor/components/RuleMessageTemplates'
import EndpointTabs from 'src/kapacitor/components/EndpointTabs'
import Dropdown from 'shared/components/Dropdown' import Dropdown from 'shared/components/Dropdown'
import {DEFAULT_ALERTS, RULE_ALERT_OPTIONS} from 'src/kapacitor/constants' import {DEFAULT_ALERTS} from 'src/kapacitor/constants'
const alertNodesToEndpoints = rule => { const alertNodesToEndpoints = rule => {
const endpointsOfKind = {} const endpointsOfKind = {}
@ -17,9 +16,12 @@ const alertNodesToEndpoints = rule => {
const count = _.get(endpointsOfKind, ep.name, 0) + 1 const count = _.get(endpointsOfKind, ep.name, 0) + 1
endpointsOfKind[ep.name] = count endpointsOfKind[ep.name] = count
endpointsOnThisAlert.push({ endpointsOnThisAlert.push({
text: ep.name + count, alias: ep.name + count,
kind: ep.name, type: ep.name,
text: ep.name,
ruleID: rule.id, ruleID: rule.id,
args: ep.args,
properties: ep.properties,
}) })
}) })
const selectedEndpoint = endpointsOnThisAlert.length const selectedEndpoint = endpointsOnThisAlert.length
@ -50,61 +52,74 @@ class RuleMessage extends Component {
} }
handleChooseAlert = item => () => { handleChooseAlert = item => () => {
const {actions} = this.props
actions.updateAlerts(item.ruleID, [item.text])
actions.updateAlertNodes(item.ruleID, item.text, '')
this.setState({selectedEndpoint: item}) this.setState({selectedEndpoint: item})
} }
handleAddEndpoint = selectedItem => { handleAddEndpoint = selectedItem => {
const {endpointsOnThisAlert, endpointsOfKind} = this.state const {endpointsOnThisAlert, endpointsOfKind} = this.state
const newItemNumbering = _.get(endpointsOfKind, selectedItem.text, 0) + 1 const newItemNumbering = _.get(endpointsOfKind, selectedItem.alias, 0) + 1
const newItemName = selectedItem.text + newItemNumbering const newItemName = selectedItem.alias + newItemNumbering
const newEndpoint = { const newEndpoint = {
text: newItemName, alias: newItemName,
kind: selectedItem.text, type: selectedItem.alias,
ruleID: selectedItem.ruleID, ruleID: selectedItem.ruleID,
} }
this.setState({ this.setState(
endpointsOnThisAlert: [...endpointsOnThisAlert, newEndpoint], {
endpointsOfKind: { endpointsOnThisAlert: [...endpointsOnThisAlert, newEndpoint],
...endpointsOfKind, endpointsOfKind: {
[selectedItem.text]: newItemNumbering, ...endpointsOfKind,
[selectedItem.alias]: newItemNumbering,
},
selectedEndpoint: newEndpoint,
}, },
selectedEndpoint: newEndpoint, this.handleUpdateAllAlerts
}) )
} }
handleRemoveEndpoint = alert => e => { handleRemoveEndpoint = alert => e => {
e.stopPropagation() e.stopPropagation()
const {endpointsOnThisAlert, selectedEndpoint} = this.state const {endpointsOnThisAlert, selectedEndpoint} = this.state
const removedIndex = _.findIndex(endpointsOnThisAlert, ['text', alert.text]) const removedIndex = _.findIndex(endpointsOnThisAlert, [
const remainingEndpoints = _.reject(endpointsOnThisAlert, [ 'alias',
'text', alert.alias,
alert.text,
]) ])
if (selectedEndpoint.text === alert.text) { const remainingEndpoints = _.reject(endpointsOnThisAlert, [
'alias',
alert.alias,
])
if (selectedEndpoint.alias === alert.alias) {
const selectedIndex = removedIndex > 0 ? removedIndex - 1 : 0 const selectedIndex = removedIndex > 0 ? removedIndex - 1 : 0
const newSelected = remainingEndpoints.length const newSelected = remainingEndpoints.length
? remainingEndpoints[selectedIndex] ? remainingEndpoints[selectedIndex]
: null : null
this.setState({selectedEndpoint: newSelected}) this.setState({selectedEndpoint: newSelected})
} }
this.setState({ this.setState(
endpointsOnThisAlert: remainingEndpoints, {
}) endpointsOnThisAlert: remainingEndpoints,
},
this.handleUpdateAllAlerts
)
}
handleUpdateAllAlerts = () => {
const {rule, actions} = this.props
const {endpointsOnThisAlert} = this.state
actions.updateAlertNodes(rule.id, endpointsOnThisAlert)
actions.updateAlerts(rule.id, endpointsOnThisAlert)
} }
render() { render() {
const {rule, actions, enabledAlerts} = this.props const {rule, actions, enabledAlerts} = this.props
const {endpointsOnThisAlert, selectedEndpoint} = this.state const {endpointsOnThisAlert, selectedEndpoint} = this.state
const defaultAlertEndpoints = DEFAULT_ALERTS.map(text => { const defaultAlertEndpoints = DEFAULT_ALERTS.map(alias => {
return {text, kind: text, ruleID: rule.id} return {alias, text: alias, type: alias, ruleID: rule.id}
}) })
const alerts = [ const alerts = [
...defaultAlertEndpoints, ...defaultAlertEndpoints,
...enabledAlerts.map(text => { ...enabledAlerts.map(alias => {
return {text, kind: text, ruleID: rule.id} return {text: alias, type: alias, ruleID: rule.id}
}), }),
] ]
return ( return (
@ -113,31 +128,12 @@ class RuleMessage extends Component {
<div className="rule-section--body"> <div className="rule-section--body">
<div className="rule-section--row rule-section--row-first rule-section--border-bottom"> <div className="rule-section--row rule-section--row-first rule-section--border-bottom">
<p>Send this Alert to:</p> <p>Send this Alert to:</p>
{endpointsOnThisAlert.length <EndpointTabs
? <ul className="nav nav-tablist nav-tablist-sm nav-tablist-malachite"> endpointsOnThisAlert={endpointsOnThisAlert}
{endpointsOnThisAlert selectedEndpoint={selectedEndpoint}
.filter(alert => handleChooseAlert={this.handleChooseAlert}
_.get(RULE_ALERT_OPTIONS, alert.kind, false) handleRemoveEndpoint={this.handleRemoveEndpoint}
) />
.map(alert =>
<li
key={uuid.v4()}
className={classnames({
active:
alert.text ===
(selectedEndpoint && selectedEndpoint.text),
})}
onClick={this.handleChooseAlert(alert)}
>
{alert.text}
<div
className="nav-tab--delete"
onClick={this.handleRemoveEndpoint(alert)}
/>
</li>
)}
</ul>
: null}
<Dropdown <Dropdown
items={alerts} items={alerts}
menuClass="dropdown-malachite" menuClass="dropdown-malachite"
@ -151,9 +147,12 @@ class RuleMessage extends Component {
<RuleMessageOptions <RuleMessageOptions
rule={rule} rule={rule}
alertNode={selectedEndpoint} alertNode={selectedEndpoint}
selectedEndpoint={selectedEndpoint}
updateAlertNodes={actions.updateAlertNodes} updateAlertNodes={actions.updateAlertNodes}
updateDetails={actions.updateDetails} updateDetails={actions.updateDetails}
updateAlertProperty={actions.updateAlertProperty} updateAlertProperty={actions.updateAlertProperty}
handleEditAlert={this.handleEditAlert}
handleUpdateArg={this.handleUpdateArg}
/> />
<RuleMessageText <RuleMessageText
rule={rule} rule={rule}

View File

@ -1,9 +1,6 @@
import React, {Component, PropTypes} from 'react' import React, {Component, PropTypes} from 'react'
import { import {RULE_ALERT_OPTIONS} from 'src/kapacitor/constants'
RULE_ALERT_OPTIONS,
ALERT_NODES_ACCESSORS,
} from 'src/kapacitor/constants'
class RuleMessageOptions extends Component { class RuleMessageOptions extends Component {
constructor(props) { constructor(props) {
@ -31,8 +28,8 @@ class RuleMessageOptions extends Component {
} }
handleUpdateAlertNodes = e => { handleUpdateAlertNodes = e => {
const {updateAlertNodes, alertNode, rule} = this.props const {handleUpdateArg, selectedEndpoint} = this.props
updateAlertNodes(rule.id, alertNode, e.target.value) handleUpdateArg(selectedEndpoint, e.target.value)
} }
handleUpdateAlertProperty = propertyName => e => { handleUpdateAlertProperty = propertyName => e => {
@ -44,8 +41,10 @@ class RuleMessageOptions extends Component {
} }
render() { render() {
const {rule, alertNode} = this.props const {rule, selectedEndpoint, handleEditAlert} = this.props
const {args, details, properties} = RULE_ALERT_OPTIONS[alertNode.kind] const {args, details, properties} = RULE_ALERT_OPTIONS[
selectedEndpoint.type
]
return ( return (
<div> <div>
@ -61,7 +60,7 @@ class RuleMessageOptions extends Component {
type="text" type="text"
placeholder={args.placeholder} placeholder={args.placeholder}
onChange={this.handleUpdateAlertNodes} onChange={this.handleUpdateAlertNodes}
value={ALERT_NODES_ACCESSORS[alertNode.kind](rule)} value={selectedEndpoint.args}
autoComplete="off" autoComplete="off"
spellCheck="false" spellCheck="false"
/> />
@ -116,7 +115,7 @@ const {func, shape} = PropTypes
RuleMessageOptions.propTypes = { RuleMessageOptions.propTypes = {
rule: shape({}).isRequired, rule: shape({}).isRequired,
alertNode: shape({}), selectedEndpoint: shape({}),
updateAlertNodes: func.isRequired, updateAlertNodes: func.isRequired,
updateDetails: func.isRequired, updateDetails: func.isRequired,
updateAlertProperty: func.isRequired, updateAlertProperty: func.isRequired,

View File

@ -76,65 +76,57 @@ export default function rules(state = {}, action) {
case 'UPDATE_RULE_ALERTS': { case 'UPDATE_RULE_ALERTS': {
const {ruleID, alerts} = action.payload const {ruleID, alerts} = action.payload
const culledalerts = alerts.map(a => a.type)
return Object.assign({}, state, { return Object.assign({}, state, {
[ruleID]: Object.assign({}, state[ruleID], { [ruleID]: Object.assign({}, state[ruleID], {
alerts, alerts: culledalerts,
}), }),
}) })
} }
case 'UPDATE_RULE_ALERT_NODES': { case 'UPDATE_RULE_ALERT_NODES': {
const {ruleID, alertNodeName, alertNodesText} = action.payload const {ruleID, alerts} = action.payload
const alertNodesByType = alerts.map(alert => {
let alertNodesByType const {type, alias} = alert
switch (type) {
switch (alertNodeName) { case 'http':
case 'http': case 'tcp':
case 'tcp': case 'log':
case 'log': return {
alertNodesByType = [ name: type,
{ args: [alias],
name: alertNodeName,
args: [alertNodesText],
properties: [], properties: [],
}, }
] case 'exec':
break case 'smtp':
case 'exec': return [
case 'smtp': {
alertNodesByType = [ name: type,
{ args: alias.split(' '),
name: alertNodeName, properties: [],
args: alertNodesText.split(' '), },
properties: [], ]
}, case 'alerta':
] return {
break name: type,
case 'alerta':
alertNodesByType = [
{
name: alertNodeName,
args: [], args: [],
properties: parseAlerta(alertNodesText), properties: parseAlerta(alias),
}, }
] case 'hipchat':
break case 'opsgenie':
case 'hipchat': case 'pagerduty':
case 'opsgenie': case 'slack':
case 'pagerduty': case 'telegram':
case 'slack': case 'victorops':
case 'telegram': case 'pushover':
case 'victorops': default:
case 'pushover': return {
default: name: type,
alertNodesByType = [
{
name: alertNodeName,
args: [], args: [],
properties: [], properties: [],
}, }
] }
} })
return Object.assign({}, state, { return Object.assign({}, state, {
[ruleID]: Object.assign({}, state[ruleID], { [ruleID]: Object.assign({}, state[ruleID], {
alertNodes: alertNodesByType, alertNodes: alertNodesByType,