From 2a0dee6536f70718cbbd2601bfecc068676a6ee3 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 13 Apr 2017 13:33:58 -0700 Subject: [PATCH 01/47] clean up kapacitor swagger docs --- server/swagger.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/server/swagger.json b/server/swagger.json index 13047913b..08272a7a8 100644 --- a/server/swagger.json +++ b/server/swagger.json @@ -1154,7 +1154,7 @@ "required": true } ], - "summary": "Configured kapacitors", + "summary": "Retrieve list of configured kapacitors", "responses": { "200": { "description": "An array of kapacitors", @@ -1239,7 +1239,7 @@ } ], "summary": "Configured kapacitors", - "description": "These kapacitors are used for monitoring and alerting.", + "description": "Retrieve information on a single kapacitor instance", "responses": { "200": { "description": "Kapacitor connection information", @@ -1334,7 +1334,8 @@ "required": true } ], - "summary": "This specific kapacitor will be removed. All associated rule resources will also be removed from the store.", + "summary": "Remove Kapacitor backend", + "description": "This specific kapacitor will be removed. All associated rule resources will also be removed from the store.", "responses": { "204": { "description": "kapacitor has been removed." @@ -1683,7 +1684,7 @@ "kapacitors", "proxy" ], - "description": "DELETE to `path` of kapacitor. The response and status code from kapacitor is directly returned.", + "description": "DELETE to `path` of kapacitor. The response and status code from kapacitor is directly returned.", "parameters": [ { "name": "id", From b198755da26534aa9e5c3423d2f2fe75ebcde3d9 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 13 Apr 2017 14:07:30 -0700 Subject: [PATCH 02/47] remove navigation to kapacitor config app --- ui/src/side_nav/containers/SideNav.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/src/side_nav/containers/SideNav.js b/ui/src/side_nav/containers/SideNav.js index c98893d2d..6a30c20f2 100644 --- a/ui/src/side_nav/containers/SideNav.js +++ b/ui/src/side_nav/containers/SideNav.js @@ -56,8 +56,6 @@ const SideNav = React.createClass({ - InfluxDB - Kapacitor { showLogout ? ( From 429ef27661a9255838fbe6f52a143be6364b8ff3 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 13 Apr 2017 16:33:27 -0700 Subject: [PATCH 03/47] move influx source managament table to separate component --- ui/src/sources/components/InfluxTable.js | 81 ++++++++++++++++++++++ ui/src/sources/containers/ManageSources.js | 63 ++++------------- 2 files changed, 94 insertions(+), 50 deletions(-) create mode 100644 ui/src/sources/components/InfluxTable.js diff --git a/ui/src/sources/components/InfluxTable.js b/ui/src/sources/components/InfluxTable.js new file mode 100644 index 000000000..8f24d7f03 --- /dev/null +++ b/ui/src/sources/components/InfluxTable.js @@ -0,0 +1,81 @@ +import React, {PropTypes} from 'react' +import {Link} from 'react-router' + +const InfluxTable = ({ + sources, + source, + handleDeleteSource, + kapacitors, + location, +}) => ( +
+
+ +
+
+

InfluxDB Sources

+ Add New Source +
+
+
+ + + + + + + + + + + { + sources.map((s) => { + const kapacitorName = kapacitors[s.id] ? kapacitors[s.id].name : '' + return ( + + + + + + + ) + }) + } + +
NameHostKapacitor
{s.name}{s.default ? Default : null}{s.url}{kapacitorName ? kapacitorName : "--"} + + Connect + +
+
+
+
+
+
+) + +const { + array, + func, + object, + shape, + string, +} = PropTypes + +InfluxTable.propTypes = { + sources: array.isRequired, + kapacitors: object, + location: shape({ + pathname: string.isRequired, + }).isRequired, + handleDeleteSource: func.isRequired, + source: shape({ + id: string.isRequired, + links: shape({ + proxy: string.isRequired, + self: string.isRequired, + }), + }), +} + +export default InfluxTable diff --git a/ui/src/sources/containers/ManageSources.js b/ui/src/sources/containers/ManageSources.js index 715afb06f..d5108c964 100644 --- a/ui/src/sources/containers/ManageSources.js +++ b/ui/src/sources/containers/ManageSources.js @@ -1,9 +1,11 @@ import React, {PropTypes} from 'react' -import {withRouter, Link} from 'react-router' +import {withRouter} from 'react-router' import {getKapacitor} from 'shared/apis' import {removeAndLoadSources} from 'src/shared/actions/sources' import {connect} from 'react-redux' +import InfluxTable from '../components/InfluxTable' + const { array, func, @@ -59,67 +61,28 @@ export const ManageSources = React.createClass({ }, render() { + // probably should move kapacitors to props and use redux store const {kapacitors} = this.state - const {sources} = this.props - const {pathname} = this.props.location - const numSources = sources.length - const sourcesTitle = `${numSources} ${numSources === 1 ? 'Source' : 'Sources'}` + const {sources, source, location} = this.props return (
-

InfluxDB Sources

+

Configuration

-
-
- -
-
-

{sourcesTitle}

- Add New Source -
-
-
- - - - - - - - - - - { - sources.map((source) => { - const kapacitorName = kapacitors[source.id] ? kapacitors[source.id].name : '' - return ( - - - - - - - ) - }) - } - -
NameHostKapacitor
{source.name}{source.default ? Default : null}{source.url}{kapacitorName ? kapacitorName : "--"} - - Connect - -
-
-
-
-
-
+
From 17e700767f26b55c5c15efb990409bd437e9e85a Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 13 Apr 2017 16:34:13 -0700 Subject: [PATCH 04/47] remove unused withRouter call --- ui/src/kapacitor/containers/KapacitorRulePage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/src/kapacitor/containers/KapacitorRulePage.js b/ui/src/kapacitor/containers/KapacitorRulePage.js index df55b05d9..9057014d9 100644 --- a/ui/src/kapacitor/containers/KapacitorRulePage.js +++ b/ui/src/kapacitor/containers/KapacitorRulePage.js @@ -1,5 +1,4 @@ import React, {PropTypes} from 'react' -import {withRouter} from 'react-router' import {connect} from 'react-redux' import _ from 'lodash' import * as kapacitorActionCreators from '../actions/view' @@ -117,4 +116,4 @@ function mapDispatchToProps(dispatch) { } } -export default connect(mapStateToProps, mapDispatchToProps)(withRouter(KapacitorRulePage)) +export default connect(mapStateToProps, mapDispatchToProps)(KapacitorRulePage) From 998a3ad7d02064f941a0adf8685455f10322e56d Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 13 Apr 2017 16:43:45 -0700 Subject: [PATCH 05/47] add kapacitor table to manage sources page --- ui/src/sources/components/InfluxTable.js | 1 - ui/src/sources/components/KapacitorTable.js | 33 +++++++++++++++++++++ ui/src/sources/containers/ManageSources.js | 2 ++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 ui/src/sources/components/KapacitorTable.js diff --git a/ui/src/sources/components/InfluxTable.js b/ui/src/sources/components/InfluxTable.js index 8f24d7f03..bdbde89f0 100644 --- a/ui/src/sources/components/InfluxTable.js +++ b/ui/src/sources/components/InfluxTable.js @@ -10,7 +10,6 @@ const InfluxTable = ({ }) => (
-

InfluxDB Sources

diff --git a/ui/src/sources/components/KapacitorTable.js b/ui/src/sources/components/KapacitorTable.js new file mode 100644 index 000000000..9765d0887 --- /dev/null +++ b/ui/src/sources/components/KapacitorTable.js @@ -0,0 +1,33 @@ +import React from 'react' +import {Link} from 'react-router' + +const KapacitorTable = () => ( +
+
+
+
+

Kapacitor Nodes

+ Add New Source +
+
+
+ + + + + + + + + + + +
NameURLConfigured Endpoints
+
+
+
+
+
+) + +export default KapacitorTable diff --git a/ui/src/sources/containers/ManageSources.js b/ui/src/sources/containers/ManageSources.js index d5108c964..01e138bda 100644 --- a/ui/src/sources/containers/ManageSources.js +++ b/ui/src/sources/containers/ManageSources.js @@ -5,6 +5,7 @@ import {removeAndLoadSources} from 'src/shared/actions/sources' import {connect} from 'react-redux' import InfluxTable from '../components/InfluxTable' +import KapacitorTable from '../components/KapacitorTable' const { array, @@ -83,6 +84,7 @@ export const ManageSources = React.createClass({ kapacitors={kapacitors} location={location} /> +
From 17ff852d94bafe521c29c3eee08eb15836974542 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 13 Apr 2017 17:35:26 -0700 Subject: [PATCH 06/47] add kapacitors to AllRoutes --- server/routes.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/routes.go b/server/routes.go index 879766744..67dbf25cd 100644 --- a/server/routes.go +++ b/server/routes.go @@ -34,6 +34,7 @@ type getRoutesResponse struct { Sources string `json:"sources"` // Location of the sources endpoint Me string `json:"me"` // Location of the me endpoint Dashboards string `json:"dashboards"` // Location of the dashboards endpoint + Kapacitors string `json:"kapacitors"` // Location of the kapacitors endpoint Auth []AuthRoute `json:"auth"` // Location of all auth routes. } @@ -45,6 +46,7 @@ func AllRoutes(authRoutes []AuthRoute, logger chronograf.Logger) http.HandlerFun Me: "/chronograf/v1/me", Mappings: "/chronograf/v1/mappings", Dashboards: "/chronograf/v1/dashboards", + Kapacitors: "/chronograf/v1/kapacitors", Auth: make([]AuthRoute, len(authRoutes)), } From 7a5457644a7222a3e062e936256bd3ccc8e08f6e Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 13 Apr 2017 17:44:11 -0700 Subject: [PATCH 07/47] WIP --- server/routes.go | 2 -- ui/src/sources/components/InfluxTable.js | 2 +- ui/src/sources/components/KapacitorTable.js | 12 ++++++++++-- ui/src/sources/containers/ManageSources.js | 8 +++++++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/server/routes.go b/server/routes.go index 67dbf25cd..879766744 100644 --- a/server/routes.go +++ b/server/routes.go @@ -34,7 +34,6 @@ type getRoutesResponse struct { Sources string `json:"sources"` // Location of the sources endpoint Me string `json:"me"` // Location of the me endpoint Dashboards string `json:"dashboards"` // Location of the dashboards endpoint - Kapacitors string `json:"kapacitors"` // Location of the kapacitors endpoint Auth []AuthRoute `json:"auth"` // Location of all auth routes. } @@ -46,7 +45,6 @@ func AllRoutes(authRoutes []AuthRoute, logger chronograf.Logger) http.HandlerFun Me: "/chronograf/v1/me", Mappings: "/chronograf/v1/mappings", Dashboards: "/chronograf/v1/dashboards", - Kapacitors: "/chronograf/v1/kapacitors", Auth: make([]AuthRoute, len(authRoutes)), } diff --git a/ui/src/sources/components/InfluxTable.js b/ui/src/sources/components/InfluxTable.js index bdbde89f0..c6f93320d 100644 --- a/ui/src/sources/components/InfluxTable.js +++ b/ui/src/sources/components/InfluxTable.js @@ -13,7 +13,7 @@ const InfluxTable = ({

InfluxDB Sources

- Add New Source + Add Source
diff --git a/ui/src/sources/components/KapacitorTable.js b/ui/src/sources/components/KapacitorTable.js index 9765d0887..44c750141 100644 --- a/ui/src/sources/components/KapacitorTable.js +++ b/ui/src/sources/components/KapacitorTable.js @@ -1,4 +1,4 @@ -import React from 'react' +import React, {PropTypes} from 'react' import {Link} from 'react-router' const KapacitorTable = () => ( @@ -7,7 +7,7 @@ const KapacitorTable = () => (

Kapacitor Nodes

- Add New Source + Add Node
@@ -30,4 +30,12 @@ const KapacitorTable = () => (
) +// const { +// array, +// } = PropTypes +// +// KapacitorTable.propTypes = { +// kapacitors: array.isRequired, +// } + export default KapacitorTable diff --git a/ui/src/sources/containers/ManageSources.js b/ui/src/sources/containers/ManageSources.js index 01e138bda..11b750f6e 100644 --- a/ui/src/sources/containers/ManageSources.js +++ b/ui/src/sources/containers/ManageSources.js @@ -1,6 +1,6 @@ import React, {PropTypes} from 'react' import {withRouter} from 'react-router' -import {getKapacitor} from 'shared/apis' +import {getKapacitor, getKapacitors} from 'shared/apis' import {removeAndLoadSources} from 'src/shared/actions/sources' import {connect} from 'react-redux' @@ -40,6 +40,12 @@ export const ManageSources = React.createClass({ componentDidMount() { const updates = [] const kapas = {} + // getKapacitors.then((kapacitors) => { + // console.log(kapacitors) + // }).catch(() => { + // console.log("wow you suck") + // }) + this.props.sources.forEach((source) => { const prom = getKapacitor(source).then((kapacitor) => { kapas[source.id] = kapacitor From 10d422b062008ac56389cbdfb204f552ae754843 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Fri, 14 Apr 2017 15:21:58 -0700 Subject: [PATCH 08/47] add dropdown for kapacitors --- ui/src/sources/components/InfluxTable.js | 9 ++++- ui/src/sources/components/KapacitorTable.js | 41 --------------------- ui/src/sources/containers/ManageSources.js | 9 +---- 3 files changed, 9 insertions(+), 50 deletions(-) delete mode 100644 ui/src/sources/components/KapacitorTable.js diff --git a/ui/src/sources/components/InfluxTable.js b/ui/src/sources/components/InfluxTable.js index c6f93320d..56da36ad8 100644 --- a/ui/src/sources/components/InfluxTable.js +++ b/ui/src/sources/components/InfluxTable.js @@ -1,6 +1,8 @@ import React, {PropTypes} from 'react' import {Link} from 'react-router' +import Dropdown from 'shared/components/Dropdown' + const InfluxTable = ({ sources, source, @@ -34,7 +36,12 @@ const InfluxTable = ({ {s.name}{s.default ? Default : null} {s.url} - {kapacitorName ? kapacitorName : "--"} + { + kapacitorName ? + {}} selected={kapacitorName} /> : + -- + } + Connect diff --git a/ui/src/sources/components/KapacitorTable.js b/ui/src/sources/components/KapacitorTable.js deleted file mode 100644 index 44c750141..000000000 --- a/ui/src/sources/components/KapacitorTable.js +++ /dev/null @@ -1,41 +0,0 @@ -import React, {PropTypes} from 'react' -import {Link} from 'react-router' - -const KapacitorTable = () => ( -
-
-
-
-

Kapacitor Nodes

- Add Node -
-
-
- - - - - - - - - - - -
NameURLConfigured Endpoints
-
-
-
-
-
-) - -// const { -// array, -// } = PropTypes -// -// KapacitorTable.propTypes = { -// kapacitors: array.isRequired, -// } - -export default KapacitorTable diff --git a/ui/src/sources/containers/ManageSources.js b/ui/src/sources/containers/ManageSources.js index 11b750f6e..d4662470e 100644 --- a/ui/src/sources/containers/ManageSources.js +++ b/ui/src/sources/containers/ManageSources.js @@ -1,11 +1,10 @@ import React, {PropTypes} from 'react' import {withRouter} from 'react-router' -import {getKapacitor, getKapacitors} from 'shared/apis' +import {getKapacitor} from 'shared/apis' import {removeAndLoadSources} from 'src/shared/actions/sources' import {connect} from 'react-redux' import InfluxTable from '../components/InfluxTable' -import KapacitorTable from '../components/KapacitorTable' const { array, @@ -40,11 +39,6 @@ export const ManageSources = React.createClass({ componentDidMount() { const updates = [] const kapas = {} - // getKapacitors.then((kapacitors) => { - // console.log(kapacitors) - // }).catch(() => { - // console.log("wow you suck") - // }) this.props.sources.forEach((source) => { const prom = getKapacitor(source).then((kapacitor) => { @@ -90,7 +84,6 @@ export const ManageSources = React.createClass({ kapacitors={kapacitors} location={location} /> -
From f8a4b1bae41a3ea848df23328cf172ca695a99fd Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Fri, 14 Apr 2017 17:53:09 -0700 Subject: [PATCH 09/47] store kapacitor response in redux store --- ui/src/shared/actions/sources.js | 22 ++++++++++++++- ui/src/shared/apis/index.js | 12 ++++++++ ui/src/shared/reducers/sources.js | 12 ++++++++ ui/src/sources/components/InfluxTable.js | 2 +- ui/src/sources/containers/ManageSources.js | 33 +++++++++------------- 5 files changed, 60 insertions(+), 21 deletions(-) diff --git a/ui/src/shared/actions/sources.js b/ui/src/shared/actions/sources.js index 9fb356ffb..17c5e4656 100644 --- a/ui/src/shared/actions/sources.js +++ b/ui/src/shared/actions/sources.js @@ -1,4 +1,7 @@ -import {deleteSource, getSources} from 'src/shared/apis' +import {deleteSource, + getSources, + getKapacitors as getKapacitorsAJAX, +} from 'src/shared/apis' import {publishNotification} from './notifications' export const loadSources = (sources) => ({ @@ -22,6 +25,14 @@ export const addSource = (source) => ({ }, }) +export const fetchKapacitors = (source, kapacitors) => ({ + type: 'LOAD_KAPACITORS', + payload: { + source, + kapacitors, + }, +}) + // Async action creators export const removeAndLoadSources = (source) => async (dispatch) => { @@ -42,3 +53,12 @@ export const removeAndLoadSources = (source) => async (dispatch) => { dispatch(publishNotification("error", "Internal Server Error. Check API Logs")) } } + +export const fetchKapacitorsAsync = (source) => async (dispatch) => { + try { + const {data} = await getKapacitorsAJAX(source) + dispatch(fetchKapacitors(source, data.kapacitors)) + } catch (err) { + dispatch(publishNotification('error', `Internal Server Error. Could not retrieve kapacitors for source ${source.id}.`)) + } +} diff --git a/ui/src/shared/apis/index.js b/ui/src/shared/apis/index.js index d42f63885..66ea8e82d 100644 --- a/ui/src/shared/apis/index.js +++ b/ui/src/shared/apis/index.js @@ -67,6 +67,18 @@ export function getKapacitor(source) { }) } +export const getKapacitors = async (source) => { + try { + return await AJAX({ + method: 'GET', + url: source.links.kapacitors, + }) + } catch (error) { + console.error(error) + throw error + } +} + export function createKapacitor(source, {url, name = 'My Kapacitor', username, password}) { return AJAX({ url: source.links.kapacitors, diff --git a/ui/src/shared/reducers/sources.js b/ui/src/shared/reducers/sources.js index cfef11c40..9903b69e1 100644 --- a/ui/src/shared/reducers/sources.js +++ b/ui/src/shared/reducers/sources.js @@ -1,8 +1,12 @@ +import _ from 'lodash' + const getInitialState = () => [] const initialState = getInitialState() const sourcesReducer = (state = initialState, action) => { + +export default function sources(state = [], action) { switch (action.type) { case 'LOAD_SOURCES': { return action.payload.sources @@ -25,6 +29,14 @@ const sourcesReducer = (state = initialState, action) => { }) : state return [...updatedSources, source] } + + case 'LOAD_KAPACITORS': { + const {source, kapacitors} = action.payload + const sourceIndex = state.findIndex((s) => s.id === source.id) + const updatedSources = _.cloneDeep(state) + updatedSources[sourceIndex].kapacitors = kapacitors + return updatedSources + } } return state diff --git a/ui/src/sources/components/InfluxTable.js b/ui/src/sources/components/InfluxTable.js index 56da36ad8..e37ad412b 100644 --- a/ui/src/sources/components/InfluxTable.js +++ b/ui/src/sources/components/InfluxTable.js @@ -39,7 +39,7 @@ const InfluxTable = ({ { kapacitorName ? {}} selected={kapacitorName} /> : - -- + -- } diff --git a/ui/src/sources/containers/ManageSources.js b/ui/src/sources/containers/ManageSources.js index d4662470e..e2933753b 100644 --- a/ui/src/sources/containers/ManageSources.js +++ b/ui/src/sources/containers/ManageSources.js @@ -1,8 +1,8 @@ import React, {PropTypes} from 'react' import {withRouter} from 'react-router' -import {getKapacitor} from 'shared/apis' -import {removeAndLoadSources} from 'src/shared/actions/sources' +import {removeAndLoadSources, fetchKapacitorsAsync} from 'src/shared/actions/sources' import {connect} from 'react-redux' +import {bindActionCreators} from 'redux' import InfluxTable from '../components/InfluxTable' @@ -27,7 +27,8 @@ export const ManageSources = React.createClass({ }), sources: array, addFlashMessage: func, - removeAndLoadSources: func, + removeAndLoadSources: func.isRequired, + fetchKapacitors: func.isRequired, }, getInitialState() { @@ -37,17 +38,8 @@ export const ManageSources = React.createClass({ }, componentDidMount() { - const updates = [] - const kapas = {} - this.props.sources.forEach((source) => { - const prom = getKapacitor(source).then((kapacitor) => { - kapas[source.id] = kapacitor - }) - updates.push(prom) - }) - Promise.all(updates).then(() => { - this.setState({kapacitors: kapas}) + this.props.fetchKapacitors(source) }) }, @@ -91,10 +83,13 @@ export const ManageSources = React.createClass({ }, }) -function mapStateToProps(state) { - return { - sources: state.sources, - } -} +const mapStateToProps = ({sources}) => ({ + sources, +}) -export default connect(mapStateToProps, {removeAndLoadSources})(withRouter(ManageSources)) +const mapDispatchToProps = (dispatch) => ({ + removeAndLoadSources: bindActionCreators(removeAndLoadSources, dispatch), + fetchKapacitors: bindActionCreators(fetchKapacitorsAsync, dispatch), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(withRouter(ManageSources)) From 54ad4aaa7656e23d46822e45635ff364743c04c3 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Fri, 14 Apr 2017 19:57:00 -0700 Subject: [PATCH 10/47] use kapacitor redux to display multiple kapacitors per source --- ui/src/sources/components/InfluxTable.js | 27 ++++++++++++++-------- ui/src/sources/containers/ManageSources.js | 9 -------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ui/src/sources/components/InfluxTable.js b/ui/src/sources/components/InfluxTable.js index e37ad412b..338931bcc 100644 --- a/ui/src/sources/components/InfluxTable.js +++ b/ui/src/sources/components/InfluxTable.js @@ -3,11 +3,24 @@ import {Link} from 'react-router' import Dropdown from 'shared/components/Dropdown' +const kapacitorDropdown = (kapacitors) => { + if (!kapacitors || kapacitors.length === 0) { + return ( + -- + ) + } + const kapacitorItems = kapacitors.map((k) => { + return {text: k.name} + }) + return ( + {}} selected={kapacitorItems[0].text} /> + ) +} + const InfluxTable = ({ sources, source, handleDeleteSource, - kapacitors, location, }) => (
@@ -31,16 +44,14 @@ const InfluxTable = ({ { sources.map((s) => { - const kapacitorName = kapacitors[s.id] ? kapacitors[s.id].name : '' return ( {s.name}{s.default ? Default : null} {s.url} - { - kapacitorName ? - {}} selected={kapacitorName} /> : - -- - } + + { + kapacitorDropdown(s.kapacitors) + } @@ -63,14 +74,12 @@ const InfluxTable = ({ const { array, func, - object, shape, string, } = PropTypes InfluxTable.propTypes = { sources: array.isRequired, - kapacitors: object, location: shape({ pathname: string.isRequired, }).isRequired, diff --git a/ui/src/sources/containers/ManageSources.js b/ui/src/sources/containers/ManageSources.js index e2933753b..3bba7b1a5 100644 --- a/ui/src/sources/containers/ManageSources.js +++ b/ui/src/sources/containers/ManageSources.js @@ -31,12 +31,6 @@ export const ManageSources = React.createClass({ fetchKapacitors: func.isRequired, }, - getInitialState() { - return { - kapacitors: {}, - } - }, - componentDidMount() { this.props.sources.forEach((source) => { this.props.fetchKapacitors(source) @@ -54,8 +48,6 @@ export const ManageSources = React.createClass({ }, render() { - // probably should move kapacitors to props and use redux store - const {kapacitors} = this.state const {sources, source, location} = this.props return ( @@ -73,7 +65,6 @@ export const ManageSources = React.createClass({ handleDeleteSource={this.handleDeleteSource} source={source} sources={sources} - kapacitors={kapacitors} location={location} />
From 678cd8ab18b617795811845bc9efb605ac779edf Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Fri, 14 Apr 2017 20:12:49 -0700 Subject: [PATCH 11/47] Revert "remove navigation to kapacitor config app" This reverts commit de7bcf7040a246c9d606f3f1547d8f5afd54e457. --- ui/src/side_nav/containers/SideNav.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/src/side_nav/containers/SideNav.js b/ui/src/side_nav/containers/SideNav.js index 6a30c20f2..c98893d2d 100644 --- a/ui/src/side_nav/containers/SideNav.js +++ b/ui/src/side_nav/containers/SideNav.js @@ -56,6 +56,8 @@ const SideNav = React.createClass({ + InfluxDB + Kapacitor { showLogout ? ( From 95698388c0ef5999877547a29ae6779b7f41d9ce Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Sat, 15 Apr 2017 10:00:50 -0700 Subject: [PATCH 12/47] use modern component declaration for ManageSources --- ui/src/sources/containers/ManageSources.js | 62 ++++++++++++---------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/ui/src/sources/containers/ManageSources.js b/ui/src/sources/containers/ManageSources.js index 3bba7b1a5..750fa5dc4 100644 --- a/ui/src/sources/containers/ManageSources.js +++ b/ui/src/sources/containers/ManageSources.js @@ -1,4 +1,4 @@ -import React, {PropTypes} from 'react' +import React, {Component, PropTypes} from 'react' import {withRouter} from 'react-router' import {removeAndLoadSources, fetchKapacitorsAsync} from 'src/shared/actions/sources' import {connect} from 'react-redux' @@ -6,36 +6,18 @@ import {bindActionCreators} from 'redux' import InfluxTable from '../components/InfluxTable' -const { - array, - func, - shape, - string, -} = PropTypes +class ManageSources extends Component { + constructor(props) { + super(props) -export const ManageSources = React.createClass({ - propTypes: { - location: shape({ - pathname: string.isRequired, - }).isRequired, - source: shape({ - id: string.isRequired, - links: shape({ - proxy: string.isRequired, - self: string.isRequired, - }), - }), - sources: array, - addFlashMessage: func, - removeAndLoadSources: func.isRequired, - fetchKapacitors: func.isRequired, - }, + this.handleDeleteSource = ::this.handleDeleteSource + } componentDidMount() { this.props.sources.forEach((source) => { this.props.fetchKapacitors(source) }) - }, + } handleDeleteSource(source) { const {addFlashMessage} = this.props @@ -45,7 +27,7 @@ export const ManageSources = React.createClass({ } catch (e) { addFlashMessage({type: 'error', text: 'Could not remove source from Chronograf'}) } - }, + } render() { const {sources, source, location} = this.props @@ -71,8 +53,32 @@ export const ManageSources = React.createClass({
) - }, -}) + } +} + +const { + array, + func, + shape, + string, +} = PropTypes + +ManageSources.propTypes = { + location: shape({ + pathname: string.isRequired, + }).isRequired, + source: shape({ + id: string.isRequired, + links: shape({ + proxy: string.isRequired, + self: string.isRequired, + }), + }), + sources: array, + addFlashMessage: func, + removeAndLoadSources: func.isRequired, + fetchKapacitors: func.isRequired, +} const mapStateToProps = ({sources}) => ({ sources, From e58e9efdedda3f7589a4896dcf84ecd14bcbb63d Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Sun, 16 Apr 2017 15:17:04 -0700 Subject: [PATCH 13/47] update Dropdown to use new es6 format --- ui/src/shared/components/Dropdown.js | 74 ++++++++++++++++------------ 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/ui/src/shared/components/Dropdown.js b/ui/src/shared/components/Dropdown.js index 8d18c6a55..8815aaebf 100644 --- a/ui/src/shared/components/Dropdown.js +++ b/ui/src/shared/components/Dropdown.js @@ -1,51 +1,44 @@ -import React, {PropTypes} from 'react' +import React, {Component, PropTypes} from 'react' import classnames from 'classnames' import OnClickOutside from 'shared/components/OnClickOutside' -const { - arrayOf, - shape, - string, - func, -} = PropTypes - -const Dropdown = React.createClass({ - propTypes: { - items: arrayOf(shape({ - text: string.isRequired, - })).isRequired, - onChoose: func.isRequired, - selected: string.isRequired, - iconName: string, - className: string, - }, - getInitialState() { - return { +class Dropdown extends Component { + constructor(props) { + super(props) + this.state = { isOpen: false, } - }, - getDefaultProps() { - return { - actions: [], - } - }, + + this.handleClickOutside = ::this.handleClickOutside + this.handleSelection = ::this.handleSelection + this.toggleMenu = ::this.toggleMenu + } + + static defaultProps = { + actions: [], + } + handleClickOutside() { this.setState({isOpen: false}) - }, + } + handleSelection(item) { this.toggleMenu() this.props.onChoose(item) - }, + } + toggleMenu(e) { if (e) { e.stopPropagation() } this.setState({isOpen: !this.state.isOpen}) - }, + } + handleAction(e, action, item) { e.stopPropagation() action.handler(item) - }, + } + render() { const self = this const {items, selected, className, iconName, actions} = self.props @@ -81,7 +74,24 @@ const Dropdown = React.createClass({ : null}
) - }, -}) + } +} + +const { + arrayOf, + shape, + string, + func, +} = PropTypes + +Dropdown.propTypes = { + items: arrayOf(shape({ + text: string.isRequired, + })).isRequired, + onChoose: func.isRequired, + selected: string.isRequired, + iconName: string, + className: string, +} export default OnClickOutside(Dropdown) From 272d78c8d9639c74ae6fc6ca56bc979fa1f3a3c3 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Sun, 16 Apr 2017 16:31:21 -0700 Subject: [PATCH 14/47] provide link in dropdown to new kapacitor page --- ui/src/index.js | 1 + .../containers/CreateKapacitorPage.js | 15 ++++++++++ ui/src/kapacitor/index.js | 3 +- ui/src/shared/components/Dropdown.js | 29 ++++++++++++++++--- ui/src/sources/components/InfluxTable.js | 14 +++++++-- 5 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 ui/src/kapacitor/containers/CreateKapacitorPage.js diff --git a/ui/src/index.js b/ui/src/index.js index dd4844022..8c618e3ee 100644 --- a/ui/src/index.js +++ b/ui/src/index.js @@ -109,6 +109,7 @@ const Root = React.createClass({ + diff --git a/ui/src/kapacitor/containers/CreateKapacitorPage.js b/ui/src/kapacitor/containers/CreateKapacitorPage.js new file mode 100644 index 000000000..d8f6e46db --- /dev/null +++ b/ui/src/kapacitor/containers/CreateKapacitorPage.js @@ -0,0 +1,15 @@ +import React, {Component} from 'react' + +class CreateKapacitorPage extends Component { + constructor(props) { + super(props) + } + + render() { + return ( +
+ ) + } +} + +export default CreateKapacitorPage diff --git a/ui/src/kapacitor/index.js b/ui/src/kapacitor/index.js index 951ce6ff1..20300ea9b 100644 --- a/ui/src/kapacitor/index.js +++ b/ui/src/kapacitor/index.js @@ -1,5 +1,6 @@ +import CreateKapacitorPage from './containers/CreateKapacitorPage' import KapacitorPage from './containers/KapacitorPage' import KapacitorRulePage from './containers/KapacitorRulePage' import KapacitorRulesPage from './containers/KapacitorRulesPage' import KapacitorTasksPage from './containers/KapacitorTasksPage' -export {KapacitorPage, KapacitorRulePage, KapacitorRulesPage, KapacitorTasksPage} +export {CreateKapacitorPage, KapacitorPage, KapacitorRulePage, KapacitorRulesPage, KapacitorTasksPage} diff --git a/ui/src/shared/components/Dropdown.js b/ui/src/shared/components/Dropdown.js index 8815aaebf..3211dba08 100644 --- a/ui/src/shared/components/Dropdown.js +++ b/ui/src/shared/components/Dropdown.js @@ -1,5 +1,7 @@ import React, {Component, PropTypes} from 'react' +import {Link} from 'react-router' import classnames from 'classnames' + import OnClickOutside from 'shared/components/OnClickOutside' class Dropdown extends Component { @@ -12,6 +14,7 @@ class Dropdown extends Component { this.handleClickOutside = ::this.handleClickOutside this.handleSelection = ::this.handleSelection this.toggleMenu = ::this.toggleMenu + this.handleAction = ::this.handleAction } static defaultProps = { @@ -40,8 +43,8 @@ class Dropdown extends Component { } render() { - const self = this - const {items, selected, className, iconName, actions} = self.props + const {items, selected, className, iconName, actions, addNew} = this.props + const {isOpen} = this.state return (
@@ -50,7 +53,7 @@ class Dropdown extends Component { {selected}
- {self.state.isOpen ? + {isOpen ?
    {items.map((item, i) => { return ( @@ -61,7 +64,7 @@ class Dropdown extends Component {
    {actions.map((action) => { return ( - ) @@ -70,6 +73,15 @@ class Dropdown extends Component { ) })} + { + addNew ? +
  • + + {addNew.text} + +
  • : + null + }
: null}
@@ -85,10 +97,19 @@ const { } = PropTypes Dropdown.propTypes = { + actions: arrayOf(shape({ + icon: string.isRequired, + target: string.isRequired, + text: string.isRequired, + })), items: arrayOf(shape({ text: string.isRequired, })).isRequired, onChoose: func.isRequired, + addNew: shape({ + url: string.isRequired, + text: string.isRequired, + }), selected: string.isRequired, iconName: string, className: string, diff --git a/ui/src/sources/components/InfluxTable.js b/ui/src/sources/components/InfluxTable.js index 338931bcc..a7bc01c79 100644 --- a/ui/src/sources/components/InfluxTable.js +++ b/ui/src/sources/components/InfluxTable.js @@ -3,7 +3,7 @@ import {Link} from 'react-router' import Dropdown from 'shared/components/Dropdown' -const kapacitorDropdown = (kapacitors) => { +const kapacitorDropdown = (kapacitors, source) => { if (!kapacitors || kapacitors.length === 0) { return ( -- @@ -13,7 +13,15 @@ const kapacitorDropdown = (kapacitors) => { return {text: k.name} }) return ( - {}} selected={kapacitorItems[0].text} /> + {}} + addNew={{ + url: `/sources/${source.id}/kapacitors/new`, + text: "Add Kapacitor", + }} + selected={kapacitorItems[0].text} + /> ) } @@ -50,7 +58,7 @@ const InfluxTable = ({ {s.url} { - kapacitorDropdown(s.kapacitors) + kapacitorDropdown(s.kapacitors, source) } From ac32361d141cafc39d273496845b499fdaf41958 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Mon, 17 Apr 2017 11:52:15 -0700 Subject: [PATCH 15/47] refactor KapacitorPage to use es6 syntax --- ui/src/index.js | 4 +- .../containers/CreateKapacitorPage.js | 15 --- ui/src/kapacitor/containers/KapacitorPage.js | 94 ++++++++++--------- ui/src/kapacitor/index.js | 3 +- 4 files changed, 54 insertions(+), 62 deletions(-) delete mode 100644 ui/src/kapacitor/containers/CreateKapacitorPage.js diff --git a/ui/src/index.js b/ui/src/index.js index 8c618e3ee..e6f7665c0 100644 --- a/ui/src/index.js +++ b/ui/src/index.js @@ -109,8 +109,8 @@ const Root = React.createClass({ - - + + diff --git a/ui/src/kapacitor/containers/CreateKapacitorPage.js b/ui/src/kapacitor/containers/CreateKapacitorPage.js deleted file mode 100644 index d8f6e46db..000000000 --- a/ui/src/kapacitor/containers/CreateKapacitorPage.js +++ /dev/null @@ -1,15 +0,0 @@ -import React, {Component} from 'react' - -class CreateKapacitorPage extends Component { - constructor(props) { - super(props) - } - - render() { - return ( -
- ) - } -} - -export default CreateKapacitorPage diff --git a/ui/src/kapacitor/containers/KapacitorPage.js b/ui/src/kapacitor/containers/KapacitorPage.js index d7a1651ef..9032ac076 100644 --- a/ui/src/kapacitor/containers/KapacitorPage.js +++ b/ui/src/kapacitor/containers/KapacitorPage.js @@ -1,4 +1,4 @@ -import React, {PropTypes} from 'react' +import React, {Component, PropTypes} from 'react' import { getKapacitor, createKapacitor, @@ -10,23 +10,10 @@ import KapacitorForm from '../components/KapacitorForm' const defaultName = "My Kapacitor" const kapacitorPort = "9092" -const { - func, - shape, - string, -} = PropTypes - -export const KapacitorPage = React.createClass({ - propTypes: { - source: shape({ - id: string.isRequired, - url: string.isRequired, - }), - addFlashMessage: func, - }, - - getInitialState() { - return { +class KapacitorPage extends Component { + constructor(props) { + super(props) + this.state = { kapacitor: { url: this._parseKapacitorURL(), name: defaultName, @@ -35,7 +22,12 @@ export const KapacitorPage = React.createClass({ }, exists: false, } - }, + + this.handleInputChange = ::this.handleInputChange + this.handleSubmit = ::this.handleSubmit + this.handleResetToDefaults = ::this.handleResetToDefaults + this._parseKapacitorURL = ::this._parseKapacitorURL + } componentDidMount() { const {source} = this.props @@ -50,24 +42,7 @@ export const KapacitorPage = React.createClass({ }) }) }) - }, - - render() { - const {source, addFlashMessage} = this.props - const {kapacitor, exists} = this.state - - return ( - - ) - }, + } handleInputChange(e) { const {value, name} = e.target @@ -76,8 +51,7 @@ export const KapacitorPage = React.createClass({ const update = {[name]: value.trim()} return {kapacitor: {...prevState.kapacitor, ...update}} }) - }, - + } handleSubmit(e) { e.preventDefault() @@ -99,7 +73,7 @@ export const KapacitorPage = React.createClass({ addFlashMessage({type: 'error', text: 'There was a problem creating the Kapacitor record'}) }) } - }, + } handleResetToDefaults(e) { e.preventDefault() @@ -111,14 +85,48 @@ export const KapacitorPage = React.createClass({ } this.setState({kapacitor: {...defaultState}}) - }, + } _parseKapacitorURL() { const parser = document.createElement('a') parser.href = this.props.source.url return `${parser.protocol}//${parser.hostname}:${kapacitorPort}` - }, -}) + } + + render() { + const {source, addFlashMessage} = this.props + const {kapacitor, exists} = this.state + + return ( + + ) + } +} + +const { + func, + shape, + string, +} = PropTypes + +KapacitorPage.propTypes = { + addFlashMessage: func, + params: shape({ + id: string, + }).isRequired, + source: shape({ + id: string.isRequired, + url: string.isRequired, + }), +} export default KapacitorPage diff --git a/ui/src/kapacitor/index.js b/ui/src/kapacitor/index.js index 20300ea9b..951ce6ff1 100644 --- a/ui/src/kapacitor/index.js +++ b/ui/src/kapacitor/index.js @@ -1,6 +1,5 @@ -import CreateKapacitorPage from './containers/CreateKapacitorPage' import KapacitorPage from './containers/KapacitorPage' import KapacitorRulePage from './containers/KapacitorRulePage' import KapacitorRulesPage from './containers/KapacitorRulesPage' import KapacitorTasksPage from './containers/KapacitorTasksPage' -export {CreateKapacitorPage, KapacitorPage, KapacitorRulePage, KapacitorRulesPage, KapacitorTasksPage} +export {KapacitorPage, KapacitorRulePage, KapacitorRulesPage, KapacitorTasksPage} From 057c3a9eb837aea2d0020dd97d209255e7725fb8 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Mon, 17 Apr 2017 12:21:50 -0700 Subject: [PATCH 16/47] make KapacitorPage work when visiting kapacitors/:id/edit --- ui/src/kapacitor/actions/view/index.js | 2 +- ui/src/kapacitor/containers/KapacitorPage.js | 10 +++++----- ui/src/kapacitor/containers/KapacitorRulePage.js | 2 +- ui/src/kapacitor/containers/KapacitorRulesPage.js | 2 +- ui/src/shared/apis/index.js | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ui/src/kapacitor/actions/view/index.js b/ui/src/kapacitor/actions/view/index.js index 8877dc9d3..666a22167 100644 --- a/ui/src/kapacitor/actions/view/index.js +++ b/ui/src/kapacitor/actions/view/index.js @@ -10,7 +10,7 @@ import { export function fetchRule(source, ruleID) { return (dispatch) => { - getKapacitor(source).then((kapacitor) => { + getKapacitor(source, '1').then((kapacitor) => { getRule(kapacitor, ruleID).then(({data: rule}) => { dispatch({ type: 'LOAD_RULE', diff --git a/ui/src/kapacitor/containers/KapacitorPage.js b/ui/src/kapacitor/containers/KapacitorPage.js index 9032ac076..496bb0bb8 100644 --- a/ui/src/kapacitor/containers/KapacitorPage.js +++ b/ui/src/kapacitor/containers/KapacitorPage.js @@ -30,12 +30,12 @@ class KapacitorPage extends Component { } componentDidMount() { - const {source} = this.props - getKapacitor(source).then((kapacitor) => { - if (!kapacitor) { - return - } + const {source, params: {id}} = this.props + if (!id) { + return + } + getKapacitor(source, id).then((kapacitor) => { this.setState({kapacitor, exists: true}, () => { pingKapacitor(kapacitor).catch(() => { this.props.addFlashMessage({type: 'error', text: 'Could not connect to Kapacitor. Check settings.'}) diff --git a/ui/src/kapacitor/containers/KapacitorRulePage.js b/ui/src/kapacitor/containers/KapacitorRulePage.js index 9057014d9..d7bc68e94 100644 --- a/ui/src/kapacitor/containers/KapacitorRulePage.js +++ b/ui/src/kapacitor/containers/KapacitorRulePage.js @@ -52,7 +52,7 @@ export const KapacitorRulePage = React.createClass({ kapacitorActions.loadDefaultRule() } - getKapacitor(source).then((kapacitor) => { + getKapacitor(source, '1').then((kapacitor) => { this.setState({kapacitor}) getKapacitorConfig(kapacitor).then(({data: {sections}}) => { const enabledAlerts = Object.keys(sections).filter((section) => { diff --git a/ui/src/kapacitor/containers/KapacitorRulesPage.js b/ui/src/kapacitor/containers/KapacitorRulesPage.js index a7344a239..bff06e05b 100644 --- a/ui/src/kapacitor/containers/KapacitorRulesPage.js +++ b/ui/src/kapacitor/containers/KapacitorRulesPage.js @@ -18,7 +18,7 @@ class KapacitorRulesPage extends Component { } componentDidMount() { - getKapacitor(this.props.source).then((kapacitor) => { + getKapacitor(this.props.source, '1').then((kapacitor) => { if (kapacitor) { this.props.actions.fetchRules(kapacitor) } diff --git a/ui/src/shared/apis/index.js b/ui/src/shared/apis/index.js index 66ea8e82d..f526539d7 100644 --- a/ui/src/shared/apis/index.js +++ b/ui/src/shared/apis/index.js @@ -58,12 +58,12 @@ export function pingKapacitor(kapacitor) { }) } -export function getKapacitor(source) { +export function getKapacitor(source, kapacitorID) { return AJAX({ - url: source.links.kapacitors, + url: `${source.links.kapacitors}/${kapacitorID}`, method: 'GET', }).then(({data}) => { - return data.kapacitors[0] + return data }) } From 814f81132d91b83dee1f0a4afbea9d55f2df40e3 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Mon, 17 Apr 2017 14:53:18 -0700 Subject: [PATCH 17/47] allow users to edit their existing kapacitor instances --- ui/src/shared/components/Dropdown.js | 4 ++-- ui/src/side_nav/containers/SideNav.js | 2 -- ui/src/sources/components/InfluxTable.js | 26 +++++++++++++++++------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/ui/src/shared/components/Dropdown.js b/ui/src/shared/components/Dropdown.js index 3211dba08..9830495d5 100644 --- a/ui/src/shared/components/Dropdown.js +++ b/ui/src/shared/components/Dropdown.js @@ -64,7 +64,7 @@ class Dropdown extends Component {
{actions.map((action) => { return ( - ) @@ -99,8 +99,8 @@ const { Dropdown.propTypes = { actions: arrayOf(shape({ icon: string.isRequired, - target: string.isRequired, text: string.isRequired, + handler: func.isRequired, })), items: arrayOf(shape({ text: string.isRequired, diff --git a/ui/src/side_nav/containers/SideNav.js b/ui/src/side_nav/containers/SideNav.js index c98893d2d..6a30c20f2 100644 --- a/ui/src/side_nav/containers/SideNav.js +++ b/ui/src/side_nav/containers/SideNav.js @@ -56,8 +56,6 @@ const SideNav = React.createClass({ - InfluxDB - Kapacitor { showLogout ? ( diff --git a/ui/src/sources/components/InfluxTable.js b/ui/src/sources/components/InfluxTable.js index a7bc01c79..1c68a0774 100644 --- a/ui/src/sources/components/InfluxTable.js +++ b/ui/src/sources/components/InfluxTable.js @@ -1,16 +1,16 @@ import React, {PropTypes} from 'react' -import {Link} from 'react-router' +import {Link, withRouter} from 'react-router' import Dropdown from 'shared/components/Dropdown' -const kapacitorDropdown = (kapacitors, source) => { +const kapacitorDropdown = (kapacitors, source, router) => { if (!kapacitors || kapacitors.length === 0) { return ( -- ) } const kapacitorItems = kapacitors.map((k) => { - return {text: k.name} + return {text: k.name, resource: `/sources/${source.id}/kapacitors/${k.id}`} }) return ( { url: `/sources/${source.id}/kapacitors/new`, text: "Add Kapacitor", }} + actions={ + [{ + icon: "pencil", + text: "edit", + handler: (item) => { + router.push(`${item.resource}/edit`) + }, + }]} selected={kapacitorItems[0].text} /> ) @@ -30,6 +38,7 @@ const InfluxTable = ({ source, handleDeleteSource, location, + router, }) => (
@@ -58,7 +67,7 @@ const InfluxTable = ({ {s.url} { - kapacitorDropdown(s.kapacitors, source) + kapacitorDropdown(s.kapacitors, source, router) } @@ -87,11 +96,13 @@ const { } = PropTypes InfluxTable.propTypes = { - sources: array.isRequired, + handleDeleteSource: func.isRequired, location: shape({ pathname: string.isRequired, }).isRequired, - handleDeleteSource: func.isRequired, + router: PropTypes.shape({ + push: PropTypes.func.isRequired, + }).isRequired, source: shape({ id: string.isRequired, links: shape({ @@ -99,6 +110,7 @@ InfluxTable.propTypes = { self: string.isRequired, }), }), + sources: array.isRequired, } -export default InfluxTable +export default withRouter(InfluxTable) From b617827b93a78c063b44e49e2f155b694f2dff24 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Mon, 17 Apr 2017 14:57:43 -0700 Subject: [PATCH 18/47] unbreak item selection --- ui/src/shared/components/Dropdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/shared/components/Dropdown.js b/ui/src/shared/components/Dropdown.js index 9830495d5..b2b260462 100644 --- a/ui/src/shared/components/Dropdown.js +++ b/ui/src/shared/components/Dropdown.js @@ -58,7 +58,7 @@ class Dropdown extends Component { {items.map((item, i) => { return (
  • - self.handleSelection(item)}> + this.handleSelection(item)}> {item.text}
    From 6d51ac876b7f59c9ebee9f663485e3764ea5beaa Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Mon, 17 Apr 2017 15:24:59 -0700 Subject: [PATCH 19/47] add a link to create new kapacitors for hosts with no kapacitor --- ui/src/sources/components/InfluxTable.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/sources/components/InfluxTable.js b/ui/src/sources/components/InfluxTable.js index 1c68a0774..e0e818354 100644 --- a/ui/src/sources/components/InfluxTable.js +++ b/ui/src/sources/components/InfluxTable.js @@ -6,7 +6,7 @@ import Dropdown from 'shared/components/Dropdown' const kapacitorDropdown = (kapacitors, source, router) => { if (!kapacitors || kapacitors.length === 0) { return ( - -- + Add Kapacitor ) } const kapacitorItems = kapacitors.map((k) => { @@ -67,7 +67,7 @@ const InfluxTable = ({ {s.url} { - kapacitorDropdown(s.kapacitors, source, router) + kapacitorDropdown(s.kapacitors, s, router) } From 18d6303b8b442b27826ed277083f8a3d499edf42 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Mon, 17 Apr 2017 16:55:48 -0700 Subject: [PATCH 20/47] prefer single quotes in eslintrc --- ui/.eslintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/.eslintrc b/ui/.eslintrc index b2b1adb83..996fed43a 100644 --- a/ui/.eslintrc +++ b/ui/.eslintrc @@ -40,7 +40,7 @@ }, }, rules: { - 'quotes': [0, "double"], + 'quotes': [1, 'single'], 'func-style': 0, 'func-names': 0, 'arrow-parens': 0, From a0bf56fcf211e2d12cdd5f1603e1027f72cf1476 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Tue, 18 Apr 2017 21:29:45 -0700 Subject: [PATCH 21/47] WIP redesign of KapacitorPage --- .../{AlertOutputs.js => AlertTabs.js} | 9 +- ui/src/kapacitor/components/KapacitorForm.js | 94 +++++++++---------- ui/src/kapacitor/containers/KapacitorPage.js | 4 +- 3 files changed, 51 insertions(+), 56 deletions(-) rename ui/src/kapacitor/components/{AlertOutputs.js => AlertTabs.js} (96%) diff --git a/ui/src/kapacitor/components/AlertOutputs.js b/ui/src/kapacitor/components/AlertTabs.js similarity index 96% rename from ui/src/kapacitor/components/AlertOutputs.js rename to ui/src/kapacitor/components/AlertTabs.js index 921b29177..61937f6e4 100644 --- a/ui/src/kapacitor/components/AlertOutputs.js +++ b/ui/src/kapacitor/components/AlertTabs.js @@ -12,7 +12,7 @@ import TalkConfig from './TalkConfig' import TelegramConfig from './TelegramConfig' import VictorOpsConfig from './VictorOpsConfig' -const AlertOutputs = React.createClass({ +const AlertTabs = React.createClass({ propTypes: { source: PropTypes.shape({ id: PropTypes.string.isRequired, @@ -104,9 +104,10 @@ const AlertOutputs = React.createClass({ return (
    +
    +

    Configure Alert Endpoints

    +
    -

    Configure Alert Endpoints

    -
    @@ -181,4 +182,4 @@ const AlertOutputs = React.createClass({ }, }) -export default AlertOutputs +export default AlertTabs diff --git a/ui/src/kapacitor/components/KapacitorForm.js b/ui/src/kapacitor/components/KapacitorForm.js index afa6bf331..a55e3dadb 100644 --- a/ui/src/kapacitor/components/KapacitorForm.js +++ b/ui/src/kapacitor/components/KapacitorForm.js @@ -1,31 +1,9 @@ -import React, {PropTypes} from 'react' -import AlertOutputs from './AlertOutputs' - -const { - func, - shape, - string, - bool, -} = PropTypes - -const KapacitorForm = React.createClass({ - propTypes: { - onSubmit: func.isRequired, - onInputChange: func.isRequired, - onReset: func.isRequired, - kapacitor: shape({ - url: string.isRequired, - name: string.isRequired, - username: string, - password: string, - }).isRequired, - source: shape({}).isRequired, - addFlashMessage: func.isRequired, - exists: bool.isRequired, - }, +import React, {Component, PropTypes} from 'react' +import AlertTabs from './AlertTabs' +class KapacitorForm extends Component { render() { - const {onInputChange, onReset, kapacitor, source, onSubmit} = this.props + const {onInputChange, onReset, kapacitor, onSubmit} = this.props const {url, name, username, password} = kapacitor return ( @@ -42,21 +20,15 @@ const KapacitorForm = React.createClass({
    -
    +
    +
    +

    Connection Details

    +
    -

    - Kapacitor is used as the monitoring and alerting agent. - This page will let you configure which Kapacitor to use and - set up alert end points like email, Slack, and others. -

    -
    -

    Connect Kapacitor to Source

    -

    {source.url}

    -
    -
    +
    -
    +
    -
    +
    -
    +
    - - + +
    -
    -
    -
    +
    {this.renderAlertOutputs()}
    @@ -121,26 +91,50 @@ const KapacitorForm = React.createClass({
    ) - }, + } // TODO: move these to another page. they dont belong on this page renderAlertOutputs() { const {exists, kapacitor, addFlashMessage, source} = this.props if (exists) { - return + return } return (
    +
    +

    Configure Alert Endpoints

    +
    -

    Configure Alert Endpoints


    Set your Kapacitor connection info to configure alerting endpoints.

    ) - }, -}) + } +} + +const { + func, + shape, + string, + bool, +} = PropTypes + +KapacitorForm.propTypes = { + onSubmit: func.isRequired, + onInputChange: func.isRequired, + onReset: func.isRequired, + kapacitor: shape({ + url: string.isRequired, + name: string.isRequired, + username: string, + password: string, + }).isRequired, + source: shape({}).isRequired, + addFlashMessage: func.isRequired, + exists: bool.isRequired, +} export default KapacitorForm diff --git a/ui/src/kapacitor/containers/KapacitorPage.js b/ui/src/kapacitor/containers/KapacitorPage.js index 496bb0bb8..012eec4f3 100644 --- a/ui/src/kapacitor/containers/KapacitorPage.js +++ b/ui/src/kapacitor/containers/KapacitorPage.js @@ -7,8 +7,8 @@ import { } from 'shared/apis' import KapacitorForm from '../components/KapacitorForm' -const defaultName = "My Kapacitor" -const kapacitorPort = "9092" +const defaultName = 'My Kapacitor' +const kapacitorPort = '9092' class KapacitorPage extends Component { constructor(props) { From 0c4bcb883bcd5335e93817e38bce8f18a915bd5f Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Tue, 18 Apr 2017 23:11:19 -0700 Subject: [PATCH 22/47] update kapacitor config page to new redesign --- ui/src/kapacitor/components/AlertTabs.js | 229 +++++++++--------- .../components/{ => config}/AlertaConfig.js | 0 .../components/{ => config}/HipChatConfig.js | 0 .../components/{ => config}/OpsGenieConfig.js | 0 .../{ => config}/PagerDutyConfig.js | 0 .../components/{ => config}/SMTPConfig.js | 0 .../components/{ => config}/SensuConfig.js | 0 .../components/{ => config}/SlackConfig.js | 0 .../components/{ => config}/TalkConfig.js | 0 .../components/{ => config}/TelegramConfig.js | 0 .../{ => config}/VictorOpsConfig.js | 0 ui/src/kapacitor/components/config/index.js | 23 ++ 12 files changed, 139 insertions(+), 113 deletions(-) rename ui/src/kapacitor/components/{ => config}/AlertaConfig.js (100%) rename ui/src/kapacitor/components/{ => config}/HipChatConfig.js (100%) rename ui/src/kapacitor/components/{ => config}/OpsGenieConfig.js (100%) rename ui/src/kapacitor/components/{ => config}/PagerDutyConfig.js (100%) rename ui/src/kapacitor/components/{ => config}/SMTPConfig.js (100%) rename ui/src/kapacitor/components/{ => config}/SensuConfig.js (100%) rename ui/src/kapacitor/components/{ => config}/SlackConfig.js (100%) rename ui/src/kapacitor/components/{ => config}/TalkConfig.js (100%) rename ui/src/kapacitor/components/{ => config}/TelegramConfig.js (100%) rename ui/src/kapacitor/components/{ => config}/VictorOpsConfig.js (100%) create mode 100644 ui/src/kapacitor/components/config/index.js diff --git a/ui/src/kapacitor/components/AlertTabs.js b/ui/src/kapacitor/components/AlertTabs.js index 61937f6e4..2100c6a2b 100644 --- a/ui/src/kapacitor/components/AlertTabs.js +++ b/ui/src/kapacitor/components/AlertTabs.js @@ -1,60 +1,58 @@ -import React, {PropTypes} from 'react' +import React, {Component, PropTypes} from 'react' import _ from 'lodash' + +import {Tab, Tabs, TabPanel, TabPanels, TabList} from 'shared/components/Tabs' import {getKapacitorConfig, updateKapacitorConfigSection, testAlertOutput} from 'shared/apis' -import AlertaConfig from './AlertaConfig' -import HipChatConfig from './HipChatConfig' -import OpsGenieConfig from './OpsGenieConfig' -import PagerDutyConfig from './PagerDutyConfig' -import SensuConfig from './SensuConfig' -import SlackConfig from './SlackConfig' -import SMTPConfig from './SMTPConfig' -import TalkConfig from './TalkConfig' -import TelegramConfig from './TelegramConfig' -import VictorOpsConfig from './VictorOpsConfig' -const AlertTabs = React.createClass({ - propTypes: { - source: PropTypes.shape({ - id: PropTypes.string.isRequired, - }).isRequired, - kapacitor: PropTypes.shape({ - url: PropTypes.string.isRequired, - links: PropTypes.shape({ - proxy: PropTypes.string.isRequired, - }).isRequired, - }), - addFlashMessage: PropTypes.func.isRequired, - }, +import { + AlertaConfig, + HipChatConfig, + OpsGenieConfig, + PagerDutyConfig, + SensuConfig, + SlackConfig, + SMTPConfig, + TalkConfig, + TelegramConfig, + VictorOpsConfig, +} from './config' - getInitialState() { - return { +class AlertTabs extends Component { + constructor(props) { + super(props) + this.state = { selectedEndpoint: 'smtp', configSections: null, } - }, + this.refreshKapacitorConfig = ::this.refreshKapacitorConfig + this.getSection = ::this.getSection + this.handleSaveConfig = ::this.handleSaveConfig + this.handleTest = ::this.handleTest + this.sanitizeProperties = ::this.sanitizeProperties + } componentDidMount() { this.refreshKapacitorConfig(this.props.kapacitor) - }, + } componentWillReceiveProps(nextProps) { if (this.props.kapacitor.url !== nextProps.kapacitor.url) { this.refreshKapacitorConfig(nextProps.kapacitor) } - }, + } refreshKapacitorConfig(kapacitor) { getKapacitorConfig(kapacitor).then(({data: {sections}}) => { this.setState({configSections: sections}) }).catch(() => { this.setState({configSections: null}) - this.props.addFlashMessage({type: 'error', text: `There was an error getting the Kapacitor config`}) + this.props.addFlashMessage({type: 'error', text: 'There was an error getting the Kapacitor config'}) }) - }, + } getSection(sections, section) { return _.get(sections, [section, 'elements', '0'], null) - }, + } handleSaveConfig(section, properties) { if (section !== '') { @@ -63,25 +61,19 @@ const AlertTabs = React.createClass({ this.refreshKapacitorConfig(this.props.kapacitor) this.props.addFlashMessage({type: 'success', text: `Alert for ${section} successfully saved`}) }).catch(() => { - this.props.addFlashMessage({type: 'error', text: `There was an error saving the kapacitor config`}) + this.props.addFlashMessage({type: 'error', text: 'There was an error saving the kapacitor config'}) }) } - }, - - changeSelectedEndpoint(e) { - this.setState({ - selectedEndpoint: e.target.value, - }) - }, + } handleTest(section, properties) { const propsToSend = this.sanitizeProperties(section, properties) testAlertOutput(this.props.kapacitor, section, propsToSend).then(() => { this.props.addFlashMessage({type: 'success', text: 'Slack test message sent'}) }).catch(() => { - this.props.addFlashMessage({type: 'error', text: `There was an error testing the slack alert`}) + this.props.addFlashMessage({type: 'error', text: 'There was an error testing the slack alert'}) }) - }, + } sanitizeProperties(section, properties) { const cleanProps = Object.assign({}, properties, {enabled: true}) @@ -94,92 +86,103 @@ const AlertTabs = React.createClass({ }) } return cleanProps - }, + } render() { - const {configSections, selectedEndpoint} = this.state - if (!configSections) { // could use this state to conditionally render spinner or error message + const {configSections} = this.state + + if (!configSections) { return null } + const test = (properties) => { + this.handleTest('slack', properties) + } + + const tabs = [ + { + type: 'Alerta', + component: ( this.handleSaveConfig('alerta', p)} config={this.getSection(configSections, 'alerta')} />), + }, + { + type: 'SMTP', + component: ( this.handleSaveConfig('smtp', p)} config={this.getSection(configSections, 'smtp')} />), + }, + { + type: 'Slack', + component: ( this.handleSaveConfig('slack', p)} onTest={test} config={this.getSection(configSections, 'slack')} />), + }, + { + type: 'VictorOps', + component: ( this.handleSaveConfig('victorops', p)} config={this.getSection(configSections, 'victorops')} />), + }, + { + type: 'Telegram', + component: ( this.handleSaveConfig('telegram', p)} config={this.getSection(configSections, 'telegram')} />), + }, + { + type: 'OpsGenie', + component: ( this.handleSaveConfig('opsgenie', p)} config={this.getSection(configSections, 'opsgenie')} />), + }, + { + type: 'PagerDuty', + component: ( this.handleSaveConfig('pagerduty', p)} config={this.getSection(configSections, 'pagerduty')} />), + }, + { + type: 'HipChat', + component: ( this.handleSaveConfig('hipchat', p)} config={this.getSection(configSections, 'hipchat')} />), + }, + { + type: 'Sensu', + component: ( this.handleSaveConfig('sensu', p)} config={this.getSection(configSections, 'sensu')} />), + }, + { + type: 'Talk', + component: ( this.handleSaveConfig('talk', p)} config={this.getSection(configSections, 'talk')} />), + }, + ] + return (

    Configure Alert Endpoints

    -
    -
    - - -
    -
    -
    -
    -
    -
    -
    - {this.renderAlertConfig(selectedEndpoint)} -
    -
    + + + { + tabs.map((t, i) => ({tabs[i].type})) + } + + + { + tabs.map((t, i) => ({t.component})) + } + +
    ) - }, + } +} - renderAlertConfig(endpoint) { - const {configSections} = this.state - const save = (properties) => { - this.handleSaveConfig(endpoint, properties) - } - const test = (properties) => { - this.handleTest(endpoint, properties) - } +const { + func, + shape, + string, +} = PropTypes - switch (endpoint) { - case 'alerta': { - return - } - case 'smtp': { - return - } - case 'slack': { - return - } - case 'victorops': { - return - } - case 'telegram': { - return - } - case 'opsgenie': { - return - } - case 'pagerduty': { - return - } - case 'hipchat': { - return - } - case 'sensu': { - return - } - case 'talk': { - return - } - } - }, -}) +AlertTabs.propTypes = { + source: shape({ + id: string.isRequired, + }).isRequired, + kapacitor: shape({ + url: string.isRequired, + links: shape({ + proxy: string.isRequired, + }).isRequired, + }), + addFlashMessage: func.isRequired, +} export default AlertTabs diff --git a/ui/src/kapacitor/components/AlertaConfig.js b/ui/src/kapacitor/components/config/AlertaConfig.js similarity index 100% rename from ui/src/kapacitor/components/AlertaConfig.js rename to ui/src/kapacitor/components/config/AlertaConfig.js diff --git a/ui/src/kapacitor/components/HipChatConfig.js b/ui/src/kapacitor/components/config/HipChatConfig.js similarity index 100% rename from ui/src/kapacitor/components/HipChatConfig.js rename to ui/src/kapacitor/components/config/HipChatConfig.js diff --git a/ui/src/kapacitor/components/OpsGenieConfig.js b/ui/src/kapacitor/components/config/OpsGenieConfig.js similarity index 100% rename from ui/src/kapacitor/components/OpsGenieConfig.js rename to ui/src/kapacitor/components/config/OpsGenieConfig.js diff --git a/ui/src/kapacitor/components/PagerDutyConfig.js b/ui/src/kapacitor/components/config/PagerDutyConfig.js similarity index 100% rename from ui/src/kapacitor/components/PagerDutyConfig.js rename to ui/src/kapacitor/components/config/PagerDutyConfig.js diff --git a/ui/src/kapacitor/components/SMTPConfig.js b/ui/src/kapacitor/components/config/SMTPConfig.js similarity index 100% rename from ui/src/kapacitor/components/SMTPConfig.js rename to ui/src/kapacitor/components/config/SMTPConfig.js diff --git a/ui/src/kapacitor/components/SensuConfig.js b/ui/src/kapacitor/components/config/SensuConfig.js similarity index 100% rename from ui/src/kapacitor/components/SensuConfig.js rename to ui/src/kapacitor/components/config/SensuConfig.js diff --git a/ui/src/kapacitor/components/SlackConfig.js b/ui/src/kapacitor/components/config/SlackConfig.js similarity index 100% rename from ui/src/kapacitor/components/SlackConfig.js rename to ui/src/kapacitor/components/config/SlackConfig.js diff --git a/ui/src/kapacitor/components/TalkConfig.js b/ui/src/kapacitor/components/config/TalkConfig.js similarity index 100% rename from ui/src/kapacitor/components/TalkConfig.js rename to ui/src/kapacitor/components/config/TalkConfig.js diff --git a/ui/src/kapacitor/components/TelegramConfig.js b/ui/src/kapacitor/components/config/TelegramConfig.js similarity index 100% rename from ui/src/kapacitor/components/TelegramConfig.js rename to ui/src/kapacitor/components/config/TelegramConfig.js diff --git a/ui/src/kapacitor/components/VictorOpsConfig.js b/ui/src/kapacitor/components/config/VictorOpsConfig.js similarity index 100% rename from ui/src/kapacitor/components/VictorOpsConfig.js rename to ui/src/kapacitor/components/config/VictorOpsConfig.js diff --git a/ui/src/kapacitor/components/config/index.js b/ui/src/kapacitor/components/config/index.js new file mode 100644 index 000000000..8cf004a07 --- /dev/null +++ b/ui/src/kapacitor/components/config/index.js @@ -0,0 +1,23 @@ +import AlertaConfig from './AlertaConfig' +import HipChatConfig from './HipChatConfig' +import OpsGenieConfig from './OpsGenieConfig' +import PagerDutyConfig from './PagerDutyConfig' +import SensuConfig from './SensuConfig' +import SlackConfig from './SlackConfig' +import SMTPConfig from './SMTPConfig' +import TalkConfig from './TalkConfig' +import TelegramConfig from './TelegramConfig' +import VictorOpsConfig from './VictorOpsConfig' + +export { + AlertaConfig, + HipChatConfig, + OpsGenieConfig, + PagerDutyConfig, + SensuConfig, + SlackConfig, + SMTPConfig, + TalkConfig, + TelegramConfig, + VictorOpsConfig, +} From 0e8eb78f709ca3e036ebef8fdc5f06403942246d Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Tue, 18 Apr 2017 23:36:00 -0700 Subject: [PATCH 23/47] polish polish --- ui/src/kapacitor/components/AlertTabs.js | 37 ++++++++++++++---------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/ui/src/kapacitor/components/AlertTabs.js b/ui/src/kapacitor/components/AlertTabs.js index 2100c6a2b..5734b2246 100644 --- a/ui/src/kapacitor/components/AlertTabs.js +++ b/ui/src/kapacitor/components/AlertTabs.js @@ -143,23 +143,28 @@ class AlertTabs extends Component { ] return ( -
    -
    -

    Configure Alert Endpoints

    +
    +
    +
    +

    Configure Alert Endpoints

    +
    -
    - - - { - tabs.map((t, i) => ({tabs[i].type})) - } - - - { - tabs.map((t, i) => ({t.component})) - } - - + +
    +
    + + + { + tabs.map((t, i) => ({tabs[i].type})) + } + + + { + tabs.map((t, i) => ({t.component})) + } + + +
    ) From 3ef5850d8bc8636d1fec5565b8c3b13d2a84c9ac Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Tue, 18 Apr 2017 23:51:59 -0700 Subject: [PATCH 24/47] make kapacitor alerts tab look like an a tabbed interface --- ui/src/kapacitor/components/AlertTabs.js | 28 ++++++++----------- .../components/config/AlertaConfig.js | 6 ++-- .../components/config/HipChatConfig.js | 6 ++-- .../components/config/OpsGenieConfig.js | 6 ++-- .../components/config/PagerDutyConfig.js | 6 ++-- .../kapacitor/components/config/SMTPConfig.js | 6 ++-- .../components/config/SensuConfig.js | 6 ++-- .../components/config/SlackConfig.js | 6 ++-- .../kapacitor/components/config/TalkConfig.js | 6 ++-- .../components/config/TelegramConfig.js | 6 ++-- .../components/config/VictorOpsConfig.js | 6 ++-- 11 files changed, 52 insertions(+), 36 deletions(-) diff --git a/ui/src/kapacitor/components/AlertTabs.js b/ui/src/kapacitor/components/AlertTabs.js index 5734b2246..58370d7ce 100644 --- a/ui/src/kapacitor/components/AlertTabs.js +++ b/ui/src/kapacitor/components/AlertTabs.js @@ -150,22 +150,18 @@ class AlertTabs extends Component {
    -
    -
    - - - { - tabs.map((t, i) => ({tabs[i].type})) - } - - - { - tabs.map((t, i) => ({t.component})) - } - - -
    -
    + + + { + tabs.map((t, i) => ({tabs[i].type})) + } + + + { + tabs.map((t, i) => ({t.component})) + } + +
    ) } diff --git a/ui/src/kapacitor/components/config/AlertaConfig.js b/ui/src/kapacitor/components/config/AlertaConfig.js index dd37d4483..060d09547 100644 --- a/ui/src/kapacitor/components/config/AlertaConfig.js +++ b/ui/src/kapacitor/components/config/AlertaConfig.js @@ -30,8 +30,10 @@ const AlertaConfig = React.createClass({ const {environment, origin, token, url} = this.props.config.options return ( -
    -

    Alerta Alert

    +
    +
    +

    Alerta Alert

    +

    diff --git a/ui/src/kapacitor/components/config/HipChatConfig.js b/ui/src/kapacitor/components/config/HipChatConfig.js index 42c392e93..c6be2ed50 100644 --- a/ui/src/kapacitor/components/config/HipChatConfig.js +++ b/ui/src/kapacitor/components/config/HipChatConfig.js @@ -40,8 +40,10 @@ const HipchatConfig = React.createClass({ const subdomain = url.replace('https://', '').replace('.hipchat.com/v2/room', '') return ( -

    -

    HipChat Alert

    +
    +
    +

    HipChat Alert

    +

    Send alert messages to HipChat.

    diff --git a/ui/src/kapacitor/components/config/OpsGenieConfig.js b/ui/src/kapacitor/components/config/OpsGenieConfig.js index 53833d707..7e8c1016e 100644 --- a/ui/src/kapacitor/components/config/OpsGenieConfig.js +++ b/ui/src/kapacitor/components/config/OpsGenieConfig.js @@ -64,8 +64,10 @@ const OpsGenieConfig = React.createClass({ const {currentTeams, currentRecipients} = this.state return ( -
    -

    OpsGenie Alert

    +
    +
    +

    OpsGenie Alert

    +

    Have alerts sent to OpsGenie.

    diff --git a/ui/src/kapacitor/components/config/PagerDutyConfig.js b/ui/src/kapacitor/components/config/PagerDutyConfig.js index d6e96b958..5b7092011 100644 --- a/ui/src/kapacitor/components/config/PagerDutyConfig.js +++ b/ui/src/kapacitor/components/config/PagerDutyConfig.js @@ -28,8 +28,10 @@ const PagerDutyConfig = React.createClass({ const serviceKey = options['service-key'] return ( -
    -

    PagerDuty Alert

    +
    +
    +

    PagerDuty Alert

    +

    You can have alerts sent to PagerDuty by entering info below.

    diff --git a/ui/src/kapacitor/components/config/SMTPConfig.js b/ui/src/kapacitor/components/config/SMTPConfig.js index 2a61c5165..21954c81c 100644 --- a/ui/src/kapacitor/components/config/SMTPConfig.js +++ b/ui/src/kapacitor/components/config/SMTPConfig.js @@ -32,8 +32,10 @@ const SMTPConfig = React.createClass({ const {host, port, from, username, password} = this.props.config.options return ( -
    -

    SMTP Alert

    +
    +
    +

    SMTP Alert

    +

    You can have alerts sent to an email address by setting up an SMTP endpoint.

    diff --git a/ui/src/kapacitor/components/config/SensuConfig.js b/ui/src/kapacitor/components/config/SensuConfig.js index b3c9bad5e..ccc88b3a5 100644 --- a/ui/src/kapacitor/components/config/SensuConfig.js +++ b/ui/src/kapacitor/components/config/SensuConfig.js @@ -26,8 +26,10 @@ const SensuConfig = React.createClass({ const {source, addr} = this.props.config.options return ( -
    -

    Sensu Alert

    +
    +
    +

    Sensu Alert

    +

    Have alerts sent to Sensu.

    diff --git a/ui/src/kapacitor/components/config/SlackConfig.js b/ui/src/kapacitor/components/config/SlackConfig.js index c6ab16eec..b1e1d3136 100644 --- a/ui/src/kapacitor/components/config/SlackConfig.js +++ b/ui/src/kapacitor/components/config/SlackConfig.js @@ -47,8 +47,10 @@ const SlackConfig = React.createClass({ const {url, channel} = this.props.config.options return ( -
    -

    Slack Alert

    +
    +
    +

    Slack Alert

    +

    Post alerts to a Slack channel.

    diff --git a/ui/src/kapacitor/components/config/TalkConfig.js b/ui/src/kapacitor/components/config/TalkConfig.js index 05ff984c3..d99420b42 100644 --- a/ui/src/kapacitor/components/config/TalkConfig.js +++ b/ui/src/kapacitor/components/config/TalkConfig.js @@ -33,8 +33,10 @@ const TalkConfig = React.createClass({ const {url, author_name: author} = this.props.config.options return ( -
    -

    Talk Alert

    +
    +
    +

    Talk Alert

    +

    Have alerts sent to Talk.

    diff --git a/ui/src/kapacitor/components/config/TelegramConfig.js b/ui/src/kapacitor/components/config/TelegramConfig.js index b29f950aa..d30086adf 100644 --- a/ui/src/kapacitor/components/config/TelegramConfig.js +++ b/ui/src/kapacitor/components/config/TelegramConfig.js @@ -54,8 +54,10 @@ const TelegramConfig = React.createClass({ const parseMode = options['parse-mode'] return ( -
    -

    Telegram Alert

    +
    +
    +

    Telegram Alert

    +

    Send alert messages to a Telegram bot. diff --git a/ui/src/kapacitor/components/config/VictorOpsConfig.js b/ui/src/kapacitor/components/config/VictorOpsConfig.js index b821117d7..523e94a77 100644 --- a/ui/src/kapacitor/components/config/VictorOpsConfig.js +++ b/ui/src/kapacitor/components/config/VictorOpsConfig.js @@ -31,8 +31,10 @@ const VictorOpsConfig = React.createClass({ const {url} = options return ( -

    -

    VictorOps Alert

    +
    +
    +

    VictorOps Alert

    +

    Have alerts sent to VictorOps.

    From adce105499a41dbcbb3ddc8689bbd06596142d68 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 19 Apr 2017 13:19:22 -0700 Subject: [PATCH 25/47] Replace edit button with a linked source name more space efficient, still intuitive --- ui/src/sources/components/InfluxTable.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/src/sources/components/InfluxTable.js b/ui/src/sources/components/InfluxTable.js index e0e818354..01d2d3279 100644 --- a/ui/src/sources/components/InfluxTable.js +++ b/ui/src/sources/components/InfluxTable.js @@ -63,7 +63,7 @@ const InfluxTable = ({ sources.map((s) => { return ( - {s.name}{s.default ? Default : null} + {s.name} {s.default ? Default : null} {s.url} { @@ -71,7 +71,6 @@ const InfluxTable = ({ } - Connect From c7bace6c9ddae1ae472bc0c1994f5629e7eea811 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 19 Apr 2017 13:20:04 -0700 Subject: [PATCH 26/47] Remove responsive table, causes too many UI problems --- ui/src/sources/components/InfluxTable.js | 64 ++++++++++++------------ 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/ui/src/sources/components/InfluxTable.js b/ui/src/sources/components/InfluxTable.js index 01d2d3279..572f97c1a 100644 --- a/ui/src/sources/components/InfluxTable.js +++ b/ui/src/sources/components/InfluxTable.js @@ -48,39 +48,37 @@ const InfluxTable = ({ Add Source
    -
    - - - - - - - - - - - { - sources.map((s) => { - return ( - - - - - - - ) - }) - } - -
    NameHostKapacitor
    {s.name} {s.default ? Default : null}{s.url} - { - kapacitorDropdown(s.kapacitors, s, router) - } - - Connect - -
    -
    + + + + + + + + + + + { + sources.map((s) => { + return ( + + + + + + + ) + }) + } + +
    NameHostKapacitor
    {s.name} {s.default ? Default : null}{s.url} + { + kapacitorDropdown(s.kapacitors, s, router) + } + + Connect + +
    From 8361f4331e62da1fd6ebae87271949836971513e Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 19 Apr 2017 13:51:54 -0700 Subject: [PATCH 27/47] Make dropdown easier to customize --- ui/src/shared/components/Dropdown.js | 36 +++++++++++++++++----------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/ui/src/shared/components/Dropdown.js b/ui/src/shared/components/Dropdown.js index b2b260462..a18589cd8 100644 --- a/ui/src/shared/components/Dropdown.js +++ b/ui/src/shared/components/Dropdown.js @@ -19,6 +19,9 @@ class Dropdown extends Component { static defaultProps = { actions: [], + buttonSize: 'btn-sm', + buttonColor: 'btn-info', + menuWidth: '100%', } handleClickOutside() { @@ -43,33 +46,35 @@ class Dropdown extends Component { } render() { - const {items, selected, className, iconName, actions, addNew} = this.props + const {items, selected, className, iconName, actions, addNew, buttonSize, buttonColor, menuWidth} = this.props const {isOpen} = this.state return ( -
    -
    - {iconName ? : null} +
    +
    + {iconName ? : null} {selected}
    {isOpen ? -
      +
        {items.map((item, i) => { return (
      • this.handleSelection(item)}> {item.text} -
        - {actions.map((action) => { - return ( - - ) - })} -
        + {actions.length > 0 ? +
        + {actions.map((action) => { + return ( + + ) + })} +
        + : null}
      • ) })} @@ -113,6 +118,9 @@ Dropdown.propTypes = { selected: string.isRequired, iconName: string, className: string, + buttonSize: string, + buttonColor: string, + menuWidth: string, } export default OnClickOutside(Dropdown) From 6a0a6c2a6df7a2002d918e9059744a83fc55e0a6 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 19 Apr 2017 13:52:21 -0700 Subject: [PATCH 28/47] Styles for kapacitor selector dropdown --- ui/src/style/unsorted.scss | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/ui/src/style/unsorted.scss b/ui/src/style/unsorted.scss index 0c61f8d6b..3aae03605 100644 --- a/ui/src/style/unsorted.scss +++ b/ui/src/style/unsorted.scss @@ -154,3 +154,28 @@ div:nth-child(3) {left: 100%; animation: refreshingSpinnerC 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) infinite;} } + +/* + Kapacitor selector dropdown + ---------------------------------------------- +*/ +.dropdown .dropdown-toggle.btn-xs { + height: 22px !important; + line-height: 22px !important; + padding: 0 9px !important; +} +.dropdown-selected { + display: inline-block; + width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + padding-right: 15px; +} +.dropdown.sources--kapacitor-selector { + .dropdown-toggle { + width: 160px; + } +} + + From f0bd2b1b87fdc89ae247bdb54f508cb0383b68de Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 19 Apr 2017 14:18:33 -0700 Subject: [PATCH 29/47] Make kapacitor selector dropdown pretty --- ui/src/sources/components/InfluxTable.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/src/sources/components/InfluxTable.js b/ui/src/sources/components/InfluxTable.js index 572f97c1a..dc4176f01 100644 --- a/ui/src/sources/components/InfluxTable.js +++ b/ui/src/sources/components/InfluxTable.js @@ -14,6 +14,9 @@ const kapacitorDropdown = (kapacitors, source, router) => { }) return ( {}} addNew={{ From 7b151c2a6d6345d26a19958e4be0a4d6029fc941 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 19 Apr 2017 15:37:14 -0700 Subject: [PATCH 30/47] Create stylesheet for endpoint config page --- ui/src/style/chronograf.scss | 3 ++ ui/src/style/pages/config-endpoints.scss | 61 ++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 ui/src/style/pages/config-endpoints.scss diff --git a/ui/src/style/chronograf.scss b/ui/src/style/chronograf.scss index 3b7c63c27..c501ba410 100644 --- a/ui/src/style/chronograf.scss +++ b/ui/src/style/chronograf.scss @@ -45,6 +45,9 @@ @import 'components/query-maker'; // Pages + +@import 'pages/alerts'; +@import 'pages/config-endpoints'; @import 'pages/signup'; @import 'pages/auth-page'; @import 'pages/kapacitor'; diff --git a/ui/src/style/pages/config-endpoints.scss b/ui/src/style/pages/config-endpoints.scss new file mode 100644 index 000000000..e60690839 --- /dev/null +++ b/ui/src/style/pages/config-endpoints.scss @@ -0,0 +1,61 @@ +/* + Kapacitor Config Styles + ---------------------------------------------- +*/ + +$config-endpoint-tab-height: 40px; + +$config-endpoint-tab-text: $g10-wolf; +$config-endpoint-tab-text-hover: $g15-platinum; +$config-endpoint-tab-text-active: $g18-cloud; + +$config-endpoint-tab-bg: transparent; +$config-endpoint-tab-bg-hover: $g3-castle; +$config-endpoint-tab-bg-active: $g3-castle; + +.config-endpoint { + display: flex; + align-items: stretch; +} +.config-endpoint--tabs { + flex: 0 0 0; + display: flex; + + .btn-group.tab-group { + background-color: $g2-kevlar; + border-radius: $radius 0 0 $radius; + margin: 0; + display: flex; + flex: 1 0 0; + flex-direction: column; + align-items: stretch; + + .btn.tab { + color: $config-endpoint-tab-text; + background-color: $config-endpoint-tab-bg; + border-radius: 0; + height: $config-endpoint-tab-height; + border: 0; + padding: 0 40px 0 15px; + display: flex; + align-items: center; + justify-content: space-between; + font-weight: 500; + font-size: 16px; + + &:first-child {border-top-left-radius: $radius;} + + &:hover { + color: $config-endpoint-tab-text-hover; + background-color: $config-endpoint-tab-bg-hover; + } + &.active { + color: $config-endpoint-tab-text-active; + background-color: $config-endpoint-tab-bg-active; + } + } + } +} +.config-endpoint--tab-contents { + flex: 1 0 0; +} \ No newline at end of file From 486ebf03076a62c1f3ccad122607cc66dd61f6dd Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 19 Apr 2017 15:37:35 -0700 Subject: [PATCH 31/47] Block spellcheck on kapacitor config form --- ui/src/kapacitor/components/KapacitorForm.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ui/src/kapacitor/components/KapacitorForm.js b/ui/src/kapacitor/components/KapacitorForm.js index a55e3dadb..5db261ad1 100644 --- a/ui/src/kapacitor/components/KapacitorForm.js +++ b/ui/src/kapacitor/components/KapacitorForm.js @@ -36,7 +36,8 @@ class KapacitorForm extends Component { name="url" placeholder={url} value={url} - onChange={onInputChange}> + onChange={onInputChange} + spellCheck="false">
    @@ -47,7 +48,8 @@ class KapacitorForm extends Component { name="name" placeholder={name} value={name} - onChange={onInputChange}> + onChange={onInputChange} + spellCheck="false">
    @@ -58,7 +60,8 @@ class KapacitorForm extends Component { name="username" placeholder="username" value={username} - onChange={onInputChange}> + onChange={onInputChange} + spellCheck="false">
    @@ -71,6 +74,7 @@ class KapacitorForm extends Component { placeholder="password" value={password} onChange={onInputChange} + spellCheck="false" />
    From 5a05fe9f34986f0dfa84759cf3e584dbab210a6a Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 19 Apr 2017 15:37:59 -0700 Subject: [PATCH 32/47] Apply new styles to endpoint config tabs --- ui/src/kapacitor/components/AlertTabs.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/src/kapacitor/components/AlertTabs.js b/ui/src/kapacitor/components/AlertTabs.js index 58370d7ce..3d02ceed8 100644 --- a/ui/src/kapacitor/components/AlertTabs.js +++ b/ui/src/kapacitor/components/AlertTabs.js @@ -150,13 +150,13 @@ class AlertTabs extends Component {
    - - + + { tabs.map((t, i) => ({tabs[i].type})) } - + { tabs.map((t, i) => ({t.component})) } From fdde4f3fc8451624922a04d57cde333f5c3093e3 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 19 Apr 2017 15:45:03 -0700 Subject: [PATCH 33/47] Remove excess text from endpoint configs --- .../components/config/AlertaConfig.js | 52 +++---- .../components/config/HipChatConfig.js | 93 ++++++----- .../components/config/OpsGenieConfig.js | 29 ++-- .../components/config/PagerDutyConfig.js | 33 ++-- .../kapacitor/components/config/SMTPConfig.js | 55 +++---- .../components/config/SensuConfig.js | 31 ++-- .../components/config/SlackConfig.js | 35 ++--- .../kapacitor/components/config/TalkConfig.js | 33 ++-- .../components/config/TelegramConfig.js | 144 +++++++++--------- .../components/config/VictorOpsConfig.js | 41 +++-- 10 files changed, 237 insertions(+), 309 deletions(-) diff --git a/ui/src/kapacitor/components/config/AlertaConfig.js b/ui/src/kapacitor/components/config/AlertaConfig.js index 060d09547..f2b3d48bd 100644 --- a/ui/src/kapacitor/components/config/AlertaConfig.js +++ b/ui/src/kapacitor/components/config/AlertaConfig.js @@ -30,42 +30,32 @@ const AlertaConfig = React.createClass({ const {environment, origin, token, url} = this.props.config.options return ( -
    -
    -

    Alerta Alert

    + +
    + + this.environment = r} defaultValue={environment || ''}>
    -
    - -

    - Have alerts sent to Alerta -

    -
    - - this.environment = r} defaultValue={environment || ''}> -
    +
    + + this.origin = r} defaultValue={origin || ''}> +
    -
    - - this.origin = r} defaultValue={origin || ''}> -
    +
    + + this.token = r} defaultValue={token || ''}> + Note: a value of true indicates the Alerta Token has been set +
    -
    - - this.token = r} defaultValue={token || ''}> - Note: a value of true indicates the Alerta Token has been set -
    +
    + + this.url = r} defaultValue={url || ''}> +
    -
    - - this.url = r} defaultValue={url || ''}> -
    - -
    - -
    - -
    +
    + +
    + ) }, }) diff --git a/ui/src/kapacitor/components/config/HipChatConfig.js b/ui/src/kapacitor/components/config/HipChatConfig.js index c6be2ed50..421e71627 100644 --- a/ui/src/kapacitor/components/config/HipChatConfig.js +++ b/ui/src/kapacitor/components/config/HipChatConfig.js @@ -40,61 +40,54 @@ const HipchatConfig = React.createClass({ const subdomain = url.replace('https://', '').replace('.hipchat.com/v2/room', '') return ( -
    -
    -

    HipChat Alert

    +
    +
    + + this.url = r} + defaultValue={subdomain && subdomain.length ? subdomain : ''} + />
    -
    -

    Send alert messages to HipChat.

    - -
    - - this.url = r} - defaultValue={subdomain && subdomain.length ? subdomain : ''} - /> -
    -
    - - this.room = r} - defaultValue={room || ''} - /> -
    +
    + + this.room = r} + defaultValue={room || ''} + /> +
    -
    - - this.token = r} - defaultValue={token || ''} +
    +
    + + this.token = r} + defaultValue={token || ''} + /> + +
    -
    - -
    -
    -
    +
    + +
    + ) }, }) diff --git a/ui/src/kapacitor/components/config/OpsGenieConfig.js b/ui/src/kapacitor/components/config/OpsGenieConfig.js index 7e8c1016e..9d626a207 100644 --- a/ui/src/kapacitor/components/config/OpsGenieConfig.js +++ b/ui/src/kapacitor/components/config/OpsGenieConfig.js @@ -64,27 +64,20 @@ const OpsGenieConfig = React.createClass({ const {currentTeams, currentRecipients} = this.state return ( -
    -
    -

    OpsGenie Alert

    +
    +
    + + this.apiKey = r} defaultValue={apiKey || ''}> +
    -
    -

    Have alerts sent to OpsGenie.

    - -
    - - this.apiKey = r} defaultValue={apiKey || ''}> - -
    - - + + -
    - -
    - -
    +
    + +
    + ) }, }) diff --git a/ui/src/kapacitor/components/config/PagerDutyConfig.js b/ui/src/kapacitor/components/config/PagerDutyConfig.js index 5b7092011..224289524 100644 --- a/ui/src/kapacitor/components/config/PagerDutyConfig.js +++ b/ui/src/kapacitor/components/config/PagerDutyConfig.js @@ -28,29 +28,22 @@ const PagerDutyConfig = React.createClass({ const serviceKey = options['service-key'] return ( -
    -
    -

    PagerDuty Alert

    +
    +
    + + this.serviceKey = r} defaultValue={serviceKey || ''}> +
    -
    -

    You can have alerts sent to PagerDuty by entering info below.

    - -
    - - this.serviceKey = r} defaultValue={serviceKey || ''}> - -
    -
    - - this.url = r} defaultValue={url || ''}> -
    +
    + + this.url = r} defaultValue={url || ''}> +
    -
    - -
    -
    -
    +
    + +
    + ) }, }) diff --git a/ui/src/kapacitor/components/config/SMTPConfig.js b/ui/src/kapacitor/components/config/SMTPConfig.js index 21954c81c..545875c27 100644 --- a/ui/src/kapacitor/components/config/SMTPConfig.js +++ b/ui/src/kapacitor/components/config/SMTPConfig.js @@ -32,43 +32,36 @@ const SMTPConfig = React.createClass({ const {host, port, from, username, password} = this.props.config.options return ( -
    -
    -

    SMTP Alert

    +
    +
    + + this.host = r} defaultValue={host || ''}>
    -
    -

    You can have alerts sent to an email address by setting up an SMTP endpoint.

    - -
    - - this.host = r} defaultValue={host || ''}> -
    -
    - - this.port = r} defaultValue={port || ''}> -
    +
    + + this.port = r} defaultValue={port || ''}> +
    -
    - - this.from = r} defaultValue={from || ''}> -
    +
    + + this.from = r} defaultValue={from || ''}> +
    -
    - - this.username = r} defaultValue={username || ''}> -
    +
    + + this.username = r} defaultValue={username || ''}> +
    -
    - - this.password = r} defaultValue={`${password}`}> -
    +
    + + this.password = r} defaultValue={`${password}`}> +
    -
    - -
    -
    -
    +
    + +
    + ) }, }) diff --git a/ui/src/kapacitor/components/config/SensuConfig.js b/ui/src/kapacitor/components/config/SensuConfig.js index ccc88b3a5..588c789c3 100644 --- a/ui/src/kapacitor/components/config/SensuConfig.js +++ b/ui/src/kapacitor/components/config/SensuConfig.js @@ -26,28 +26,21 @@ const SensuConfig = React.createClass({ const {source, addr} = this.props.config.options return ( -
    -
    -

    Sensu Alert

    +
    +
    + + this.source = r} defaultValue={source || ''}>
    -
    -

    Have alerts sent to Sensu.

    - -
    - - this.source = r} defaultValue={source || ''}> -
    -
    - - this.addr = r} defaultValue={addr || ''}> -
    +
    + + this.addr = r} defaultValue={addr || ''}> +
    -
    - -
    -
    -
    +
    + +
    + ) }, }) diff --git a/ui/src/kapacitor/components/config/SlackConfig.js b/ui/src/kapacitor/components/config/SlackConfig.js index b1e1d3136..b9855b939 100644 --- a/ui/src/kapacitor/components/config/SlackConfig.js +++ b/ui/src/kapacitor/components/config/SlackConfig.js @@ -47,30 +47,23 @@ const SlackConfig = React.createClass({ const {url, channel} = this.props.config.options return ( -
    -
    -

    Slack Alert

    +
    +
    + + this.url = r} defaultValue={url || ''}> +
    -
    -

    Post alerts to a Slack channel.

    - -
    - - this.url = r} defaultValue={url || ''}> - -
    -
    - - this.channel = r} defaultValue={channel || ''}> -
    +
    + + this.channel = r} defaultValue={channel || ''}> +
    -
    - Send Test Message - -
    -
    -
    +
    + Send Test Message + +
    + ) }, }) diff --git a/ui/src/kapacitor/components/config/TalkConfig.js b/ui/src/kapacitor/components/config/TalkConfig.js index d99420b42..240346d1c 100644 --- a/ui/src/kapacitor/components/config/TalkConfig.js +++ b/ui/src/kapacitor/components/config/TalkConfig.js @@ -33,29 +33,22 @@ const TalkConfig = React.createClass({ const {url, author_name: author} = this.props.config.options return ( -
    -
    -

    Talk Alert

    +
    +
    + + this.url = r} defaultValue={url || ''}> +
    -
    -

    Have alerts sent to Talk.

    - -
    - - this.url = r} defaultValue={url || ''}> - -
    -
    - - this.author = r} defaultValue={author || ''}> -
    +
    + + this.author = r} defaultValue={author || ''}> +
    -
    - -
    -
    -
    +
    + +
    + ) }, }) diff --git a/ui/src/kapacitor/components/config/TelegramConfig.js b/ui/src/kapacitor/components/config/TelegramConfig.js index d30086adf..52ff92b8b 100644 --- a/ui/src/kapacitor/components/config/TelegramConfig.js +++ b/ui/src/kapacitor/components/config/TelegramConfig.js @@ -54,89 +54,83 @@ const TelegramConfig = React.createClass({ const parseMode = options['parse-mode'] return ( -
    -
    -

    Telegram Alert

    -
    -
    +

    - Send alert messages to a Telegram bot. + You need a Telegram Bot to use this endpoint

    - -
    -