From 423f02ba7388239e6b0071ee4e4158ff95a60a55 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 13 Jul 2017 13:37:30 -0700 Subject: [PATCH 01/42] Add skeleton for tickscript editor page --- ui/src/index.js | 3 ++ ui/src/kapacitor/components/KapacitorRules.js | 10 +++++-- ui/src/kapacitor/components/Tickscript.js | 27 +++++++++++++++++ .../kapacitor/components/TickscriptHeader.js | 29 +++++++++++++++++++ ui/src/kapacitor/containers/TickscriptPage.js | 28 ++++++++++++++++++ ui/src/kapacitor/index.js | 2 ++ 6 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 ui/src/kapacitor/components/Tickscript.js create mode 100644 ui/src/kapacitor/components/TickscriptHeader.js create mode 100644 ui/src/kapacitor/containers/TickscriptPage.js diff --git a/ui/src/index.js b/ui/src/index.js index 0cc35d03a6..8a8838dfc5 100644 --- a/ui/src/index.js +++ b/ui/src/index.js @@ -23,6 +23,7 @@ import { KapacitorRulePage, KapacitorRulesPage, KapacitorTasksPage, + TickscriptPage, } from 'src/kapacitor' import {AdminPage} from 'src/admin' import {CreateSource, SourcePage, ManageSources} from 'src/sources' @@ -140,6 +141,8 @@ const Root = React.createClass({ + + diff --git a/ui/src/kapacitor/components/KapacitorRules.js b/ui/src/kapacitor/components/KapacitorRules.js index f6e6c2f489..7ff8cc6837 100644 --- a/ui/src/kapacitor/components/KapacitorRules.js +++ b/ui/src/kapacitor/components/KapacitorRules.js @@ -61,7 +61,13 @@ const KapacitorRules = ({ to={`/sources/${source.id}/alert-rules/new`} className="btn btn-sm btn-primary" > - Create Rule + Build Rule + + + Write Rule +const PageContents = ({children, source}) =>
diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js new file mode 100644 index 0000000000..2fc53c999b --- /dev/null +++ b/ui/src/kapacitor/components/Tickscript.js @@ -0,0 +1,27 @@ +import React, {PropTypes} from 'react' +import TickscriptHeader from 'src/kapacitor/components/TickscriptHeader' +import FancyScrollbar from 'shared/components/FancyScrollbar' + +const Tickscript = ({source, onSave}) => ( +
+ + +
+
+
+
+
+
+
+ +
+) + +const {func, shape} = PropTypes + +Tickscript.propTypes = { + onSave: func, + source: shape(), +} + +export default Tickscript diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js new file mode 100644 index 0000000000..561e35f788 --- /dev/null +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -0,0 +1,29 @@ +import React, {PropTypes} from 'react' +import SourceIndicator from 'shared/components/SourceIndicator' + +const TickscriptHeader = ({source, onSave}) => ( +
+
+
+

+ TICKscript Editor +

+
+
+ + +
+
+
+) + +const {func, shape} = PropTypes + +TickscriptHeader.propTypes = { + onSave: func, + source: shape(), +} + +export default TickscriptHeader diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js new file mode 100644 index 0000000000..5f1faf04ba --- /dev/null +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -0,0 +1,28 @@ +import React, {PropTypes, Component} from 'react' +import Tickscript from 'src/kapacitor/components/Tickscript' + +class TickscriptPage extends Component { + constructor(props) { + super(props) + } + + handleSave() { + console.log('save me!') // eslint-disable-line no-console + } + + render() { + const {source} = this.props + + return + } +} + +const {shape, string} = PropTypes + +TickscriptPage.propTypes = { + source: shape({ + name: string, + }), +} + +export default TickscriptPage diff --git a/ui/src/kapacitor/index.js b/ui/src/kapacitor/index.js index fa44936ffb..b017f9ac3d 100644 --- a/ui/src/kapacitor/index.js +++ b/ui/src/kapacitor/index.js @@ -2,9 +2,11 @@ import KapacitorPage from './containers/KapacitorPage' import KapacitorRulePage from './containers/KapacitorRulePage' import KapacitorRulesPage from './containers/KapacitorRulesPage' import KapacitorTasksPage from './containers/KapacitorTasksPage' +import TickscriptPage from './containers/TickscriptPage' export { KapacitorPage, KapacitorRulePage, KapacitorRulesPage, KapacitorTasksPage, + TickscriptPage, } From b5aa82fe0859efe9e47ee835f2814cf348bf237e Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 13 Jul 2017 14:49:58 -0700 Subject: [PATCH 02/42] Introduce TICKscript editor --- .../kapacitor/components/TickscriptEditor.js | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 ui/src/kapacitor/components/TickscriptEditor.js diff --git a/ui/src/kapacitor/components/TickscriptEditor.js b/ui/src/kapacitor/components/TickscriptEditor.js new file mode 100644 index 0000000000..48f2b6ab46 --- /dev/null +++ b/ui/src/kapacitor/components/TickscriptEditor.js @@ -0,0 +1,42 @@ +import React, {Component} from 'react' +import MonacoEditor from 'react-monaco-editor' + +class TickscriptEditor extends Component { + constructor(props) { + super(props) + this.state = { + code: '', + } + } + + editorDidMount(editor, monaco) { + console.log('editorDidMount', editor, monaco) + editor.focus() + } + + onChange(newValue, e) { + console.log('onChange', newValue, e) + } + + render() { + const {code} = this.state + + const options = { + selectOnLineNumbers: true, + } + + return ( + + ) + } +} + +export default TickscriptEditor From 202702b7390de77d1658ca76b50e7ca4d4b5e066 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 13 Jul 2017 16:06:02 -0700 Subject: [PATCH 03/42] Introduce CodeMirror for TICKscript editor --- ui/package.json | 2 + ui/src/external/codemirror.js | 352 ++++++++++++++++++ ui/src/kapacitor/components/Tickscript.js | 3 +- .../kapacitor/components/TickscriptEditor.js | 25 +- ui/src/style/chronograf.scss | 4 + ui/src/style/external/codemirror.scss | 340 +++++++++++++++++ ui/yarn.lock | 29 +- 7 files changed, 735 insertions(+), 20 deletions(-) create mode 100644 ui/src/external/codemirror.js create mode 100644 ui/src/style/external/codemirror.scss diff --git a/ui/package.json b/ui/package.json index ce6f46bf6d..75aca0b065 100644 --- a/ui/package.json +++ b/ui/package.json @@ -112,6 +112,8 @@ "query-string": "^5.0.0", "react": "^15.0.2", "react-addons-shallow-compare": "^15.0.2", + "react-addons-update": "^15.1.0", + "react-codemirror": "^1.0.0", "react-custom-scrollbars": "^4.1.1", "react-dimensions": "^1.2.0", "react-dom": "^15.0.2", diff --git a/ui/src/external/codemirror.js b/ui/src/external/codemirror.js new file mode 100644 index 0000000000..03c4aae05d --- /dev/null +++ b/ui/src/external/codemirror.js @@ -0,0 +1,352 @@ +/* eslint-disable */ +const CodeMirror = require('codemirror') + +CodeMirror.defineSimpleMode = function(name, states) { + CodeMirror.defineMode(name, function(config) { + return CodeMirror.simpleMode(config, states) + }) +} + +CodeMirror.simpleMode = function(config, states) { + ensureState(states, 'start') + const states_ = {}, meta = states.meta || {} + let hasIndentation = false + for (const state in states) { + if (state !== meta && states.hasOwnProperty(state)) { + const list = (states_[state] = []), orig = states[state] + for (let i = 0; i < orig.length; i++) { + const data = orig[i] + list.push(new Rule(data, states)) + if (data.indent || data.dedent) { + hasIndentation = true + } + } + } + } + const mode = { + startState() { + return { + state: 'start', + pending: null, + local: null, + localState: null, + indent: hasIndentation ? [] : null, + } + }, + copyState(state) { + const s = { + state: state.state, + pending: state.pending, + local: state.local, + localState: null, + indent: state.indent && state.indent.slice(0), + } + if (state.localState) { + s.localState = CodeMirror.copyState(state.local.mode, state.localState) + } + if (state.stack) { + s.stack = state.stack.slice(0) + } + for (let pers = state.persistentStates; pers; pers = pers.next) { + s.persistentStates = { + mode: pers.mode, + spec: pers.spec, + state: pers.state === state.localState + ? s.localState + : CodeMirror.copyState(pers.mode, pers.state), + next: s.persistentStates, + } + } + return s + }, + token: tokenFunction(states_, config), + innerMode(state) { + return state.local && {mode: state.local.mode, state: state.localState} + }, + indent: indentFunction(states_, meta), + } + if (meta) { + for (const prop in meta) { + if (meta.hasOwnProperty(prop)) { + mode[prop] = meta[prop] + } + } + } + return mode +} + +function ensureState(states, name) { + if (!states.hasOwnProperty(name)) { + throw new Error(`Undefined state ${name} in simple mode`) + } +} + +function toRegex(val, caret) { + if (!val) { + return /(?:)/ + } + let flags = '' + if (val instanceof RegExp) { + if (val.ignoreCase) { + flags = 'i' + } + val = val.source + } else { + val = String(val) + } + return new RegExp(`${caret === false ? '' : '^'}(?:${val})`, flags) +} + +function asToken(val) { + if (!val) { + return null + } + if (val.apply) { + return val + } + if (typeof val === 'string') { + return val.replace(/\./g, ' ') + } + const result = [] + for (let i = 0; i < val.length; i++) { + result.push(val[i] && val[i].replace(/\./g, ' ')) + } + return result +} + +function Rule(data, states) { + if (data.next || data.push) { + ensureState(states, data.next || data.push) + } + this.regex = toRegex(data.regex) + this.token = asToken(data.token) + this.data = data +} + +function tokenFunction(states, config) { + return function(stream, state) { + if (state.pending) { + const pend = state.pending.shift() + if (state.pending.length === 0) { + state.pending = null + } + stream.pos += pend.text.length + return pend.token + } + + if (state.local) { + let tok, m + if (state.local.end && stream.match(state.local.end)) { + tok = state.local.endToken || null + state.local = state.localState = null + return tok + } + + tok = state.local.mode.token(stream, state.localState) + if ( + state.local.endScan && (m = state.local.endScan.exec(stream.current())) + ) { + stream.pos = stream.start + m.index + } + return tok + } + + const curState = states[state.state] + for (let i = 0; i < curState.length; i++) { + const rule = curState[i] + const matches = + (!rule.data.sol || stream.sol()) && stream.match(rule.regex) + if (matches) { + if (rule.data.next) { + state.state = rule.data.next + } else if (rule.data.push) { + ;(state.stack || (state.stack = [])).push(state.state) + state.state = rule.data.push + } else if (rule.data.pop && state.stack && state.stack.length) { + state.state = state.stack.pop() + } + + if (rule.data.mode) { + enterLocalMode(config, state, rule.data.mode, rule.token) + } + if (rule.data.indent) { + state.indent.push(stream.indentation() + config.indentUnit) + } + if (rule.data.dedent) { + state.indent.pop() + } + let token = rule.token + if (token && token.apply) { + token = token(matches) + } + if (matches.length > 2) { + state.pending = [] + for (let j = 2; j < matches.length; j++) { + if (matches[j]) { + state.pending.push({text: matches[j], token: rule.token[j - 1]}) + } + } + stream.backUp( + matches[0].length - (matches[1] ? matches[1].length : 0) + ) + return token[0] + } else if (token && token.join) { + return token[0] + } + return token + } + } + stream.next() + return null + } +} + +function cmp(a, b) { + if (a === b) { + return true + } + if (!a || typeof a !== 'object' || !b || typeof b !== 'object') { + return false + } + let props = 0 + for (const prop in a) { + if (a.hasOwnProperty(prop)) { + if (!b.hasOwnProperty(prop) || !cmp(a[prop], b[prop])) { + return false + } + props += 1 + } + } + for (const prop in b) { + if (b.hasOwnProperty(prop)) { + props -= 1 + } + } + return props === 0 +} + +function enterLocalMode(config, state, spec, token) { + let pers + if (spec.persistent) { + for (let p = state.persistentStates; p && !pers; p = p.next) { + if (spec.spec ? cmp(spec.spec, p.spec) : spec.mode === p.mode) { + pers = p + } + } + } + const mode = pers + ? pers.mode + : spec.mode || CodeMirror.getMode(config, spec.spec) + const lState = pers ? pers.state : CodeMirror.startState(mode) + if (spec.persistent && !pers) { + state.persistentStates = { + mode, + spec: spec.spec, + state: lState, + next: state.persistentStates, + } + } + + state.localState = lState + state.local = { + mode, + end: spec.end && toRegex(spec.end), + endScan: spec.end && spec.forceEnd !== false && toRegex(spec.end, false), + endToken: token && token.join ? token[token.length - 1] : token, + } +} + +function indexOf(val, arr) { + for (let i = 0; i < arr.length; i++) { + if (arr[i] === val) { + return true + } + } +} + +function indentFunction(states, meta) { + return function(state, textAfter, line) { + if (state.local && state.local.mode.indent) { + return state.local.mode.indent(state.localState, textAfter, line) + } + if ( + state.indent === null || + state.local || + (meta.dontIndentStates && + indexOf(state.state, meta.dontIndentStates) > -1) + ) { + return CodeMirror.Pass + } + + let pos = state.indent.length - 1, rules = states[state.state] + scan: for (;;) { + for (let i = 0; i < rules.length; i++) { + const rule = rules[i] + if (rule.data.dedent && rule.data.dedentIfLineStart !== false) { + const m = rule.regex.exec(textAfter) + if (m && m[0]) { + pos -= 1 + if (rule.next || rule.push) { + rules = states[rule.next || rule.push] + } + textAfter = textAfter.slice(m[0].length) + continue scan + } + } + } + break + } + return pos < 0 ? 0 : state.indent[pos] + } +} + +CodeMirror.defineSimpleMode('tickscript', { + // The start state contains the rules that are intially used + start: [ + // The regex matches the token, the token property contains the type + {regex: /"(?:[^\\]|\\.)*?(?:"|$)/, token: 'string'}, + // You can match multiple tokens at once. Note that the captured + // groups must span the whole string in this case + { + regex: /(function)(\s+)([a-z$][\w$]*)/, + token: ['keyword', null, 'variable-2'], + }, + // Rules are matched in the order in which they appear, so there is + // no ambiguity between this one and the one above + { + regex: /(?:function|var|return|if|for|while|else|do|this)\b/, + token: 'keyword', + }, + {regex: /true|false|null|undefined/, token: 'atom'}, + { + regex: /0x[a-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:e[-+]?\d+)?/i, + token: 'number', + }, + {regex: /\/\/.*/, token: 'comment'}, + {regex: /\/(?:[^\\]|\\.)*?\//, token: 'variable-3'}, + // A next property will cause the mode to move to a different state + {regex: /\/\*/, token: 'comment', next: 'comment'}, + {regex: /[-+\/*=<>!]+/, token: 'operator'}, + // indent and dedent properties guide autoindentation + {regex: /[\{\[\(]/, indent: true}, + {regex: /[\}\]\)]/, dedent: true}, + {regex: /[a-z$][\w$]*/, token: 'variable'}, + // You can embed other modes with the mode property. This rule + // causes all code between << and >> to be highlighted with the XML + // mode. + {regex: /<>/}}, + ], + // The multi-line comment state. + comment: [ + {regex: /.*?\*\//, token: 'comment', next: 'start'}, + {regex: /.*/, token: 'comment'}, + ], + // The meta property contains global information about the mode. It + // can contain properties like lineComment, which are supported by + // all modes, and also directives like dontIndentStates, which are + // specific to simple modes. + meta: { + dontIndentStates: ['comment'], + lineComment: '//', + }, +}) diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index 2fc53c999b..f25fe3a8e3 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -1,6 +1,7 @@ import React, {PropTypes} from 'react' import TickscriptHeader from 'src/kapacitor/components/TickscriptHeader' import FancyScrollbar from 'shared/components/FancyScrollbar' +import TickscriptEditor from 'src/kapacitor/components/TickscriptEditor' const Tickscript = ({source, onSave}) => (
@@ -9,7 +10,7 @@ const Tickscript = ({source, onSave}) => (
-
+
diff --git a/ui/src/kapacitor/components/TickscriptEditor.js b/ui/src/kapacitor/components/TickscriptEditor.js index 48f2b6ab46..c8f687c01b 100644 --- a/ui/src/kapacitor/components/TickscriptEditor.js +++ b/ui/src/kapacitor/components/TickscriptEditor.js @@ -1,5 +1,6 @@ import React, {Component} from 'react' -import MonacoEditor from 'react-monaco-editor' +import CodeMirror from 'react-codemirror' +import 'src/external/codemirror' class TickscriptEditor extends Component { constructor(props) { @@ -9,32 +10,20 @@ class TickscriptEditor extends Component { } } - editorDidMount(editor, monaco) { - console.log('editorDidMount', editor, monaco) - editor.focus() - } - - onChange(newValue, e) { - console.log('onChange', newValue, e) + updateCode(code) { + this.setState({code}) } render() { const {code} = this.state const options = { - selectOnLineNumbers: true, + lineNumbers: true, + theme: 'material', } return ( - + ) } } diff --git a/ui/src/style/chronograf.scss b/ui/src/style/chronograf.scss index a8bc1d7191..dfb12062d9 100644 --- a/ui/src/style/chronograf.scss +++ b/ui/src/style/chronograf.scss @@ -59,3 +59,7 @@ // TODO @import 'unsorted'; + +// Vendor +@import "external/codemirror"; +@import "../../node_modules/codemirror/theme/material.css"; diff --git a/ui/src/style/external/codemirror.scss b/ui/src/style/external/codemirror.scss new file mode 100644 index 0000000000..e615180d65 --- /dev/null +++ b/ui/src/style/external/codemirror.scss @@ -0,0 +1,340 @@ +/* BASICS */ + +.CodeMirror { + /* Set height, width, borders, and global font properties here */ + font-family: monospace; + height: calc(100% - 76px); + color: black; +} + +/* PADDING */ + +.CodeMirror-lines { + padding: 4px 0; /* Vertical padding around content */ +} +.CodeMirror pre { + padding: 0 4px; /* Horizontal padding of content */ +} + +.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + background-color: white; /* The little square between H and V scrollbars */ +} + +/* GUTTER */ + +.CodeMirror-gutters { + border-right: 1px solid #ddd; + background-color: #f7f7f7; + white-space: nowrap; +} +.CodeMirror-linenumbers {} +.CodeMirror-linenumber { + padding: 0 3px 0 5px; + min-width: 20px; + text-align: right; + color: #999; + white-space: nowrap; +} + +.CodeMirror-guttermarker { color: black; } +.CodeMirror-guttermarker-subtle { color: #999; } + +/* CURSOR */ + +.CodeMirror-cursor { + border-left: 1px solid black; + border-right: none; + width: 0; +} +/* Shown when moving in bi-directional text */ +.CodeMirror div.CodeMirror-secondarycursor { + border-left: 1px solid silver; +} +.cm-fat-cursor .CodeMirror-cursor { + width: auto; + border: 0 !important; + background: #7e7; +} +.cm-fat-cursor div.CodeMirror-cursors { + z-index: 1; +} + +.cm-animate-fat-cursor { + width: auto; + border: 0; + -webkit-animation: blink 1.06s steps(1) infinite; + -moz-animation: blink 1.06s steps(1) infinite; + animation: blink 1.06s steps(1) infinite; + background-color: #7e7; +} +@-moz-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@-webkit-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} + +/* Can style cursor different in overwrite (non-insert) mode */ +.CodeMirror-overwrite .CodeMirror-cursor {} + +.cm-tab { display: inline-block; text-decoration: inherit; } + +.CodeMirror-rulers { + position: absolute; + left: 0; right: 0; top: -50px; bottom: -20px; + overflow: hidden; +} +.CodeMirror-ruler { + border-left: 1px solid #ccc; + top: 0; bottom: 0; + position: absolute; +} + +/* DEFAULT THEME */ + +.cm-s-default .cm-header {color: blue;} +.cm-s-default .cm-quote {color: #090;} +.cm-negative {color: #d44;} +.cm-positive {color: #292;} +.cm-header, .cm-strong {font-weight: bold;} +.cm-em {font-style: italic;} +.cm-link {text-decoration: underline;} +.cm-strikethrough {text-decoration: line-through;} + +.cm-s-default .cm-keyword {color: #708;} +.cm-s-default .cm-atom {color: #219;} +.cm-s-default .cm-number {color: #164;} +.cm-s-default .cm-def {color: #00f;} +.cm-s-default .cm-variable, +.cm-s-default .cm-punctuation, +.cm-s-default .cm-property, +.cm-s-default .cm-operator {} +.cm-s-default .cm-variable-2 {color: #05a;} +.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} +.cm-s-default .cm-comment {color: #a50;} +.cm-s-default .cm-string {color: #a11;} +.cm-s-default .cm-string-2 {color: #f50;} +.cm-s-default .cm-meta {color: #555;} +.cm-s-default .cm-qualifier {color: #555;} +.cm-s-default .cm-builtin {color: #30a;} +.cm-s-default .cm-bracket {color: #997;} +.cm-s-default .cm-tag {color: #170;} +.cm-s-default .cm-attribute {color: #00c;} +.cm-s-default .cm-hr {color: #999;} +.cm-s-default .cm-link {color: #00c;} + +.cm-s-default .cm-error {color: #f00;} +.cm-invalidchar {color: #f00;} + +.CodeMirror-composing { border-bottom: 2px solid; } + +/* Default styles for common addons */ + +div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} +div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} +.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } +.CodeMirror-activeline-background {background: #e8f2ff;} + +/* STOP */ + +/* The rest of this file contains styles related to the mechanics of + the editor. You probably shouldn't touch them. */ + +.CodeMirror { + position: relative; + overflow: hidden; + background: white; +} + +.CodeMirror-scroll { + overflow: scroll !important; /* Things will break if this is overridden */ + /* 30px is the magic margin used to hide the element's real scrollbars */ + /* See overflow: hidden in .CodeMirror */ + margin-bottom: -30px; margin-right: -30px; + padding-bottom: 30px; + height: 100%; + outline: none; /* Prevent dragging from highlighting the element */ + position: relative; +} +.CodeMirror-sizer { + position: relative; + border-right: 30px solid transparent; +} + +/* The fake, visible scrollbars. Used to force redraw during scrolling + before actual scrolling happens, thus preventing shaking and + flickering artifacts. */ +.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + position: absolute; + z-index: 6; + display: none; +} +.CodeMirror-vscrollbar { + right: 0; top: 0; + overflow-x: hidden; + overflow-y: scroll; +} +.CodeMirror-hscrollbar { + bottom: 0; left: 0; + overflow-y: hidden; + overflow-x: scroll; +} +.CodeMirror-scrollbar-filler { + right: 0; bottom: 0; +} +.CodeMirror-gutter-filler { + left: 0; bottom: 0; +} + +.CodeMirror-gutters { + position: absolute; left: 0; top: 0; + min-height: 100%; + z-index: 3; +} +.CodeMirror-gutter { + white-space: normal; + height: 100%; + display: inline-block; + vertical-align: top; + margin-bottom: -30px; +} +.CodeMirror-gutter-wrapper { + position: absolute; + z-index: 4; + background: none !important; + border: none !important; +} +.CodeMirror-gutter-background { + position: absolute; + top: 0; bottom: 0; + z-index: 4; +} +.CodeMirror-gutter-elt { + position: absolute; + cursor: default; + z-index: 4; +} +.CodeMirror-gutter-wrapper ::selection { background-color: transparent } +.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } + +.CodeMirror-lines { + cursor: text; + min-height: 1px; /* prevents collapsing before first draw */ +} +.CodeMirror pre { + /* Reset some styles that the rest of the page might have set */ + -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; + border-width: 0; + background: transparent; + font-family: inherit; + font-size: inherit; + margin: 0; + white-space: pre; + word-wrap: normal; + line-height: inherit; + color: inherit; + z-index: 2; + position: relative; + overflow: visible; + -webkit-tap-highlight-color: transparent; + -webkit-font-variant-ligatures: contextual; + font-variant-ligatures: contextual; +} +.CodeMirror-wrap pre { + word-wrap: break-word; + white-space: pre-wrap; + word-break: normal; +} + +.CodeMirror-linebackground { + position: absolute; + left: 0; right: 0; top: 0; bottom: 0; + z-index: 0; +} + +.CodeMirror-linewidget { + position: relative; + z-index: 2; + overflow: auto; +} + +.CodeMirror-widget {} + +.CodeMirror-rtl pre { direction: rtl; } + +.CodeMirror-code { + outline: none; +} + +/* Force content-box sizing for the elements where we expect it */ +.CodeMirror-scroll, +.CodeMirror-sizer, +.CodeMirror-gutter, +.CodeMirror-gutters, +.CodeMirror-linenumber { + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +.CodeMirror-measure { + position: absolute; + width: 100%; + height: 0; + overflow: hidden; + visibility: hidden; +} + +.CodeMirror-cursor { + position: absolute; + pointer-events: none; +} +.CodeMirror-measure pre { position: static; } + +div.CodeMirror-cursors { + visibility: hidden; + position: relative; + z-index: 3; +} +div.CodeMirror-dragcursors { + visibility: visible; +} + +.CodeMirror-focused div.CodeMirror-cursors { + visibility: visible; +} + +.CodeMirror-selected { background: #d9d9d9; } +.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } +.CodeMirror-crosshair { cursor: crosshair; } +.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } +.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } + +.cm-searching { + background: #ffa; + background: rgba(255, 255, 0, .4); +} + +/* Used to force a border model for a node */ +.cm-force-border { padding-right: .1px; } + +@media print { + /* Hide the cursor when printing */ + .CodeMirror div.CodeMirror-cursors { + visibility: hidden; + } +} + +/* See issue #2901 */ +.cm-tab-wrap-hack:after { content: ''; } + +/* Help users use markselection to safely style text background */ +span.CodeMirror-selectedtext { background: none; } diff --git a/ui/yarn.lock b/ui/yarn.lock index af7e628795..9c37d4b41d 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -1794,6 +1794,10 @@ code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" +codemirror@^5.18.2: + version "5.27.4" + resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.27.4.tgz#0e817c839bfea9959dd16cd48ae14acc0e43c3b6" + color-convert@^1.3.0: version "1.8.2" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.8.2.tgz#be868184d7c8631766d54e7078e2672d7c7e3339" @@ -4402,6 +4406,10 @@ lodash.debounce@^3.1.1: dependencies: lodash._getnative "^3.0.0" +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + lodash.deburr@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-3.2.0.tgz#6da8f54334a366a7cf4c4c76ef8d80aa1b365ed5" @@ -4462,6 +4470,10 @@ lodash.isequal@^4.0.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.4.0.tgz#6295768e98e14dc15ce8d362ef6340db82852031" +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + lodash.keys@^3.0.0, lodash.keys@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" @@ -5814,7 +5826,7 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prop-types@^15.5.6, prop-types@^15.5.8: +prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8: version "15.5.8" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394" dependencies: @@ -5932,6 +5944,21 @@ react-addons-test-utils@^15.0.2: version "15.4.1" resolved "https://registry.yarnpkg.com/react-addons-test-utils/-/react-addons-test-utils-15.4.1.tgz#1e4caab151bf27cce26df5f9cb714f4fd8359ae1" +react-addons-update@^15.1.0: + version "15.4.1" + resolved "https://registry.yarnpkg.com/react-addons-update/-/react-addons-update-15.4.1.tgz#00c07f45243aa9715e1706bbfd1f23d3d8d80bd1" + +react-codemirror@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/react-codemirror/-/react-codemirror-1.0.0.tgz#91467b53b1f5d80d916a2fd0b4c7adb85a9001ba" + dependencies: + classnames "^2.2.5" + codemirror "^5.18.2" + create-react-class "^15.5.1" + lodash.debounce "^4.0.8" + lodash.isequal "^4.5.0" + prop-types "^15.5.4" + react-custom-scrollbars@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/react-custom-scrollbars/-/react-custom-scrollbars-4.1.1.tgz#cf08cd43b1297ab11e6fcc5c9a800e7b70b6f248" From ad1f78aed0eeae080c91161aedf6596ac27bb986 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 13 Jul 2017 16:28:36 -0700 Subject: [PATCH 04/42] Polish the editor --- ui/src/external/codemirror.js | 16 ++++++---------- ui/src/style/external/codemirror.scss | 2 +- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/ui/src/external/codemirror.js b/ui/src/external/codemirror.js index 03c4aae05d..24f06cd817 100644 --- a/ui/src/external/codemirror.js +++ b/ui/src/external/codemirror.js @@ -144,7 +144,8 @@ function tokenFunction(states, config) { tok = state.local.mode.token(stream, state.localState) if ( - state.local.endScan && (m = state.local.endScan.exec(stream.current())) + state.local.endScan && + (m = state.local.endScan.exec(stream.current())) ) { stream.pos = stream.start + m.index } @@ -304,9 +305,8 @@ CodeMirror.defineSimpleMode('tickscript', { // The start state contains the rules that are intially used start: [ // The regex matches the token, the token property contains the type - {regex: /"(?:[^\\]|\\.)*?(?:"|$)/, token: 'string'}, - // You can match multiple tokens at once. Note that the captured - // groups must span the whole string in this case + {regex: /"(?:[^\\]|\\.)*?(?:"|$)/, token: 'string.double'}, + {regex: /'(?:[^\\]|\\.)*?(?:'|$)/, token: 'string.single'}, { regex: /(function)(\s+)([a-z$][\w$]*)/, token: ['keyword', null, 'variable-2'], @@ -314,10 +314,10 @@ CodeMirror.defineSimpleMode('tickscript', { // Rules are matched in the order in which they appear, so there is // no ambiguity between this one and the one above { - regex: /(?:function|var|return|if|for|while|else|do|this)\b/, + regex: /(?:function|var|return|if|for|while|else|do|this|stream|batch|influxql)\b/, token: 'keyword', }, - {regex: /true|false|null|undefined/, token: 'atom'}, + {regex: /true|false|null|undefined|TRUE|FALSE/, token: 'atom'}, { regex: /0x[a-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:e[-+]?\d+)?/i, token: 'number', @@ -331,10 +331,6 @@ CodeMirror.defineSimpleMode('tickscript', { {regex: /[\{\[\(]/, indent: true}, {regex: /[\}\]\)]/, dedent: true}, {regex: /[a-z$][\w$]*/, token: 'variable'}, - // You can embed other modes with the mode property. This rule - // causes all code between << and >> to be highlighted with the XML - // mode. - {regex: /<>/}}, ], // The multi-line comment state. comment: [ diff --git a/ui/src/style/external/codemirror.scss b/ui/src/style/external/codemirror.scss index e615180d65..935821d4a1 100644 --- a/ui/src/style/external/codemirror.scss +++ b/ui/src/style/external/codemirror.scss @@ -3,7 +3,7 @@ .CodeMirror { /* Set height, width, borders, and global font properties here */ font-family: monospace; - height: calc(100% - 76px); + height: calc(100vh - 140px); color: black; } From 87221200bb5ccc9631b8248ae0a01bcdbff31ff2 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 11 Jul 2017 14:38:24 -0700 Subject: [PATCH 05/42] Display TICKscript on rules index --- ui/src/kapacitor/components/KapacitorRules.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/src/kapacitor/components/KapacitorRules.js b/ui/src/kapacitor/components/KapacitorRules.js index 7ff8cc6837..2697b1dbe0 100644 --- a/ui/src/kapacitor/components/KapacitorRules.js +++ b/ui/src/kapacitor/components/KapacitorRules.js @@ -81,7 +81,11 @@ const KapacitorRules = ({ ) } +<<<<<<< HEAD const PageContents = ({children, source}) => +======= +const PageContents = ({children, source, tickscript, onCloseTickscript}) => ( +>>>>>>> Display TICKscript on rules index
From 1e1c9555606f30a762827fe71b902effa7cf6106 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 12 Jul 2017 11:20:31 -0700 Subject: [PATCH 06/42] Polish alert rules table --- ui/src/kapacitor/components/KapacitorRulesTable.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ui/src/kapacitor/components/KapacitorRulesTable.js b/ui/src/kapacitor/components/KapacitorRulesTable.js index 8b3d944cfd..c22357b5c1 100644 --- a/ui/src/kapacitor/components/KapacitorRulesTable.js +++ b/ui/src/kapacitor/components/KapacitorRulesTable.js @@ -12,6 +12,16 @@ const { colActions, } = KAPACITOR_RULES_TABLE +import {KAPACITOR_RULES_TABLE} from 'src/kapacitor/constants/tableSizing' +const { + colName, + colType, + colMessage, + colAlerts, + colEnabled, + colActions, +} = KAPACITOR_RULES_TABLE + const KapacitorRulesTable = ({ rules, source, From c4562aa84510beb72252e09a867cb4a1c6a18da5 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 13 Jul 2017 12:47:08 -0700 Subject: [PATCH 07/42] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4254d1a947..21198245f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -145,6 +145,8 @@ 1. [#1738](https://github.com/influxdata/chronograf/pull/1738): Add shared secret JWT authorization to InfluxDB 1. [#1724](https://github.com/influxdata/chronograf/pull/1724): Add Pushover alert support 1. [#1762](https://github.com/influxdata/chronograf/pull/1762): Restore all supported Kapacitor services when creating rules, and add most optional message parameters +1. [#1681](https://github.com/influxdata/chronograf/pull/1681): Add the ability to select Custom Time Ranges in the Hostpages, Data Explorer, and Dashboards. +1. [#1717](https://github.com/influxdata/chronograf/pull/1717): View server generated TICKscripts ### UI Improvements 1. [#1707](https://github.com/influxdata/chronograf/pull/1707): Polish alerts table in status page to wrap text less From b66be556ed83e4c59b0d04b1d507f3cf3d37a2c7 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 14 Jul 2017 11:37:39 -0700 Subject: [PATCH 08/42] Update kapacitor links to include tasks and ping paths --- server/kapacitors.go | 4 ++++ ui/src/shared/apis/index.js | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/server/kapacitors.go b/server/kapacitors.go index a0a838cb31..e7982a8cb3 100644 --- a/server/kapacitors.go +++ b/server/kapacitors.go @@ -39,6 +39,8 @@ type kapaLinks struct { Proxy string `json:"proxy"` // URL location of proxy endpoint for this source Self string `json:"self"` // Self link mapping to this resource Rules string `json:"rules"` // Rules link for defining roles alerts for kapacitor + Tasks string `json:"tasks"` // Tasks link to define a task against the proxy + Ping string `json:"ping"` // Ping path to kapacitor } type kapacitor struct { @@ -108,6 +110,8 @@ func newKapacitor(srv chronograf.Server) kapacitor { Self: fmt.Sprintf("%s/%d/kapacitors/%d", httpAPISrcs, srv.SrcID, srv.ID), Proxy: fmt.Sprintf("%s/%d/kapacitors/%d/proxy", httpAPISrcs, srv.SrcID, srv.ID), Rules: fmt.Sprintf("%s/%d/kapacitors/%d/rules", httpAPISrcs, srv.SrcID, srv.ID), + Tasks: fmt.Sprintf("%s/%d/kapacitors/%d/proxy?path=/kapacitor/v1/tasks", httpAPISrcs, srv.SrcID, srv.ID), + Ping: fmt.Sprintf("%s/%d/kapacitors/%d/proxy?path=/kapacitor/v1/ping", httpAPISrcs, srv.SrcID, srv.ID), }, } } diff --git a/ui/src/shared/apis/index.js b/ui/src/shared/apis/index.js index f632afc3af..9ab2ac57b3 100644 --- a/ui/src/shared/apis/index.js +++ b/ui/src/shared/apis/index.js @@ -54,7 +54,7 @@ export function deleteSource(source) { export function pingKapacitor(kapacitor) { return AJAX({ method: 'GET', - url: `${kapacitor.links.proxy}?path=/kapacitor/v1/ping`, + url: kapacitor.links.ping, }) } From b6880c252422073838362212739df048613dad9f Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 14 Jul 2017 11:44:03 -0700 Subject: [PATCH 09/42] ES6ify --- ui/src/kapacitor/apis/index.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ui/src/kapacitor/apis/index.js b/ui/src/kapacitor/apis/index.js index 8d1959e0ff..ac33423a7e 100644 --- a/ui/src/kapacitor/apis/index.js +++ b/ui/src/kapacitor/apis/index.js @@ -1,6 +1,6 @@ import AJAX from 'utils/ajax' -function rangeRule(rule) { +const rangeRule = rule => { const {value, rangeValue, operator} = rule.values if (operator === 'inside range' || operator === 'outside range') { @@ -11,7 +11,7 @@ function rangeRule(rule) { return rule } -export function createRule(kapacitor, rule) { +export const createRule = (kapacitor, rule) => { return AJAX({ method: 'POST', url: kapacitor.links.rules, @@ -19,21 +19,21 @@ export function createRule(kapacitor, rule) { }) } -export function getRules(kapacitor) { +export const getRules = kapacitor => { return AJAX({ method: 'GET', url: kapacitor.links.rules, }) } -export function getRule(kapacitor, ruleID) { +export const getRule = (kapacitor, ruleID) => { return AJAX({ method: 'GET', url: `${kapacitor.links.rules}/${ruleID}`, }) } -export function editRule(rule) { +export const editRule = rule => { return AJAX({ method: 'PUT', url: rule.links.self, @@ -41,14 +41,14 @@ export function editRule(rule) { }) } -export function deleteRule(rule) { +export const deleteRule = rule => { return AJAX({ method: 'DELETE', url: rule.links.self, }) } -export function updateRuleStatus(rule, status) { +export const updateRuleStatus = (rule, status) => { return AJAX({ method: 'PATCH', url: rule.links.self, From 4f1372a01bba5ac30471ff769ad52db81fe9e1da Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 14 Jul 2017 14:27:28 -0700 Subject: [PATCH 10/42] Introduce skeleton to create task through proxy --- ui/src/kapacitor/actions/view/index.js | 16 ++++ ui/src/kapacitor/apis/index.js | 19 +++++ ui/src/kapacitor/components/Tickscript.js | 23 +++++- .../kapacitor/components/TickscriptEditor.js | 24 ++++-- ui/src/kapacitor/containers/TickscriptPage.js | 80 +++++++++++++++++-- 5 files changed, 145 insertions(+), 17 deletions(-) diff --git a/ui/src/kapacitor/actions/view/index.js b/ui/src/kapacitor/actions/view/index.js index 85d92dfcaa..198a1803b2 100644 --- a/ui/src/kapacitor/actions/view/index.js +++ b/ui/src/kapacitor/actions/view/index.js @@ -6,6 +6,7 @@ import { getRule, deleteRule as deleteRuleAPI, updateRuleStatus as updateRuleStatusAPI, + createTask as createTaskAJAX, } from 'src/kapacitor/apis' import {errorThrown} from 'shared/actions/errors' @@ -208,3 +209,18 @@ export function updateRuleStatus(rule, status) { }) } } + +export const createTask = (kapacitor, task) => async dispatch => { + try { + const {data} = await createTaskAJAX(kapacitor, task) + dispatch(publishNotification('success', 'We made a tick script!')) + return data + } catch (error) { + if (!error) { + dispatch(errorThrown('Could not communicate with server')) + return + } + + return error.data + } +} diff --git a/ui/src/kapacitor/apis/index.js b/ui/src/kapacitor/apis/index.js index ac33423a7e..9f636026e9 100644 --- a/ui/src/kapacitor/apis/index.js +++ b/ui/src/kapacitor/apis/index.js @@ -55,3 +55,22 @@ export const updateRuleStatus = (rule, status) => { data: {status}, }) } + +// tickscript contains script, dbsrps, id, and type +export const createTask = async (kapacitor, {id, dbsrps, script, type}) => { + try { + return await AJAX({ + method: 'POST', + url: kapacitor.links.tasks, + data: { + id, + type, + dbsrps, + script, + }, + }) + } catch (error) { + console.error(error) + throw error + } +} diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index f25fe3a8e3..cfb25899e8 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -3,14 +3,22 @@ import TickscriptHeader from 'src/kapacitor/components/TickscriptHeader' import FancyScrollbar from 'shared/components/FancyScrollbar' import TickscriptEditor from 'src/kapacitor/components/TickscriptEditor' -const Tickscript = ({source, onSave}) => ( +const Tickscript = ({source, onSave, task, validation, onChangeScript}) => (
- + {validation} +
+
+
+
+
@@ -18,11 +26,18 @@ const Tickscript = ({source, onSave}) => (
) -const {func, shape} = PropTypes +const {arrayOf, func, shape, string} = PropTypes Tickscript.propTypes = { - onSave: func, + onSave: func.isRequired, source: shape(), + task: shape({ + id: string, + script: string, + dbsrps: arrayOf(shape()), + }).isRequired, + onChangeScript: func.isRequired, + validation: string, } export default Tickscript diff --git a/ui/src/kapacitor/components/TickscriptEditor.js b/ui/src/kapacitor/components/TickscriptEditor.js index c8f687c01b..b30de74b07 100644 --- a/ui/src/kapacitor/components/TickscriptEditor.js +++ b/ui/src/kapacitor/components/TickscriptEditor.js @@ -1,21 +1,18 @@ -import React, {Component} from 'react' +import React, {PropTypes, Component} from 'react' import CodeMirror from 'react-codemirror' import 'src/external/codemirror' class TickscriptEditor extends Component { constructor(props) { super(props) - this.state = { - code: '', - } } - updateCode(code) { - this.setState({code}) + updateCode(script) { + this.props.onChangeScript(script) } render() { - const {code} = this.state + const {script} = this.props const options = { lineNumbers: true, @@ -23,9 +20,20 @@ class TickscriptEditor extends Component { } return ( - + ) } } +const {func, string} = PropTypes + +TickscriptEditor.propTypes = { + onChangeScript: func, + script: string, +} + export default TickscriptEditor diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 5f1faf04ba..6d7e64ae9b 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -1,28 +1,98 @@ import React, {PropTypes, Component} from 'react' +import {connect} from 'react-redux' +import {bindActionCreators} from 'redux' + import Tickscript from 'src/kapacitor/components/Tickscript' +import * as kapactiorActionCreators from 'src/kapacitor/actions/view' +import {getActiveKapacitor} from 'src/shared/apis' +import {errorThrown as errorAction} from 'shared/actions/errors' class TickscriptPage extends Component { constructor(props) { super(props) + this.state = { + kapacitor: {}, + task: { + id: 'testing', + status: 'enabled', + script: '', + dbsrps: [ + { + db: '_internal', + rp: 'monitor', + }, + ], + type: 'stream', + }, + validation: '', + } } - handleSave() { - console.log('save me!') // eslint-disable-line no-console + async componentDidMount() { + const {source, errorThrown} = this.props + const kapacitor = await getActiveKapacitor(source) + + if (!kapacitor) { + errorThrown('We could not find a configured Kapacitor for this source') + } + + this.setState({kapacitor}) + } + + async handleSave() { + const {kapacitor, task} = this.state + const {source, router, kapactiorActions: {createTask}} = this.props + + const response = await createTask(kapacitor, task) + if (response && response.error) { + return this.setState({validation: response.error}) + } + + router.push(`/sources/${source.id}/alert-rules`) + } + + handleChangeScript(script) { + this.setState({task: {...this.state.task, script}}) } render() { const {source} = this.props + const {task, validation} = this.state - return + return ( + + ) } } -const {shape, string} = PropTypes +const {func, shape, string} = PropTypes TickscriptPage.propTypes = { source: shape({ name: string, }), + errorThrown: func.isRequired, + kapactiorActions: shape({ + createTask: func.isRequired, + }), + router: shape({ + push: func.isRequired, + }).isRequired, } -export default TickscriptPage +const mapStateToProps = () => { + return {} +} + +const mapDispatchToProps = dispatch => ({ + errorThrown: bindActionCreators(errorAction, dispatch), + kapactiorActions: bindActionCreators(kapactiorActionCreators, dispatch), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(TickscriptPage) From b6ca02160428e94b1d6bfbe6648d16384001914e Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 14 Jul 2017 16:33:42 -0700 Subject: [PATCH 11/42] WIP Edit scripts returned from the server Had to downgrade react-codemirror due to a bug. Issues and PRs are open to resolve: - https://github.com/JedWatson/react-codemirror/issues/121 - https://github.com/JedWatson/react-codemirror/issues/106 --- ui/package.json | 2 +- ui/src/kapacitor/actions/view/index.js | 27 +++++++++- ui/src/kapacitor/apis/index.js | 19 ++++--- .../components/KapacitorRulesTable.js | 9 ++-- ui/src/kapacitor/containers/TickscriptPage.js | 53 ++++++++++++++----- ui/yarn.lock | 25 ++++----- 6 files changed, 93 insertions(+), 42 deletions(-) diff --git a/ui/package.json b/ui/package.json index 75aca0b065..9e5dae1faf 100644 --- a/ui/package.json +++ b/ui/package.json @@ -113,7 +113,7 @@ "react": "^15.0.2", "react-addons-shallow-compare": "^15.0.2", "react-addons-update": "^15.1.0", - "react-codemirror": "^1.0.0", + "react-codemirror": "^0.2.6", "react-custom-scrollbars": "^4.1.1", "react-dimensions": "^1.2.0", "react-dom": "^15.0.2", diff --git a/ui/src/kapacitor/actions/view/index.js b/ui/src/kapacitor/actions/view/index.js index 198a1803b2..b15deff19a 100644 --- a/ui/src/kapacitor/actions/view/index.js +++ b/ui/src/kapacitor/actions/view/index.js @@ -3,7 +3,7 @@ import {getActiveKapacitor} from 'shared/apis' import {publishNotification} from 'shared/actions/notifications' import { getRules, - getRule, + getRule as getRuleAJAX, deleteRule as deleteRuleAPI, updateRuleStatus as updateRuleStatusAPI, createTask as createTaskAJAX, @@ -20,7 +20,7 @@ const loadQuery = query => ({ export function fetchRule(source, ruleID) { return dispatch => { getActiveKapacitor(source).then(kapacitor => { - getRule(kapacitor, ruleID).then(({data: rule}) => { + getRuleAJAX(kapacitor, ruleID).then(({data: rule}) => { dispatch({ type: 'LOAD_RULE', payload: { @@ -40,6 +40,29 @@ const addQuery = queryID => ({ }, }) +export const getRule = (kapacitor, ruleID) => async dispatch => { + try { + const {data: rule} = await getRuleAJAX(kapacitor, ruleID) + + dispatch({ + type: 'LOAD_RULE', + payload: { + rule: {...rule, queryID: rule.query.id}, + }, + }) + + dispatch({ + type: 'LOAD_KAPACITOR_QUERY', + payload: { + query: rule.query, + }, + }) + } catch (error) { + console.error(error) + throw error + } +} + export function loadDefaultRule() { return dispatch => { const queryID = uuid.v4() diff --git a/ui/src/kapacitor/apis/index.js b/ui/src/kapacitor/apis/index.js index 9f636026e9..0968ab3a69 100644 --- a/ui/src/kapacitor/apis/index.js +++ b/ui/src/kapacitor/apis/index.js @@ -26,11 +26,16 @@ export const getRules = kapacitor => { }) } -export const getRule = (kapacitor, ruleID) => { - return AJAX({ - method: 'GET', - url: `${kapacitor.links.rules}/${ruleID}`, - }) +export const getRule = async (kapacitor, ruleID) => { + try { + return await AJAX({ + method: 'GET', + url: `${kapacitor.links.rules}/${ruleID}`, + }) + } catch (error) { + console.error(error) + throw error + } } export const editRule = rule => { @@ -57,7 +62,7 @@ export const updateRuleStatus = (rule, status) => { } // tickscript contains script, dbsrps, id, and type -export const createTask = async (kapacitor, {id, dbsrps, script, type}) => { +export const createTask = async (kapacitor, {id, dbrps, script, type}) => { try { return await AJAX({ method: 'POST', @@ -65,7 +70,7 @@ export const createTask = async (kapacitor, {id, dbsrps, script, type}) => { data: { id, type, - dbsrps, + dbrps, script, }, }) diff --git a/ui/src/kapacitor/components/KapacitorRulesTable.js b/ui/src/kapacitor/components/KapacitorRulesTable.js index c22357b5c1..bc91aa8e8f 100644 --- a/ui/src/kapacitor/components/KapacitorRulesTable.js +++ b/ui/src/kapacitor/components/KapacitorRulesTable.js @@ -92,9 +92,12 @@ const RuleRow = ({rule, source, onRead, onDelete, onChangeRuleStatus}) =>
- + + Edit TICKscript + diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 6d7e64ae9b..0b4a8c4fbe 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -4,8 +4,8 @@ import {bindActionCreators} from 'redux' import Tickscript from 'src/kapacitor/components/Tickscript' import * as kapactiorActionCreators from 'src/kapacitor/actions/view' +import * as errorActionCreators from 'shared/actions/errors' import {getActiveKapacitor} from 'src/shared/apis' -import {errorThrown as errorAction} from 'shared/actions/errors' class TickscriptPage extends Component { constructor(props) { @@ -16,7 +16,7 @@ class TickscriptPage extends Component { id: 'testing', status: 'enabled', script: '', - dbsrps: [ + dbrps: [ { db: '_internal', rp: 'monitor', @@ -29,11 +29,24 @@ class TickscriptPage extends Component { } async componentDidMount() { - const {source, errorThrown} = this.props - const kapacitor = await getActiveKapacitor(source) + const { + source, + errorActions, + kapacitorActions, + params: {ruleID}, + } = this.props + const kapacitor = await getActiveKapacitor(source) if (!kapacitor) { - errorThrown('We could not find a configured Kapacitor for this source') + errorActions.errorThrown( + 'We could not find a configured Kapacitor for this source' + ) + } + + if (this.isEditing()) { + await kapacitorActions.getRule(kapacitor, ruleID) + const activeRule = this.props.rules.find(r => r.id === ruleID) + this.setState({task: {...this.state.task, script: activeRule.tickscript}}) } this.setState({kapacitor}) @@ -41,7 +54,7 @@ class TickscriptPage extends Component { async handleSave() { const {kapacitor, task} = this.state - const {source, router, kapactiorActions: {createTask}} = this.props + const {source, router, kapacitorActions: {createTask}} = this.props const response = await createTask(kapacitor, task) if (response && response.error) { @@ -69,30 +82,44 @@ class TickscriptPage extends Component { /> ) } + + isEditing() { + const {params} = this.props + return params.ruleID && params.ruleID !== 'new' + } } -const {func, shape, string} = PropTypes +const {arrayOf, func, shape, string} = PropTypes TickscriptPage.propTypes = { source: shape({ name: string, }), - errorThrown: func.isRequired, - kapactiorActions: shape({ + errorActions: shape({ + errorThrown: func.isRequired, + }).isRequired, + kapacitorActions: shape({ createTask: func.isRequired, + getRule: func.isRequired, }), router: shape({ push: func.isRequired, }).isRequired, + params: shape({ + ruleID: string.isRequired, + }).isRequired, + rules: arrayOf(shape()), } -const mapStateToProps = () => { - return {} +const mapStateToProps = state => { + return { + rules: Object.values(state.rules), + } } const mapDispatchToProps = dispatch => ({ - errorThrown: bindActionCreators(errorAction, dispatch), - kapactiorActions: bindActionCreators(kapactiorActionCreators, dispatch), + kapacitorActions: bindActionCreators(kapactiorActionCreators, dispatch), + errorActions: bindActionCreators(errorActionCreators, dispatch), }) export default connect(mapStateToProps, mapDispatchToProps)(TickscriptPage) diff --git a/ui/yarn.lock b/ui/yarn.lock index 9c37d4b41d..086a85b2e1 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -1794,7 +1794,7 @@ code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" -codemirror@^5.18.2: +codemirror@^5.13.4: version "5.27.4" resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.27.4.tgz#0e817c839bfea9959dd16cd48ae14acc0e43c3b6" @@ -4406,7 +4406,7 @@ lodash.debounce@^3.1.1: dependencies: lodash._getnative "^3.0.0" -lodash.debounce@^4.0.8: +lodash.debounce@^4.0.4: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -4470,10 +4470,6 @@ lodash.isequal@^4.0.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.4.0.tgz#6295768e98e14dc15ce8d362ef6340db82852031" -lodash.isequal@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - lodash.keys@^3.0.0, lodash.keys@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" @@ -5826,7 +5822,7 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8: +prop-types@^15.5.6, prop-types@^15.5.8: version "15.5.8" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394" dependencies: @@ -5948,16 +5944,13 @@ react-addons-update@^15.1.0: version "15.4.1" resolved "https://registry.yarnpkg.com/react-addons-update/-/react-addons-update-15.4.1.tgz#00c07f45243aa9715e1706bbfd1f23d3d8d80bd1" -react-codemirror@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/react-codemirror/-/react-codemirror-1.0.0.tgz#91467b53b1f5d80d916a2fd0b4c7adb85a9001ba" +react-codemirror@^0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/react-codemirror/-/react-codemirror-0.2.6.tgz#e71e35717ce6effae68df1dbf2b5a75b84a44f84" dependencies: - classnames "^2.2.5" - codemirror "^5.18.2" - create-react-class "^15.5.1" - lodash.debounce "^4.0.8" - lodash.isequal "^4.5.0" - prop-types "^15.5.4" + classnames "^2.2.3" + codemirror "^5.13.4" + lodash.debounce "^4.0.4" react-custom-scrollbars@^4.1.1: version "4.1.1" From cea9d077a429a2feaf4f347c5c018af01fde2d6c Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 14 Jul 2017 17:02:51 -0700 Subject: [PATCH 12/42] Introduce TICKscript editing --- ui/src/kapacitor/actions/view/index.js | 32 +++++++++++++++---- ui/src/kapacitor/apis/index.js | 23 ++++++++++++- ui/src/kapacitor/containers/TickscriptPage.js | 17 ++++++++-- 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/ui/src/kapacitor/actions/view/index.js b/ui/src/kapacitor/actions/view/index.js index b15deff19a..8bcd3539d0 100644 --- a/ui/src/kapacitor/actions/view/index.js +++ b/ui/src/kapacitor/actions/view/index.js @@ -7,6 +7,7 @@ import { deleteRule as deleteRuleAPI, updateRuleStatus as updateRuleStatusAPI, createTask as createTaskAJAX, + updateTask as updateTaskAJAX, } from 'src/kapacitor/apis' import {errorThrown} from 'shared/actions/errors' @@ -47,16 +48,18 @@ export const getRule = (kapacitor, ruleID) => async dispatch => { dispatch({ type: 'LOAD_RULE', payload: { - rule: {...rule, queryID: rule.query.id}, + rule: {...rule, queryID: rule.query && rule.query.id}, }, }) - dispatch({ - type: 'LOAD_KAPACITOR_QUERY', - payload: { - query: rule.query, - }, - }) + if (rule.query) { + dispatch({ + type: 'LOAD_KAPACITOR_QUERY', + payload: { + query: rule.query, + }, + }) + } } catch (error) { console.error(error) throw error @@ -247,3 +250,18 @@ export const createTask = (kapacitor, task) => async dispatch => { return error.data } } + +export const updateTask = (kapacitor, task, ruleID) => async dispatch => { + try { + const {data} = await updateTaskAJAX(kapacitor, task, ruleID) + dispatch(publishNotification('success', 'TICKscript updated successully')) + return data + } catch (error) { + if (!error) { + dispatch(errorThrown('Could not communicate with server')) + return + } + + return error.data + } +} diff --git a/ui/src/kapacitor/apis/index.js b/ui/src/kapacitor/apis/index.js index 0968ab3a69..b4a2a435bd 100644 --- a/ui/src/kapacitor/apis/index.js +++ b/ui/src/kapacitor/apis/index.js @@ -61,7 +61,6 @@ export const updateRuleStatus = (rule, status) => { }) } -// tickscript contains script, dbsrps, id, and type export const createTask = async (kapacitor, {id, dbrps, script, type}) => { try { return await AJAX({ @@ -79,3 +78,25 @@ export const createTask = async (kapacitor, {id, dbrps, script, type}) => { throw error } } + +export const updateTask = async ( + kapacitor, + {id, dbrps, script, type}, + ruleID +) => { + try { + return await AJAX({ + method: 'PATCH', + url: `${kapacitor.links.tasks}/${ruleID}`, + data: { + id, + type, + dbrps, + script, + }, + }) + } catch (error) { + console.error(error) + throw error + } +} diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 0b4a8c4fbe..1295b40cd3 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -7,6 +7,7 @@ import * as kapactiorActionCreators from 'src/kapacitor/actions/view' import * as errorActionCreators from 'shared/actions/errors' import {getActiveKapacitor} from 'src/shared/apis' +// TODO: collect dbsrps, stream, and name for tasks (needs design) class TickscriptPage extends Component { constructor(props) { super(props) @@ -54,9 +55,20 @@ class TickscriptPage extends Component { async handleSave() { const {kapacitor, task} = this.state - const {source, router, kapacitorActions: {createTask}} = this.props + const { + source, + router, + kapacitorActions: {createTask, updateTask}, + params: {ruleID}, + } = this.props + + let response + if (this.isEditing()) { + response = await updateTask(kapacitor, task, ruleID) + } else { + response = await createTask(kapacitor, task) + } - const response = await createTask(kapacitor, task) if (response && response.error) { return this.setState({validation: response.error}) } @@ -99,6 +111,7 @@ TickscriptPage.propTypes = { errorThrown: func.isRequired, }).isRequired, kapacitorActions: shape({ + updateTask: func.isRequired, createTask: func.isRequired, getRule: func.isRequired, }), From 9f371f17c2f22db6ba36151caaaff4d9297afe97 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 17 Jul 2017 09:54:30 -0700 Subject: [PATCH 13/42] Set default props for ruleID --- ui/src/kapacitor/containers/TickscriptPage.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 1295b40cd3..1cc8fd8b2a 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -14,7 +14,7 @@ class TickscriptPage extends Component { this.state = { kapacitor: {}, task: { - id: 'testing', + id: props.params.ruleID, status: 'enabled', script: '', dbrps: [ @@ -103,6 +103,12 @@ class TickscriptPage extends Component { const {arrayOf, func, shape, string} = PropTypes +TickscriptPage.defaultProps = { + params: { + ruleID: '', + }, +} + TickscriptPage.propTypes = { source: shape({ name: string, From 0e8fecff6ec64cb514624e9c34bbf7d7d4d90ce7 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 17 Jul 2017 10:17:20 -0700 Subject: [PATCH 14/42] Add lambda to syntax highlighting --- ui/src/external/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/external/codemirror.js b/ui/src/external/codemirror.js index 24f06cd817..0c961ee1d2 100644 --- a/ui/src/external/codemirror.js +++ b/ui/src/external/codemirror.js @@ -314,7 +314,7 @@ CodeMirror.defineSimpleMode('tickscript', { // Rules are matched in the order in which they appear, so there is // no ambiguity between this one and the one above { - regex: /(?:function|var|return|if|for|while|else|do|this|stream|batch|influxql)\b/, + regex: /(?:var|return|if|for|while|else|do|this|stream|batch|influxql|lambda)/, token: 'keyword', }, {regex: /true|false|null|undefined|TRUE|FALSE/, token: 'atom'}, From 44374752070f0e3cd040e99ddbcc592929b1849f Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 17 Jul 2017 13:58:03 -0700 Subject: [PATCH 15/42] Apply selected dbrps to state --- ui/src/kapacitor/components/Tickscript.js | 17 +++- .../kapacitor/components/TickscriptHeader.js | 20 ++++- ui/src/kapacitor/containers/TickscriptPage.js | 12 +-- .../components/MultiSelectDBDropdown.js | 77 +++++++++++++++++++ .../shared/components/MultiSelectDropdown.js | 24 ++++-- 5 files changed, 133 insertions(+), 17 deletions(-) create mode 100644 ui/src/shared/components/MultiSelectDBDropdown.js diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index cfb25899e8..6871c079b8 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -3,9 +3,21 @@ import TickscriptHeader from 'src/kapacitor/components/TickscriptHeader' import FancyScrollbar from 'shared/components/FancyScrollbar' import TickscriptEditor from 'src/kapacitor/components/TickscriptEditor' -const Tickscript = ({source, onSave, task, validation, onChangeScript}) => ( +const Tickscript = ({ + source, + onSave, + task, + validation, + onSelectDbrps, + onChangeScript, +}) => (
- +
@@ -37,6 +49,7 @@ Tickscript.propTypes = { dbsrps: arrayOf(shape()), }).isRequired, onChangeScript: func.isRequired, + onSelectDbrps: func.isRequired, validation: string, } diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js index 561e35f788..11d445b580 100644 --- a/ui/src/kapacitor/components/TickscriptHeader.js +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -1,7 +1,10 @@ import React, {PropTypes} from 'react' import SourceIndicator from 'shared/components/SourceIndicator' +import MultiSelectDBDropdown from 'shared/components/MultiSelectDBDropdown' -const TickscriptHeader = ({source, onSave}) => ( +const addName = list => list.map(l => ({...l, name: `${l.db}.${l.rp}`})) + +const TickscriptHeader = ({source, onSave, task, onSelectDbrps}) => (
@@ -11,6 +14,10 @@ const TickscriptHeader = ({source, onSave}) => (
+ @@ -19,11 +26,20 @@ const TickscriptHeader = ({source, onSave}) => (
) -const {func, shape} = PropTypes +const {arrayOf, func, shape, string} = PropTypes TickscriptHeader.propTypes = { onSave: func, source: shape(), + onSelectDbrps: func.isRequired, + task: shape({ + dbrps: arrayOf( + shape({ + db: string, + rp: string, + }) + ), + }), } export default TickscriptHeader diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 1cc8fd8b2a..952ed28c01 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -17,12 +17,7 @@ class TickscriptPage extends Component { id: props.params.ruleID, status: 'enabled', script: '', - dbrps: [ - { - db: '_internal', - rp: 'monitor', - }, - ], + dbrps: [], type: 'stream', }, validation: '', @@ -80,6 +75,10 @@ class TickscriptPage extends Component { this.setState({task: {...this.state.task, script}}) } + handleSelectDbrps(dbrps) { + this.setState({task: {...this.state.task, dbrps}}) + } + render() { const {source} = this.props const {task, validation} = this.state @@ -89,6 +88,7 @@ class TickscriptPage extends Component { task={task} source={source} onSave={::this.handleSave} + onSelectDbrps={::this.handleSelectDbrps} onChangeScript={::this.handleChangeScript} validation={validation} /> diff --git a/ui/src/shared/components/MultiSelectDBDropdown.js b/ui/src/shared/components/MultiSelectDBDropdown.js new file mode 100644 index 0000000000..278c22c268 --- /dev/null +++ b/ui/src/shared/components/MultiSelectDBDropdown.js @@ -0,0 +1,77 @@ +import React, {PropTypes, Component} from 'react' + +import {showDatabases, showRetentionPolicies} from 'shared/apis/metaQuery' +import showDatabasesParser from 'shared/parsing/showDatabases' +import showRetentionPoliciesParser from 'shared/parsing/showRetentionPolicies' +import MultiSelectDropdown from 'shared/components/MultiSelectDropdown' + +class MultiSelectDBDropdown extends Component { + constructor(props) { + super(props) + this.state = { + dbrps: [], + } + + this._getDbRps = ::this._getDbRps + } + + componentDidMount() { + this._getDbRps() + } + + render() { + const {dbrps} = this.state + const {onApply} = this.props + + return + } + + async _getDbRps() { + const {source: {links: {proxy}}} = this.context + const {onErrorThrown} = this.props + + try { + const {data} = await showDatabases(proxy) + const {databases, errors} = showDatabasesParser(data) + if (errors.length > 0) { + throw errors[0] // only one error can come back from this, but it's returned as an array + } + + const response = await showRetentionPolicies(proxy, databases) + const dbrps = response.data.results.reduce((acc, result, i) => { + const {retentionPolicies} = showRetentionPoliciesParser(result) + const db = databases[i] + + const rps = retentionPolicies.map(({name: rp}) => ({ + db, + rp, + name: `${db}.${rp}`, + })) + + return [...acc, ...rps] + }, []) + + this.setState({dbrps}) + } catch (error) { + console.error(error) + onErrorThrown(error) + } + } +} + +const {func, shape, string} = PropTypes + +MultiSelectDBDropdown.contextTypes = { + source: shape({ + links: shape({ + proxy: string.isRequired, + }).isRequired, + }).isRequired, +} + +MultiSelectDBDropdown.propTypes = { + onErrorThrown: func, + onApply: func.isRequired, +} + +export default MultiSelectDBDropdown diff --git a/ui/src/shared/components/MultiSelectDropdown.js b/ui/src/shared/components/MultiSelectDropdown.js index d279e6fc9d..7ded93e756 100644 --- a/ui/src/shared/components/MultiSelectDropdown.js +++ b/ui/src/shared/components/MultiSelectDropdown.js @@ -11,7 +11,7 @@ const labelText = ({localSelectedItems, isOpen, label}) => { if (label) { return label } else if (localSelectedItems.length) { - return localSelectedItems.map(s => s).join(', ') + return localSelectedItems.map(s => s.name).join(', ') } // TODO: be smarter about the text displayed here @@ -50,7 +50,7 @@ class MultiSelectDropdown extends Component { let nextItems if (this.isSelected(item)) { - nextItems = localSelectedItems.filter(i => i !== item) + nextItems = localSelectedItems.filter(i => i.name !== item.name) } else { nextItems = [...localSelectedItems, item] } @@ -59,7 +59,7 @@ class MultiSelectDropdown extends Component { } isSelected(item) { - return !!this.state.localSelectedItems.find(text => text === item) + return !!this.state.localSelectedItems.find(({name}) => name === item.name) } onApplyFunctions(e) { @@ -119,7 +119,7 @@ class MultiSelectDropdown extends Component { >
- {listItem} + {listItem.name} ) @@ -130,22 +130,32 @@ class MultiSelectDropdown extends Component { } } -const {arrayOf, func, string} = PropTypes +const {arrayOf, func, shape, string} = PropTypes MultiSelectDropdown.propTypes = { onApply: func.isRequired, - items: arrayOf(string.isRequired).isRequired, - selectedItems: arrayOf(string.isRequired).isRequired, + items: arrayOf( + shape({ + name: string.isRequired, + }) + ).isRequired, + selectedItems: arrayOf( + shape({ + name: string.isRequired, + }) + ), label: string, buttonSize: string, buttonColor: string, customClass: string, iconName: string, } + MultiSelectDropdown.defaultProps = { buttonSize: 'btn-sm', buttonColor: 'btn-default', customClass: 'dropdown-160', + selectedItems: [], } export default OnClickOutside(MultiSelectDropdown) From fa2192f3fbd9a291d78b23fdad12d700d239163e Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 18 Jul 2017 11:18:08 -0700 Subject: [PATCH 16/42] Prettier --- ui/src/external/codemirror.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/src/external/codemirror.js b/ui/src/external/codemirror.js index 0c961ee1d2..15e86c76ee 100644 --- a/ui/src/external/codemirror.js +++ b/ui/src/external/codemirror.js @@ -144,8 +144,7 @@ function tokenFunction(states, config) { tok = state.local.mode.token(stream, state.localState) if ( - state.local.endScan && - (m = state.local.endScan.exec(stream.current())) + state.local.endScan && (m = state.local.endScan.exec(stream.current())) ) { stream.pos = stream.start + m.index } From 026b1f10187b273aa97cdf864648db71c5040347 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 18 Jul 2017 11:18:22 -0700 Subject: [PATCH 17/42] Add comment --- ui/src/kapacitor/containers/TickscriptPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 952ed28c01..ef8be18de7 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -75,6 +75,7 @@ class TickscriptPage extends Component { this.setState({task: {...this.state.task, script}}) } + // TODO: make this fire on every click so user doesn't have to 'apply' handleSelectDbrps(dbrps) { this.setState({task: {...this.state.task, dbrps}}) } From 018bd1709620c920f01f2d4e8f4153325b59c220 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 18 Jul 2017 14:04:22 -0700 Subject: [PATCH 18/42] Add ability to apply changes per click --- .../components/MultiSelectDBDropdown.js | 8 ++++- .../shared/components/MultiSelectDropdown.js | 31 ++++++++++++------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/ui/src/shared/components/MultiSelectDBDropdown.js b/ui/src/shared/components/MultiSelectDBDropdown.js index 278c22c268..41d5d6a304 100644 --- a/ui/src/shared/components/MultiSelectDBDropdown.js +++ b/ui/src/shared/components/MultiSelectDBDropdown.js @@ -23,7 +23,13 @@ class MultiSelectDBDropdown extends Component { const {dbrps} = this.state const {onApply} = this.props - return + return ( + + ) } async _getDbRps() { diff --git a/ui/src/shared/components/MultiSelectDropdown.js b/ui/src/shared/components/MultiSelectDropdown.js index 7ded93e756..84fce64b81 100644 --- a/ui/src/shared/components/MultiSelectDropdown.js +++ b/ui/src/shared/components/MultiSelectDropdown.js @@ -31,7 +31,7 @@ class MultiSelectDropdown extends Component { } this.onSelect = ::this.onSelect - this.onApplyFunctions = ::this.onApplyFunctions + this.handleApply = ::this.handleApply } handleClickOutside() { @@ -46,6 +46,7 @@ class MultiSelectDropdown extends Component { onSelect(item, e) { e.stopPropagation() + const {onApply, isApplyShown} = this.props const {localSelectedItems} = this.state let nextItems @@ -55,6 +56,10 @@ class MultiSelectDropdown extends Component { nextItems = [...localSelectedItems, item] } + if (!isApplyShown) { + onApply(nextItems) + } + this.setState({localSelectedItems: nextItems}) } @@ -62,7 +67,7 @@ class MultiSelectDropdown extends Component { return !!this.state.localSelectedItems.find(({name}) => name === item.name) } - onApplyFunctions(e) { + handleApply(e) { e.stopPropagation() this.setState({isOpen: false}) @@ -91,18 +96,18 @@ class MultiSelectDropdown extends Component { } renderMenu() { - const {items} = this.props - - return ( -
    -
  • -
  • + : null + + return ( +
      + {applyButton} Date: Tue, 18 Jul 2017 14:39:05 -0700 Subject: [PATCH 19/42] Make UserRow work with new MultiSelectDropdown shape --- ui/src/admin/components/UserRow.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ui/src/admin/components/UserRow.js b/ui/src/admin/components/UserRow.js index 9d5589501c..1a3fa6c44a 100644 --- a/ui/src/admin/components/UserRow.js +++ b/ui/src/admin/components/UserRow.js @@ -27,7 +27,8 @@ const UserRow = ({ onUpdateRoles, onUpdatePassword, }) => { - const handleUpdatePermissions = allowed => { + const handleUpdatePermissions = perms => { + const allowed = perms.map(p => p.name) onUpdatePermissions(user, [{scope: 'all', allowed}]) } @@ -104,8 +105,12 @@ const UserRow = ({ {allPermissions && allPermissions.length ? ({name: p}))} + selectedItems={_.get( + permissions, + ['0', 'allowed'], + [] + ).map(p => ({name: p}))} label={ permissions && permissions.length ? '' : 'Select Permissions' } From 74145b0a496fcb15d124185a32c7240e1fa44b5c Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 18 Jul 2017 14:39:23 -0700 Subject: [PATCH 20/42] Cleanup checks with default props --- ui/src/admin/components/RoleRow.js | 8 ++++---- ui/src/admin/components/UserRow.js | 12 ++++-------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/ui/src/admin/components/RoleRow.js b/ui/src/admin/components/RoleRow.js index 2a86465582..e699e9c16a 100644 --- a/ui/src/admin/components/RoleRow.js +++ b/ui/src/admin/components/RoleRow.js @@ -10,7 +10,7 @@ import DeleteConfirmTableCell from 'shared/components/DeleteConfirmTableCell' import {ROLES_TABLE} from 'src/admin/constants/tableSizing' const RoleRow = ({ - role: {name, permissions, users}, + role: {name, permissions, users = []}, role, allUsers, allPermissions, @@ -85,9 +85,9 @@ const RoleRow = ({ {allUsers && allUsers.length ? u.name)} - selectedItems={users === undefined ? [] : users.map(u => u.name)} - label={users && users.length ? '' : 'Select Users'} + items={allUsers} + selectedItems={users} + label={users.length ? '' : 'Select Users'} onApply={handleUpdateUsers} buttonSize="btn-xs" buttonColor="btn-primary" diff --git a/ui/src/admin/components/UserRow.js b/ui/src/admin/components/UserRow.js index 1a3fa6c44a..0f900cac4c 100644 --- a/ui/src/admin/components/UserRow.js +++ b/ui/src/admin/components/UserRow.js @@ -12,7 +12,7 @@ import ChangePassRow from 'src/admin/components/ChangePassRow' import {USERS_TABLE} from 'src/admin/constants/tableSizing' const UserRow = ({ - user: {name, roles, permissions, password}, + user: {name, roles = [], permissions, password}, user, allRoles, allPermissions, @@ -86,13 +86,9 @@ const UserRow = ({ {hasRoles ? r.name)} - selectedItems={ - roles - ? roles.map(r => r.name) - : [] /* TODO remove check when server returns empty list */ - } - label={roles && roles.length ? '' : 'Select Roles'} + items={allRoles} + selectedItems={roles.map(r => r.name)} + label={roles.length ? '' : 'Select Roles'} onApply={handleUpdateRoles} buttonSize="btn-xs" buttonColor="btn-primary" From 03e40c8ce16ae59945811d91bcfd53041d446eb1 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 18 Jul 2017 14:54:59 -0700 Subject: [PATCH 21/42] Cleanup --- ui/src/admin/components/UserRow.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ui/src/admin/components/UserRow.js b/ui/src/admin/components/UserRow.js index 0f900cac4c..ca6941ec5b 100644 --- a/ui/src/admin/components/UserRow.js +++ b/ui/src/admin/components/UserRow.js @@ -43,6 +43,8 @@ const UserRow = ({ onUpdatePassword(user, password) } + const perms = _.get(permissions, ['0', 'allowed'], []) + if (isEditing) { return ( @@ -102,11 +104,7 @@ const UserRow = ({ {allPermissions && allPermissions.length ? ({name: p}))} - selectedItems={_.get( - permissions, - ['0', 'allowed'], - [] - ).map(p => ({name: p}))} + selectedItems={perms.map(p => ({name: p}))} label={ permissions && permissions.length ? '' : 'Select Permissions' } From f4aeefa95ed7dbf57f7a49c53cf2029b5b1d1ce9 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 18 Jul 2017 15:23:28 -0700 Subject: [PATCH 22/42] Add tickscript type selector --- ui/src/kapacitor/components/Tickscript.js | 5 +++- .../kapacitor/components/TickscriptHeader.js | 11 ++++++- ui/src/kapacitor/components/TickscriptType.js | 29 +++++++++++++++++++ ui/src/kapacitor/containers/TickscriptPage.js | 6 +++- 4 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 ui/src/kapacitor/components/TickscriptType.js diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index 6871c079b8..64604e74ed 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -10,13 +10,15 @@ const Tickscript = ({ validation, onSelectDbrps, onChangeScript, + onChangeType, }) => (
      @@ -51,6 +53,7 @@ Tickscript.propTypes = { onChangeScript: func.isRequired, onSelectDbrps: func.isRequired, validation: string, + onChangeType: func.isRequired, } export default Tickscript diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js index 11d445b580..b4bebe2005 100644 --- a/ui/src/kapacitor/components/TickscriptHeader.js +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -1,10 +1,17 @@ import React, {PropTypes} from 'react' import SourceIndicator from 'shared/components/SourceIndicator' +import TickscriptType from 'src/kapacitor/components/TickscriptType' import MultiSelectDBDropdown from 'shared/components/MultiSelectDBDropdown' const addName = list => list.map(l => ({...l, name: `${l.db}.${l.rp}`})) -const TickscriptHeader = ({source, onSave, task, onSelectDbrps}) => ( +const TickscriptHeader = ({ + source, + onChangeType, + onSave, + task, + onSelectDbrps, +}) => (
      @@ -14,6 +21,7 @@ const TickscriptHeader = ({source, onSave, task, onSelectDbrps}) => (
      + ( +
        +
      • + Stream +
      • +
      • + Batch +
      • +
      +) + +const {string, func} = PropTypes + +TickscriptType.propTypes = { + type: string, + onChangeType: func, +} + +export default TickscriptType diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index ef8be18de7..c02c200971 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -7,7 +7,6 @@ import * as kapactiorActionCreators from 'src/kapacitor/actions/view' import * as errorActionCreators from 'shared/actions/errors' import {getActiveKapacitor} from 'src/shared/apis' -// TODO: collect dbsrps, stream, and name for tasks (needs design) class TickscriptPage extends Component { constructor(props) { super(props) @@ -80,6 +79,10 @@ class TickscriptPage extends Component { this.setState({task: {...this.state.task, dbrps}}) } + handleChangeType(type) { + return () => this.setState({task: {...this.state.task, type}}) + } + render() { const {source} = this.props const {task, validation} = this.state @@ -91,6 +94,7 @@ class TickscriptPage extends Component { onSave={::this.handleSave} onSelectDbrps={::this.handleSelectDbrps} onChangeScript={::this.handleChangeScript} + onChangeType={::this.handleChangeType} validation={validation} /> ) From a1ecdb6e5d1d047aeee7adb7d64bcea71d9636a8 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 18 Jul 2017 16:06:37 -0700 Subject: [PATCH 23/42] WIP Introduce tickscript id editing --- ui/src/kapacitor/components/Tickscript.js | 11 +++- .../kapacitor/components/TickscriptHeader.js | 31 ++++++---- ui/src/kapacitor/components/TickscriptID.js | 61 +++++++++++++++++++ ui/src/kapacitor/containers/TickscriptPage.js | 14 ++++- 4 files changed, 104 insertions(+), 13 deletions(-) create mode 100644 ui/src/kapacitor/components/TickscriptID.js diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index 64604e74ed..5534b9ecb8 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -11,12 +11,18 @@ const Tickscript = ({ onSelectDbrps, onChangeScript, onChangeType, + isEditingID, + onStartEditID, + onStopEditID, }) => (
      @@ -40,7 +46,7 @@ const Tickscript = ({
      ) -const {arrayOf, func, shape, string} = PropTypes +const {arrayOf, bool, func, shape, string} = PropTypes Tickscript.propTypes = { onSave: func.isRequired, @@ -54,6 +60,9 @@ Tickscript.propTypes = { onSelectDbrps: func.isRequired, validation: string, onChangeType: func.isRequired, + isEditingID: bool.isRequired, + onStartEditID: func.isRequired, + onStopEditID: func.isRequired, } export default Tickscript diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js index b4bebe2005..0f699c20cb 100644 --- a/ui/src/kapacitor/components/TickscriptHeader.js +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -2,28 +2,34 @@ import React, {PropTypes} from 'react' import SourceIndicator from 'shared/components/SourceIndicator' import TickscriptType from 'src/kapacitor/components/TickscriptType' import MultiSelectDBDropdown from 'shared/components/MultiSelectDBDropdown' - +import TickscrtiptID from 'src/kapacitor/components/TickscriptID' const addName = list => list.map(l => ({...l, name: `${l.db}.${l.rp}`})) const TickscriptHeader = ({ - source, - onChangeType, + task: {id, type, dbrps}, + source: {name}, onSave, - task, + isEditing, + onStopEdit, + onStartEdit, + onChangeType, onSelectDbrps, }) => (
      -

      - TICKscript Editor -

      +
      - - + +
      ) -const {arrayOf, func, shape, string} = PropTypes +const {arrayOf, bool, func, shape, string} = PropTypes TickscriptHeader.propTypes = { onSave: func, @@ -49,6 +55,9 @@ TickscriptHeader.propTypes = { ), }), onChangeType: func.isRequired, + isEditing: bool.isRequired, + onStartEdit: func.isRequired, + onStopEdit: func.isRequired, } export default TickscriptHeader diff --git a/ui/src/kapacitor/components/TickscriptID.js b/ui/src/kapacitor/components/TickscriptID.js new file mode 100644 index 0000000000..8974d6eed6 --- /dev/null +++ b/ui/src/kapacitor/components/TickscriptID.js @@ -0,0 +1,61 @@ +import React, {PropTypes, Component} from 'react' +import ReactTooltip from 'react-tooltip' + +class TickscriptID extends Component { + constructor(props) { + super(props) + this.state = { + tickscriptID: props.id, + } + } + + handleChangeID(e) { + const tickscriptID = e.target.value + this.setState({tickscriptID}) + } + + render() { + const {isEditing, onStartEdit, id} = this.props + const {tickscriptID} = this.state + + return isEditing + ? + :

      + {id} + + +

      + } +} + +const {bool, func, string} = PropTypes + +TickscriptID.propTypes = { + isEditing: bool.isRequired, + onStartEdit: func.isRequired, + onStopEdit: func.isRequired, + id: string.isRequired, +} + +export default TickscriptID diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index c02c200971..ab6221da86 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -20,6 +20,7 @@ class TickscriptPage extends Component { type: 'stream', }, validation: '', + isEditingID: false, } } @@ -83,9 +84,17 @@ class TickscriptPage extends Component { return () => this.setState({task: {...this.state.task, type}}) } + handleStartEditID() { + this.setState({isEditingID: true}) + } + + handleStopEditID() { + this.setState({isEditingID: false}) + } + render() { const {source} = this.props - const {task, validation} = this.state + const {task, validation, isEditingID} = this.state return ( ) } From 879685d11f368747573f80933151425bc583f2ec Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 19 Jul 2017 13:06:29 -0700 Subject: [PATCH 24/42] Remove view only Tickscript --- ui/src/kapacitor/components/KapacitorRules.js | 55 ++++++------------- .../components/KapacitorRulesTable.js | 25 +-------- .../containers/KapacitorRulesPage.js | 15 +++-- 3 files changed, 31 insertions(+), 64 deletions(-) diff --git a/ui/src/kapacitor/components/KapacitorRules.js b/ui/src/kapacitor/components/KapacitorRules.js index 2697b1dbe0..913c1c45d0 100644 --- a/ui/src/kapacitor/components/KapacitorRules.js +++ b/ui/src/kapacitor/components/KapacitorRules.js @@ -5,7 +5,6 @@ import NoKapacitorError from 'shared/components/NoKapacitorError' import SourceIndicator from 'shared/components/SourceIndicator' import KapacitorRulesTable from 'src/kapacitor/components/KapacitorRulesTable' import FancyScrollbar from 'shared/components/FancyScrollbar' -import TICKscriptOverlay from 'src/kapacitor/components/TICKscriptOverlay' const KapacitorRules = ({ source, @@ -13,10 +12,7 @@ const KapacitorRules = ({ hasKapacitor, loading, onDelete, - tickscript, onChangeRuleStatus, - onReadTickscript, - onCloseTickscript, }) => { if (loading) { return ( @@ -47,45 +43,38 @@ const KapacitorRules = ({ const tableHeader = rules.length === 1 ? '1 Alert Rule' : `${rules.length} Alert Rules` return ( - +

      {tableHeader}

      - - Build Rule - - - Write Rule - +
      + + Build Rule + + + Write TICKscript + +
      ) } -<<<<<<< HEAD const PageContents = ({children, source}) => -======= -const PageContents = ({children, source, tickscript, onCloseTickscript}) => ( ->>>>>>> Display TICKscript on rules index
      @@ -108,15 +97,9 @@ const PageContents = ({children, source, tickscript, onCloseTickscript}) => (
      - {tickscript - ? - : null}
      -const {arrayOf, bool, func, node, shape, string} = PropTypes +const {arrayOf, bool, func, node, shape} = PropTypes KapacitorRules.propTypes = { source: shape(), @@ -125,15 +108,11 @@ KapacitorRules.propTypes = { loading: bool, onChangeRuleStatus: func, onDelete: func, - tickscript: string, - onReadTickscript: func, - onCloseTickscript: func, } PageContents.propTypes = { children: node, source: shape(), - tickscript: string, onCloseTickscript: func, } diff --git a/ui/src/kapacitor/components/KapacitorRulesTable.js b/ui/src/kapacitor/components/KapacitorRulesTable.js index bc91aa8e8f..bd40ece258 100644 --- a/ui/src/kapacitor/components/KapacitorRulesTable.js +++ b/ui/src/kapacitor/components/KapacitorRulesTable.js @@ -12,23 +12,7 @@ const { colActions, } = KAPACITOR_RULES_TABLE -import {KAPACITOR_RULES_TABLE} from 'src/kapacitor/constants/tableSizing' -const { - colName, - colType, - colMessage, - colAlerts, - colEnabled, - colActions, -} = KAPACITOR_RULES_TABLE - -const KapacitorRulesTable = ({ - rules, - source, - onDelete, - onReadTickscript, - onChangeRuleStatus, -}) => +const KapacitorRulesTable = ({rules, source, onDelete, onChangeRuleStatus}) =>
      @@ -52,7 +36,6 @@ const KapacitorRulesTable = ({ source={source} onDelete={onDelete} onChangeRuleStatus={onChangeRuleStatus} - onRead={onReadTickscript} /> ) })} @@ -60,7 +43,7 @@ const KapacitorRulesTable = ({
      -const RuleRow = ({rule, source, onRead, onDelete, onChangeRuleStatus}) => +const RuleRow = ({rule, source, onDelete, onChangeRuleStatus}) => @@ -98,7 +81,7 @@ const RuleRow = ({rule, source, onRead, onDelete, onChangeRuleStatus}) => > Edit TICKscript - @@ -130,7 +113,6 @@ KapacitorRulesTable.propTypes = { source: shape({ id: string.isRequired, }).isRequired, - onReadTickscript: func, } RuleRow.propTypes = { @@ -138,7 +120,6 @@ RuleRow.propTypes = { source: shape(), onChangeRuleStatus: func, onDelete: func, - onRead: func, } RuleTitle.propTypes = { diff --git a/ui/src/kapacitor/containers/KapacitorRulesPage.js b/ui/src/kapacitor/containers/KapacitorRulesPage.js index 993e9787b6..bb44017ea5 100644 --- a/ui/src/kapacitor/containers/KapacitorRulesPage.js +++ b/ui/src/kapacitor/containers/KapacitorRulesPage.js @@ -11,8 +11,14 @@ class KapacitorRulesPage extends Component { this.state = { hasKapacitor: false, loading: true, - tickscript: null, } +<<<<<<< HEAD +======= + + this.handleDeleteRule = ::this.handleDeleteRule + this.handleRuleStatus = ::this.handleRuleStatus + this.handleCloseTickscript = ::this.handleCloseTickscript +>>>>>>> Remove view only Tickscript } async componentDidMount() { @@ -38,6 +44,7 @@ class KapacitorRulesPage extends Component { actions.updateRuleStatusSuccess(rule.id, status) } +<<<<<<< HEAD handleReadTickscript = ({tickscript}) => () => { this.setState({tickscript}) } @@ -46,9 +53,11 @@ class KapacitorRulesPage extends Component { this.setState({tickscript: null}) } +======= +>>>>>>> Remove view only Tickscript render() { const {source, rules} = this.props - const {hasKapacitor, loading, tickscript} = this.state + const {hasKapacitor, loading} = this.state return ( ) From fe6c5ac29d676036e8919b0ae9a00ce074a9122c Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 19 Jul 2017 13:09:23 -0700 Subject: [PATCH 25/42] Cleanup --- .../components/KapacitorRulesTable.js | 2 +- .../containers/KapacitorRulesPage.js | 23 ++----------------- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/ui/src/kapacitor/components/KapacitorRulesTable.js b/ui/src/kapacitor/components/KapacitorRulesTable.js index bd40ece258..8d6654648c 100644 --- a/ui/src/kapacitor/components/KapacitorRulesTable.js +++ b/ui/src/kapacitor/components/KapacitorRulesTable.js @@ -76,7 +76,7 @@ const RuleRow = ({rule, source, onDelete, onChangeRuleStatus}) => Edit TICKscript diff --git a/ui/src/kapacitor/containers/KapacitorRulesPage.js b/ui/src/kapacitor/containers/KapacitorRulesPage.js index bb44017ea5..8c22600296 100644 --- a/ui/src/kapacitor/containers/KapacitorRulesPage.js +++ b/ui/src/kapacitor/containers/KapacitorRulesPage.js @@ -12,13 +12,6 @@ class KapacitorRulesPage extends Component { hasKapacitor: false, loading: true, } -<<<<<<< HEAD -======= - - this.handleDeleteRule = ::this.handleDeleteRule - this.handleRuleStatus = ::this.handleRuleStatus - this.handleCloseTickscript = ::this.handleCloseTickscript ->>>>>>> Remove view only Tickscript } async componentDidMount() { @@ -44,17 +37,6 @@ class KapacitorRulesPage extends Component { actions.updateRuleStatusSuccess(rule.id, status) } -<<<<<<< HEAD - handleReadTickscript = ({tickscript}) => () => { - this.setState({tickscript}) - } - - handleCloseTickscript = () => { - this.setState({tickscript: null}) - } - -======= ->>>>>>> Remove view only Tickscript render() { const {source, rules} = this.props const {hasKapacitor, loading} = this.state @@ -65,9 +47,8 @@ class KapacitorRulesPage extends Component { rules={rules} hasKapacitor={hasKapacitor} loading={loading} - onDelete={this.handleDeleteRule} - onChangeRuleStatus={this.handleRuleStatus} - onCloseTickscript={this.handleCloseTickscript} + onDelete={::this.handleDeleteRule} + onChangeRuleStatus={::this.handleRuleStatus} /> ) } From c653982216e9f2f5a7e27469c7f9237984bf36e4 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 19 Jul 2017 13:51:43 -0700 Subject: [PATCH 26/42] Make task ids only edit when making new task --- ui/src/kapacitor/components/Tickscript.js | 3 ++ .../kapacitor/components/TickscriptHeader.js | 20 ++++++++----- ui/src/kapacitor/components/TickscriptID.js | 28 +++++++++++++----- ui/src/kapacitor/containers/TickscriptPage.js | 29 ++++++++----------- 4 files changed, 49 insertions(+), 31 deletions(-) diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index 5534b9ecb8..b8f59bbda7 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -14,6 +14,7 @@ const Tickscript = ({ isEditingID, onStartEditID, onStopEditID, + isNewTickscript, }) => (
      @@ -63,6 +65,7 @@ Tickscript.propTypes = { isEditingID: bool.isRequired, onStartEditID: func.isRequired, onStopEditID: func.isRequired, + isNewTickscript: bool.isRequired, } export default Tickscript diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js index 0f699c20cb..ff22c53ce4 100644 --- a/ui/src/kapacitor/components/TickscriptHeader.js +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -2,7 +2,9 @@ import React, {PropTypes} from 'react' import SourceIndicator from 'shared/components/SourceIndicator' import TickscriptType from 'src/kapacitor/components/TickscriptType' import MultiSelectDBDropdown from 'shared/components/MultiSelectDBDropdown' -import TickscrtiptID from 'src/kapacitor/components/TickscriptID' +import TickscrtiptNewID, { + TickscriptEditID, +} from 'src/kapacitor/components/TickscriptID' const addName = list => list.map(l => ({...l, name: `${l.db}.${l.rp}`})) const TickscriptHeader = ({ @@ -14,16 +16,19 @@ const TickscriptHeader = ({ onStartEdit, onChangeType, onSelectDbrps, + isNewTickscript, }) => (
      - + {isNewTickscript + ? + : }
      @@ -58,6 +63,7 @@ TickscriptHeader.propTypes = { isEditing: bool.isRequired, onStartEdit: func.isRequired, onStopEdit: func.isRequired, + isNewTickscript: bool.isRequired, } export default TickscriptHeader diff --git a/ui/src/kapacitor/components/TickscriptID.js b/ui/src/kapacitor/components/TickscriptID.js index 8974d6eed6..1e21dc6f7a 100644 --- a/ui/src/kapacitor/components/TickscriptID.js +++ b/ui/src/kapacitor/components/TickscriptID.js @@ -1,11 +1,12 @@ import React, {PropTypes, Component} from 'react' import ReactTooltip from 'react-tooltip' +import onClickOutside from 'react-onclickoutside' -class TickscriptID extends Component { +class TickscriptNewID extends Component { constructor(props) { super(props) this.state = { - tickscriptID: props.id, + tickscriptID: '', } } @@ -14,8 +15,12 @@ class TickscriptID extends Component { this.setState({tickscriptID}) } + handleClickOutside() { + this.props.onStopEdit() + } + render() { - const {isEditing, onStartEdit, id} = this.props + const {isEditing, onStartEdit} = this.props const {tickscriptID} = this.state return isEditing @@ -34,8 +39,7 @@ class TickscriptID extends Component { data-for="rename-kapacitor-tooltip" data-tip="Click to Rename" > - {id} - + {tickscriptID} ( +

      + {id} +

      +) + const {bool, func, string} = PropTypes -TickscriptID.propTypes = { +TickscriptNewID.propTypes = { isEditing: bool.isRequired, onStartEdit: func.isRequired, onStopEdit: func.isRequired, + isNewTickscript: bool.isRequired, +} + +TickscriptEditID.propTypes = { id: string.isRequired, } -export default TickscriptID +export default onClickOutside(TickscriptNewID) diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index ab6221da86..5843e3c89e 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -10,17 +10,18 @@ import {getActiveKapacitor} from 'src/shared/apis' class TickscriptPage extends Component { constructor(props) { super(props) + this.state = { kapacitor: {}, task: { - id: props.params.ruleID, + id: '', status: 'enabled', script: '', dbrps: [], type: 'stream', }, validation: '', - isEditingID: false, + isEditingID: true, } } @@ -39,7 +40,7 @@ class TickscriptPage extends Component { ) } - if (this.isEditing()) { + if (this._isEditing()) { await kapacitorActions.getRule(kapacitor, ruleID) const activeRule = this.props.rules.find(r => r.id === ruleID) this.setState({task: {...this.state.task, script: activeRule.tickscript}}) @@ -58,7 +59,7 @@ class TickscriptPage extends Component { } = this.props let response - if (this.isEditing()) { + if (this._isEditing()) { response = await updateTask(kapacitor, task, ruleID) } else { response = await createTask(kapacitor, task) @@ -75,7 +76,6 @@ class TickscriptPage extends Component { this.setState({task: {...this.state.task, script}}) } - // TODO: make this fire on every click so user doesn't have to 'apply' handleSelectDbrps(dbrps) { this.setState({task: {...this.state.task, dbrps}}) } @@ -100,19 +100,20 @@ class TickscriptPage extends Component { ) } - isEditing() { + _isEditing() { const {params} = this.props return params.ruleID && params.ruleID !== 'new' } @@ -120,12 +121,6 @@ class TickscriptPage extends Component { const {arrayOf, func, shape, string} = PropTypes -TickscriptPage.defaultProps = { - params: { - ruleID: '', - }, -} - TickscriptPage.propTypes = { source: shape({ name: string, @@ -142,7 +137,7 @@ TickscriptPage.propTypes = { push: func.isRequired, }).isRequired, params: shape({ - ruleID: string.isRequired, + ruleID: string, }).isRequired, rules: arrayOf(shape()), } From 25d5310cf579d3e365fdec61e1a340304329c638 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 25 Jul 2017 13:38:12 -0700 Subject: [PATCH 27/42] Prettier --- ui/src/external/codemirror.js | 19 ++++++++++++------- ui/src/kapacitor/components/Tickscript.js | 3 +-- .../kapacitor/components/TickscriptHeader.js | 3 +-- ui/src/kapacitor/components/TickscriptID.js | 3 +-- ui/src/kapacitor/components/TickscriptType.js | 3 +-- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/ui/src/external/codemirror.js b/ui/src/external/codemirror.js index 15e86c76ee..b10a9ddbca 100644 --- a/ui/src/external/codemirror.js +++ b/ui/src/external/codemirror.js @@ -9,11 +9,13 @@ CodeMirror.defineSimpleMode = function(name, states) { CodeMirror.simpleMode = function(config, states) { ensureState(states, 'start') - const states_ = {}, meta = states.meta || {} + const states_ = {}, + meta = states.meta || {} let hasIndentation = false for (const state in states) { if (state !== meta && states.hasOwnProperty(state)) { - const list = (states_[state] = []), orig = states[state] + const list = (states_[state] = []), + orig = states[state] for (let i = 0; i < orig.length; i++) { const data = orig[i] list.push(new Rule(data, states)) @@ -51,9 +53,10 @@ CodeMirror.simpleMode = function(config, states) { s.persistentStates = { mode: pers.mode, spec: pers.spec, - state: pers.state === state.localState - ? s.localState - : CodeMirror.copyState(pers.mode, pers.state), + state: + pers.state === state.localState + ? s.localState + : CodeMirror.copyState(pers.mode, pers.state), next: s.persistentStates, } } @@ -144,7 +147,8 @@ function tokenFunction(states, config) { tok = state.local.mode.token(stream, state.localState) if ( - state.local.endScan && (m = state.local.endScan.exec(stream.current())) + state.local.endScan && + (m = state.local.endScan.exec(stream.current())) ) { stream.pos = stream.start + m.index } @@ -278,7 +282,8 @@ function indentFunction(states, meta) { return CodeMirror.Pass } - let pos = state.indent.length - 1, rules = states[state.state] + let pos = state.indent.length - 1, + rules = states[state.state] scan: for (;;) { for (let i = 0; i < rules.length; i++) { const rule = rules[i] diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index b8f59bbda7..0ffb082828 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -15,7 +15,7 @@ const Tickscript = ({ onStartEditID, onStopEditID, isNewTickscript, -}) => ( +}) =>
      -) const {arrayOf, bool, func, shape, string} = PropTypes diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js index ff22c53ce4..6b19f9e0dc 100644 --- a/ui/src/kapacitor/components/TickscriptHeader.js +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -17,7 +17,7 @@ const TickscriptHeader = ({ onChangeType, onSelectDbrps, isNewTickscript, -}) => ( +}) =>
      @@ -43,7 +43,6 @@ const TickscriptHeader = ({
      -) const {arrayOf, bool, func, shape, string} = PropTypes diff --git a/ui/src/kapacitor/components/TickscriptID.js b/ui/src/kapacitor/components/TickscriptID.js index 1e21dc6f7a..a7c8beb861 100644 --- a/ui/src/kapacitor/components/TickscriptID.js +++ b/ui/src/kapacitor/components/TickscriptID.js @@ -53,11 +53,10 @@ class TickscriptNewID extends Component { } } -export const TickscriptEditID = ({id}) => ( +export const TickscriptEditID = ({id}) =>

      {id}

      -) const {bool, func, string} = PropTypes diff --git a/ui/src/kapacitor/components/TickscriptType.js b/ui/src/kapacitor/components/TickscriptType.js index e1365fcf7c..e28a454bc2 100644 --- a/ui/src/kapacitor/components/TickscriptType.js +++ b/ui/src/kapacitor/components/TickscriptType.js @@ -2,7 +2,7 @@ import React, {PropTypes} from 'react' const STREAM = 'stream' const BATCH = 'batch' -const TickscriptType = ({type, onChangeType}) => ( +const TickscriptType = ({type, onChangeType}) =>
      • ( Batch
      -) const {string, func} = PropTypes From 09d554cd4df1b04adee7b588329e38719331e32a Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 25 Jul 2017 17:30:29 -0700 Subject: [PATCH 28/42] Organize styles and use custom theme for code mirror --- ui/src/style/chronograf.scss | 9 +- .../style/components/code-mirror-theme.scss | 124 ++++++++++++++++++ 2 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 ui/src/style/components/code-mirror-theme.scss diff --git a/ui/src/style/chronograf.scss b/ui/src/style/chronograf.scss index dfb12062d9..d7ec2232fc 100644 --- a/ui/src/style/chronograf.scss +++ b/ui/src/style/chronograf.scss @@ -11,11 +11,13 @@ @import 'theme/bootstrap-theme'; @import 'theme/reset'; -// External +// Vendor @import 'external/react-grid-layout'; @import 'external/fixed-data-table-base'; @import 'external/fixed-data-table-style'; @import 'external/fixed-data-table'; +@import "external/codemirror"; +@import "../../node_modules/codemirror/theme/material.css"; // Layout @import 'layout/page'; @@ -27,6 +29,7 @@ // Components @import 'components/ceo-display-options'; @import 'components/confirm-buttons'; +@import 'components/code-mirror-theme'; @import 'components/custom-time-range'; @import 'components/dygraphs'; @import 'components/fancy-scrollbars'; @@ -59,7 +62,3 @@ // TODO @import 'unsorted'; - -// Vendor -@import "external/codemirror"; -@import "../../node_modules/codemirror/theme/material.css"; diff --git a/ui/src/style/components/code-mirror-theme.scss b/ui/src/style/components/code-mirror-theme.scss new file mode 100644 index 0000000000..a067d11453 --- /dev/null +++ b/ui/src/style/components/code-mirror-theme.scss @@ -0,0 +1,124 @@ +/* + + Name: CHRONOGRAF YO + Author: Michael Kaminsky (http://github.com/mkaminsky11) + + Original material color scheme by Mattia Astorino (https://github.com/equinusocio/material-theme) + +*/ + +$tickscript-console-height: 120px; + +.tickscript-console, +.tickscript-editor { + padding-left: $page-wrapper-padding; + padding-right: $page-wrapper-padding; + margin: 0 auto; + max-width: $page-wrapper-max-width; + position: relative; +} +.tickscript-console { + height: $tickscript-console-height; + padding-top: 30px; +} +.tickscript-console--output { + padding: 0 60px; + font-family: $code-font; + font-weight: 600; + display: flex; + align-items: center; + background-color: $g3-castle; + position: relative; + height: 100%; + width: 100%; + border-radius: $radius $radius 0 0; + + > p { + margin: 0; + } +} +.tickscript-console--default { + color: $g10-wolf; + font-style: italic; +} +.tickscript-editor { + margin: 0 auto; + padding-bottom: 30px; + height: calc(100% - #{$tickscript-console-height}); +} +.ReactCodeMirror { + position: relative; + width: 100%; + height: 100%; +} +.cm-s-material.CodeMirror { + border-radius: 0 0 $radius $radius; + font-family: $code-font; + background-color: $g2-kevlar; + color: $c-neutrino; + font-weight: 600; + height: 100%; +} +.CodeMirror-vscrollbar { + @include custom-scrollbar-round($g2-kevlar,$g6-smoke); +} +.cm-s-material .CodeMirror-gutters { + background-color: fade-out($g4-onyx, 0.5); + border: none; +} +.CodeMirror-gutter.CodeMirror-linenumbers { + width: 60px; +} +.cm-s-material.CodeMirror .CodeMirror-sizer { + margin-left: 60px; +} +.cm-s-material.CodeMirror .CodeMirror-linenumber.CodeMirror-gutter-elt { + padding-right: 9px; + width: 46px; + color: $g8-storm; +} +.cm-s-material .CodeMirror-guttermarker, .cm-s-material .CodeMirror-guttermarker-subtle, .cm-s-material .CodeMirror-linenumber { color: rgb(83,127,126); } +.cm-s-material .CodeMirror-cursor { + width: 2px; + border: 0; + background-color: $g20-white; + box-shadow: + 0 0 3px $c-laser, + 0 0 6px $c-ocean, + 0 0 11px $c-amethyst; +} +.cm-s-material div.CodeMirror-selected, +.cm-s-material.CodeMirror-focused div.CodeMirror-selected { + background-color: fade-out($g8-storm,0.7); +} +.cm-s-material .CodeMirror-line::selection, .cm-s-material .CodeMirror-line > span::selection, .cm-s-material .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); } +.cm-s-material .CodeMirror-line::-moz-selection, .cm-s-material .CodeMirror-line > span::-moz-selection, .cm-s-material .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); } + +.cm-s-material .CodeMirror-activeline-background { background: rgba(0, 0, 0, 0); } +.cm-s-material .cm-keyword { color: $c-comet; } +.cm-s-material .cm-operator { color: $c-dreamsicle; } +.cm-s-material .cm-variable-2 { color: #80CBC4; } +.cm-s-material .cm-variable-3, .cm-s-material .cm-type { color: $c-laser; } +.cm-s-material .cm-builtin { color: #DECB6B; } +.cm-s-material .cm-atom { color: $c-viridian; } +.cm-s-material .cm-number { color: $c-daisy; } +.cm-s-material .cm-def { color: rgba(233, 237, 237, 1); } +.cm-s-material .cm-string { color: $c-krypton; } +.cm-s-material .cm-string-2 { color: #80CBC4; } +.cm-s-material .cm-comment { color: $g10-wolf; } +.cm-s-material .cm-variable { color: $c-laser; } +.cm-s-material .cm-tag { color: #80CBC4; } +.cm-s-material .cm-meta { color: #80CBC4; } +.cm-s-material .cm-attribute { color: #FFCB6B; } +.cm-s-material .cm-property { color: #80CBAE; } +.cm-s-material .cm-qualifier { color: #DECB6B; } +.cm-s-material .cm-variable-3, .cm-s-material .cm-type { color: #DECB6B; } +.cm-s-material .cm-tag { color: rgba(255, 83, 112, 1); } +.cm-s-material .cm-error { + color: rgba(255, 255, 255, 1.0); + background-color: #EC5F67; +} +.cm-s-material .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} From 077bdf20fb67669d799e0de2bacc3e5b1eee7dff Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 25 Jul 2017 17:30:45 -0700 Subject: [PATCH 29/42] Change layout of tickscript editor --- ui/src/kapacitor/components/Tickscript.js | 33 ++++++++++++----------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index 0ffb082828..64ca8058c6 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -1,6 +1,5 @@ import React, {PropTypes} from 'react' import TickscriptHeader from 'src/kapacitor/components/TickscriptHeader' -import FancyScrollbar from 'shared/components/FancyScrollbar' import TickscriptEditor from 'src/kapacitor/components/TickscriptEditor' const Tickscript = ({ @@ -28,23 +27,25 @@ const Tickscript = ({ onSelectDbrps={onSelectDbrps} isNewTickscript={isNewTickscript} /> - -
      -
      -
      - {validation} -
      -
      -
      -
      - -
      +
      +
      +
      + {validation + ?

      + {validation} +

      + :

      + Save your TICKscript to validate it +

      }
      - +
      + +
      +
      const {arrayOf, bool, func, shape, string} = PropTypes From cce44b168124d4f7d992ef3568cf83d1ec0d2b2f Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 7 Aug 2017 16:42:45 -0700 Subject: [PATCH 30/42] Update to react-codemirror 1.0 --- ui/package.json | 3 +- .../kapacitor/components/TickscriptEditor.js | 2 +- ui/yarn.lock | 50 +++++++++++++++---- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/ui/package.json b/ui/package.json index 9e5dae1faf..1de98d7fe5 100644 --- a/ui/package.json +++ b/ui/package.json @@ -95,6 +95,7 @@ "webpack-dev-server": "^1.14.1" }, "dependencies": { + "@skidding/react-codemirror": "^1.0.1", "axios": "^0.13.1", "bignumber.js": "^4.0.2", "bootstrap": "^3.3.7", @@ -113,7 +114,7 @@ "react": "^15.0.2", "react-addons-shallow-compare": "^15.0.2", "react-addons-update": "^15.1.0", - "react-codemirror": "^0.2.6", + "react-codemirror": "^1.0.0", "react-custom-scrollbars": "^4.1.1", "react-dimensions": "^1.2.0", "react-dom": "^15.0.2", diff --git a/ui/src/kapacitor/components/TickscriptEditor.js b/ui/src/kapacitor/components/TickscriptEditor.js index b30de74b07..90803725a2 100644 --- a/ui/src/kapacitor/components/TickscriptEditor.js +++ b/ui/src/kapacitor/components/TickscriptEditor.js @@ -1,5 +1,5 @@ import React, {PropTypes, Component} from 'react' -import CodeMirror from 'react-codemirror' +import CodeMirror from '@skidding/react-codemirror' import 'src/external/codemirror' class TickscriptEditor extends Component { diff --git a/ui/yarn.lock b/ui/yarn.lock index 086a85b2e1..fc92c6a639 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -98,6 +98,16 @@ webpack-dev-middleware "^1.6.0" webpack-hot-middleware "^2.13.2" +"@skidding/react-codemirror@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@skidding/react-codemirror/-/react-codemirror-1.0.1.tgz#ce7927a10248e2369f8bce03669b92d88fead797" + dependencies: + classnames "^2.2.5" + codemirror "^5.18.2" + create-react-class "^15.5.1" + lodash.isequal "^4.5.0" + prop-types "^15.5.4" + abab@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d" @@ -1794,9 +1804,9 @@ code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" -codemirror@^5.13.4: - version "5.27.4" - resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.27.4.tgz#0e817c839bfea9959dd16cd48ae14acc0e43c3b6" +codemirror@^5.18.2: + version "5.28.0" + resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.28.0.tgz#2978d9280d671351a4f5737d06bbd681a0fd6f83" color-convert@^1.3.0: version "1.8.2" @@ -4406,7 +4416,7 @@ lodash.debounce@^3.1.1: dependencies: lodash._getnative "^3.0.0" -lodash.debounce@^4.0.4: +lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -4470,6 +4480,10 @@ lodash.isequal@^4.0.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.4.0.tgz#6295768e98e14dc15ce8d362ef6340db82852031" +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + lodash.keys@^3.0.0, lodash.keys@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" @@ -4567,6 +4581,12 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0: dependencies: js-tokens "^2.0.0" +loose-envify@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + dependencies: + js-tokens "^3.0.0" + loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -5822,6 +5842,13 @@ promise@^7.1.1: dependencies: asap "~2.0.3" +prop-types@^15.5.4: + version "15.5.10" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" + dependencies: + fbjs "^0.8.9" + loose-envify "^1.3.1" + prop-types@^15.5.6, prop-types@^15.5.8: version "15.5.8" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394" @@ -5944,13 +5971,16 @@ react-addons-update@^15.1.0: version "15.4.1" resolved "https://registry.yarnpkg.com/react-addons-update/-/react-addons-update-15.4.1.tgz#00c07f45243aa9715e1706bbfd1f23d3d8d80bd1" -react-codemirror@^0.2.6: - version "0.2.6" - resolved "https://registry.yarnpkg.com/react-codemirror/-/react-codemirror-0.2.6.tgz#e71e35717ce6effae68df1dbf2b5a75b84a44f84" +react-codemirror@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/react-codemirror/-/react-codemirror-1.0.0.tgz#91467b53b1f5d80d916a2fd0b4c7adb85a9001ba" dependencies: - classnames "^2.2.3" - codemirror "^5.13.4" - lodash.debounce "^4.0.4" + classnames "^2.2.5" + codemirror "^5.18.2" + create-react-class "^15.5.1" + lodash.debounce "^4.0.8" + lodash.isequal "^4.5.0" + prop-types "^15.5.4" react-custom-scrollbars@^4.1.1: version "4.1.1" From b817c823de26cd9ee6e45f72994b43d250a93054 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 7 Sep 2017 10:05:31 -0700 Subject: [PATCH 31/42] Update branch with new linter rules --- .../components/KapacitorRulesTable.js | 7 +++++- .../kapacitor/components/TickscriptEditor.js | 8 ++----- ui/src/kapacitor/components/TickscriptID.js | 4 ++-- .../containers/KapacitorRulesPage.js | 4 ++-- ui/src/kapacitor/containers/TickscriptPage.js | 24 +++++++++---------- 5 files changed, 24 insertions(+), 23 deletions(-) diff --git a/ui/src/kapacitor/components/KapacitorRulesTable.js b/ui/src/kapacitor/components/KapacitorRulesTable.js index 8d6654648c..8c2b65aa82 100644 --- a/ui/src/kapacitor/components/KapacitorRulesTable.js +++ b/ui/src/kapacitor/components/KapacitorRulesTable.js @@ -43,6 +43,8 @@ const KapacitorRulesTable = ({rules, source, onDelete, onChangeRuleStatus}) =>
      +const handleDelete = (rule, onDelete) => onDelete(rule) + const RuleRow = ({rule, source, onDelete, onChangeRuleStatus}) => @@ -81,7 +83,10 @@ const RuleRow = ({rule, source, onDelete, onChangeRuleStatus}) => > Edit TICKscript - diff --git a/ui/src/kapacitor/components/TickscriptEditor.js b/ui/src/kapacitor/components/TickscriptEditor.js index 90803725a2..5fba239bee 100644 --- a/ui/src/kapacitor/components/TickscriptEditor.js +++ b/ui/src/kapacitor/components/TickscriptEditor.js @@ -7,7 +7,7 @@ class TickscriptEditor extends Component { super(props) } - updateCode(script) { + updateCode = script => { this.props.onChangeScript(script) } @@ -20,11 +20,7 @@ class TickscriptEditor extends Component { } return ( - + ) } } diff --git a/ui/src/kapacitor/components/TickscriptID.js b/ui/src/kapacitor/components/TickscriptID.js index a7c8beb861..0fee91f94d 100644 --- a/ui/src/kapacitor/components/TickscriptID.js +++ b/ui/src/kapacitor/components/TickscriptID.js @@ -10,7 +10,7 @@ class TickscriptNewID extends Component { } } - handleChangeID(e) { + handleChangeID = e => { const tickscriptID = e.target.value this.setState({tickscriptID}) } @@ -28,7 +28,7 @@ class TickscriptNewID extends Component { className="page-header--editing kapacitor-theme" autoFocus={true} value={tickscriptID} - onChange={::this.handleChangeID} + onChange={this.handleChangeID} placeholder="Name your tickscript" spellCheck={false} autoComplete={false} diff --git a/ui/src/kapacitor/containers/KapacitorRulesPage.js b/ui/src/kapacitor/containers/KapacitorRulesPage.js index 8c22600296..8652e1fd11 100644 --- a/ui/src/kapacitor/containers/KapacitorRulesPage.js +++ b/ui/src/kapacitor/containers/KapacitorRulesPage.js @@ -47,8 +47,8 @@ class KapacitorRulesPage extends Component { rules={rules} hasKapacitor={hasKapacitor} loading={loading} - onDelete={::this.handleDeleteRule} - onChangeRuleStatus={::this.handleRuleStatus} + onDelete={this.handleDeleteRule} + onChangeRuleStatus={this.handleRuleStatus} /> ) } diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 5843e3c89e..0884a3e8a9 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -49,7 +49,7 @@ class TickscriptPage extends Component { this.setState({kapacitor}) } - async handleSave() { + handleSave = async () => { const {kapacitor, task} = this.state const { source, @@ -72,23 +72,23 @@ class TickscriptPage extends Component { router.push(`/sources/${source.id}/alert-rules`) } - handleChangeScript(script) { + handleChangeScript = script => { this.setState({task: {...this.state.task, script}}) } - handleSelectDbrps(dbrps) { + handleSelectDbrps = dbrps => { this.setState({task: {...this.state.task, dbrps}}) } - handleChangeType(type) { + handleChangeType = type => { return () => this.setState({task: {...this.state.task, type}}) } - handleStartEditID() { + handleStartEditID = () => { this.setState({isEditingID: true}) } - handleStopEditID() { + handleStopEditID = () => { this.setState({isEditingID: false}) } @@ -103,12 +103,12 @@ class TickscriptPage extends Component { validation={validation} isEditingID={isEditingID} isNewTickscript={!this._isEditing()} - onSave={::this.handleSave} - onStartEditID={::this.handleStartEditID} - onStopEditID={::this.handleStopEditID} - onSelectDbrps={::this.handleSelectDbrps} - onChangeScript={::this.handleChangeScript} - onChangeType={::this.handleChangeType} + onSave={this.handleSave} + onStartEditID={this.handleStartEditID} + onStopEditID={this.handleStopEditID} + onSelectDbrps={this.handleSelectDbrps} + onChangeScript={this.handleChangeScript} + onChangeType={this.handleChangeType} /> ) } From b151e8173319e4b1c845e19e172b004c0278b14b Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 12 Sep 2017 11:03:30 -0700 Subject: [PATCH 32/42] Use /rules to create tickscripts --- ui/src/kapacitor/apis/index.js | 17 +++++++++++-- .../kapacitor/components/TickscriptHeader.js | 24 +++---------------- ui/src/kapacitor/containers/TickscriptPage.js | 24 ++++++++++++------- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/ui/src/kapacitor/apis/index.js b/ui/src/kapacitor/apis/index.js index b4a2a435bd..7290f26166 100644 --- a/ui/src/kapacitor/apis/index.js +++ b/ui/src/kapacitor/apis/index.js @@ -19,6 +19,19 @@ export const createRule = (kapacitor, rule) => { }) } +export const createTickScript = async (kapacitor, rule) => { + try { + return await AJAX({ + method: 'POST', + url: kapacitor.links.rules, + data: rule, + }) + } catch (error) { + console.error(error) + throw error + } +} + export const getRules = kapacitor => { return AJAX({ method: 'GET', @@ -65,12 +78,12 @@ export const createTask = async (kapacitor, {id, dbrps, script, type}) => { try { return await AJAX({ method: 'POST', - url: kapacitor.links.tasks, + url: kapacitor.links.rules, data: { id, type, dbrps, - script, + tickscript: script, }, }) } catch (error) { diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js index 6b19f9e0dc..a16433df51 100644 --- a/ui/src/kapacitor/components/TickscriptHeader.js +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -2,34 +2,19 @@ import React, {PropTypes} from 'react' import SourceIndicator from 'shared/components/SourceIndicator' import TickscriptType from 'src/kapacitor/components/TickscriptType' import MultiSelectDBDropdown from 'shared/components/MultiSelectDBDropdown' -import TickscrtiptNewID, { - TickscriptEditID, -} from 'src/kapacitor/components/TickscriptID' + const addName = list => list.map(l => ({...l, name: `${l.db}.${l.rp}`})) const TickscriptHeader = ({ - task: {id, type, dbrps}, + task: {type, dbrps}, source: {name}, onSave, - isEditing, - onStopEdit, - onStartEdit, onChangeType, onSelectDbrps, - isNewTickscript, }) =>
      -
      - {isNewTickscript - ? - : } -
      +
      @@ -60,9 +45,6 @@ TickscriptHeader.propTypes = { }), onChangeType: func.isRequired, isEditing: bool.isRequired, - onStartEdit: func.isRequired, - onStopEdit: func.isRequired, - isNewTickscript: bool.isRequired, } export default TickscriptHeader diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 0884a3e8a9..1ef8d7eb79 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -59,17 +59,23 @@ class TickscriptPage extends Component { } = this.props let response - if (this._isEditing()) { - response = await updateTask(kapacitor, task, ruleID) - } else { - response = await createTask(kapacitor, task) - } - if (response && response.error) { - return this.setState({validation: response.error}) + try { + if (this._isEditing()) { + response = await updateTask(kapacitor, task, ruleID) + } else { + response = await createTask(kapacitor, task) + } + + if (response && response.code === 500) { + return this.setState({validation: response.message}) + } + + router.push(`/sources/${source.id}/alert-rules`) + } catch (error) { + console.error(error) + throw error } - - router.push(`/sources/${source.id}/alert-rules`) } handleChangeScript = script => { From 4915e978c96712ec8b409577eca755bc984e4082 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 12 Sep 2017 11:44:06 -0700 Subject: [PATCH 33/42] Update existing tickscript --- ui/src/kapacitor/apis/index.js | 12 +++++----- ui/src/kapacitor/components/Tickscript.js | 2 +- ui/src/kapacitor/containers/TickscriptPage.js | 13 ++++++---- .../components/MultiSelectDBDropdown.js | 12 ++++++---- .../shared/components/MultiSelectDropdown.js | 24 ++++++++++++------- 5 files changed, 38 insertions(+), 25 deletions(-) diff --git a/ui/src/kapacitor/apis/index.js b/ui/src/kapacitor/apis/index.js index 7290f26166..c0f4b18305 100644 --- a/ui/src/kapacitor/apis/index.js +++ b/ui/src/kapacitor/apis/index.js @@ -74,7 +74,7 @@ export const updateRuleStatus = (rule, status) => { }) } -export const createTask = async (kapacitor, {id, dbrps, script, type}) => { +export const createTask = async (kapacitor, {id, dbrps, tickscript, type}) => { try { return await AJAX({ method: 'POST', @@ -83,7 +83,7 @@ export const createTask = async (kapacitor, {id, dbrps, script, type}) => { id, type, dbrps, - tickscript: script, + tickscript, }, }) } catch (error) { @@ -94,18 +94,18 @@ export const createTask = async (kapacitor, {id, dbrps, script, type}) => { export const updateTask = async ( kapacitor, - {id, dbrps, script, type}, + {id, dbrps, tickscript, type}, ruleID ) => { try { return await AJAX({ - method: 'PATCH', - url: `${kapacitor.links.tasks}/${ruleID}`, + method: 'PUT', + url: `${kapacitor.links.rules}/${ruleID}`, data: { id, type, dbrps, - script, + tickscript, }, }) } catch (error) { diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index 64ca8058c6..b319dd6fd1 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -41,7 +41,7 @@ const Tickscript = ({
      diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 1ef8d7eb79..da2c891778 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -16,7 +16,7 @@ class TickscriptPage extends Component { task: { id: '', status: 'enabled', - script: '', + tickscript: '', dbrps: [], type: 'stream', }, @@ -42,8 +42,11 @@ class TickscriptPage extends Component { if (this._isEditing()) { await kapacitorActions.getRule(kapacitor, ruleID) - const activeRule = this.props.rules.find(r => r.id === ruleID) - this.setState({task: {...this.state.task, script: activeRule.tickscript}}) + const {id, tickscript, dbrps, type} = this.props.rules.find( + r => r.id === ruleID + ) + + this.setState({task: {tickscript, dbrps, type, status, id}}) } this.setState({kapacitor}) @@ -78,8 +81,8 @@ class TickscriptPage extends Component { } } - handleChangeScript = script => { - this.setState({task: {...this.state.task, script}}) + handleChangeScript = tickscript => { + this.setState({task: {...this.state.task, tickscript}}) } handleSelectDbrps = dbrps => { diff --git a/ui/src/shared/components/MultiSelectDBDropdown.js b/ui/src/shared/components/MultiSelectDBDropdown.js index 41d5d6a304..050789a33e 100644 --- a/ui/src/shared/components/MultiSelectDBDropdown.js +++ b/ui/src/shared/components/MultiSelectDBDropdown.js @@ -11,8 +11,6 @@ class MultiSelectDBDropdown extends Component { this.state = { dbrps: [], } - - this._getDbRps = ::this._getDbRps } componentDidMount() { @@ -21,18 +19,21 @@ class MultiSelectDBDropdown extends Component { render() { const {dbrps} = this.state - const {onApply} = this.props + const {onApply, selectedItems} = this.props + const label = 'Select databases' return ( ) } - async _getDbRps() { + _getDbRps = async () => { const {source: {links: {proxy}}} = this.context const {onErrorThrown} = this.props @@ -65,7 +66,7 @@ class MultiSelectDBDropdown extends Component { } } -const {func, shape, string} = PropTypes +const {arrayOf, func, shape, string} = PropTypes MultiSelectDBDropdown.contextTypes = { source: shape({ @@ -78,6 +79,7 @@ MultiSelectDBDropdown.contextTypes = { MultiSelectDBDropdown.propTypes = { onErrorThrown: func, onApply: func.isRequired, + selectedItems: arrayOf(shape()), } export default MultiSelectDBDropdown diff --git a/ui/src/shared/components/MultiSelectDropdown.js b/ui/src/shared/components/MultiSelectDropdown.js index 84fce64b81..5abbd159b3 100644 --- a/ui/src/shared/components/MultiSelectDropdown.js +++ b/ui/src/shared/components/MultiSelectDropdown.js @@ -8,16 +8,19 @@ import FancyScrollbar from 'shared/components/FancyScrollbar' import {DROPDOWN_MENU_MAX_HEIGHT} from 'shared/constants/index' const labelText = ({localSelectedItems, isOpen, label}) => { + if (localSelectedItems.length) { + return localSelectedItems.map(s => s.name).join(', ') + } + if (label) { return label - } else if (localSelectedItems.length) { - return localSelectedItems.map(s => s.name).join(', ') } // TODO: be smarter about the text displayed here if (isOpen) { return '0 Selected' } + return 'None' } @@ -27,23 +30,28 @@ class MultiSelectDropdown extends Component { this.state = { isOpen: false, - localSelectedItems: this.props.selectedItems, + localSelectedItems: props.selectedItems, } - - this.onSelect = ::this.onSelect - this.handleApply = ::this.handleApply } handleClickOutside() { this.setState({isOpen: false}) } + componentWillReceiveProps(nextProps) { + if (!_.isEqual(this.props.selectedItems, nextProps.selectedItems)) { + return + } + + this.setState({localSelectedItems: nextProps.selectedItems}) + } + toggleMenu = e => { e.stopPropagation() this.setState({isOpen: !this.state.isOpen}) } - onSelect(item, e) { + onSelect = (item, e) => { e.stopPropagation() const {onApply, isApplyShown} = this.props @@ -67,7 +75,7 @@ class MultiSelectDropdown extends Component { return !!this.state.localSelectedItems.find(({name}) => name === item.name) } - handleApply(e) { + handleApply = e => { e.stopPropagation() this.setState({isOpen: false}) From f7d59f6a4f1aec27c46a13a721f04696fa76d000 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 12 Sep 2017 13:17:51 -0700 Subject: [PATCH 34/42] Fix auto indendation to match indentation of previous line --- ui/src/external/codemirror.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/ui/src/external/codemirror.js b/ui/src/external/codemirror.js index b10a9ddbca..3129a886d6 100644 --- a/ui/src/external/codemirror.js +++ b/ui/src/external/codemirror.js @@ -331,9 +331,6 @@ CodeMirror.defineSimpleMode('tickscript', { // A next property will cause the mode to move to a different state {regex: /\/\*/, token: 'comment', next: 'comment'}, {regex: /[-+\/*=<>!]+/, token: 'operator'}, - // indent and dedent properties guide autoindentation - {regex: /[\{\[\(]/, indent: true}, - {regex: /[\}\]\)]/, dedent: true}, {regex: /[a-z$][\w$]*/, token: 'variable'}, ], // The multi-line comment state. From 701c4bf92e0e72379da3ed1fe0774812366d6302 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 12 Sep 2017 14:25:40 -0700 Subject: [PATCH 35/42] Publish notification after route change --- ui/src/kapacitor/actions/view/index.js | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/ui/src/kapacitor/actions/view/index.js b/ui/src/kapacitor/actions/view/index.js index 8bcd3539d0..20914ac606 100644 --- a/ui/src/kapacitor/actions/view/index.js +++ b/ui/src/kapacitor/actions/view/index.js @@ -236,10 +236,16 @@ export function updateRuleStatus(rule, status) { } } -export const createTask = (kapacitor, task) => async dispatch => { +export const createTask = ( + kapacitor, + task, + router, + sourceID +) => async dispatch => { try { const {data} = await createTaskAJAX(kapacitor, task) - dispatch(publishNotification('success', 'We made a tick script!')) + router.push(`/sources/${sourceID}/alert-rules`) + dispatch(publishNotification('success', 'You made a TICKscript!')) return data } catch (error) { if (!error) { @@ -251,9 +257,16 @@ export const createTask = (kapacitor, task) => async dispatch => { } } -export const updateTask = (kapacitor, task, ruleID) => async dispatch => { +export const updateTask = ( + kapacitor, + task, + ruleID, + router, + sourceID +) => async dispatch => { try { - const {data} = await updateTaskAJAX(kapacitor, task, ruleID) + const {data} = await updateTaskAJAX(kapacitor, task, ruleID, sourceID) + router.push(`/sources/${sourceID}/alert-rules`) dispatch(publishNotification('success', 'TICKscript updated successully')) return data } catch (error) { From dc7fc85bac272ba1400d0ab82194858ad699f1fd Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 12 Sep 2017 14:27:48 -0700 Subject: [PATCH 36/42] Remove TICKscript name creation --- ui/src/kapacitor/apis/index.js | 13 ---- ui/src/kapacitor/components/Tickscript.js | 9 --- .../kapacitor/components/TickscriptHeader.js | 8 +- ui/src/kapacitor/components/TickscriptID.js | 74 ------------------- ui/src/kapacitor/components/TickscriptName.js | 44 +++++++++++ ui/src/kapacitor/containers/TickscriptPage.js | 28 +++---- 6 files changed, 60 insertions(+), 116 deletions(-) delete mode 100644 ui/src/kapacitor/components/TickscriptID.js create mode 100644 ui/src/kapacitor/components/TickscriptName.js diff --git a/ui/src/kapacitor/apis/index.js b/ui/src/kapacitor/apis/index.js index c0f4b18305..d24847f279 100644 --- a/ui/src/kapacitor/apis/index.js +++ b/ui/src/kapacitor/apis/index.js @@ -19,19 +19,6 @@ export const createRule = (kapacitor, rule) => { }) } -export const createTickScript = async (kapacitor, rule) => { - try { - return await AJAX({ - method: 'POST', - url: kapacitor.links.rules, - data: rule, - }) - } catch (error) { - console.error(error) - throw error - } -} - export const getRules = kapacitor => { return AJAX({ method: 'GET', diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index b319dd6fd1..dfe50e3224 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -10,9 +10,6 @@ const Tickscript = ({ onSelectDbrps, onChangeScript, onChangeType, - isEditingID, - onStartEditID, - onStopEditID, isNewTickscript, }) =>
      @@ -20,9 +17,6 @@ const Tickscript = ({ task={task} source={source} onSave={onSave} - isEditing={isEditingID} - onStopEdit={onStopEditID} - onStartEdit={onStartEditID} onChangeType={onChangeType} onSelectDbrps={onSelectDbrps} isNewTickscript={isNewTickscript} @@ -62,9 +56,6 @@ Tickscript.propTypes = { onSelectDbrps: func.isRequired, validation: string, onChangeType: func.isRequired, - isEditingID: bool.isRequired, - onStartEditID: func.isRequired, - onStopEditID: func.isRequired, isNewTickscript: bool.isRequired, } diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js index a16433df51..bbea2491cf 100644 --- a/ui/src/kapacitor/components/TickscriptHeader.js +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -2,11 +2,13 @@ import React, {PropTypes} from 'react' import SourceIndicator from 'shared/components/SourceIndicator' import TickscriptType from 'src/kapacitor/components/TickscriptType' import MultiSelectDBDropdown from 'shared/components/MultiSelectDBDropdown' +import {TickscriptStaticName} from 'src/kapacitor/components/TickscriptName' const addName = list => list.map(l => ({...l, name: `${l.db}.${l.rp}`})) const TickscriptHeader = ({ task: {type, dbrps}, + task, source: {name}, onSave, onChangeType, @@ -14,7 +16,9 @@ const TickscriptHeader = ({ }) =>
      -
      +
      + +
      @@ -44,7 +48,7 @@ TickscriptHeader.propTypes = { ), }), onChangeType: func.isRequired, - isEditing: bool.isRequired, + isNewTickscript: bool.isRequired, } export default TickscriptHeader diff --git a/ui/src/kapacitor/components/TickscriptID.js b/ui/src/kapacitor/components/TickscriptID.js deleted file mode 100644 index 0fee91f94d..0000000000 --- a/ui/src/kapacitor/components/TickscriptID.js +++ /dev/null @@ -1,74 +0,0 @@ -import React, {PropTypes, Component} from 'react' -import ReactTooltip from 'react-tooltip' -import onClickOutside from 'react-onclickoutside' - -class TickscriptNewID extends Component { - constructor(props) { - super(props) - this.state = { - tickscriptID: '', - } - } - - handleChangeID = e => { - const tickscriptID = e.target.value - this.setState({tickscriptID}) - } - - handleClickOutside() { - this.props.onStopEdit() - } - - render() { - const {isEditing, onStartEdit} = this.props - const {tickscriptID} = this.state - - return isEditing - ? - :

      - {tickscriptID} - -

      - } -} - -export const TickscriptEditID = ({id}) => -

      - {id} -

      - -const {bool, func, string} = PropTypes - -TickscriptNewID.propTypes = { - isEditing: bool.isRequired, - onStartEdit: func.isRequired, - onStopEdit: func.isRequired, - isNewTickscript: bool.isRequired, -} - -TickscriptEditID.propTypes = { - id: string.isRequired, -} - -export default onClickOutside(TickscriptNewID) diff --git a/ui/src/kapacitor/components/TickscriptName.js b/ui/src/kapacitor/components/TickscriptName.js new file mode 100644 index 0000000000..761571c245 --- /dev/null +++ b/ui/src/kapacitor/components/TickscriptName.js @@ -0,0 +1,44 @@ +import React, {PropTypes, Component} from 'react' + +class TickscriptName extends Component { + constructor(props) { + super(props) + } + + render() { + const {onChangeName, name} = this.props + + return ( + + ) + } +} + +export const TickscriptStaticName = ({name}) => +

      + {name} +

      + +const {func, string} = PropTypes + +TickscriptName.propTypes = { + onChangeName: func.isRequired, + name: string.isRequired, +} + +TickscriptStaticName.propTypes = { + name: string.isRequired, +} + +export default TickscriptName diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index da2c891778..8455565825 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -15,6 +15,7 @@ class TickscriptPage extends Component { kapacitor: {}, task: { id: '', + name: '', status: 'enabled', tickscript: '', dbrps: [], @@ -42,11 +43,11 @@ class TickscriptPage extends Component { if (this._isEditing()) { await kapacitorActions.getRule(kapacitor, ruleID) - const {id, tickscript, dbrps, type} = this.props.rules.find( + const {id, name, tickscript, dbrps, type} = this.props.rules.find( r => r.id === ruleID ) - this.setState({task: {tickscript, dbrps, type, status, id}}) + this.setState({task: {tickscript, dbrps, type, status, name, id}}) } this.setState({kapacitor}) @@ -55,7 +56,7 @@ class TickscriptPage extends Component { handleSave = async () => { const {kapacitor, task} = this.state const { - source, + source: {id: sourceID}, router, kapacitorActions: {createTask, updateTask}, params: {ruleID}, @@ -65,16 +66,14 @@ class TickscriptPage extends Component { try { if (this._isEditing()) { - response = await updateTask(kapacitor, task, ruleID) + response = await updateTask(kapacitor, task, ruleID, router, sourceID) } else { - response = await createTask(kapacitor, task) + response = await createTask(kapacitor, task, router, sourceID) } if (response && response.code === 500) { return this.setState({validation: response.message}) } - - router.push(`/sources/${source.id}/alert-rules`) } catch (error) { console.error(error) throw error @@ -93,28 +92,21 @@ class TickscriptPage extends Component { return () => this.setState({task: {...this.state.task, type}}) } - handleStartEditID = () => { - this.setState({isEditingID: true}) - } - - handleStopEditID = () => { - this.setState({isEditingID: false}) + handleChangeName = e => { + this.setState({task: {...this.state.task, name: e.target.value}}) } render() { const {source} = this.props - const {task, validation, isEditingID} = this.state + const {task, validation} = this.state return ( Date: Thu, 14 Sep 2017 13:04:05 -0700 Subject: [PATCH 37/42] Use tickscript id as 'name' for raw tickscripts --- ui/src/kapacitor/components/Tickscript.js | 3 +++ .../kapacitor/components/TickscriptEditor.js | 1 + .../kapacitor/components/TickscriptHeader.js | 20 ++++++++++++---- .../{TickscriptName.js => TickscriptID.js} | 24 +++++++++---------- ui/src/kapacitor/containers/TickscriptPage.js | 9 +++---- 5 files changed, 37 insertions(+), 20 deletions(-) rename ui/src/kapacitor/components/{TickscriptName.js => TickscriptID.js} (57%) diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index dfe50e3224..539d02fb04 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -10,6 +10,7 @@ const Tickscript = ({ onSelectDbrps, onChangeScript, onChangeType, + onChangeID, isNewTickscript, }) =>
      @@ -17,6 +18,7 @@ const Tickscript = ({ task={task} source={source} onSave={onSave} + onChangeID={onChangeID} onChangeType={onChangeType} onSelectDbrps={onSelectDbrps} isNewTickscript={isNewTickscript} @@ -56,6 +58,7 @@ Tickscript.propTypes = { onSelectDbrps: func.isRequired, validation: string, onChangeType: func.isRequired, + onChangeID: func.isRequired, isNewTickscript: bool.isRequired, } diff --git a/ui/src/kapacitor/components/TickscriptEditor.js b/ui/src/kapacitor/components/TickscriptEditor.js index 5fba239bee..0ade89c9ad 100644 --- a/ui/src/kapacitor/components/TickscriptEditor.js +++ b/ui/src/kapacitor/components/TickscriptEditor.js @@ -17,6 +17,7 @@ class TickscriptEditor extends Component { const options = { lineNumbers: true, theme: 'material', + tabIndex: 1, } return ( diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js index bbea2491cf..8cc1531aa6 100644 --- a/ui/src/kapacitor/components/TickscriptHeader.js +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -2,22 +2,28 @@ import React, {PropTypes} from 'react' import SourceIndicator from 'shared/components/SourceIndicator' import TickscriptType from 'src/kapacitor/components/TickscriptType' import MultiSelectDBDropdown from 'shared/components/MultiSelectDBDropdown' -import {TickscriptStaticName} from 'src/kapacitor/components/TickscriptName' +import TickscriptID, { + TickscriptStaticID, +} from 'src/kapacitor/components/TickscriptID' const addName = list => list.map(l => ({...l, name: `${l.db}.${l.rp}`})) const TickscriptHeader = ({ - task: {type, dbrps}, + task: {id, type, dbrps}, task, source: {name}, onSave, onChangeType, + onChangeID, onSelectDbrps, + isNewTickscript, }) =>
      - + {isNewTickscript + ? + : }
      @@ -26,7 +32,12 @@ const TickscriptHeader = ({ selectedItems={addName(dbrps)} onApply={onSelectDbrps} /> -
      @@ -48,6 +59,7 @@ TickscriptHeader.propTypes = { ), }), onChangeType: func.isRequired, + onChangeID: func.isRequired, isNewTickscript: bool.isRequired, } diff --git a/ui/src/kapacitor/components/TickscriptName.js b/ui/src/kapacitor/components/TickscriptID.js similarity index 57% rename from ui/src/kapacitor/components/TickscriptName.js rename to ui/src/kapacitor/components/TickscriptID.js index 761571c245..3640d56220 100644 --- a/ui/src/kapacitor/components/TickscriptName.js +++ b/ui/src/kapacitor/components/TickscriptID.js @@ -1,19 +1,19 @@ import React, {PropTypes, Component} from 'react' -class TickscriptName extends Component { +class TickscriptID extends Component { constructor(props) { super(props) } render() { - const {onChangeName, name} = this.props + const {onChangeID, id} = this.props return ( +export const TickscriptStaticID = ({id}) =>

      - {name} + {id}

      const {func, string} = PropTypes -TickscriptName.propTypes = { - onChangeName: func.isRequired, - name: string.isRequired, +TickscriptID.propTypes = { + onChangeID: func.isRequired, + id: string.isRequired, } -TickscriptStaticName.propTypes = { - name: string.isRequired, +TickscriptStaticID.propTypes = { + id: string.isRequired, } -export default TickscriptName +export default TickscriptID diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 8455565825..b882ecf8a1 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -88,12 +88,12 @@ class TickscriptPage extends Component { this.setState({task: {...this.state.task, dbrps}}) } - handleChangeType = type => { - return () => this.setState({task: {...this.state.task, type}}) + handleChangeType = type => () => { + this.setState({task: {...this.state.task, type}}) } - handleChangeName = e => { - this.setState({task: {...this.state.task, name: e.target.value}}) + handleChangeID = e => { + this.setState({task: {...this.state.task, id: e.target.value}}) } render() { @@ -110,6 +110,7 @@ class TickscriptPage extends Component { onSelectDbrps={this.handleSelectDbrps} onChangeScript={this.handleChangeScript} onChangeType={this.handleChangeType} + onChangeID={this.handleChangeID} /> ) } From e646f42b25ea8d20e9c3d14c21dadb7f463a7576 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 14 Sep 2017 14:06:43 -0700 Subject: [PATCH 38/42] Add a TICKscript table to alert rules page --- ui/src/kapacitor/components/KapacitorRules.js | 45 +++++++-- .../components/KapacitorRulesTable.js | 6 +- ui/src/kapacitor/components/TasksTable.js | 96 +++++++++++++++++++ .../kapacitor/components/TickscriptHeader.js | 2 +- ui/src/kapacitor/components/TickscriptID.js | 4 +- ui/src/kapacitor/constants/tableSizing.js | 9 +- 6 files changed, 145 insertions(+), 17 deletions(-) create mode 100644 ui/src/kapacitor/components/TasksTable.js diff --git a/ui/src/kapacitor/components/KapacitorRules.js b/ui/src/kapacitor/components/KapacitorRules.js index 913c1c45d0..6980dda058 100644 --- a/ui/src/kapacitor/components/KapacitorRules.js +++ b/ui/src/kapacitor/components/KapacitorRules.js @@ -4,6 +4,7 @@ import {Link} from 'react-router' import NoKapacitorError from 'shared/components/NoKapacitorError' import SourceIndicator from 'shared/components/SourceIndicator' import KapacitorRulesTable from 'src/kapacitor/components/KapacitorRulesTable' +import TasksTable from 'src/kapacitor/components/TasksTable' import FancyScrollbar from 'shared/components/FancyScrollbar' const KapacitorRules = ({ @@ -40,13 +41,17 @@ const KapacitorRules = ({ ) } - const tableHeader = - rules.length === 1 ? '1 Alert Rule' : `${rules.length} Alert Rules` + const rulez = rules.filter(r => r.query) + const tasks = rules.filter(r => !r.query) + + const rHeader = `${rulez.length} Alert Rule${rulez.length === 1 ? '' : 's'}` + const tHeader = `${tasks.length} TICKscript${tasks.length === 1 ? '' : 's'}` + return (

      - {tableHeader} + {rHeader}

      Build Rule - - Write TICKscript -
      + +
      +
      +
      +
      +

      + {tHeader} +

      +
      + + Write TICKscript + +
      +
      + +
      +
      +
      ) } diff --git a/ui/src/kapacitor/components/KapacitorRulesTable.js b/ui/src/kapacitor/components/KapacitorRulesTable.js index 8c2b65aa82..00f6ab2689 100644 --- a/ui/src/kapacitor/components/KapacitorRulesTable.js +++ b/ui/src/kapacitor/components/KapacitorRulesTable.js @@ -5,7 +5,7 @@ import _ from 'lodash' import {KAPACITOR_RULES_TABLE} from 'src/kapacitor/constants/tableSizing' const { colName, - colType, + colTrigger, colMessage, colAlerts, colEnabled, @@ -18,7 +18,7 @@ const KapacitorRulesTable = ({rules, source, onDelete, onChangeRuleStatus}) => Name - Rule Type + Rule Trigger Message Alerts @@ -50,7 +50,7 @@ const RuleRow = ({rule, source, onDelete, onChangeRuleStatus}) => - + {rule.trigger} diff --git a/ui/src/kapacitor/components/TasksTable.js b/ui/src/kapacitor/components/TasksTable.js new file mode 100644 index 0000000000..75784e508d --- /dev/null +++ b/ui/src/kapacitor/components/TasksTable.js @@ -0,0 +1,96 @@ +import React, {PropTypes} from 'react' +import {Link} from 'react-router' +import _ from 'lodash' + +import {TASKS_TABLE} from 'src/kapacitor/constants/tableSizing' + +const {colID, colType, colEnabled, colActions} = TASKS_TABLE + +const TasksTable = ({tasks, source, onDelete, onChangeRuleStatus}) => +
      + + + + + + + + + + {_.sortBy(tasks, t => t.name.toLowerCase()).map(task => { + return ( + + ) + })} + +
      IDType + Enabled + +
      +
      + +const handleDelete = (task, onDelete) => onDelete(task) + +const TaskRow = ({task, source, onDelete, onChangeRuleStatus}) => + + + + {task.id} + + + + {task.type} + + +
      + +
      + + + + Edit TICKscript + + + + + +const {arrayOf, func, shape, string} = PropTypes + +TasksTable.propTypes = { + tasks: arrayOf(shape()), + onChangeRuleStatus: func, + onDelete: func, + source: shape({ + id: string.isRequired, + }).isRequired, +} + +TaskRow.propTypes = { + task: shape(), + source: shape(), + onChangeRuleStatus: func, + onDelete: func, +} + +export default TasksTable diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js index 8cc1531aa6..c6dcaee71d 100644 --- a/ui/src/kapacitor/components/TickscriptHeader.js +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -34,7 +34,7 @@ const TickscriptHeader = ({ />