diff --git a/ui/src/kapacitor/components/AlertTabs.js b/ui/src/kapacitor/components/AlertTabs.tsx similarity index 79% rename from ui/src/kapacitor/components/AlertTabs.js rename to ui/src/kapacitor/components/AlertTabs.tsx index 1f8329e0b0..34e79ef20e 100644 --- a/ui/src/kapacitor/components/AlertTabs.js +++ b/ui/src/kapacitor/components/AlertTabs.tsx @@ -1,15 +1,20 @@ -import React, {Component} from 'react' -import PropTypes from 'prop-types' +import React, {PureComponent, MouseEvent} from 'react' import _ from 'lodash' -import {Tab, Tabs, TabPanel, TabPanels, TabList} from 'shared/components/Tabs' +import { + Tab, + Tabs, + TabPanel, + TabPanels, + TabList, +} from 'src/shared/components/Tabs' import { getKapacitorConfig, updateKapacitorConfigSection, testAlertOutput, getAllServices, -} from 'shared/apis' +} from 'src/shared/apis' import { AlertaConfig, @@ -32,12 +37,100 @@ import { notifyTestAlertSent, notifyTestAlertFailed, notifyCouldNotRetrieveKapacitorServices, -} from 'shared/copy/notifications' +} from 'src/shared/copy/notifications' import DeprecationWarning from 'src/admin/components/DeprecationWarning' import {ErrorHandling} from 'src/shared/decorators/errors' +import {Source, Kapacitor} from 'src/types' + +interface Service { + link: Link + name: string + options: { + id: string + } +} + +interface Link { + rel: string + href: string +} + +interface Element { + link: Link + options: any + redacted: string[] +} + +interface Section { + link: string + elements: Element[] +} + +interface Sections { + alerta: Section + hipchat: Section + httppost: Section + influxdb: Section + mqtt: Section + opsgenie: Section + opsgenie2: Section + pagerduty: Section + pagerduty2: Section + pushover: Section + sensu: Section + slack: Section + smtp: Section + snmptrap: Section + talk: Section + telegram: Section + victorops: Section +} + +interface Config { + type: string + enabled: boolean + renderComponent: () => JSX.Element +} + +interface SupportedConfig { + alerta: Config + hipchat: Config + opsgenie: Config + opsgenie2: Config + pagerduty: Config + pagerduty2: Config + pushover: Config + sensu: Config + slack: Config + smtp: Config + talk: Config + telegram: Config + victorops: Config +} + +interface Notification { + id?: string + type: string + icon: string + duration: number + message: string +} + +interface Props { + source: Source + kapacitor: Kapacitor + notify: (message: Notification) => void + hash: string +} + +interface State { + configSections: Sections + services: Service[] +} + @ErrorHandling -class AlertTabs extends Component { +class AlertTabs extends PureComponent { constructor(props) { super(props) @@ -47,11 +140,11 @@ class AlertTabs extends Component { } } - async componentDidMount() { + public async componentDidMount() { const {kapacitor} = this.props try { this.refreshKapacitorConfig(kapacitor) - const services = await getAllServices(kapacitor) + const services: Service[] = await getAllServices(kapacitor) this.setState({services}) } catch (error) { this.setState({services: null}) @@ -59,117 +152,32 @@ class AlertTabs extends Component { } } - componentWillReceiveProps(nextProps) { + public componentWillReceiveProps(nextProps) { if (this.props.kapacitor.url !== nextProps.kapacitor.url) { this.refreshKapacitorConfig(nextProps.kapacitor) } } - refreshKapacitorConfig = async kapacitor => { - try { - const { - data: {sections}, - } = await getKapacitorConfig(kapacitor) - this.setState({configSections: sections}) - } catch (error) { - this.setState({configSections: null}) - this.props.notify(notifyRefreshKapacitorFailed()) - } - } - - getSection = (sections, section) => { - return _.get(sections, [section, 'elements', '0'], null) - } - - getEnabled = (sections, section) => { - return _.get( - sections, - [section, 'elements', '0', 'options', 'enabled'], - null - ) - } - - handleGetSection = (sections, section) => () => { - return this.getSection(sections, section) - } - - handleSaveConfig = section => async properties => { - if (section !== '') { - const propsToSend = this.sanitizeProperties(section, properties) - try { - await updateKapacitorConfigSection( - this.props.kapacitor, - section, - propsToSend - ) - this.refreshKapacitorConfig(this.props.kapacitor) - this.props.notify(notifyAlertEndpointSaved(section)) - return true - } catch ({ - data: {error}, - }) { - const errorMsg = _.join(_.drop(_.split(error, ': '), 2), ': ') - this.props.notify(notifyAlertEndpointSaveFailed(section, errorMsg)) - return false - } - } - } - - handleTestConfig = section => async e => { - e.preventDefault() - - try { - const {data} = await testAlertOutput(this.props.kapacitor, section) - if (data.success) { - this.props.notify(notifyTestAlertSent(section)) - } else { - this.props.notify(notifyTestAlertFailed(section, data.message)) - } - } catch (error) { - this.props.notify(notifyTestAlertFailed(section)) - } - } - - sanitizeProperties = (section, properties) => { - const cleanProps = {...properties, enabled: true} - const {redacted} = this.getSection(this.state.configSections, section) - if (redacted && redacted.length) { - redacted.forEach(badProp => { - if (properties[badProp] === 'true') { - delete cleanProps[badProp] - } - }) - } - - return cleanProps - } - - getInitialIndex = (supportedConfigs, hash) => { - const index = _.indexOf(_.keys(supportedConfigs), _.replace(hash, '#', '')) - return index >= 0 ? index : 0 - } - - isSupportedService = config => { - return ( - config && - this.state.services.find(service => { - return service.name === _.toLower(config.type) - }) - ) - } - - render() { + public render() { const {configSections} = this.state const {hash} = this.props + // console.log(configSections) + if (!configSections) { return null } - const pagerDutyV1Enabled = this.getEnabled(configSections, 'pagerduty') - const opsGenieV1Enabled = this.getEnabled(configSections, 'opsgenie') + const pagerDutyV1Enabled: boolean = this.getEnabled( + configSections, + 'pagerduty' + ) + const opsGenieV1Enabled: boolean = this.getEnabled( + configSections, + 'opsgenie' + ) - const pagerDutyDeprecationMessage = ( + const pagerDutyDeprecationMessage: JSX.Element = (
PagerDuty v1 is being{' '} { @@ -184,14 +192,14 @@ class AlertTabs extends Component {
) - const opsGenieDeprecationMessage = ( + const opsGenieDeprecationMessage: JSX.Element = (
OpsGenie v1 is being deprecated. Please update your Kapacitor and configure OpsGenie v2.
) - const supportedConfigs = { + const supportedConfigs: SupportedConfig = { alerta: { type: 'Alerta', enabled: this.getEnabled(configSections, 'alerta'), @@ -368,7 +376,7 @@ class AlertTabs extends Component { {_.reduce( configSections, - (acc, _cur, k) => { + (acc, __, k) => { return this.isSupportedService(supportedConfigs[k]) ? acc.concat( {_.reduce( configSections, - (acc, _cur, k) => + (acc, __, k) => this.isSupportedService(supportedConfigs[k]) ? acc.concat( @@ -401,22 +409,103 @@ class AlertTabs extends Component { ) } -} -const {func, shape, string} = PropTypes + private refreshKapacitorConfig = async ( + kapacitor: Kapacitor + ): Promise => { + try { + const { + data: {sections}, + } = await getKapacitorConfig(kapacitor) + this.setState({configSections: sections}) + } catch (error) { + this.setState({configSections: null}) + this.props.notify(notifyRefreshKapacitorFailed()) + } + } -AlertTabs.propTypes = { - source: shape({ - id: string.isRequired, - }).isRequired, - kapacitor: shape({ - url: string.isRequired, - links: shape({ - proxy: string.isRequired, - }).isRequired, - }), - notify: func.isRequired, - hash: string.isRequired, + private getSection = (sections: Sections, section: string): Element => { + return _.get(sections, [section, 'elements', '0'], null) + } + + private getEnabled = (sections: Sections, section: string): boolean => { + return _.get( + sections, + [section, 'elements', '0', 'options', 'enabled'], + null + ) + } + + private handleSaveConfig = (section: string) => async ( + properties + ): Promise => { + if (section !== '') { + const propsToSend = this.sanitizeProperties(section, properties) + try { + await updateKapacitorConfigSection( + this.props.kapacitor, + section, + propsToSend + ) + this.refreshKapacitorConfig(this.props.kapacitor) + this.props.notify(notifyAlertEndpointSaved(section)) + return true + } catch ({ + data: {error}, + }) { + const errorMsg = _.join(_.drop(_.split(error, ': '), 2), ': ') + this.props.notify(notifyAlertEndpointSaveFailed(section, errorMsg)) + return false + } + } + } + private handleTestConfig = (section: string) => async ( + e: MouseEvent + ): Promise => { + e.preventDefault() + + try { + const {data} = await testAlertOutput(this.props.kapacitor, section) + if (data.success) { + this.props.notify(notifyTestAlertSent(section)) + } else { + this.props.notify(notifyTestAlertFailed(section, data.message)) + } + } catch (error) { + this.props.notify(notifyTestAlertFailed(section)) + } + } + + private sanitizeProperties = (section: string, properties: Props): Props => { + const cleanProps = {...properties, enabled: true} + const {redacted} = this.getSection(this.state.configSections, section) + if (redacted && redacted.length) { + redacted.forEach(badProp => { + if (properties[badProp] === 'true') { + delete cleanProps[badProp] + } + }) + } + + return cleanProps + } + + private getInitialIndex = ( + supportedConfigs: SupportedConfig, + hash: string + ): number => { + const index = _.indexOf(_.keys(supportedConfigs), _.replace(hash, '#', '')) + return index >= 0 ? index : 0 + } + + private isSupportedService = config => { + return ( + config && + this.state.services.find(service => { + return service.name === _.toLower(config.type) + }) + ) + } } export default AlertTabs diff --git a/ui/src/shared/components/Tabs.tsx b/ui/src/shared/components/Tabs.tsx index c10b7337a3..b5c46c2f19 100644 --- a/ui/src/shared/components/Tabs.tsx +++ b/ui/src/shared/components/Tabs.tsx @@ -96,7 +96,7 @@ TabList.defaultProps = { interface TabPanelsProps { children: JSX.Element[] | JSX.Element - activeIndex: number + activeIndex?: number customClass: string }