From 7b9d320713cc2c582cdfdc6d0d41c439d765fa93 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 16 Oct 2017 11:37:41 -0700 Subject: [PATCH 001/138] Add state and corresponding toggle for showing/hiding logs --- ui/src/kapacitor/components/LogsToggle.js | 26 +++++++++++++++++++ .../kapacitor/components/TickscriptHeader.js | 9 +++++++ ui/src/kapacitor/containers/TickscriptPage.js | 9 ++++++- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 ui/src/kapacitor/components/LogsToggle.js diff --git a/ui/src/kapacitor/components/LogsToggle.js b/ui/src/kapacitor/components/LogsToggle.js new file mode 100644 index 000000000..fcbde1969 --- /dev/null +++ b/ui/src/kapacitor/components/LogsToggle.js @@ -0,0 +1,26 @@ +import React, {PropTypes} from 'react' + +const LogsToggle = ({areLogsVisible, onToggleLogsVisbility}) => + + +const {bool, func} = PropTypes + +LogsToggle.propTypes = { + areLogsVisible: bool, + onToggleLogsVisbility: func.isRequired, +} + +export default LogsToggle diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js index d826fe43f..8c83edb5c 100644 --- a/ui/src/kapacitor/components/TickscriptHeader.js +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -5,6 +5,7 @@ import MultiSelectDBDropdown from 'shared/components/MultiSelectDBDropdown' import TickscriptID, { TickscriptStaticID, } from 'src/kapacitor/components/TickscriptID' +import LogsToggle from 'src/kapacitor/components/LogsToggle' const addName = list => list.map(l => ({...l, name: `${l.db}.${l.rp}`})) @@ -16,6 +17,8 @@ const TickscriptHeader = ({ onChangeID, onSelectDbrps, isNewTickscript, + areLogsVisible, + onToggleLogsVisbility, }) =>
@@ -26,6 +29,10 @@ const TickscriptHeader = ({
+ { + this.setState({areLogsVisible: !this.state.areLogsVisible}) + } + render() { const {source} = this.props - const {task, validation} = this.state + const {task, validation, areLogsVisible} = this.state return ( ) } From e0c5e10f4212ab3592fe1c9b3811706379a9b4e9 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 16 Oct 2017 11:37:59 -0700 Subject: [PATCH 002/138] Create Logs Table skeleton component --- ui/src/kapacitor/components/LogsTable.js | 67 ++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 ui/src/kapacitor/components/LogsTable.js diff --git a/ui/src/kapacitor/components/LogsTable.js b/ui/src/kapacitor/components/LogsTable.js new file mode 100644 index 000000000..d9ec29083 --- /dev/null +++ b/ui/src/kapacitor/components/LogsTable.js @@ -0,0 +1,67 @@ +import React, {Component, PropTypes} from 'react' + +class LogsTable extends Component { + constructor(props) { + super(props) + } + + renderTable = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + +
BlarghSwoggleHorglesChortle
sdfsdfsfsdfsdfsdfsdfsdfsdfjnsdfkjlnjsdjkfsfsdbnds
sdfsdfsfsdfsdfsdfsdfsdfsdfjnsdfkjlnjsdjkfsfsdbnds
+ ) + } + render() { + const {isWidget} = this.props + + const output = isWidget + ? this.renderTable() + :
+
+
+
+
+

Logs

+
FILTER
+
+
+ {this.renderTable()} +
+
+
+
+
+ + return output + } +} + +const {bool} = PropTypes + +LogsTable.propTypes = { + isWidget: bool, +} + +export default LogsTable From 394de68c85252e89c16187caf53156b9f0aba1c7 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 16 Oct 2017 11:38:41 -0700 Subject: [PATCH 003/138] Use resizer component to simultaneously display logs and the editor --- ui/src/kapacitor/components/Tickscript.js | 69 ++++++++++++++++------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index 539d02fb0..0e99f7350 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -1,6 +1,8 @@ import React, {PropTypes} from 'react' import TickscriptHeader from 'src/kapacitor/components/TickscriptHeader' import TickscriptEditor from 'src/kapacitor/components/TickscriptEditor' +import ResizeContainer from 'shared/components/ResizeContainer' +import LogsTable from 'src/kapacitor/components/LogsTable' const Tickscript = ({ source, @@ -12,6 +14,8 @@ const Tickscript = ({ onChangeType, onChangeID, isNewTickscript, + areLogsVisible, + onToggleLogsVisbility, }) =>
-
-
-
- {validation - ?

- {validation} -

- :

- Save your TICKscript to validate it -

} -
-
-
- -
-
+ {areLogsVisible + ? +
+
+
+ {validation + ?

+ {validation} +

+ :

+ Save your TICKscript to validate it +

} +
+
+
+ +
+
+ +
+ :
+
+
+ {validation + ?

+ {validation} +

+ :

+ Save your TICKscript to validate it +

} +
+
+
+ +
+
}
const {arrayOf, bool, func, shape, string} = PropTypes Tickscript.propTypes = { onSave: func.isRequired, + areLogsVisible: bool, + onToggleLogsVisbility: func.isRequired, source: shape(), task: shape({ id: string, From b46ff9c0aec68ef25edf9af0341f40ce2b4a459a Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 16 Oct 2017 12:17:01 -0700 Subject: [PATCH 004/138] Add styles to handle editor & logs inside resizer --- ui/src/kapacitor/components/LogsTable.js | 20 ++++++--------- ui/src/kapacitor/components/Tickscript.js | 2 +- ui/src/style/chronograf.scss | 1 + ui/src/style/pages/tickscript-editor.scss | 31 +++++++++++++++++++++++ 4 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 ui/src/style/pages/tickscript-editor.scss diff --git a/ui/src/kapacitor/components/LogsTable.js b/ui/src/kapacitor/components/LogsTable.js index d9ec29083..5d9c7e2ba 100644 --- a/ui/src/kapacitor/components/LogsTable.js +++ b/ui/src/kapacitor/components/LogsTable.js @@ -38,18 +38,14 @@ class LogsTable extends Component { const output = isWidget ? this.renderTable() - :
-
-
-
-
-

Logs

-
FILTER
-
-
- {this.renderTable()} -
-
+ :
+
+
+

Logs

+
FILTER
+
+
+ {this.renderTable()}
diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index 0e99f7350..fcdd71dc4 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -31,7 +31,7 @@ const Tickscript = ({ /> {areLogsVisible ? -
+
{validation diff --git a/ui/src/style/chronograf.scss b/ui/src/style/chronograf.scss index 2be5b0290..00f64bb6d 100644 --- a/ui/src/style/chronograf.scss +++ b/ui/src/style/chronograf.scss @@ -60,6 +60,7 @@ @import 'pages/kapacitor'; @import 'pages/dashboards'; @import 'pages/admin'; +@import 'pages/tickscript-editor'; // TODO @import 'unsorted'; diff --git a/ui/src/style/pages/tickscript-editor.scss b/ui/src/style/pages/tickscript-editor.scss new file mode 100644 index 000000000..b3907b6d4 --- /dev/null +++ b/ui/src/style/pages/tickscript-editor.scss @@ -0,0 +1,31 @@ +/* + Styles for TICKscript Editor + ---------------------------------------------- +*/ + +.resize--container .tickscript { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.resize--container .logs-table-container { + position: absolute; + top: 0; + left: 50%; + height: 100%; + transform: translateX(-50%); + width: 100%; + max-width: 1300px; + padding: 0 60px; + padding-bottom: 30px; +} +.resize--container .logs-table-container .panel { + display: flex; + flex-direction: column; + height: 100%; +} +.resize--container .logs-table-container .panel-body { + flex: 1 0 0; +} From 966e0a3cad56e35b5f5cfa935d3735a809df4ed8 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 16 Oct 2017 12:24:20 -0700 Subject: [PATCH 005/138] Create and use kapacitor theme for resizer Makes the handle glow green instead of blue --- ui/src/kapacitor/components/Tickscript.js | 7 ++++++- ui/src/shared/components/ResizeContainer.js | 6 ++++-- ui/src/shared/components/ResizeHandle.js | 8 ++++++-- ui/src/style/components/resizer.scss | 12 +++++++++++- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index fcdd71dc4..fcd67463e 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -30,7 +30,12 @@ const Tickscript = ({ onToggleLogsVisbility={onToggleLogsVisbility} /> {areLogsVisible - ? + ?
diff --git a/ui/src/shared/components/ResizeContainer.js b/ui/src/shared/components/ResizeContainer.js index a8baab69e..07fd59417 100644 --- a/ui/src/shared/components/ResizeContainer.js +++ b/ui/src/shared/components/ResizeContainer.js @@ -97,7 +97,7 @@ class ResizeContainer extends Component { render() { const {bottomHeightPixels, topHeight, bottomHeight, isDragging} = this.state - const {containerClass, children} = this.props + const {containerClass, children, isKapacitorTheme} = this.props if (React.Children.count(children) > maximumNumChildren) { console.error( @@ -122,6 +122,7 @@ class ResizeContainer extends Component { })}
diff --git a/ui/src/style/components/resizer.scss b/ui/src/style/components/resizer.scss index d5d20872f..0cc9107db 100644 --- a/ui/src/style/components/resizer.scss +++ b/ui/src/style/components/resizer.scss @@ -13,6 +13,7 @@ $resizer-dots: $g3-castle; $resizer-color: $g5-pepper; $resizer-color-hover: $g8-storm; $resizer-color-active: $c-pool; +$resizer-color-kapacitor: $c-rainforest; .resize--container { overflow: hidden !important; @@ -109,4 +110,13 @@ $resizer-color-active: $c-pool; box-shadow: 0 0 $resizer-glow $resizer-color-active; } } -} \ No newline at end of file +} + +/* Kapacitor Theme */ +.resizer--handle.resizer--malachite.dragging { + &:before, + &:after { + background-color: $resizer-color-kapacitor; + box-shadow: 0 0 $resizer-glow $resizer-color-kapacitor; + } +} From eb159d2f43b3ca0832787d1ed38c824c74b16009 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 16 Oct 2017 12:40:02 -0700 Subject: [PATCH 006/138] Add dummy logs object --- ui/src/kapacitor/components/LogsTable.js | 177 +++++++++++++++++++++++ 1 file changed, 177 insertions(+) diff --git a/ui/src/kapacitor/components/LogsTable.js b/ui/src/kapacitor/components/LogsTable.js index 5d9c7e2ba..c1d8d660b 100644 --- a/ui/src/kapacitor/components/LogsTable.js +++ b/ui/src/kapacitor/components/LogsTable.js @@ -1,5 +1,182 @@ import React, {Component, PropTypes} from 'react' +const dummyLogs = [ + { + ts: '2017-10-16T15:36:31.329-04:00', + lvl: 'info', + msg: 'created log session', + service: 'sessions', + id: 'aa87c0b6-26d0-484a-8f15-7445c2d5f386', + 'content-type': 'application/json', + tags: 'nil', + }, + { + ts: '2017-10-16T15:36:31.329-04:00', + lvl: 'error', + msg: '2017/10/16 15:36:31 http: multiple response.WriteHeader calls\n', + service: 'httpd_server_errors', + }, + { + ts: '2017-10-16T15:36:32.090-04:00', + lvl: 'debug', + msg: 'starting next batch query', + service: 'kapacitor', + task_master: 'main', + task: 'batch', + node: 'query1', + query: + "SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= '2017-10-16T19:35:32.089928906Z' AND time < '2017-10-16T19:36:32.089928906Z'", + }, + { + ts: '2017-10-16T15:36:32.241-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-16T15:36:32.241436147-04:00', + method: 'POST', + uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + 'user-agent': 'InfluxDBClient', + 'request-id': '502cb71e-b2a9-11e7-8186-000000000000', + duration: '59.135µs', + }, + { + ts: '2017-10-16T15:36:35.313-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-16T15:36:35.312244484-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + 'user-agent': 'InfluxDBClient', + 'request-id': '52014892-b2a9-11e7-8187-000000000000', + duration: '1.468473ms', + }, + { + ts: '2017-10-16T15:36:35.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + load15: 1.71, + n_users: 8, + load5: 1.78, + n_cpus: 8, + load1: 1.74, + }, + time: '2017-10-16T19:36:35Z', + }, + { + ts: '2017-10-16T15:36:35.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + uptime_format: '11 days, 2:08', + uptime: 958109, + }, + time: '2017-10-16T19:36:35Z', + }, + { + ts: '2017-10-16T15:36:36.664-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-16T15:36:36.663967463-04:00', + method: 'POST', + uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + 'user-agent': 'InfluxDBClient', + 'request-id': '52cf8a43-b2a9-11e7-8188-000000000000', + duration: '624.155µs', + }, + { + ts: '2017-10-16T15:36:40.313-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-16T15:36:40.312324709-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + 'user-agent': 'InfluxDBClient', + 'request-id': '54fc3c33-b2a9-11e7-8189-000000000000', + duration: '1.0192ms', + }, + { + ts: '2017-10-16T15:36:40.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + host: 'Michaels-MBP-2.router.edm', + cluster_id: 'michaels-example-cluster', + }, + field: { + load15: 1.71, + n_cpus: 8, + load1: 1.68, + load5: 1.77, + n_users: 8, + }, + time: '2017-10-16T19:36:40Z', + }, +] class LogsTable extends Component { constructor(props) { super(props) From 0017185d659f2c11524172e04c73f4a6593a0d49 Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 17 Oct 2017 11:41:50 -0700 Subject: [PATCH 007/138] Simplify markup and styles for table --- ui/src/kapacitor/components/LogsTable.js | 125 +++++++++++++++++----- ui/src/style/pages/tickscript-editor.scss | 45 ++++++-- 2 files changed, 134 insertions(+), 36 deletions(-) diff --git a/ui/src/kapacitor/components/LogsTable.js b/ui/src/kapacitor/components/LogsTable.js index c1d8d660b..8766a89d3 100644 --- a/ui/src/kapacitor/components/LogsTable.js +++ b/ui/src/kapacitor/components/LogsTable.js @@ -133,7 +133,7 @@ const dummyLogs = [ }, { ts: '2017-10-16T15:36:40.313-04:00', - lvl: 'info', + lvl: 'warn', msg: 'http request', service: 'http', host: '::1', @@ -150,7 +150,7 @@ const dummyLogs = [ }, { ts: '2017-10-16T15:36:40.313-04:00', - lvl: 'info', + lvl: 'ok', msg: 'point', service: 'kapacitor', task_master: 'main', @@ -182,30 +182,101 @@ class LogsTable extends Component { super(props) } + renderAlertLevel = level => { + let alertCSS + switch (level) { + case 'ok': + alertCSS = 'label label-success' + break + case 'warn': + alertCSS = 'label label-info' + break + case 'error': + alertCSS = 'label label-danger' + break + case 'debug': + alertCSS = 'label label-primary' + break + default: + alertCSS = 'label label-default' + } + return alertCSS + } + + renderKeysAndValues = object => { + if (!object) { + return -- + } + const objKeys = Object.keys(object) + const objValues = Object.values(object) + + const objElements = objKeys.map((objKey, i) => +
+ {objKey}: {objValues[i]} +
+ ) + return objElements + } + + renderEmptyCell = () => { + return -- + } + renderMessage = log => { + if (log.msg === 'http request') { + return `HTTP Request ${log.username}@${log.host}` + } + return log.msg + } + renderTable = () => { return ( - +
- - - - + {/* */} + + + + + + + - - - - - - - - - - - - + {dummyLogs.map((l, i) => + + {/* */} + + + + + + + + + )}
BlarghSwoggleHorglesChortleTimestampServiceLevelTaskNodeDurationMessageTags & Fields
sdfsdfsfsdfsdfsdfsdfsdfsdfjnsdfkjlnjsdjkfsfsdbnds
sdfsdfsfsdfsdfsdfsdfsdfsdfjnsdfkjlnjsdjkfsfsdbnds
+ {l.ts} + + {l.service} + + + {l.lvl} + + + {l.task || this.renderEmptyCell()} + + {l.node || this.renderEmptyCell()} + + {l.duration || this.renderEmptyCell()} + + {this.renderMessage(l)} + + {l.tag ?
TAGS
: null} + {this.renderKeysAndValues(l.tag)} + {l.field ?
FIELDS
: null} + {this.renderKeysAndValues(l.field)} +
) @@ -215,15 +286,13 @@ class LogsTable extends Component { const output = isWidget ? this.renderTable() - :
-
-
-

Logs

-
FILTER
-
-
- {this.renderTable()} -
+ :
+
+

Logs

+
FILTER
+
+
+ {this.renderTable()}
diff --git a/ui/src/style/pages/tickscript-editor.scss b/ui/src/style/pages/tickscript-editor.scss index b3907b6d4..42d749074 100644 --- a/ui/src/style/pages/tickscript-editor.scss +++ b/ui/src/style/pages/tickscript-editor.scss @@ -3,6 +3,9 @@ ---------------------------------------------- */ +$logs-table-header-height: 60px; +$logs-table-padding: 60px; + .resize--container .tickscript { position: absolute; top: 0; @@ -10,7 +13,7 @@ width: 100%; height: 100%; } -.resize--container .logs-table-container { +.resize--container .logs-table--container { position: absolute; top: 0; left: 50%; @@ -18,14 +21,40 @@ transform: translateX(-50%); width: 100%; max-width: 1300px; - padding: 0 60px; - padding-bottom: 30px; + padding: 0 $logs-table-padding; } -.resize--container .logs-table-container .panel { +.logs-table--header { display: flex; - flex-direction: column; - height: 100%; + align-items: center; + justify-content: space-between; + flex-wrap: nowrap; + height: $logs-table-header-height; } -.resize--container .logs-table-container .panel-body { - flex: 1 0 0; +.logs-table--panel { + padding: 30px; + position: absolute; + top: $logs-table-header-height; + left: $logs-table-padding; + width: calc(100% - #{$logs-table-padding * 2}); + height: calc(100% - #{$logs-table-header-height + 30px}); + background-color: $g3-castle; + border-radius: 4px; + overflow: auto; +} + +.logs-table--empty-cell { + color: $g6-smoke; +} +table.logs-table { + margin: 0; +} +table.logs-table tbody tr td { + font-weight: 600; +} +table.logs-table tbody tr td .logs-table--key-value { + padding-left: 10px; +} +table.logs-table tbody tr td .logs-table--key-value span { + color: $g10-wolf; + font-weight: 400; } From c3e8038d2d74289b37395b2d89b71d5211e7f9e3 Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 17 Oct 2017 12:56:50 -0700 Subject: [PATCH 008/138] Move logs into constant file They are big now, starting to clutter up the component file --- ui/src/kapacitor/components/LogsTable.js | 181 +-- ui/src/kapacitor/constants/dummyLogs.js | 1323 ++++++++++++++++++++++ 2 files changed, 1326 insertions(+), 178 deletions(-) create mode 100644 ui/src/kapacitor/constants/dummyLogs.js diff --git a/ui/src/kapacitor/components/LogsTable.js b/ui/src/kapacitor/components/LogsTable.js index 8766a89d3..ce9b34251 100644 --- a/ui/src/kapacitor/components/LogsTable.js +++ b/ui/src/kapacitor/components/LogsTable.js @@ -1,182 +1,7 @@ import React, {Component, PropTypes} from 'react' -const dummyLogs = [ - { - ts: '2017-10-16T15:36:31.329-04:00', - lvl: 'info', - msg: 'created log session', - service: 'sessions', - id: 'aa87c0b6-26d0-484a-8f15-7445c2d5f386', - 'content-type': 'application/json', - tags: 'nil', - }, - { - ts: '2017-10-16T15:36:31.329-04:00', - lvl: 'error', - msg: '2017/10/16 15:36:31 http: multiple response.WriteHeader calls\n', - service: 'httpd_server_errors', - }, - { - ts: '2017-10-16T15:36:32.090-04:00', - lvl: 'debug', - msg: 'starting next batch query', - service: 'kapacitor', - task_master: 'main', - task: 'batch', - node: 'query1', - query: - "SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= '2017-10-16T19:35:32.089928906Z' AND time < '2017-10-16T19:36:32.089928906Z'", - }, - { - ts: '2017-10-16T15:36:32.241-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-16T15:36:32.241436147-04:00', - method: 'POST', - uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - 'user-agent': 'InfluxDBClient', - 'request-id': '502cb71e-b2a9-11e7-8186-000000000000', - duration: '59.135µs', - }, - { - ts: '2017-10-16T15:36:35.313-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-16T15:36:35.312244484-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - 'user-agent': 'InfluxDBClient', - 'request-id': '52014892-b2a9-11e7-8187-000000000000', - duration: '1.468473ms', - }, - { - ts: '2017-10-16T15:36:35.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - load15: 1.71, - n_users: 8, - load5: 1.78, - n_cpus: 8, - load1: 1.74, - }, - time: '2017-10-16T19:36:35Z', - }, - { - ts: '2017-10-16T15:36:35.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - uptime_format: '11 days, 2:08', - uptime: 958109, - }, - time: '2017-10-16T19:36:35Z', - }, - { - ts: '2017-10-16T15:36:36.664-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-16T15:36:36.663967463-04:00', - method: 'POST', - uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - 'user-agent': 'InfluxDBClient', - 'request-id': '52cf8a43-b2a9-11e7-8188-000000000000', - duration: '624.155µs', - }, - { - ts: '2017-10-16T15:36:40.313-04:00', - lvl: 'warn', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-16T15:36:40.312324709-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - 'user-agent': 'InfluxDBClient', - 'request-id': '54fc3c33-b2a9-11e7-8189-000000000000', - duration: '1.0192ms', - }, - { - ts: '2017-10-16T15:36:40.313-04:00', - lvl: 'ok', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - host: 'Michaels-MBP-2.router.edm', - cluster_id: 'michaels-example-cluster', - }, - field: { - load15: 1.71, - n_cpus: 8, - load1: 1.68, - load5: 1.77, - n_users: 8, - }, - time: '2017-10-16T19:36:40Z', - }, -] +import {DUMMY_LOGS} from 'src/kapacitor/constants/dummyLogs' + class LogsTable extends Component { constructor(props) { super(props) @@ -244,7 +69,7 @@ class LogsTable extends Component { - {dummyLogs.map((l, i) => + {DUMMY_LOGS.map((l, i) => {/* {l.ts} diff --git a/ui/src/kapacitor/constants/dummyLogs.js b/ui/src/kapacitor/constants/dummyLogs.js new file mode 100644 index 000000000..a94d67b3f --- /dev/null +++ b/ui/src/kapacitor/constants/dummyLogs.js @@ -0,0 +1,1323 @@ +export const DUMMY_LOGS = [ + { + ts: '2017-10-16T15:36:31.329-04:00', + lvl: 'info', + msg: 'created log session', + service: 'sessions', + id: 'aa87c0b6-26d0-484a-8f15-7445c2d5f386', + 'content-type': 'application/json', + tags: 'nil', + }, + { + ts: '2017-10-16T15:36:31.329-04:00', + lvl: 'error', + msg: '2017/10/16 15:36:31 http: multiple response.WriteHeader calls\n', + service: 'httpd_server_errors', + }, + { + ts: '2017-10-16T15:36:32.090-04:00', + lvl: 'debug', + msg: 'starting next batch query', + service: 'kapacitor', + task_master: 'main', + task: 'batch', + node: 'query1', + query: + "SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= '2017-10-16T19:35:32.089928906Z' AND time < '2017-10-16T19:36:32.089928906Z'", + }, + { + ts: '2017-10-16T15:36:32.241-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-16T15:36:32.241436147-04:00', + method: 'POST', + uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + 'request-id': '502cb71e-b2a9-11e7-8186-000000000000', + duration: '59.135µs', + }, + { + ts: '2017-10-16T15:36:35.313-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-16T15:36:35.312244484-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + 'request-id': '52014892-b2a9-11e7-8187-000000000000', + duration: '1.468473ms', + }, + { + ts: '2017-10-16T15:36:35.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + load15: 1.71, + n_users: 8, + load5: 1.78, + n_cpus: 8, + load1: 1.74, + }, + time: '2017-10-16T19:36:35Z', + }, + { + ts: '2017-10-16T15:36:35.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + uptime_format: '11 days, 2:08', + uptime: 958109, + }, + time: '2017-10-16T19:36:35Z', + }, + { + ts: '2017-10-16T15:36:36.664-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-16T15:36:36.663967463-04:00', + method: 'POST', + uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + 'request-id': '52cf8a43-b2a9-11e7-8188-000000000000', + duration: '624.155µs', + }, + { + ts: '2017-10-16T15:36:40.313-04:00', + lvl: 'warn', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-16T15:36:40.312324709-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + 'request-id': '54fc3c33-b2a9-11e7-8189-000000000000', + duration: '1.0192ms', + }, + { + ts: '2017-10-16T15:36:40.313-04:00', + lvl: 'ok', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + host: 'Michaels-MBP-2.router.edm', + cluster_id: 'michaels-example-cluster', + }, + field: { + load15: 1.71, + n_cpus: 8, + load1: 1.68, + load5: 1.77, + n_users: 8, + }, + time: '2017-10-16T19:36:40Z', + }, + + { + ts: '2017-10-17T14:41:20.882-04:00', + lvl: 'info', + msg: 'created log session', + service: 'sessions', + id: 'fe0be004-4417-4889-a6be-fce3bc56b5f8', + 'content-type': 'application/json', + tags: 'nil', + }, + { + ts: '2017-10-17T14:41:20.882-04:00', + lvl: 'error', + msg: '2017/10/17 14:41:20 http: multiple response.WriteHeader calls\n', + service: 'httpd_server_errors', + }, + { + ts: '2017-10-17T14:41:25.125-04:00', + lvl: 'debug', + msg: 'starting next batch query', + service: 'kapacitor', + task_master: 'main', + task: 'batch', + node: 'query1', + query: + 'SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= ‘2017-10-17T18:40:25.125253198Z’ AND time < ‘2017-10-17T18:41:25.125253198Z’', + }, + { + ts: '2017-10-17T14:41:25.285-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:41:25.285657166-04:00', + method: 'POST', + uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'c77d0f01-b36a-11e7-966d-000000000000', + duration: '81.785µs', + }, + { + ts: '2017-10-17T14:41:25.312-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:41:25.310586775-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'c780dcd5-b36a-11e7-966e-000000000000', + duration: '1.898055ms', + }, + { + ts: '2017-10-17T14:41:25.312-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + n_users: 8, + load1: 1.95, + load5: 2.22, + load15: 2.04, + n_cpus: 8, + }, + time: '2017-10-17T18:41:25Z', + }, + { + ts: '2017-10-17T14:41:25.312-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + uptime: 1041199, + uptime_format: '12 days, 1:13', + }, + time: '2017-10-17T18:41:25Z', + }, + { + ts: '2017-10-17T14:41:25.314-04:00', + lvl: 'error', + msg: 'failed to POST data', + service: 'kapacitor', + task_master: 'main', + task: 'httppost', + node: 'http_post3', + err: + 'Post http://localhost:8080/example: dial tcp [::1]:8080: getsockopt: connection refused', + }, + { + ts: '2017-10-17T14:41:30.313-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:41:30.312728774-04:00', + method: 'POST', + uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'ca7c2100-b36a-11e7-966f-000000000000', + duration: '919.036µs', + }, + { + ts: '2017-10-17T14:41:30.314-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:41:30.312814501-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'ca7c2454-b36a-11e7-9670-000000000000', + duration: '1.237587ms', + }, + { + ts: '2017-10-17T14:41:30.314-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + n_cpus: 8, + load1: 2.19, + n_users: 8, + load5: 2.26, + load15: 2.05, + }, + time: '2017-10-17T18:41:30Z', + }, + { + ts: '2017-10-17T14:41:30.314-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + uptime: 1041204, + uptime_format: '12 days, 1:13', + }, + time: '2017-10-17T18:41:30Z', + }, + { + ts: '2017-10-17T14:41:35.126-04:00', + lvl: 'debug', + msg: 'starting next batch query', + service: 'kapacitor', + task_master: 'main', + task: 'batch', + node: 'query1', + query: + 'SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= ‘2017-10-17T18:40:35.125820959Z’ AND time < ‘2017-10-17T18:41:35.125820959Z’', + }, + { + ts: '2017-10-17T14:41:35.284-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:41:35.284066121-04:00', + method: 'POST', + uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'cd72b1da-b36a-11e7-9671-000000000000', + duration: '56.943µs', + }, + { + ts: '2017-10-17T14:41:35.310-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:41:35.309032877-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'cd76811f-b36a-11e7-9672-000000000000', + duration: '1.262848ms', + }, + { + ts: '2017-10-17T14:41:35.310-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + n_cpus: 8, + load1: 2.1, + load15: 2.05, + n_users: 8, + load5: 2.24, + }, + time: '2017-10-17T18:41:35Z', + }, + { + ts: '2017-10-17T14:41:35.310-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + uptime: 1041209, + uptime_format: '12 days, 1:13', + }, + time: '2017-10-17T18:41:35Z', + }, + { + ts: '2017-10-17T14:41:35.312-04:00', + lvl: 'error', + msg: 'failed to POST data', + service: 'kapacitor', + task_master: 'main', + task: 'httppost', + node: 'http_post3', + err: + 'Post http://localhost:8080/example: dial tcp [::1]:8080: getsockopt: connection refused', + }, + { + ts: '2017-10-17T14:41:40.311-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:41:40.310966717-04:00', + method: 'POST', + uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'd071bd27-b36a-11e7-9673-000000000000', + duration: '625.873µs', + }, + { + ts: '2017-10-17T14:41:40.313-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:41:40.312029958-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'd071e6b0-b36a-11e7-9674-000000000000', + duration: '1.021583ms', + }, + { + ts: '2017-10-17T14:41:40.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + load1: 2.01, + load15: 2.04, + n_users: 8, + load5: 2.22, + n_cpus: 8, + }, + time: '2017-10-17T18:41:40Z', + }, + { + ts: '2017-10-17T14:41:40.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + uptime_format: '12 days, 1:13', + uptime: 1041214, + }, + time: '2017-10-17T18:41:40Z', + }, + { + ts: '2017-10-17T14:41:45.029-04:00', + lvl: 'debug', + msg: 'linking subscription for cluster', + service: 'influxdb', + cluster: 'default', + }, + { + ts: '2017-10-17T14:41:45.125-04:00', + lvl: 'debug', + msg: 'starting next batch query', + service: 'kapacitor', + task_master: 'main', + task: 'batch', + node: 'query1', + query: + 'SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= ‘2017-10-17T18:40:45.125467573Z’ AND time < ‘2017-10-17T18:41:45.125467573Z’', + }, + { + ts: '2017-10-17T14:41:45.284-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:41:45.284222418-04:00', + method: 'POST', + uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'd36898f5-b36a-11e7-9675-000000000000', + duration: '83.559µs', + }, + { + ts: '2017-10-17T14:41:45.310-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:41:45.309477008-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'd36c7378-b36a-11e7-9676-000000000000', + duration: '1.420439ms', + }, + { + ts: '2017-10-17T14:41:45.311-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + load1: 2.09, + load5: 2.23, + load15: 2.05, + n_users: 8, + n_cpus: 8, + }, + time: '2017-10-17T18:41:45Z', + }, + { + ts: '2017-10-17T14:41:45.311-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + uptime_format: '12 days, 1:13', + uptime: 1041219, + }, + time: '2017-10-17T18:41:45Z', + }, + { + ts: '2017-10-17T14:41:45.312-04:00', + lvl: 'error', + msg: 'failed to POST data', + service: 'kapacitor', + task_master: 'main', + task: 'httppost', + node: 'http_post3', + err: + 'Post http://localhost:8080/example: dial tcp [::1]:8080: getsockopt: connection refused', + }, + { + ts: '2017-10-17T14:41:50.313-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:41:50.312188999-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'd667cde7-b36a-11e7-9677-000000000000', + duration: '1.16719ms', + }, + { + ts: '2017-10-17T14:41:50.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + n_users: 8, + n_cpus: 8, + load1: 2, + load5: 2.21, + load15: 2.04, + }, + time: '2017-10-17T18:41:50Z', + }, + { + ts: '2017-10-17T14:41:50.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + uptime: 1041224, + uptime_format: '12 days, 1:13', + }, + time: '2017-10-17T18:41:50Z', + }, + { + ts: '2017-10-17T14:41:50.314-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:41:50.313483319-04:00', + method: 'POST', + uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'd6680077-b36a-11e7-9678-000000000000', + duration: '920µs', + }, + { + ts: '2017-10-17T14:41:55.124-04:00', + lvl: 'debug', + msg: 'starting next batch query', + service: 'kapacitor', + task_master: 'main', + task: 'batch', + node: 'query1', + query: + 'SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= ‘2017-10-17T18:40:55.124375998Z’ AND time < ‘2017-10-17T18:41:55.124375998Z’', + }, + { + ts: '2017-10-17T14:41:55.286-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:41:55.286598214-04:00', + method: 'POST', + uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'd95ed6c3-b36a-11e7-9679-000000000000', + duration: '74.558µs', + }, + { + ts: '2017-10-17T14:41:55.313-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:41:55.312049056-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'd962b8f0-b36a-11e7-967a-000000000000', + duration: '1.436052ms', + }, + { + ts: '2017-10-17T14:41:55.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + load5: 2.19, + load15: 2.03, + n_cpus: 8, + load1: 1.92, + n_users: 8, + }, + time: '2017-10-17T18:41:55Z', + }, + { + ts: '2017-10-17T14:41:55.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + uptime: 1041229, + uptime_format: '12 days, 1:13', + }, + time: '2017-10-17T18:41:55Z', + }, + { + ts: '2017-10-17T14:41:55.315-04:00', + lvl: 'error', + msg: 'failed to POST data', + service: 'kapacitor', + task_master: 'main', + task: 'httppost', + node: 'http_post3', + err: + 'Post http://localhost:8080/example: dial tcp [::1]:8080: getsockopt: connection refused', + }, + { + ts: '2017-10-17T14:42:00.307-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:42:00.307216928-04:00', + method: 'POST', + uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'dc5cecae-b36a-11e7-967b-000000000000', + duration: '677.414µs', + }, + { + ts: '2017-10-17T14:42:00.313-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:42:00.31193199-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'dc5da4db-b36a-11e7-967c-000000000000', + duration: '1.114033ms', + }, + { + ts: '2017-10-17T14:42:00.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + n_cpus: 8, + load1: 1.85, + load5: 2.17, + n_users: 8, + load15: 2.03, + }, + time: '2017-10-17T18:42:00Z', + }, + { + ts: '2017-10-17T14:42:00.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + uptime: 1041234, + uptime_format: '12 days, 1:13', + }, + time: '2017-10-17T18:42:00Z', + }, + { + ts: '2017-10-17T14:42:05.124-04:00', + lvl: 'debug', + msg: 'starting next batch query', + service: 'kapacitor', + task_master: 'main', + task: 'batch', + node: 'query1', + query: + 'SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= ‘2017-10-17T18:41:05.124414633Z’ AND time < ‘2017-10-17T18:42:05.124414633Z’', + }, + { + ts: '2017-10-17T14:42:05.283-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:42:05.283540824-04:00', + method: 'POST', + uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'df544055-b36a-11e7-967d-000000000000', + duration: '75.496µs', + }, + { + ts: '2017-10-17T14:42:05.312-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:42:05.311360104-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'df587f07-b36a-11e7-967e-000000000000', + duration: '1.255932ms', + }, + { + ts: '2017-10-17T14:42:05.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + host: 'Michaels-MBP-2.router.edm', + cluster_id: 'michaels-example-cluster', + }, + field: { + load1: 1.86, + load15: 2.03, + n_users: 8, + load5: 2.17, + n_cpus: 8, + }, + time: '2017-10-17T18:42:05Z', + }, + { + ts: '2017-10-17T14:42:05.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + uptime: 1041239, + uptime_format: '12 days, 1:13', + }, + time: '2017-10-17T18:42:05Z', + }, + { + ts: '2017-10-17T14:42:05.315-04:00', + lvl: 'error', + msg: 'failed to POST data', + service: 'kapacitor', + task_master: 'main', + task: 'httppost', + node: 'http_post3', + err: + 'Post http://localhost:8080/example: dial tcp [::1]:8080: getsockopt: connection refused', + }, + { + ts: '2017-10-17T14:42:10.313-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:42:10.31170993-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'e2537d30-b36a-11e7-967f-000000000000', + duration: '1.304237ms', + }, + { + ts: '2017-10-17T14:42:10.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + load1: 1.87, + load5: 2.17, + load15: 2.03, + n_users: 8, + n_cpus: 8, + }, + time: '2017-10-17T18:42:10Z', + }, + { + ts: '2017-10-17T14:42:10.313-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + uptime_format: '12 days, 1:14', + uptime: 1041244, + }, + time: '2017-10-17T18:42:10Z', + }, + { + ts: '2017-10-17T14:42:10.314-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:42:10.313200647-04:00', + method: 'POST', + uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'e253b76b-b36a-11e7-9680-000000000000', + duration: '1.030371ms', + }, + { + ts: '2017-10-17T14:42:15.122-04:00', + lvl: 'debug', + msg: 'starting next batch query', + service: 'kapacitor', + task_master: 'main', + task: 'batch', + node: 'query1', + query: + 'SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= ‘2017-10-17T18:41:15.12194903Z’ AND time < ‘2017-10-17T18:42:15.12194903Z’', + }, + { + ts: '2017-10-17T14:42:15.282-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:42:15.282315534-04:00', + method: 'POST', + uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'e549f17a-b36a-11e7-9681-000000000000', + duration: '94.007µs', + }, + { + ts: '2017-10-17T14:42:15.312-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:42:15.310605432-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'e54e428d-b36a-11e7-9682-000000000000', + duration: '1.596389ms', + }, + { + ts: '2017-10-17T14:42:15.312-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + n_cpus: 8, + load1: 2.04, + load5: 2.2, + load15: 2.04, + n_users: 8, + }, + time: '2017-10-17T18:42:15Z', + }, + { + ts: '2017-10-17T14:42:15.312-04:00', + lvl: 'info', + msg: 'point', + service: 'kapacitor', + task_master: 'main', + task: 'log', + node: 'log2', + prefix: '', + name: 'system', + db: 'telegraf', + rp: 'autogen', + group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', + dimension_0: 'cluster_id', + dimension_1: 'host', + tag: { + cluster_id: 'michaels-example-cluster', + host: 'Michaels-MBP-2.router.edm', + }, + field: { + uptime: 1041249, + uptime_format: '12 days, 1:14', + }, + time: '2017-10-17T18:42:15Z', + }, + { + ts: '2017-10-17T14:42:15.314-04:00', + lvl: 'error', + msg: 'failed to POST data', + service: 'kapacitor', + task_master: 'main', + task: 'httppost', + node: 'http_post3', + err: + 'Post http://localhost:8080/example: dial tcp [::1]:8080: getsockopt: connection refused', + }, + { + ts: '2017-10-17T14:42:20.311-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:42:20.311150281-04:00', + method: 'POST', + uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'e8494853-b36a-11e7-9683-000000000000', + duration: '711.257µs', + }, + { + ts: '2017-10-17T14:42:20.313-04:00', + lvl: 'info', + msg: 'http request', + service: 'http', + host: '::1', + username: '-', + start: '2017-10-17T14:42:20.312349859-04:00', + method: 'POST', + uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', + protocol: 'HTTP/1.1', + status: 204, + referer: '-', + user_agent: 'InfluxDBClient', + request_id: 'e849772e-b36a-11e7-9684-000000000000', + duration: '1.079043ms', + }, +] From 6ce792876586d40be89d946f4338cd2723f5f07d Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 18 Oct 2017 09:37:54 -0700 Subject: [PATCH 009/138] Change layout to side-by-side --- ui/src/kapacitor/components/LogsTable.js | 215 ++++++++++-------- ui/src/kapacitor/components/Tickscript.js | 72 ++---- .../kapacitor/components/TickscriptHeader.js | 2 +- .../style/components/code-mirror-theme.scss | 32 +-- ui/src/style/pages/tickscript-editor.scss | 126 +++++++--- 5 files changed, 259 insertions(+), 188 deletions(-) diff --git a/ui/src/kapacitor/components/LogsTable.js b/ui/src/kapacitor/components/LogsTable.js index ce9b34251..6e23cebf5 100644 --- a/ui/src/kapacitor/components/LogsTable.js +++ b/ui/src/kapacitor/components/LogsTable.js @@ -7,27 +7,6 @@ class LogsTable extends Component { super(props) } - renderAlertLevel = level => { - let alertCSS - switch (level) { - case 'ok': - alertCSS = 'label label-success' - break - case 'warn': - alertCSS = 'label label-info' - break - case 'error': - alertCSS = 'label label-danger' - break - case 'debug': - alertCSS = 'label label-primary' - break - default: - alertCSS = 'label label-default' - } - return alertCSS - } - renderKeysAndValues = object => { if (!object) { return -- @@ -43,85 +22,129 @@ class LogsTable extends Component { return objElements } - renderEmptyCell = () => { - return -- - } - renderMessage = log => { - if (log.msg === 'http request') { - return `HTTP Request ${log.username}@${log.host}` - } - return log.msg - } + renderTableRow = (logItem, i) => { + let rowDetails - renderTable = () => { - return ( - - - - {/* */} - - - - - - - - - - - {DUMMY_LOGS.map((l, i) => - - {/* */} - - - - - - - - - )} - -
TimestampServiceLevelTaskNodeDurationMessageTags & Fields
- {l.ts} - - {l.service} - - - {l.lvl} - - - {l.task || this.renderEmptyCell()} - - {l.node || this.renderEmptyCell()} - - {l.duration || this.renderEmptyCell()} - - {this.renderMessage(l)} - - {l.tag ?
TAGS
: null} - {this.renderKeysAndValues(l.tag)} - {l.field ?
FIELDS
: null} - {this.renderKeysAndValues(l.field)} -
- ) - } - render() { - const {isWidget} = this.props - - const output = isWidget - ? this.renderTable() - :
-
-

Logs

-
FILTER
-
-
- {this.renderTable()} + if (logItem.service === 'sessions') { + rowDetails = ( +
+
+ {logItem.msg}
+ ) + } + if (logItem.service === 'http' && logItem.msg === 'http request') { + rowDetails = ( +
+
HTTP Request
+
+ {logItem.method} {logItem.username}@{logItem.host} ({logItem.duration}) +
+
+ ) + } + if (logItem.service === 'kapacitor' && logItem.msg === 'point') { + rowDetails = ( +
+
Kapacitor Point
+
+
+ TAGS
+ {this.renderKeysAndValues(logItem.tag)} +
+
+ FIELDS
+ {this.renderKeysAndValues(logItem.field)} +
+
+
+ ) + } + if (logItem.service === 'httpd_server_errors' && logItem.lvl === 'error') { + rowDetails = ( +
+
HTTP Server
+
+
+ ERROR: {logItem.msg} +
+
+
+ ) + } + if (logItem.service === 'kapacitor' && logItem.lvl === 'error') { + rowDetails = ( +
+
Kapacitor
+
+
+ ERROR: {logItem.msg} +
+
+
+ ) + } + if (logItem.service === 'kapacitor' && logItem.lvl === 'debug') { + rowDetails = ( +
+
Kapacitor
+
+
+ DEBUG: {logItem.msg} +
+
+
+ ) + } + if (logItem.service === 'influxdb' && logItem.lvl === 'debug') { + rowDetails = ( +
+
InfluxDB
+
+
+ DEBUG: {logItem.msg} +
+ Cluster: {logItem.cluster} +
+
+
+ ) + } - return output + return ( +
+
+
+
+ {logItem.ts} +
+
+ {rowDetails} +
+ ) + } + + render() { + return ( +
+
+

Logs

+
+ +
+
+
+
+ {DUMMY_LOGS.map((l, i) => this.renderTableRow(l, i))} +
+
+
+ ) } } diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index fcd67463e..126cc9583 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -1,7 +1,6 @@ import React, {PropTypes} from 'react' import TickscriptHeader from 'src/kapacitor/components/TickscriptHeader' import TickscriptEditor from 'src/kapacitor/components/TickscriptEditor' -import ResizeContainer from 'shared/components/ResizeContainer' import LogsTable from 'src/kapacitor/components/LogsTable' const Tickscript = ({ @@ -29,53 +28,32 @@ const Tickscript = ({ areLogsVisible={areLogsVisible} onToggleLogsVisbility={onToggleLogsVisbility} /> - {areLogsVisible - ? -
-
-
- {validation - ?

- {validation} -

- :

- Save your TICKscript to validate it -

} -
-
-
- -
+
+
+
+

sdfsdfsdf

+
CONTROLS
+
+
+
+ {validation + ?

+ {validation} +

+ :

+ Save your TICKscript to validate it +

}
- - - :
-
-
- {validation - ?

- {validation} -

- :

- Save your TICKscript to validate it -

} -
-
-
- -
-
} +
+
+ +
+
+ {areLogsVisible ? : null} +
const {arrayOf, bool, func, shape, string} = PropTypes diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js index 8c83edb5c..584823312 100644 --- a/ui/src/kapacitor/components/TickscriptHeader.js +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -20,7 +20,7 @@ const TickscriptHeader = ({ areLogsVisible, onToggleLogsVisbility, }) => -
+
{isNewTickscript diff --git a/ui/src/style/components/code-mirror-theme.scss b/ui/src/style/components/code-mirror-theme.scss index a067d1145..d2abcf709 100644 --- a/ui/src/style/components/code-mirror-theme.scss +++ b/ui/src/style/components/code-mirror-theme.scss @@ -7,19 +7,26 @@ */ -$tickscript-console-height: 120px; +$tickscript-console-height: 60px; +.tickscript-controls, .tickscript-console, .tickscript-editor { - padding-left: $page-wrapper-padding; - padding-right: $page-wrapper-padding; - margin: 0 auto; - max-width: $page-wrapper-max-width; + padding: 0; + margin: 0; + width: 100%; position: relative; } +.tickscript-controls, .tickscript-console { height: $tickscript-console-height; - padding-top: 30px; +} +.tickscript-controls { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 60px; + background-color: $g3-castle; } .tickscript-console--output { padding: 0 60px; @@ -27,7 +34,8 @@ $tickscript-console-height: 120px; font-weight: 600; display: flex; align-items: center; - background-color: $g3-castle; + background-color: $g2-kevlar; + border-bottom: 2px solid $g3-castle; position: relative; height: 100%; width: 100%; @@ -42,9 +50,7 @@ $tickscript-console-height: 120px; font-style: italic; } .tickscript-editor { - margin: 0 auto; - padding-bottom: 30px; - height: calc(100% - #{$tickscript-console-height}); + height: calc(100% - #{$tickscript-console-height * 2}); } .ReactCodeMirror { position: relative; @@ -54,8 +60,8 @@ $tickscript-console-height: 120px; .cm-s-material.CodeMirror { border-radius: 0 0 $radius $radius; font-family: $code-font; - background-color: $g2-kevlar; - color: $c-neutrino; + background-color: transparent; + color: $g13-mist; font-weight: 600; height: 100%; } @@ -63,7 +69,7 @@ $tickscript-console-height: 120px; @include custom-scrollbar-round($g2-kevlar,$g6-smoke); } .cm-s-material .CodeMirror-gutters { - background-color: fade-out($g4-onyx, 0.5); + background-color: fade-out($g4-onyx, 0.7); border: none; } .CodeMirror-gutter.CodeMirror-linenumbers { diff --git a/ui/src/style/pages/tickscript-editor.scss b/ui/src/style/pages/tickscript-editor.scss index 42d749074..d04b0f60b 100644 --- a/ui/src/style/pages/tickscript-editor.scss +++ b/ui/src/style/pages/tickscript-editor.scss @@ -3,25 +3,31 @@ ---------------------------------------------- */ -$logs-table-header-height: 60px; -$logs-table-padding: 60px; - -.resize--container .tickscript { - position: absolute; - top: 0; +.tickscript-wrapper { + position: absolute !important; + top: $chronograf-page-header-height; left: 0; width: 100%; - height: 100%; + height: calc(100% - #{$chronograf-page-header-height}) !important; + @include gradient-v($g2-kevlar,$g0-obsidian); + display: flex; + align-items: stretch; } -.resize--container .logs-table--container { - position: absolute; - top: 0; - left: 50%; + +$logs-table-header-height: 60px; +$logs-table-padding: 60px; +$logs-row-indent: 6px; +$logs-level-dot: 8px; +$logs-margin: 4px; + +.tickscript { + flex: 1 0 0; +} +.logs-table--container { + width: 50%; + position: relative; height: 100%; - transform: translateX(-50%); - width: 100%; - max-width: 1300px; - padding: 0 $logs-table-padding; + @include gradient-v($g3-castle,$g1-raven); } .logs-table--header { display: flex; @@ -29,32 +35,90 @@ $logs-table-padding: 60px; justify-content: space-between; flex-wrap: nowrap; height: $logs-table-header-height; + padding: 0 $logs-table-padding 0 ($logs-table-padding / 2); + background-color: $g4-onyx; } .logs-table--panel { - padding: 30px; position: absolute; top: $logs-table-header-height; - left: $logs-table-padding; - width: calc(100% - #{$logs-table-padding * 2}); - height: calc(100% - #{$logs-table-header-height + 30px}); - background-color: $g3-castle; - border-radius: 4px; + left: 0; + width: 100%; + height: calc(100% - #{$logs-table-header-height}); overflow: auto; } -.logs-table--empty-cell { - color: $g6-smoke; +.logs-table, +.logs-table--row { + display: flex; + align-items: stretch; + flex-direction: column; } -table.logs-table { - margin: 0; +.logs-table--row { + padding: 8px ($logs-table-padding - 16px) 8px ($logs-table-padding / 2); + border-bottom: 2px solid $g3-castle; + transition: background-color 0.25s ease; + + &:hover { + background-color: $g4-onyx; + } + &:last-child { + border-bottom: none; + } } -table.logs-table tbody tr td { +.logs-table--divider { + display: flex; + align-items: center; +} +.logs-table--level { + width: $logs-level-dot; + height: $logs-level-dot; + border-radius: 50%; + position: relative; + margin-right: $logs-row-indent; + + &.debug {background-color: $c-comet;} + &.info {background-color: $g6-smoke;} + &.warn {background-color: $c-pineapple;} + &.ok {background-color: $c-rainforest;} + &.error {background-color: $c-dreamsicle;} +} +.logs-table--timestamp { + font-family: $code-font; + font-weight: 500; + font-size: 11px; + color: $g9-mountain; + flex: 1 0 0; +} +.logs-table--details { + display: flex; + align-items: flex-start; + font-size: 13px; + color: $g13-mist; font-weight: 600; + padding-left: ($logs-level-dot + $logs-row-indent); + + .error {color: $c-dreamsicle;} + .debug {color: $c-comet;} } -table.logs-table tbody tr td .logs-table--key-value { - padding-left: 10px; + +/* Logs Table Item Types */ +.logs-table--session { + text-transform: capitalize; + font-style: italic; } -table.logs-table tbody tr td .logs-table--key-value span { - color: $g10-wolf; - font-weight: 400; +.logs-table--service { + width: 140px; +} +.logs-table--blah { + display: flex; + flex: 1 0 0; +} +.logs-table--key-values { + color: $g11-sidewalk; + flex: 1 0 50%; +} +.logs-table--key-value { +} +.logs-table--key-value span { + color: $c-pool; } From a9f1a0e7ff48fa61cd80bc999feb977995c7c515 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 18 Oct 2017 11:15:13 -0700 Subject: [PATCH 010/138] Move stream/batch toggle and dropdown out of page header and refactor associated components --- ui/src/kapacitor/components/Tickscript.js | 43 +++++++----------- .../kapacitor/components/TickscriptEditor.js | 8 +++- .../components/TickscriptEditorConsole.js | 22 ++++++++++ .../components/TickscriptEditorControls.js | 44 +++++++++++++++++++ .../kapacitor/components/TickscriptHeader.js | 36 +++------------ 5 files changed, 95 insertions(+), 58 deletions(-) create mode 100644 ui/src/kapacitor/components/TickscriptEditorConsole.js create mode 100644 ui/src/kapacitor/components/TickscriptEditorControls.js diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index 126cc9583..7bea5a46c 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -1,10 +1,11 @@ import React, {PropTypes} from 'react' import TickscriptHeader from 'src/kapacitor/components/TickscriptHeader' import TickscriptEditor from 'src/kapacitor/components/TickscriptEditor' +import TickscriptEditorControls from 'src/kapacitor/components/TickscriptEditorControls' +import TickscriptEditorConsole from 'src/kapacitor/components/TickscriptEditorConsole' import LogsTable from 'src/kapacitor/components/LogsTable' const Tickscript = ({ - source, onSave, task, validation, @@ -19,38 +20,25 @@ const Tickscript = ({
-
-

sdfsdfsdf

-
CONTROLS
-
-
-
- {validation - ?

- {validation} -

- :

- Save your TICKscript to validate it -

} -
-
-
- -
+ + +
{areLogsVisible ? : null}
@@ -62,7 +50,6 @@ Tickscript.propTypes = { onSave: func.isRequired, areLogsVisible: bool, onToggleLogsVisbility: func.isRequired, - source: shape(), task: shape({ id: string, script: string, diff --git a/ui/src/kapacitor/components/TickscriptEditor.js b/ui/src/kapacitor/components/TickscriptEditor.js index 0ade89c9a..1ca8ab717 100644 --- a/ui/src/kapacitor/components/TickscriptEditor.js +++ b/ui/src/kapacitor/components/TickscriptEditor.js @@ -21,7 +21,13 @@ class TickscriptEditor extends Component { } return ( - +
+ +
) } } diff --git a/ui/src/kapacitor/components/TickscriptEditorConsole.js b/ui/src/kapacitor/components/TickscriptEditorConsole.js new file mode 100644 index 000000000..5b9ccf5fd --- /dev/null +++ b/ui/src/kapacitor/components/TickscriptEditorConsole.js @@ -0,0 +1,22 @@ +import React, {PropTypes} from 'react' + +const TickscriptEditorConsole = ({validation}) => +
+
+ {validation + ?

+ {validation} +

+ :

+ Save your TICKscript to validate it +

} +
+
+ +const {string} = PropTypes + +TickscriptEditorConsole.propTypes = { + validation: string, +} + +export default TickscriptEditorConsole diff --git a/ui/src/kapacitor/components/TickscriptEditorControls.js b/ui/src/kapacitor/components/TickscriptEditorControls.js new file mode 100644 index 000000000..9520001f0 --- /dev/null +++ b/ui/src/kapacitor/components/TickscriptEditorControls.js @@ -0,0 +1,44 @@ +import React, {PropTypes} from 'react' +import TickscriptType from 'src/kapacitor/components/TickscriptType' +import MultiSelectDBDropdown from 'shared/components/MultiSelectDBDropdown' +import TickscriptID, { + TickscriptStaticID, +} from 'src/kapacitor/components/TickscriptID' + +const addName = list => list.map(l => ({...l, name: `${l.db}.${l.rp}`})) + +const TickscriptEditorControls = ({ + isNewTickscript, + onSelectDbrps, + onChangeType, + onChangeID, + task, +}) => +
+ {isNewTickscript + ? + : } +
+ + +
+
+ +const {arrayOf, bool, func, shape, string} = PropTypes + +TickscriptEditorControls.propTypes = { + isNewTickscript: bool.isRequired, + onSelectDbrps: func.isRequired, + onChangeType: func.isRequired, + onChangeID: func.isRequired, + task: shape({ + id: string, + script: string, + dbsrps: arrayOf(shape()), + }).isRequired, +} + +export default TickscriptEditorControls diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js index 584823312..694913b38 100644 --- a/ui/src/kapacitor/components/TickscriptHeader.js +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -1,50 +1,31 @@ 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 TickscriptID, { - TickscriptStaticID, -} from 'src/kapacitor/components/TickscriptID' import LogsToggle from 'src/kapacitor/components/LogsToggle' -const addName = list => list.map(l => ({...l, name: `${l.db}.${l.rp}`})) - const TickscriptHeader = ({ - task: {id, type, dbrps}, - task, + task: {id}, onSave, - onChangeType, - onChangeID, - onSelectDbrps, - isNewTickscript, areLogsVisible, onToggleLogsVisbility, }) =>
- {isNewTickscript - ? - : } -
-
- +

TICKscript Editor

- - +
+
+
@@ -53,8 +34,8 @@ const TickscriptHeader = ({ const {arrayOf, bool, func, shape, string} = PropTypes TickscriptHeader.propTypes = { + isNewTickscript: bool, onSave: func, - onSelectDbrps: func.isRequired, areLogsVisible: bool, onToggleLogsVisbility: func.isRequired, task: shape({ @@ -65,9 +46,6 @@ TickscriptHeader.propTypes = { }) ), }), - onChangeType: func.isRequired, - onChangeID: func.isRequired, - isNewTickscript: bool.isRequired, } export default TickscriptHeader From 76047206b01275cd2c95d763e990d2f21f4eb95c Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 18 Oct 2017 11:29:00 -0700 Subject: [PATCH 011/138] Add missing prop --- ui/src/kapacitor/components/TickscriptHeader.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js index 694913b38..00919fdbd 100644 --- a/ui/src/kapacitor/components/TickscriptHeader.js +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -6,17 +6,18 @@ const TickscriptHeader = ({ task: {id}, onSave, areLogsVisible, + isNewTickscript, onToggleLogsVisbility, }) =>

TICKscript Editor

-
+
From 0ad5eead454edc91c0d127e15c6206ffad666f72 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 18 Oct 2017 11:44:48 -0700 Subject: [PATCH 016/138] Fix CSS mishap --- ui/src/style/components/kapacitor-logs-table.scss | 7 +++++++ ui/src/style/pages/tickscript-editor.scss | 5 ----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ui/src/style/components/kapacitor-logs-table.scss b/ui/src/style/components/kapacitor-logs-table.scss index dbaf8ec21..f90730326 100644 --- a/ui/src/style/components/kapacitor-logs-table.scss +++ b/ui/src/style/components/kapacitor-logs-table.scss @@ -2,6 +2,13 @@ Styles for Kapacitor Logs Table ---------------------------------------------------------------------------- */ + +$logs-table-header-height: 60px; +$logs-table-padding: 60px; +$logs-row-indent: 6px; +$logs-level-dot: 8px; +$logs-margin: 4px; + .logs-table--container { width: 50%; position: relative; diff --git a/ui/src/style/pages/tickscript-editor.scss b/ui/src/style/pages/tickscript-editor.scss index c5145827a..b56cadedd 100644 --- a/ui/src/style/pages/tickscript-editor.scss +++ b/ui/src/style/pages/tickscript-editor.scss @@ -3,11 +3,6 @@ ---------------------------------------------------------------------------- */ -$logs-table-header-height: 60px; -$logs-table-padding: 60px; -$logs-row-indent: 6px; -$logs-level-dot: 8px; -$logs-margin: 4px; $tickscript-console-height: 60px; .tickscript { From ea58f71614440cd897fed47131da94f9d038b802 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 24 Oct 2017 16:36:41 -0700 Subject: [PATCH 017/138] Spike on fetching chunked logs --- ui/src/kapacitor/components/Tickscript.js | 9 +++++++++ ui/src/kapacitor/containers/TickscriptPage.js | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index 03e17aa7a..854a6d60a 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -7,6 +7,7 @@ const Tickscript = ({ source, onSave, task, + logs, validation, onSelectDbrps, onChangeScript, @@ -37,6 +38,13 @@ const Tickscript = ({
+
+ {logs.map((l, i) => +
+              {JSON.stringify(l, null, 2)}
+            
+ )} +
Date: Tue, 24 Oct 2017 16:54:28 -0700 Subject: [PATCH 018/138] WIP fetching the chunks --- ui/src/kapacitor/apis/index.js | 21 +++++++++++++++++++ ui/src/kapacitor/containers/TickscriptPage.js | 5 ++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/ui/src/kapacitor/apis/index.js b/ui/src/kapacitor/apis/index.js index d24847f27..693295b99 100644 --- a/ui/src/kapacitor/apis/index.js +++ b/ui/src/kapacitor/apis/index.js @@ -1,5 +1,26 @@ import AJAX from 'utils/ajax' +export const log = async () => { + try { + let response = await fetch('http://localhost:9092/kapacitor/v1/logs', { + method: 'GET', + headers: {'Content-Type': 'application/json'}, + }) + + const reader = await response.body.getReader() + const decoder = new TextDecoder() + const result = await reader.read() + const chunk = decoder.decode(result.value || new Uint8Array(), { + stream: !result.done, + }) + return chunk + console.log(chunk) + return result + } catch (error) { + console.log(error) + } +} + const rangeRule = rule => { const {value, rangeValue, operator} = rule.values diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 222bc92c3..4011dfee8 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -6,6 +6,7 @@ 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 {log} from 'src/kapacitor/apis' class TickscriptPage extends Component { constructor(props) { @@ -51,7 +52,9 @@ class TickscriptPage extends Component { this.setState({task: {tickscript, dbrps, type, status, name, id}}) } - this.setState({kapacitor}) + const logs = await log() + + this.setState({kapacitor, logs}) } handleSave = async () => { From 0d48320c96c3175676dc8b68ee21d0ac4a2f5284 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Tue, 24 Oct 2017 18:47:11 -0600 Subject: [PATCH 019/138] Add fetchChunkedLogs. Fetches logs in a while loop. Looping stops on componentWIllUnmount. --- ui/src/kapacitor/apis/index.js | 21 ------- ui/src/kapacitor/components/Tickscript.js | 16 +++-- ui/src/kapacitor/containers/TickscriptPage.js | 59 +++++++++++++++++-- 3 files changed, 67 insertions(+), 29 deletions(-) diff --git a/ui/src/kapacitor/apis/index.js b/ui/src/kapacitor/apis/index.js index 693295b99..d24847f27 100644 --- a/ui/src/kapacitor/apis/index.js +++ b/ui/src/kapacitor/apis/index.js @@ -1,26 +1,5 @@ import AJAX from 'utils/ajax' -export const log = async () => { - try { - let response = await fetch('http://localhost:9092/kapacitor/v1/logs', { - method: 'GET', - headers: {'Content-Type': 'application/json'}, - }) - - const reader = await response.body.getReader() - const decoder = new TextDecoder() - const result = await reader.read() - const chunk = decoder.decode(result.value || new Uint8Array(), { - stream: !result.done, - }) - return chunk - console.log(chunk) - return result - } catch (error) { - console.log(error) - } -} - const rangeRule = rule => { const {value, rangeValue, operator} = rule.values diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index 854a6d60a..2b8ed6144 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -39,10 +39,18 @@ const Tickscript = ({
- {logs.map((l, i) => -
-              {JSON.stringify(l, null, 2)}
-            
+ {logs.map(({key, ts, lvl, msg}) => +
+ + {ts} + + + {lvl} + +
+                {msg}
+              
+
)}
(log, i) => ({ + ...log, + key: `${log.ts}-${j}-${i}`, + }) + + fetchChunkedLogs = async () => { + try { + const response = await fetch('http://localhost:9092/kapacitor/v1/logs', { + method: 'GET', + headers: {'Content-Type': 'application/json'}, + }) + + const reader = await response.body.getReader() + const decoder = new TextDecoder() + + let result + let j = 0 + + while (this.shouldFetch === true && !(result && result.done)) { + result = await reader.read() + + const chunk = decoder.decode(result.value || new Uint8Array(), { + stream: !result.done, + }) + + // console.log(chunk) + + const json = `[${chunk.split('}{').join('},{')}]` + + const logs = JSON.parse(json).map(this.logKey(j)) + + // console.log(log) + this.setState({ + logs: [...this.state.logs, ...logs], + }) + + j += 1 + } + } catch (error) { + // console.log(error) + // TODO error handling } } @@ -52,9 +97,15 @@ class TickscriptPage extends Component { this.setState({task: {tickscript, dbrps, type, status, name, id}}) } - const logs = await log() + this.shouldFetch = true - this.setState({kapacitor, logs}) + this.fetchChunkedLogs() + + this.setState({kapacitor}) + } + + componentWillUnmount() { + this.shouldFetch = false } handleSave = async () => { From dee4b0e96b99555a244c3f1428dcb600465c7095 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Tue, 31 Oct 2017 12:21:35 -0600 Subject: [PATCH 020/138] Change chunked log fetch URL to use chronograf kapacitor proxy using rule ID. --- ui/src/kapacitor/containers/TickscriptPage.js | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 72e2d295e..a6070c656 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -34,12 +34,15 @@ class TickscriptPage extends Component { key: `${log.ts}-${j}-${i}`, }) - fetchChunkedLogs = async () => { + fetchChunkedLogs = async (kapacitor, ruleID) => { try { - const response = await fetch('http://localhost:9092/kapacitor/v1/logs', { - method: 'GET', - headers: {'Content-Type': 'application/json'}, - }) + const response = await fetch( + `${kapacitor.links.proxy}?path=/kapacitor/v1/logs?task=${ruleID}`, + { + method: 'GET', + headers: {'Content-Type': 'application/json'}, + } + ) const reader = await response.body.getReader() const decoder = new TextDecoder() @@ -54,13 +57,10 @@ class TickscriptPage extends Component { stream: !result.done, }) - // console.log(chunk) - const json = `[${chunk.split('}{').join('},{')}]` const logs = JSON.parse(json).map(this.logKey(j)) - // console.log(log) this.setState({ logs: [...this.state.logs, ...logs], }) @@ -68,7 +68,8 @@ class TickscriptPage extends Component { j += 1 } } catch (error) { - // console.log(error) + console.error(error) + throw error // TODO error handling } } @@ -99,7 +100,7 @@ class TickscriptPage extends Component { this.shouldFetch = true - this.fetchChunkedLogs() + this.fetchChunkedLogs(kapacitor, ruleID) this.setState({kapacitor}) } From 132cbd56d50a79d81944124878fe4a8018829377 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Tue, 31 Oct 2017 14:24:19 -0600 Subject: [PATCH 021/138] CHANGELOG. Fix "TICKscript" consistency. --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8ed7653d..0fa1b8d1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ 1. [#1077](https://github.com/influxdata/chronograf/pull/2087): Fix Chronograf requiring Telegraf's CPU and system plugins to ensure that all Apps appear on the HOST LIST page. ### Features +1. [#2188](https://github.com/influxdata/chronograf/pull/2188): Add Kapacitor logs to the TICKscript editor + ### UI Improvements ## v1.3.10.0 [2017-10-24] @@ -28,7 +30,7 @@ ### UI Improvements 1. [#2111](https://github.com/influxdata/chronograf/pull/2111): Increase size of Cell Editor query tabs to reveal more of their query strings 1. [#2120](https://github.com/influxdata/chronograf/pull/2120): Improve appearance of Admin Page tabs on smaller screens -1. [#2119](https://github.com/influxdata/chronograf/pull/2119): Add cancel button to Tickscript editor +1. [#2119](https://github.com/influxdata/chronograf/pull/2119): Add cancel button to TICKscript editor 1. [#2104](https://github.com/influxdata/chronograf/pull/2104): Redesign dashboard naming & renaming interaction 1. [#2104](https://github.com/influxdata/chronograf/pull/2104): Redesign dashboard switching dropdown @@ -48,7 +50,7 @@ ### Features 1. [#1885](https://github.com/influxdata/chronograf/pull/1885): Add `fill` options to data explorer and dashboard queries -1. [#1978](https://github.com/influxdata/chronograf/pull/1978): Support editing kapacitor TICKScript +1. [#1978](https://github.com/influxdata/chronograf/pull/1978): Support editing kapacitor TICKscript 1. [#1721](https://github.com/influxdata/chronograf/pull/1721): Introduce the TICKscript editor UI 1. [#1992](https://github.com/influxdata/chronograf/pull/1992): Add .csv download button to data explorer 1. [#2082](https://github.com/influxdata/chronograf/pull/2082): Add Data Explorer InfluxQL query and location query synchronization, so queries can be shared via a a URL From eb82ec4131fdb9fafb6fd239486f0d678f8f5962 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Wed, 8 Nov 2017 11:27:35 -0600 Subject: [PATCH 022/138] Refactor and simplify auto group by (:interval:) template variable --- bolt/internal/internal.go | 11 +- chronograf.go | 272 ++---------------------------- chronograf_test.go | 63 ------- influx/influx.go | 6 +- influx/influx_test.go | 10 +- influx/query.go | 46 ++++++ influx/query_test.go | 41 +++++ influx/templates.go | 122 ++++++++++---- influx/templates_test.go | 336 ++++++++++++++++++++++++++------------ server/queries.go | 14 +- server/queries_test.go | 17 +- server/templates_test.go | 12 +- 12 files changed, 467 insertions(+), 483 deletions(-) delete mode 100644 chronograf_test.go diff --git a/bolt/internal/internal.go b/bolt/internal/internal.go index 8d79dde74..5bb729ecf 100644 --- a/bolt/internal/internal.go +++ b/bolt/internal/internal.go @@ -330,9 +330,9 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error { templates := make([]chronograf.Template, len(pb.Templates)) for i, t := range pb.Templates { - vals := make([]chronograf.BasicTemplateValue, len(t.Values)) + vals := make([]chronograf.TemplateValue, len(t.Values)) for j, v := range t.Values { - vals[j] = chronograf.BasicTemplateValue{ + vals[j] = chronograf.TemplateValue{ Selected: v.Selected, Type: v.Type, Value: v.Value, @@ -341,7 +341,7 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error { template := chronograf.Template{ ID: chronograf.TemplateID(t.ID), - BasicTemplateVar: chronograf.BasicTemplateVar{ + TemplateVar: chronograf.TemplateVar{ Var: t.TempVar, Values: vals, }, @@ -434,8 +434,5 @@ func UnmarshalUser(data []byte, u *chronograf.User) error { // UnmarshalUserPB decodes a user from binary protobuf data. // We are ignoring the password for now. func UnmarshalUserPB(data []byte, u *User) error { - if err := proto.Unmarshal(data, u); err != nil { - return err - } - return nil + return proto.Unmarshal(data, u) } diff --git a/chronograf.go b/chronograf.go index f5876c163..b6e314742 100644 --- a/chronograf.go +++ b/chronograf.go @@ -1,21 +1,10 @@ package chronograf import ( - "bytes" "context" - "encoding/json" - "errors" - "fmt" "io" "net/http" - "regexp" - "strconv" - "strings" "time" - "unicode" - "unicode/utf8" - - "github.com/influxdata/influxdb/influxql" ) // General errors. @@ -136,196 +125,17 @@ type Range struct { Lower int64 `json:"lower"` // Lower is the lower bound } -type TemplateVariable interface { - fmt.Stringer - Name() string // returns the variable name - Precedence() uint // ordinal indicating precedence level for replacement -} - -type ExecutableVar interface { - Exec(string) -} - // TemplateValue is a value use to replace a template in an InfluxQL query -type BasicTemplateValue struct { +type TemplateValue struct { Value string `json:"value"` // Value is the specific value used to replace a template in an InfluxQL query Type string `json:"type"` // Type can be tagKey, tagValue, fieldKey, csv, measurement, database, constant Selected bool `json:"selected"` // Selected states that this variable has been picked to use for replacement } // TemplateVar is a named variable within an InfluxQL query to be replaced with Values -type BasicTemplateVar struct { - Var string `json:"tempVar"` // Var is the string to replace within InfluxQL - Values []BasicTemplateValue `json:"values"` // Values are the replacement values within InfluxQL -} - -func (t BasicTemplateVar) Name() string { - return t.Var -} - -// String converts the template variable into a correct InfluxQL string based -// on its type -func (t BasicTemplateVar) String() string { - if len(t.Values) == 0 { - return "" - } - switch t.Values[0].Type { - case "tagKey", "fieldKey", "measurement", "database": - return `"` + t.Values[0].Value + `"` - case "tagValue", "timeStamp": - return `'` + t.Values[0].Value + `'` - case "csv", "constant": - return t.Values[0].Value - default: - return "" - } -} - -func (t BasicTemplateVar) Precedence() uint { - return 0 -} - -type GroupByVar struct { - Var string `json:"tempVar"` // the name of the variable as present in the query - Duration time.Duration `json:"duration,omitempty"` // the Duration supplied by the query - Resolution uint `json:"resolution"` // the available screen resolution to render the results of this query - ReportingInterval time.Duration `json:"reportingInterval,omitempty"` // the interval at which data is reported to this series -} - -// Exec is responsible for extracting the Duration from the query -func (g *GroupByVar) Exec(query string) { - whereClause := "WHERE" - start := strings.Index(query, whereClause) - if start == -1 { - // no where clause - return - } - - // reposition start to after the 'where' keyword - durStr := query[start+len(whereClause):] - - // attempt to parse out a relative time range - // locate duration literal start - prefix := "time > now() - " - lowerDuration, err := g.parseRelative(durStr, prefix) - if err == nil { - prefix := "time < now() - " - upperDuration, err := g.parseRelative(durStr, prefix) - if err != nil { - g.Duration = lowerDuration - return - } - g.Duration = lowerDuration - upperDuration - if g.Duration < 0 { - g.Duration = -g.Duration - } - } - - dur, err := g.parseAbsolute(durStr) - if err == nil { - // we found an absolute time range - g.Duration = dur - } -} - -// parseRelative locates and extracts a duration value from a fragment of an -// InfluxQL query following the "where" keyword. For example, in the fragment -// "time > now() - 180d GROUP BY :interval:", parseRelative would return a -// duration equal to 180d -func (g *GroupByVar) parseRelative(fragment string, prefix string) (time.Duration, error) { - start := strings.Index(fragment, prefix) - if start == -1 { - return time.Duration(0), errors.New("not a relative duration") - } - - // reposition to duration literal - durFragment := fragment[start+len(prefix):] - - // init counters - pos := 0 - - // locate end of duration literal - for pos < len(durFragment) { - rn, _ := utf8.DecodeRuneInString(durFragment[pos:]) - if unicode.IsSpace(rn) { - break - } - pos++ - } - - // attempt to parse what we suspect is a duration literal - dur, err := influxql.ParseDuration(durFragment[:pos]) - if err != nil { - return dur, err - } - - return dur, nil -} - -// parseAbsolute will determine the duration between two absolute timestamps -// found within an InfluxQL fragment following the "where" keyword. For -// example, the fragement "time > '1985-10-25T00:01:21-0800 and time < -// '1985-10-25T00:01:22-0800'" would yield a duration of 1m' -func (g *GroupByVar) parseAbsolute(fragment string) (time.Duration, error) { - timePtn := `time\s[>|<]\s'([0-9\-T\:\.Z]+)'` // Playground: http://gobular.com/x/208f66bd-1889-4269-ab47-1efdfeeb63f0 - re, err := regexp.Compile(timePtn) - if err != nil { - // this is a developer error and should complain loudly - panic("Bad Regex: err:" + err.Error()) - } - - if !re.Match([]byte(fragment)) { - return time.Duration(0), errors.New("absolute duration not found") - } - - // extract at most two times - matches := re.FindAll([]byte(fragment), 2) - - // parse out absolute times - durs := make([]time.Time, 0, 2) - for _, match := range matches { - durStr := re.FindSubmatch(match) - if tm, err := time.Parse(time.RFC3339Nano, string(durStr[1])); err == nil { - durs = append(durs, tm) - } - } - - if len(durs) == 1 { - durs = append(durs, time.Now()) - } - - // reject more than 2 times found - if len(durs) != 2 { - return time.Duration(0), errors.New("must provide exactly two absolute times") - } - - dur := durs[1].Sub(durs[0]) - - return dur, nil -} - -func (g *GroupByVar) String() string { - // The function is: ((total_seconds * millisecond_converstion) / group_by) = pixels / 3 - // Number of points given the pixels - pixels := float64(g.Resolution) / 3.0 - msPerPixel := float64(g.Duration/time.Millisecond) / pixels - secPerPixel := float64(g.Duration/time.Second) / pixels - if secPerPixel < 1.0 { - if msPerPixel < 1.0 { - msPerPixel = 1.0 - } - return "time(" + strconv.FormatInt(int64(msPerPixel), 10) + "ms)" - } - // If groupby is more than 1 second round to the second - return "time(" + strconv.FormatInt(int64(secPerPixel), 10) + "s)" -} - -func (g *GroupByVar) Name() string { - return g.Var -} - -func (g *GroupByVar) Precedence() uint { - return 1 +type TemplateVar struct { + Var string `json:"tempVar"` // Var is the string to replace within InfluxQL + Values []TemplateValue `json:"values"` // Values are the replacement values within InfluxQL } // TemplateID is the unique ID used to identify a template @@ -333,7 +143,7 @@ type TemplateID string // Template represents a series of choices to replace TemplateVars within InfluxQL type Template struct { - BasicTemplateVar + TemplateVar ID TemplateID `json:"id"` // ID is the unique ID associated with this template Type string `json:"type"` // Type can be fieldKeys, tagKeys, tagValues, CSV, constant, query, measurements, databases Label string `json:"label"` // Label is a user-facing description of the Template @@ -342,69 +152,15 @@ type Template struct { // Query retrieves a Response from a TimeSeries. type Query struct { - Command string `json:"query"` // Command is the query itself - DB string `json:"db,omitempty"` // DB is optional and if empty will not be used. - RP string `json:"rp,omitempty"` // RP is a retention policy and optional; if empty will not be used. - TemplateVars TemplateVars `json:"tempVars,omitempty"` // TemplateVars are template variables to replace within an InfluxQL query - Wheres []string `json:"wheres,omitempty"` // Wheres restricts the query to certain attributes - GroupBys []string `json:"groupbys,omitempty"` // GroupBys collate the query by these tags - Resolution uint `json:"resolution,omitempty"` // Resolution is the available screen resolution to render query results - Label string `json:"label,omitempty"` // Label is the Y-Axis label for the data - Range *Range `json:"range,omitempty"` // Range is the default Y-Axis range for the data -} - -// TemplateVars are a heterogeneous collection of different TemplateVariables -// with the capability to decode arbitrary JSON into the appropriate template -// variable type -type TemplateVars []TemplateVariable - -func (t *TemplateVars) UnmarshalJSON(text []byte) error { - // TODO: Need to test that server throws an error when :interval:'s Resolution or ReportingInterval or zero-value - rawVars := bytes.NewReader(text) - dec := json.NewDecoder(rawVars) - - // read open bracket - rawTok, err := dec.Token() - if err != nil { - return err - } - - tok, isDelim := rawTok.(json.Delim) - if !isDelim || tok != '[' { - return errors.New("Expected JSON array, but found " + tok.String()) - } - - for dec.More() { - var halfBakedVar json.RawMessage - err := dec.Decode(&halfBakedVar) - if err != nil { - return err - } - - var agb GroupByVar - err = json.Unmarshal(halfBakedVar, &agb) - if err != nil { - return err - } - - // ensure that we really have a GroupByVar - if agb.Resolution != 0 { - (*t) = append(*t, &agb) - continue - } - - var tvar BasicTemplateVar - err = json.Unmarshal(halfBakedVar, &tvar) - if err != nil { - return err - } - - // ensure that we really have a BasicTemplateVar - if len(tvar.Values) != 0 { - (*t) = append(*t, tvar) - } - } - return nil + Command string `json:"query"` // Command is the query itself + DB string `json:"db,omitempty"` // DB is optional and if empty will not be used. + RP string `json:"rp,omitempty"` // RP is a retention policy and optional; if empty will not be used. + TemplateVars []TemplateVar `json:"tempVars,omitempty"` // TemplateVars are template variables to replace within an InfluxQL query + Wheres []string `json:"wheres,omitempty"` // Wheres restricts the query to certain attributes + GroupBys []string `json:"groupbys,omitempty"` // GroupBys collate the query by these tags + Resolution uint `json:"resolution,omitempty"` // Resolution is the available screen resolution to render query results + Label string `json:"label,omitempty"` // Label is the Y-Axis label for the data + Range *Range `json:"range,omitempty"` // Range is the default Y-Axis range for the data } // DashboardQuery includes state for the query builder. This is a transition diff --git a/chronograf_test.go b/chronograf_test.go deleted file mode 100644 index 850674a4d..000000000 --- a/chronograf_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package chronograf_test - -import ( - "testing" - - "github.com/influxdata/chronograf" -) - -func Test_GroupByVar(t *testing.T) { - gbvTests := []struct { - name string - query string - want string - resolution uint // the screen resolution to render queries into - }{ - { - name: "relative time only lower bound with one day of duration", - query: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d GROUP BY :interval:", - resolution: 1000, - want: "time(259s)", - }, - { - name: "relative time with relative upper bound with one minute of duration", - query: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 3m AND time < now() - 2m GROUP BY :interval:", - resolution: 1000, - want: "time(180ms)", - }, - { - name: "relative time with relative lower bound and now upper with one day of duration", - query: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d AND time < now() GROUP BY :interval:", - resolution: 1000, - want: "time(259s)", - }, - { - name: "absolute time with one minute of duration", - query: "SELECT mean(usage_idle) FROM cpu WHERE time > '1985-10-25T00:01:00Z' and time < '1985-10-25T00:02:00Z' GROUP BY :interval:", - resolution: 1000, - want: "time(180ms)", - }, - { - name: "absolute time with nano seconds and zero duraiton", - query: "SELECT mean(usage_idle) FROM cpu WHERE time > '2017-07-24T15:33:42.994Z' and time < '2017-07-24T15:33:42.994Z' GROUP BY :interval:", - resolution: 1000, - want: "time(1ms)", - }, - } - - for _, test := range gbvTests { - t.Run(test.name, func(t *testing.T) { - gbv := chronograf.GroupByVar{ - Var: ":interval:", - Resolution: test.resolution, - } - - gbv.Exec(test.query) - got := gbv.String() - - if got != test.want { - t.Fatalf("%q - durations not equal! Want: %s, Got: %s", test.name, test.want, got) - } - }) - } -} diff --git a/influx/influx.go b/influx/influx.go index 520d2614b..1106b96ea 100644 --- a/influx/influx.go +++ b/influx/influx.go @@ -9,6 +9,7 @@ import ( "net/http" "net/url" "strings" + "time" "github.com/influxdata/chronograf" ) @@ -55,7 +56,10 @@ func (c *Client) query(u *url.URL, q chronograf.Query) (chronograf.Response, err command := q.Command // TODO(timraymond): move this upper Query() function if len(q.TemplateVars) > 0 { - command = TemplateReplace(q.Command, q.TemplateVars) + command, err = TemplateReplace(q.Command, q.TemplateVars, time.Now()) + if err != nil { + return nil, err + } } logs := c.Logger. WithField("component", "proxy"). diff --git a/influx/influx_test.go b/influx/influx_test.go index a6dce9e11..d165ccb86 100644 --- a/influx/influx_test.go +++ b/influx/influx_test.go @@ -276,11 +276,11 @@ func Test_Influx_HTTPS_InsecureSkipVerify(t *testing.T) { called = false q = "" query = chronograf.Query{ - Command: "select $field from cpu", - TemplateVars: chronograf.TemplateVars{ - chronograf.BasicTemplateVar{ - Var: "$field", - Values: []chronograf.BasicTemplateValue{ + Command: "select :field: from cpu", + TemplateVars: []chronograf.TemplateVar{ + chronograf.TemplateVar{ + Var: ":field:", + Values: []chronograf.TemplateValue{ { Value: "usage_user", Type: "fieldKey", diff --git a/influx/query.go b/influx/query.go index 2346fde7c..15b0a62bc 100644 --- a/influx/query.go +++ b/influx/query.go @@ -10,6 +10,52 @@ import ( "github.com/influxdata/influxdb/influxql" ) +func TimeRangeAsEpochNano(expr influxql.Expr, now time.Time) (min, max int64, err error) { + tmin, tmax, err := influxql.TimeRange(expr) + if err != nil { + return 0, 0, err + } + if tmin.IsZero() { + min = time.Unix(0, influxql.MinTime).UnixNano() + } else { + min = tmin.UnixNano() + } + if tmax.IsZero() { + max = now.UnixNano() + } else { + max = tmax.UnixNano() + } + return +} + +const WhereToken = "WHERE" + +func ParseTime(influxQL string, now time.Time) (time.Duration, error) { + start := strings.Index(strings.ToUpper(influxQL), WhereToken) + if start == -1 { + return 0, fmt.Errorf("not a relative duration") + } + start += len(WhereToken) + where := influxQL[start:] + cond, err := influxql.ParseExpr(where) + if err != nil { + return 0, err + } + nowVal := &influxql.NowValuer{ + Now: now, + } + cond = influxql.Reduce(cond, nowVal) + min, max, err := TimeRangeAsEpochNano(cond, now) + if err != nil { + return 0, err + } + dur := time.Duration(max - min) + if dur < 0 { + dur = 0 + } + return dur, nil +} + // Convert changes an InfluxQL query to a QueryConfig func Convert(influxQL string) (chronograf.QueryConfig, error) { itsDashboardTime := false diff --git a/influx/query_test.go b/influx/query_test.go index d01a46fb5..dba50dfc7 100644 --- a/influx/query_test.go +++ b/influx/query_test.go @@ -2,6 +2,7 @@ package influx import ( "testing" + "time" "github.com/google/go-cmp/cmp" "github.com/influxdata/chronograf" @@ -767,3 +768,43 @@ func TestConvert(t *testing.T) { }) } } + +func TestParseTime(t *testing.T) { + tests := []struct { + name string + influxQL string + now string + want time.Duration + wantErr bool + }{ + { + name: "time equal", + now: "2000-01-01T00:00:00Z", + influxQL: `SELECT mean("numSeries") AS "mean_numSeries" FROM "_internal"."monitor"."database" WHERE time > now() - 1h and time < now() - 1h GROUP BY :interval: FILL(null);`, + want: 0, + }, + { + name: "time shifted by one hour", + now: "2000-01-01T00:00:00Z", + influxQL: `SELECT mean("numSeries") AS "mean_numSeries" FROM "_internal"."monitor"."database" WHERE time > now() - 1h - 1h and time < now() - 1h GROUP BY :interval: FILL(null);`, + want: 3599999999998, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + now, err := time.Parse(time.RFC3339, tt.now) + if err != nil { + t.Fatalf("%v", err) + } + got, err := ParseTime(tt.influxQL, now) + if (err != nil) != tt.wantErr { + t.Errorf("ParseTime() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Logf("%d", got) + t.Errorf("ParseTime() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/influx/templates.go b/influx/templates.go index c006f7fa1..8c0ad0e28 100644 --- a/influx/templates.go +++ b/influx/templates.go @@ -1,40 +1,106 @@ package influx import ( + "sort" + "strconv" "strings" + "time" "github.com/influxdata/chronograf" ) -// TemplateReplace replaces templates with values within the query string -func TemplateReplace(query string, templates chronograf.TemplateVars) string { - tvarsByPrecedence := make(map[uint]chronograf.TemplateVars, len(templates)) - maxPrecedence := uint(0) - for _, tmp := range templates { - precedence := tmp.Precedence() - if precedence > maxPrecedence { - maxPrecedence = precedence - } - tvarsByPrecedence[precedence] = append(tvarsByPrecedence[precedence], tmp) - } - - replaced := query - for prc := uint(0); prc <= maxPrecedence; prc++ { - replacements := []string{} - - for _, v := range tvarsByPrecedence[prc] { - if evar, ok := v.(chronograf.ExecutableVar); ok { - evar.Exec(replaced) - } - newVal := v.String() - if newVal != "" { - replacements = append(replacements, v.Name(), newVal) - } +func SortTemplates(ts []chronograf.TemplateVar) []chronograf.TemplateVar { + sort.Slice(ts, func(i, j int) bool { + if len(ts[i].Values) != len(ts[j].Values) { + return len(ts[i].Values) < len(ts[j].Values) } - replacer := strings.NewReplacer(replacements...) - replaced = replacer.Replace(replaced) - } + if len(ts[i].Values) == 0 { + return i < j + } - return replaced + for k := range ts[i].Values { + if ts[i].Values[k].Type != ts[j].Values[k].Type { + return ts[i].Values[k].Type < ts[j].Values[k].Type + } + if ts[i].Values[k].Value != ts[j].Values[k].Value { + return ts[i].Values[k].Value < ts[j].Values[k].Value + } + } + return i < j + }) + return ts +} + +// RenderTemplate converts the template variable into a correct InfluxQL string based +// on its type +func RenderTemplate(query string, t chronograf.TemplateVar, now time.Time) (string, error) { + if len(t.Values) == 0 { + return query, nil + } + switch t.Values[0].Type { + case "tagKey", "fieldKey", "measurement", "database": + return strings.Replace(query, t.Var, `"`+t.Values[0].Value+`"`, -1), nil + case "tagValue", "timeStamp": + return strings.Replace(query, t.Var, `'`+t.Values[0].Value+`'`, -1), nil + case "csv", "constant": + return strings.Replace(query, t.Var, t.Values[0].Value, -1), nil + } + + tv := map[string]string{} + for i := range t.Values { + tv[t.Values[i].Type] = t.Values[i].Value + } + + if res, ok := tv["resolution"]; ok { + resolution, err := strconv.ParseInt(res, 0, 64) + if err != nil { + return "", err + } + ppp, ok := tv["pointsPerPixel"] + if !ok { + ppp = "3" + } + pixelsPerPoint, err := strconv.ParseInt(ppp, 0, 64) + if err != nil { + return "", err + } + + dur, err := ParseTime(query, now) + if err != nil { + return "", err + } + interval := AutoGroupBy(resolution, pixelsPerPoint, dur) + return strings.Replace(query, t.Var, interval, -1), nil + } + return query, nil +} + +func AutoGroupBy(resolution, pixelsPerPoint int64, duration time.Duration) string { + // The function is: ((total_seconds * millisecond_converstion) / group_by) = pixels / 3 + // Number of points given the pixels + pixels := float64(resolution) / float64(pixelsPerPoint) + msPerPixel := float64(duration/time.Millisecond) / pixels + secPerPixel := float64(duration/time.Second) / pixels + if secPerPixel < 1.0 { + if msPerPixel < 1.0 { + msPerPixel = 1.0 + } + return "time(" + strconv.FormatInt(int64(msPerPixel), 10) + "ms)" + } + // If groupby is more than 1 second round to the second + return "time(" + strconv.FormatInt(int64(secPerPixel), 10) + "s)" +} + +// TemplateReplace replaces templates with values within the query string +func TemplateReplace(query string, templates []chronograf.TemplateVar, now time.Time) (string, error) { + templates = SortTemplates(templates) + for i := range templates { + var err error + query, err = RenderTemplate(query, templates[i], now) + if err != nil { + return "", err + } + } + return query, nil } diff --git a/influx/templates_test.go b/influx/templates_test.go index bb6ffc4bb..482a16dc5 100644 --- a/influx/templates_test.go +++ b/influx/templates_test.go @@ -2,6 +2,7 @@ package influx import ( "encoding/json" + "fmt" "reflect" "testing" "time" @@ -13,43 +14,43 @@ func TestTemplateReplace(t *testing.T) { tests := []struct { name string query string - vars chronograf.TemplateVars + vars []chronograf.TemplateVar want string }{ { name: "select with parameters", - query: "$METHOD field1, $field FROM $measurement WHERE temperature > $temperature", - vars: chronograf.TemplateVars{ - chronograf.BasicTemplateVar{ - Var: "$temperature", - Values: []chronograf.BasicTemplateValue{ + query: ":method: field1, :field: FROM :measurement: WHERE temperature > :temperature:", + vars: []chronograf.TemplateVar{ + chronograf.TemplateVar{ + Var: ":temperature:", + Values: []chronograf.TemplateValue{ { Type: "csv", Value: "10", }, }, }, - chronograf.BasicTemplateVar{ - Var: "$field", - Values: []chronograf.BasicTemplateValue{ + chronograf.TemplateVar{ + Var: ":field:", + Values: []chronograf.TemplateValue{ { Type: "fieldKey", Value: "field2", }, }, }, - chronograf.BasicTemplateVar{ - Var: "$METHOD", - Values: []chronograf.BasicTemplateValue{ + chronograf.TemplateVar{ + Var: ":method:", + Values: []chronograf.TemplateValue{ { Type: "csv", Value: "SELECT", }, }, }, - chronograf.BasicTemplateVar{ - Var: "$measurement", - Values: []chronograf.BasicTemplateValue{ + chronograf.TemplateVar{ + Var: ":measurement:", + Values: []chronograf.TemplateValue{ { Type: "csv", Value: `"cpu"`, @@ -62,28 +63,28 @@ func TestTemplateReplace(t *testing.T) { { name: "select with parameters and aggregates", query: `SELECT mean($field) FROM "cpu" WHERE $tag = $value GROUP BY $tag`, - vars: chronograf.TemplateVars{ - chronograf.BasicTemplateVar{ + vars: []chronograf.TemplateVar{ + chronograf.TemplateVar{ Var: "$value", - Values: []chronograf.BasicTemplateValue{ + Values: []chronograf.TemplateValue{ { Type: "tagValue", Value: "howdy.com", }, }, }, - chronograf.BasicTemplateVar{ + chronograf.TemplateVar{ Var: "$tag", - Values: []chronograf.BasicTemplateValue{ + Values: []chronograf.TemplateValue{ { Type: "tagKey", Value: "host", }, }, }, - chronograf.BasicTemplateVar{ + chronograf.TemplateVar{ Var: "$field", - Values: []chronograf.BasicTemplateValue{ + Values: []chronograf.TemplateValue{ { Type: "fieldKey", Value: "field", @@ -101,8 +102,8 @@ func TestTemplateReplace(t *testing.T) { { name: "var without a value", query: `SELECT $field FROM "cpu"`, - vars: chronograf.TemplateVars{ - chronograf.BasicTemplateVar{ + vars: []chronograf.TemplateVar{ + chronograf.TemplateVar{ Var: "$field", }, }, @@ -111,10 +112,10 @@ func TestTemplateReplace(t *testing.T) { { name: "var with unknown type", query: `SELECT $field FROM "cpu"`, - vars: chronograf.TemplateVars{ - chronograf.BasicTemplateVar{ + vars: []chronograf.TemplateVar{ + chronograf.TemplateVar{ Var: "$field", - Values: []chronograf.BasicTemplateValue{ + Values: []chronograf.TemplateValue{ { Type: "who knows?", Value: "field", @@ -127,42 +128,63 @@ func TestTemplateReplace(t *testing.T) { { name: "auto group by", query: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by :interval:`, - vars: chronograf.TemplateVars{ - &chronograf.GroupByVar{ - Var: ":interval:", - Duration: 180 * 24 * time.Hour, - Resolution: 1000, - ReportingInterval: 10 * time.Second, + vars: []chronograf.TemplateVar{ + { + Var: ":interval:", + Values: []chronograf.TemplateValue{ + { + Value: "1000", + Type: "resolution", + }, + { + Value: "3", + Type: "pointsPerPixel", + }, + }, }, }, - want: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by time(46656s)`, + want: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by time(46655s)`, }, { name: "auto group by without duration", query: `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 4320h group by :interval:`, - vars: chronograf.TemplateVars{ - &chronograf.GroupByVar{ - Var: ":interval:", - Duration: 0 * time.Minute, - Resolution: 1000, - ReportingInterval: 10 * time.Second, + vars: []chronograf.TemplateVar{ + { + Var: ":interval:", + Values: []chronograf.TemplateValue{ + { + Value: "1000", + Type: "resolution", + }, + { + Value: "3", + Type: "pointsPerPixel", + }, + }, }, }, - want: `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 4320h group by time(46656s)`, + want: `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 4320h group by time(46655s)`, }, { name: "auto group by with :dashboardTime:", query: `SELECT mean(usage_idle) from "cpu" WHERE time > :dashboardTime: group by :interval:`, - vars: chronograf.TemplateVars{ - &chronograf.GroupByVar{ - Var: ":interval:", - Duration: 0 * time.Minute, - Resolution: 1000, - ReportingInterval: 10 * time.Second, + vars: []chronograf.TemplateVar{ + { + Var: ":interval:", + Values: []chronograf.TemplateValue{ + { + Value: "1000", + Type: "resolution", + }, + { + Value: "3", + Type: "pointsPerPixel", + }, + }, }, - &chronograf.BasicTemplateVar{ + { Var: ":dashboardTime:", - Values: []chronograf.BasicTemplateValue{ + Values: []chronograf.TemplateValue{ { Type: "constant", Value: "now() - 4320h", @@ -170,20 +192,28 @@ func TestTemplateReplace(t *testing.T) { }, }, }, - want: `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 4320h group by time(46656s)`, + want: `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 4320h group by time(46655s)`, }, { name: "auto group by failing condition", query: `SELECT mean(usage_idle) FROM "cpu" WHERE time > :dashboardTime: GROUP BY :interval:`, - vars: []chronograf.TemplateVariable{ - &chronograf.GroupByVar{ - Var: ":interval:", - Resolution: 115, - ReportingInterval: 10 * time.Second, + vars: []chronograf.TemplateVar{ + { + Var: ":interval:", + Values: []chronograf.TemplateValue{ + { + Value: "115", + Type: "resolution", + }, + { + Value: "3", + Type: "pointsPerPixel", + }, + }, }, - chronograf.BasicTemplateVar{ + { Var: ":dashboardTime:", - Values: []chronograf.BasicTemplateValue{ + Values: []chronograf.TemplateValue{ { Value: "now() - 1h", Type: "constant", @@ -197,7 +227,14 @@ func TestTemplateReplace(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := TemplateReplace(tt.query, tt.vars) + now, err := time.Parse(time.RFC3339, "1985-10-25T00:01:00Z") + if err != nil { + t.Fatal(err) + } + got, err := TemplateReplace(tt.query, tt.vars, now) + if err != nil { + t.Fatalf("TestParse unexpected TemplateReplace error: %v", err) + } if got != tt.want { t.Errorf("TestParse %s =\n%s\nwant\n%s", tt.name, got, tt.want) } @@ -209,8 +246,20 @@ func Test_TemplateVarsUnmarshalling(t *testing.T) { req := `[ { "tempVar": ":interval:", - "resolution": 1000, - "reportingInterval": 10 + "values": [ + { + "value": "1000", + "type": "resolution" + }, + { + "value": "3", + "type": "pointsPerPixel" + }, + { + "value": "10", + "type": "reportingInterval" + } + ] }, { "tempVar": ":cpu:", @@ -224,15 +273,27 @@ func Test_TemplateVarsUnmarshalling(t *testing.T) { } ]` - expected := []chronograf.TemplateVariable{ - &chronograf.GroupByVar{ - Var: ":interval:", - Resolution: 1000, - ReportingInterval: 10 * time.Nanosecond, + want := []chronograf.TemplateVar{ + { + Var: ":interval:", + Values: []chronograf.TemplateValue{ + { + Value: "1000", + Type: "resolution", + }, + { + Value: "3", + Type: "pointsPerPixel", + }, + { + Value: "10", + Type: "reportingInterval", + }, + }, }, - chronograf.BasicTemplateVar{ + { Var: ":cpu:", - Values: []chronograf.BasicTemplateValue{ + Values: []chronograf.TemplateValue{ { Value: "cpu-total", Type: "tagValue", @@ -242,65 +303,128 @@ func Test_TemplateVarsUnmarshalling(t *testing.T) { }, } - var tvars chronograf.TemplateVars - err := json.Unmarshal([]byte(req), &tvars) + var got []chronograf.TemplateVar + err := json.Unmarshal([]byte(req), &got) if err != nil { t.Fatal("Err unmarshaling:", err) } - if len(tvars) != len(expected) { - t.Fatal("Expected", len(expected), "vars but found", len(tvars)) - } - - if !reflect.DeepEqual(*(tvars[0].(*chronograf.GroupByVar)), *(expected[0].(*chronograf.GroupByVar))) { - t.Errorf("UnmarshalJSON() = \n%#v\n want \n%#v\n", *(tvars[0].(*chronograf.GroupByVar)), *(expected[0].(*chronograf.GroupByVar))) - } - - if !reflect.DeepEqual(tvars[1].(chronograf.BasicTemplateVar), expected[1].(chronograf.BasicTemplateVar)) { - t.Errorf("UnmarshalJSON() = \n%#v\n want \n%#v\n", tvars[1].(chronograf.BasicTemplateVar), expected[1].(chronograf.BasicTemplateVar)) + if !reflect.DeepEqual(got, want) { + t.Errorf("UnmarshalJSON() = \n%#v\n want \n%#v\n", got, want) } } -func TestGroupByVarString(t *testing.T) { +func TestAutoGroupBy(t *testing.T) { tests := []struct { - name string - tvar *chronograf.GroupByVar - want string + name string + resolution int64 + pixelsPerPoint int64 + duration time.Duration + want string }{ { - name: "String() calculates the GROUP BY interval", - tvar: &chronograf.GroupByVar{ - Resolution: 700, - ReportingInterval: 10 * time.Second, - Duration: 24 * time.Hour, - }, - want: "time(370s)", + name: "String() calculates the GROUP BY interval", + resolution: 700, + pixelsPerPoint: 3, + duration: 24 * time.Hour, + want: "time(370s)", }, { - name: "String() milliseconds if less than one second intervals", - tvar: &chronograf.GroupByVar{ - Resolution: 100000, - ReportingInterval: 10 * time.Second, - Duration: time.Hour, - }, - want: "time(107ms)", + name: "String() milliseconds if less than one second intervals", + resolution: 100000, + pixelsPerPoint: 3, + duration: time.Hour, + want: "time(107ms)", }, { - name: "String() milliseconds if less than one millisecond", - tvar: &chronograf.GroupByVar{ - Resolution: 100000, - ReportingInterval: 10 * time.Second, - Duration: time.Second, - }, - want: "time(1ms)", + name: "String() milliseconds if less than one millisecond", + resolution: 100000, + pixelsPerPoint: 3, + duration: time.Second, + want: "time(1ms)", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := tt.tvar.String() + got := AutoGroupBy(tt.resolution, tt.pixelsPerPoint, tt.duration) if got != tt.want { - t.Errorf("TestGroupByVarString %s =\n%s\nwant\n%s", tt.name, got, tt.want) + t.Errorf("TestAutoGroupBy %s =\n%s\nwant\n%s", tt.name, got, tt.want) } }) } } + +func Test_RenderTemplate(t *testing.T) { + gbvTests := []struct { + name string + query string + want string + resolution uint // the screen resolution to render queries into + }{ + { + name: "relative time only lower bound with one day of duration", + query: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d GROUP BY :interval:", + resolution: 1000, + want: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d GROUP BY time(259s)", + }, + { + name: "relative time offset by week", + query: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d - 7d AND time < now() - 7d GROUP BY :interval:", + resolution: 1000, + want: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d - 7d AND time < now() - 7d GROUP BY time(259s)", + }, + { + name: "relative time with relative upper bound with one minute of duration", + query: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 3m AND time < now() - 2m GROUP BY :interval:", + resolution: 1000, + want: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 3m AND time < now() - 2m GROUP BY time(179ms)", + }, + { + name: "relative time with relative lower bound and now upper with one day of duration", + query: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d AND time < now() GROUP BY :interval:", + resolution: 1000, + want: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d AND time < now() GROUP BY time(259s)", + }, + { + name: "absolute time with one minute of duration", + query: "SELECT mean(usage_idle) FROM cpu WHERE time > '1985-10-25T00:01:00Z' and time < '1985-10-25T00:02:00Z' GROUP BY :interval:", + resolution: 1000, + want: "SELECT mean(usage_idle) FROM cpu WHERE time > '1985-10-25T00:01:00Z' and time < '1985-10-25T00:02:00Z' GROUP BY time(179ms)", + }, + { + name: "absolute time with nano seconds and zero duraiton", + query: "SELECT mean(usage_idle) FROM cpu WHERE time > '2017-07-24T15:33:42.994Z' and time < '2017-07-24T15:33:42.994Z' GROUP BY :interval:", + resolution: 1000, + want: "SELECT mean(usage_idle) FROM cpu WHERE time > '2017-07-24T15:33:42.994Z' and time < '2017-07-24T15:33:42.994Z' GROUP BY time(1ms)", + }, + } + + for _, tt := range gbvTests { + t.Run(tt.name, func(t *testing.T) { + now, err := time.Parse(time.RFC3339, "1985-10-25T00:01:00Z") + if err != nil { + t.Fatal(err) + } + tvar := chronograf.TemplateVar{ + Var: ":interval:", + Values: []chronograf.TemplateValue{ + { + Value: fmt.Sprintf("%d", tt.resolution), + Type: "resolution", + }, + }, + } + + got, err := RenderTemplate(tt.query, tvar, now) + if err != nil { + t.Fatalf("unexpected error rendering template %v", err) + } + + if got != tt.want { + t.Fatalf("%q - durations not equal! Want: %s, Got: %s", tt.name, tt.want, got) + } + }) + } +} + +// SELECT mean("numSeries") AS "mean_numSeries" FROM "_internal"."monitor"."database" WHERE time > now() - 1h GROUP BY :interval: FILL(null);SELECT mean("numSeries") AS "mean_numSeries_shifted__1__h" FROM "_internal"."monitor"."database" WHERE time > now() - 1h - 1h AND time < now() - 1h GROUP BY :interval: FILL(null) diff --git a/server/queries.go b/server/queries.go index 16b2c5a7f..76e5ad4b5 100644 --- a/server/queries.go +++ b/server/queries.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net/http" + "time" "golang.org/x/net/context" @@ -21,8 +22,8 @@ type QueryRequest struct { // QueriesRequest converts all queries to queryConfigs with the help // of the template variables type QueriesRequest struct { - Queries []QueryRequest `json:"queries"` - TemplateVars chronograf.TemplateVars `json:"tempVars,omitempty"` + Queries []QueryRequest `json:"queries"` + TemplateVars []chronograf.TemplateVar `json:"tempVars,omitempty"` } // QueryResponse is the return result of a QueryRequest including @@ -33,7 +34,7 @@ type QueryResponse struct { QueryConfig chronograf.QueryConfig `json:"queryConfig"` QueryAST *queries.SelectStatement `json:"queryAST,omitempty"` QueryTemplated *string `json:"queryTemplated,omitempty"` - TemplateVars chronograf.TemplateVars `json:"tempVars,omitempty"` + TemplateVars []chronograf.TemplateVar `json:"tempVars,omitempty"` } // QueriesResponse is the response for a QueriesRequest @@ -72,7 +73,12 @@ func (s *Service) Queries(w http.ResponseWriter, r *http.Request) { Query: q.Query, } - query := influx.TemplateReplace(q.Query, req.TemplateVars) + query, err := influx.TemplateReplace(q.Query, req.TemplateVars, time.Now()) + if err != nil { + Error(w, http.StatusBadRequest, err.Error(), s.Logger) + return + } + qc := ToQueryConfig(query) if err := s.DefaultRP(ctx, &qc, &src); err != nil { Error(w, http.StatusBadRequest, err.Error(), s.Logger) diff --git a/server/queries_test.go b/server/queries_test.go index 3e409779f..83967a7e7 100644 --- a/server/queries_test.go +++ b/server/queries_test.go @@ -98,7 +98,7 @@ func TestService_Queries(t *testing.T) { r: httptest.NewRequest("POST", "/queries", bytes.NewReader([]byte(`{ "queries": [ { - "query": "SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time > now() - 1m", + "query": "SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time > :dashboardTime: AND time < :upperDashboardTime: GROUP BY :interval:", "id": "82b60d37-251e-4afe-ac93-ca20a3642b11" } ], @@ -153,13 +153,20 @@ func TestService_Queries(t *testing.T) { "id": "interval", "type": "constant", "tempVar": ":interval:", - "resolution": 1000, - "reportingInterval": 10000000000, - "values": [] + "values": [ + { + "value": "1000", + "type": "resolution" + }, + { + "value": "3", + "type": "pointsPerPixel" + } + ] } ] }`))), - want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e now() - 1m","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"_internal","measurement":"httpd","retentionPolicy":"monitor","fields":[{"value":"pingReq","type":"field","alias":""}],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e now() - 1m","range":{"upper":"","lower":"now() - 1m"}},"queryAST":{"condition":{"expr":"binary","op":"\u003e","lhs":{"expr":"reference","val":"time"},"rhs":{"expr":"binary","op":"-","lhs":{"expr":"call","name":"now"},"rhs":{"expr":"literal","val":"1m","type":"duration"}}},"fields":[{"column":{"expr":"reference","val":"pingReq"}}],"sources":[{"database":"_internal","retentionPolicy":"monitor","name":"httpd","type":"measurement"}]},"queryTemplated":"SELECT \"pingReq\" FROM \"_internal\".\"monitor\".\"httpd\" WHERE time \u003e now() - 1m","tempVars":[{"tempVar":":dbs:","values":[{"value":"_internal","type":"database","selected":true}]},{"tempVar":":dashboardTime:","values":[{"value":"now() - 15m","type":"constant","selected":true}]},{"tempVar":":upperDashboardTime:","values":[{"value":"now()","type":"constant","selected":true}]},{"tempVar":":interval:","duration":60000000000,"resolution":1000,"reportingInterval":10000000000}]}]} + want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e :dashboardTime: AND time \u003c :upperDashboardTime: GROUP BY :interval:","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"","measurement":"","retentionPolicy":"","fields":[],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e :dashboardTime: AND time \u003c :upperDashboardTime: GROUP BY :interval:","range":null},"queryTemplated":"SELECT \"pingReq\" FROM \"_internal\".\"monitor\".\"httpd\" WHERE time \u003e now() - 15m AND time \u003c now() GROUP BY time(2s)","tempVars":[{"tempVar":":upperDashboardTime:","values":[{"value":"now()","type":"constant","selected":true}]},{"tempVar":":dashboardTime:","values":[{"value":"now() - 15m","type":"constant","selected":true}]},{"tempVar":":dbs:","values":[{"value":"_internal","type":"database","selected":true}]},{"tempVar":":interval:","values":[{"value":"1000","type":"resolution","selected":false},{"value":"3","type":"pointsPerPixel","selected":false}]}]}]} `, }, } diff --git a/server/templates_test.go b/server/templates_test.go index afd220afe..8a9bec46f 100644 --- a/server/templates_test.go +++ b/server/templates_test.go @@ -16,8 +16,8 @@ func TestValidTemplateRequest(t *testing.T) { name: "Valid Template", template: &chronograf.Template{ Type: "fieldKeys", - BasicTemplateVar: chronograf.BasicTemplateVar{ - Values: []chronograf.BasicTemplateValue{ + TemplateVar: chronograf.TemplateVar{ + Values: []chronograf.TemplateValue{ { Type: "fieldKey", }, @@ -30,8 +30,8 @@ func TestValidTemplateRequest(t *testing.T) { wantErr: true, template: &chronograf.Template{ Type: "Unknown Type", - BasicTemplateVar: chronograf.BasicTemplateVar{ - Values: []chronograf.BasicTemplateValue{ + TemplateVar: chronograf.TemplateVar{ + Values: []chronograf.TemplateValue{ { Type: "fieldKey", }, @@ -44,8 +44,8 @@ func TestValidTemplateRequest(t *testing.T) { wantErr: true, template: &chronograf.Template{ Type: "csv", - BasicTemplateVar: chronograf.BasicTemplateVar{ - Values: []chronograf.BasicTemplateValue{ + TemplateVar: chronograf.TemplateVar{ + Values: []chronograf.TemplateValue{ { Type: "unknown value", }, From 4180402e237531fbddb0f81763fae93423e2b910 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 8 Nov 2017 14:26:37 -0800 Subject: [PATCH 023/138] Update frontend to handle new :interval: shape --- ui/src/dashboards/containers/DashboardPage.js | 19 +++++++++++---- ui/src/shared/components/AutoRefresh.js | 23 +++++++++++++++---- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/ui/src/dashboards/containers/DashboardPage.js b/ui/src/dashboards/containers/DashboardPage.js index 6094b63ca..b28988985 100644 --- a/ui/src/dashboards/containers/DashboardPage.js +++ b/ui/src/dashboards/containers/DashboardPage.js @@ -263,14 +263,23 @@ class DashboardPage extends Component { ], } - // this controls the auto group by behavior const interval = { id: 'interval', - type: 'constant', + type: 'autoGroupBy', tempVar: ':interval:', - resolution: 1000, - reportingInterval: 10000000000, - values: [], + label: 'automatically determine the best group by time', + values: [ + { + value: '1000', // pixels + type: 'resolution', + selected: true, + }, + { + value: '3', + type: 'pointsPerPixel', + selected: true, + }, + ], } let templatesIncludingDashTime diff --git a/ui/src/shared/components/AutoRefresh.js b/ui/src/shared/components/AutoRefresh.js index 1c430091e..003f1c157 100644 --- a/ui/src/shared/components/AutoRefresh.js +++ b/ui/src/shared/components/AutoRefresh.js @@ -81,20 +81,35 @@ const AutoRefresh = ComposedComponent => { const templatesWithResolution = templates.map(temp => { if (temp.tempVar === ':interval:') { if (resolution) { - return {...temp, resolution} + return { + ...temp, + values: temp.values.map( + v => (temp.type === 'resolution' ? {...v, resolution} : v) + ), + } + } + + return { + ...temp, + values: [ + ...temp.values, + {value: '1000', type: 'resolution', selected: true}, + ], } - return {...temp, resolution: 1000} } - return {...temp} + + return temp }) + const tempVars = removeUnselectedTemplateValues(templatesWithResolution) + return fetchTimeSeriesAsync( { source: host, db: database, rp, query, - tempVars: removeUnselectedTemplateValues(templatesWithResolution), + tempVars, resolution, }, editQueryStatus From 855be3d412faeedac5e493293f7bef8fc5fa1bbd Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Wed, 8 Nov 2017 16:00:24 -0700 Subject: [PATCH 024/138] Address some issues in PR. --- ui/src/kapacitor/apis/index.js | 12 + ui/src/kapacitor/components/LogsTable.js | 6 +- ui/src/kapacitor/constants/dummyLogs.js | 1323 ----------------- ui/src/kapacitor/containers/TickscriptPage.js | 34 +- ui/src/shared/components/ResizeContainer.js | 8 +- ui/src/shared/components/ResizeHandle.js | 6 +- 6 files changed, 37 insertions(+), 1352 deletions(-) delete mode 100644 ui/src/kapacitor/constants/dummyLogs.js diff --git a/ui/src/kapacitor/apis/index.js b/ui/src/kapacitor/apis/index.js index d24847f27..7b218c03b 100644 --- a/ui/src/kapacitor/apis/index.js +++ b/ui/src/kapacitor/apis/index.js @@ -100,3 +100,15 @@ export const updateTask = async ( throw error } } + +export const getLogStream = kapacitor => + fetch(`${kapacitor.links.proxy}?path=/kapacitor/v1/logs`, { + method: 'GET', + headers: {'Content-Type': 'application/json'}, + }) + +export const getLogStreamByRuleID = (kapacitor, ruleID) => + fetch(`${kapacitor.links.proxy}?path=/kapacitor/v1/logs?task=${ruleID}`, { + method: 'GET', + headers: {'Content-Type': 'application/json'}, + }) diff --git a/ui/src/kapacitor/components/LogsTable.js b/ui/src/kapacitor/components/LogsTable.js index 0334afb00..b16de6f40 100644 --- a/ui/src/kapacitor/components/LogsTable.js +++ b/ui/src/kapacitor/components/LogsTable.js @@ -20,7 +20,7 @@ class LogsTable extends Component { return objElements } - renderTableRow = (logItem, i) => { + renderTableRow = logItem => { let rowDetails if (logItem.service === 'sessions') { @@ -111,7 +111,7 @@ class LogsTable extends Component { } return ( -
+
@@ -140,7 +140,7 @@ class LogsTable extends Component {
- {logs.map((l, i) => this.renderTableRow(l, i))} + {logs.map(l => this.renderTableRow(l))}
diff --git a/ui/src/kapacitor/constants/dummyLogs.js b/ui/src/kapacitor/constants/dummyLogs.js deleted file mode 100644 index a94d67b3f..000000000 --- a/ui/src/kapacitor/constants/dummyLogs.js +++ /dev/null @@ -1,1323 +0,0 @@ -export const DUMMY_LOGS = [ - { - ts: '2017-10-16T15:36:31.329-04:00', - lvl: 'info', - msg: 'created log session', - service: 'sessions', - id: 'aa87c0b6-26d0-484a-8f15-7445c2d5f386', - 'content-type': 'application/json', - tags: 'nil', - }, - { - ts: '2017-10-16T15:36:31.329-04:00', - lvl: 'error', - msg: '2017/10/16 15:36:31 http: multiple response.WriteHeader calls\n', - service: 'httpd_server_errors', - }, - { - ts: '2017-10-16T15:36:32.090-04:00', - lvl: 'debug', - msg: 'starting next batch query', - service: 'kapacitor', - task_master: 'main', - task: 'batch', - node: 'query1', - query: - "SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= '2017-10-16T19:35:32.089928906Z' AND time < '2017-10-16T19:36:32.089928906Z'", - }, - { - ts: '2017-10-16T15:36:32.241-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-16T15:36:32.241436147-04:00', - method: 'POST', - uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - 'request-id': '502cb71e-b2a9-11e7-8186-000000000000', - duration: '59.135µs', - }, - { - ts: '2017-10-16T15:36:35.313-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-16T15:36:35.312244484-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - 'request-id': '52014892-b2a9-11e7-8187-000000000000', - duration: '1.468473ms', - }, - { - ts: '2017-10-16T15:36:35.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - load15: 1.71, - n_users: 8, - load5: 1.78, - n_cpus: 8, - load1: 1.74, - }, - time: '2017-10-16T19:36:35Z', - }, - { - ts: '2017-10-16T15:36:35.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - uptime_format: '11 days, 2:08', - uptime: 958109, - }, - time: '2017-10-16T19:36:35Z', - }, - { - ts: '2017-10-16T15:36:36.664-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-16T15:36:36.663967463-04:00', - method: 'POST', - uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - 'request-id': '52cf8a43-b2a9-11e7-8188-000000000000', - duration: '624.155µs', - }, - { - ts: '2017-10-16T15:36:40.313-04:00', - lvl: 'warn', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-16T15:36:40.312324709-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - 'request-id': '54fc3c33-b2a9-11e7-8189-000000000000', - duration: '1.0192ms', - }, - { - ts: '2017-10-16T15:36:40.313-04:00', - lvl: 'ok', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - host: 'Michaels-MBP-2.router.edm', - cluster_id: 'michaels-example-cluster', - }, - field: { - load15: 1.71, - n_cpus: 8, - load1: 1.68, - load5: 1.77, - n_users: 8, - }, - time: '2017-10-16T19:36:40Z', - }, - - { - ts: '2017-10-17T14:41:20.882-04:00', - lvl: 'info', - msg: 'created log session', - service: 'sessions', - id: 'fe0be004-4417-4889-a6be-fce3bc56b5f8', - 'content-type': 'application/json', - tags: 'nil', - }, - { - ts: '2017-10-17T14:41:20.882-04:00', - lvl: 'error', - msg: '2017/10/17 14:41:20 http: multiple response.WriteHeader calls\n', - service: 'httpd_server_errors', - }, - { - ts: '2017-10-17T14:41:25.125-04:00', - lvl: 'debug', - msg: 'starting next batch query', - service: 'kapacitor', - task_master: 'main', - task: 'batch', - node: 'query1', - query: - 'SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= ‘2017-10-17T18:40:25.125253198Z’ AND time < ‘2017-10-17T18:41:25.125253198Z’', - }, - { - ts: '2017-10-17T14:41:25.285-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:41:25.285657166-04:00', - method: 'POST', - uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'c77d0f01-b36a-11e7-966d-000000000000', - duration: '81.785µs', - }, - { - ts: '2017-10-17T14:41:25.312-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:41:25.310586775-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'c780dcd5-b36a-11e7-966e-000000000000', - duration: '1.898055ms', - }, - { - ts: '2017-10-17T14:41:25.312-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - n_users: 8, - load1: 1.95, - load5: 2.22, - load15: 2.04, - n_cpus: 8, - }, - time: '2017-10-17T18:41:25Z', - }, - { - ts: '2017-10-17T14:41:25.312-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - uptime: 1041199, - uptime_format: '12 days, 1:13', - }, - time: '2017-10-17T18:41:25Z', - }, - { - ts: '2017-10-17T14:41:25.314-04:00', - lvl: 'error', - msg: 'failed to POST data', - service: 'kapacitor', - task_master: 'main', - task: 'httppost', - node: 'http_post3', - err: - 'Post http://localhost:8080/example: dial tcp [::1]:8080: getsockopt: connection refused', - }, - { - ts: '2017-10-17T14:41:30.313-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:41:30.312728774-04:00', - method: 'POST', - uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'ca7c2100-b36a-11e7-966f-000000000000', - duration: '919.036µs', - }, - { - ts: '2017-10-17T14:41:30.314-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:41:30.312814501-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'ca7c2454-b36a-11e7-9670-000000000000', - duration: '1.237587ms', - }, - { - ts: '2017-10-17T14:41:30.314-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - n_cpus: 8, - load1: 2.19, - n_users: 8, - load5: 2.26, - load15: 2.05, - }, - time: '2017-10-17T18:41:30Z', - }, - { - ts: '2017-10-17T14:41:30.314-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - uptime: 1041204, - uptime_format: '12 days, 1:13', - }, - time: '2017-10-17T18:41:30Z', - }, - { - ts: '2017-10-17T14:41:35.126-04:00', - lvl: 'debug', - msg: 'starting next batch query', - service: 'kapacitor', - task_master: 'main', - task: 'batch', - node: 'query1', - query: - 'SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= ‘2017-10-17T18:40:35.125820959Z’ AND time < ‘2017-10-17T18:41:35.125820959Z’', - }, - { - ts: '2017-10-17T14:41:35.284-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:41:35.284066121-04:00', - method: 'POST', - uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'cd72b1da-b36a-11e7-9671-000000000000', - duration: '56.943µs', - }, - { - ts: '2017-10-17T14:41:35.310-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:41:35.309032877-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'cd76811f-b36a-11e7-9672-000000000000', - duration: '1.262848ms', - }, - { - ts: '2017-10-17T14:41:35.310-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - n_cpus: 8, - load1: 2.1, - load15: 2.05, - n_users: 8, - load5: 2.24, - }, - time: '2017-10-17T18:41:35Z', - }, - { - ts: '2017-10-17T14:41:35.310-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - uptime: 1041209, - uptime_format: '12 days, 1:13', - }, - time: '2017-10-17T18:41:35Z', - }, - { - ts: '2017-10-17T14:41:35.312-04:00', - lvl: 'error', - msg: 'failed to POST data', - service: 'kapacitor', - task_master: 'main', - task: 'httppost', - node: 'http_post3', - err: - 'Post http://localhost:8080/example: dial tcp [::1]:8080: getsockopt: connection refused', - }, - { - ts: '2017-10-17T14:41:40.311-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:41:40.310966717-04:00', - method: 'POST', - uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'd071bd27-b36a-11e7-9673-000000000000', - duration: '625.873µs', - }, - { - ts: '2017-10-17T14:41:40.313-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:41:40.312029958-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'd071e6b0-b36a-11e7-9674-000000000000', - duration: '1.021583ms', - }, - { - ts: '2017-10-17T14:41:40.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - load1: 2.01, - load15: 2.04, - n_users: 8, - load5: 2.22, - n_cpus: 8, - }, - time: '2017-10-17T18:41:40Z', - }, - { - ts: '2017-10-17T14:41:40.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - uptime_format: '12 days, 1:13', - uptime: 1041214, - }, - time: '2017-10-17T18:41:40Z', - }, - { - ts: '2017-10-17T14:41:45.029-04:00', - lvl: 'debug', - msg: 'linking subscription for cluster', - service: 'influxdb', - cluster: 'default', - }, - { - ts: '2017-10-17T14:41:45.125-04:00', - lvl: 'debug', - msg: 'starting next batch query', - service: 'kapacitor', - task_master: 'main', - task: 'batch', - node: 'query1', - query: - 'SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= ‘2017-10-17T18:40:45.125467573Z’ AND time < ‘2017-10-17T18:41:45.125467573Z’', - }, - { - ts: '2017-10-17T14:41:45.284-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:41:45.284222418-04:00', - method: 'POST', - uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'd36898f5-b36a-11e7-9675-000000000000', - duration: '83.559µs', - }, - { - ts: '2017-10-17T14:41:45.310-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:41:45.309477008-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'd36c7378-b36a-11e7-9676-000000000000', - duration: '1.420439ms', - }, - { - ts: '2017-10-17T14:41:45.311-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - load1: 2.09, - load5: 2.23, - load15: 2.05, - n_users: 8, - n_cpus: 8, - }, - time: '2017-10-17T18:41:45Z', - }, - { - ts: '2017-10-17T14:41:45.311-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - uptime_format: '12 days, 1:13', - uptime: 1041219, - }, - time: '2017-10-17T18:41:45Z', - }, - { - ts: '2017-10-17T14:41:45.312-04:00', - lvl: 'error', - msg: 'failed to POST data', - service: 'kapacitor', - task_master: 'main', - task: 'httppost', - node: 'http_post3', - err: - 'Post http://localhost:8080/example: dial tcp [::1]:8080: getsockopt: connection refused', - }, - { - ts: '2017-10-17T14:41:50.313-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:41:50.312188999-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'd667cde7-b36a-11e7-9677-000000000000', - duration: '1.16719ms', - }, - { - ts: '2017-10-17T14:41:50.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - n_users: 8, - n_cpus: 8, - load1: 2, - load5: 2.21, - load15: 2.04, - }, - time: '2017-10-17T18:41:50Z', - }, - { - ts: '2017-10-17T14:41:50.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - uptime: 1041224, - uptime_format: '12 days, 1:13', - }, - time: '2017-10-17T18:41:50Z', - }, - { - ts: '2017-10-17T14:41:50.314-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:41:50.313483319-04:00', - method: 'POST', - uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'd6680077-b36a-11e7-9678-000000000000', - duration: '920µs', - }, - { - ts: '2017-10-17T14:41:55.124-04:00', - lvl: 'debug', - msg: 'starting next batch query', - service: 'kapacitor', - task_master: 'main', - task: 'batch', - node: 'query1', - query: - 'SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= ‘2017-10-17T18:40:55.124375998Z’ AND time < ‘2017-10-17T18:41:55.124375998Z’', - }, - { - ts: '2017-10-17T14:41:55.286-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:41:55.286598214-04:00', - method: 'POST', - uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'd95ed6c3-b36a-11e7-9679-000000000000', - duration: '74.558µs', - }, - { - ts: '2017-10-17T14:41:55.313-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:41:55.312049056-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'd962b8f0-b36a-11e7-967a-000000000000', - duration: '1.436052ms', - }, - { - ts: '2017-10-17T14:41:55.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - load5: 2.19, - load15: 2.03, - n_cpus: 8, - load1: 1.92, - n_users: 8, - }, - time: '2017-10-17T18:41:55Z', - }, - { - ts: '2017-10-17T14:41:55.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - uptime: 1041229, - uptime_format: '12 days, 1:13', - }, - time: '2017-10-17T18:41:55Z', - }, - { - ts: '2017-10-17T14:41:55.315-04:00', - lvl: 'error', - msg: 'failed to POST data', - service: 'kapacitor', - task_master: 'main', - task: 'httppost', - node: 'http_post3', - err: - 'Post http://localhost:8080/example: dial tcp [::1]:8080: getsockopt: connection refused', - }, - { - ts: '2017-10-17T14:42:00.307-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:42:00.307216928-04:00', - method: 'POST', - uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'dc5cecae-b36a-11e7-967b-000000000000', - duration: '677.414µs', - }, - { - ts: '2017-10-17T14:42:00.313-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:42:00.31193199-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'dc5da4db-b36a-11e7-967c-000000000000', - duration: '1.114033ms', - }, - { - ts: '2017-10-17T14:42:00.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - n_cpus: 8, - load1: 1.85, - load5: 2.17, - n_users: 8, - load15: 2.03, - }, - time: '2017-10-17T18:42:00Z', - }, - { - ts: '2017-10-17T14:42:00.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - uptime: 1041234, - uptime_format: '12 days, 1:13', - }, - time: '2017-10-17T18:42:00Z', - }, - { - ts: '2017-10-17T14:42:05.124-04:00', - lvl: 'debug', - msg: 'starting next batch query', - service: 'kapacitor', - task_master: 'main', - task: 'batch', - node: 'query1', - query: - 'SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= ‘2017-10-17T18:41:05.124414633Z’ AND time < ‘2017-10-17T18:42:05.124414633Z’', - }, - { - ts: '2017-10-17T14:42:05.283-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:42:05.283540824-04:00', - method: 'POST', - uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'df544055-b36a-11e7-967d-000000000000', - duration: '75.496µs', - }, - { - ts: '2017-10-17T14:42:05.312-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:42:05.311360104-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'df587f07-b36a-11e7-967e-000000000000', - duration: '1.255932ms', - }, - { - ts: '2017-10-17T14:42:05.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - host: 'Michaels-MBP-2.router.edm', - cluster_id: 'michaels-example-cluster', - }, - field: { - load1: 1.86, - load15: 2.03, - n_users: 8, - load5: 2.17, - n_cpus: 8, - }, - time: '2017-10-17T18:42:05Z', - }, - { - ts: '2017-10-17T14:42:05.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - uptime: 1041239, - uptime_format: '12 days, 1:13', - }, - time: '2017-10-17T18:42:05Z', - }, - { - ts: '2017-10-17T14:42:05.315-04:00', - lvl: 'error', - msg: 'failed to POST data', - service: 'kapacitor', - task_master: 'main', - task: 'httppost', - node: 'http_post3', - err: - 'Post http://localhost:8080/example: dial tcp [::1]:8080: getsockopt: connection refused', - }, - { - ts: '2017-10-17T14:42:10.313-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:42:10.31170993-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'e2537d30-b36a-11e7-967f-000000000000', - duration: '1.304237ms', - }, - { - ts: '2017-10-17T14:42:10.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - load1: 1.87, - load5: 2.17, - load15: 2.03, - n_users: 8, - n_cpus: 8, - }, - time: '2017-10-17T18:42:10Z', - }, - { - ts: '2017-10-17T14:42:10.313-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - uptime_format: '12 days, 1:14', - uptime: 1041244, - }, - time: '2017-10-17T18:42:10Z', - }, - { - ts: '2017-10-17T14:42:10.314-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:42:10.313200647-04:00', - method: 'POST', - uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'e253b76b-b36a-11e7-9680-000000000000', - duration: '1.030371ms', - }, - { - ts: '2017-10-17T14:42:15.122-04:00', - lvl: 'debug', - msg: 'starting next batch query', - service: 'kapacitor', - task_master: 'main', - task: 'batch', - node: 'query1', - query: - 'SELECT mean(usage_user) FROM telegraf.autogen.cpu WHERE time >= ‘2017-10-17T18:41:15.12194903Z’ AND time < ‘2017-10-17T18:42:15.12194903Z’', - }, - { - ts: '2017-10-17T14:42:15.282-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:42:15.282315534-04:00', - method: 'POST', - uri: '/write?consistency=&db=mydb&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'e549f17a-b36a-11e7-9681-000000000000', - duration: '94.007µs', - }, - { - ts: '2017-10-17T14:42:15.312-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:42:15.310605432-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'e54e428d-b36a-11e7-9682-000000000000', - duration: '1.596389ms', - }, - { - ts: '2017-10-17T14:42:15.312-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - n_cpus: 8, - load1: 2.04, - load5: 2.2, - load15: 2.04, - n_users: 8, - }, - time: '2017-10-17T18:42:15Z', - }, - { - ts: '2017-10-17T14:42:15.312-04:00', - lvl: 'info', - msg: 'point', - service: 'kapacitor', - task_master: 'main', - task: 'log', - node: 'log2', - prefix: '', - name: 'system', - db: 'telegraf', - rp: 'autogen', - group: 'cluster_id=michaels-example-cluster,host=Michaels-MBP-2.router.edm', - dimension_0: 'cluster_id', - dimension_1: 'host', - tag: { - cluster_id: 'michaels-example-cluster', - host: 'Michaels-MBP-2.router.edm', - }, - field: { - uptime: 1041249, - uptime_format: '12 days, 1:14', - }, - time: '2017-10-17T18:42:15Z', - }, - { - ts: '2017-10-17T14:42:15.314-04:00', - lvl: 'error', - msg: 'failed to POST data', - service: 'kapacitor', - task_master: 'main', - task: 'httppost', - node: 'http_post3', - err: - 'Post http://localhost:8080/example: dial tcp [::1]:8080: getsockopt: connection refused', - }, - { - ts: '2017-10-17T14:42:20.311-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:42:20.311150281-04:00', - method: 'POST', - uri: '/write?consistency=&db=_internal&precision=ns&rp=monitor', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'e8494853-b36a-11e7-9683-000000000000', - duration: '711.257µs', - }, - { - ts: '2017-10-17T14:42:20.313-04:00', - lvl: 'info', - msg: 'http request', - service: 'http', - host: '::1', - username: '-', - start: '2017-10-17T14:42:20.312349859-04:00', - method: 'POST', - uri: '/write?consistency=&db=telegraf&precision=ns&rp=autogen', - protocol: 'HTTP/1.1', - status: 204, - referer: '-', - user_agent: 'InfluxDBClient', - request_id: 'e849772e-b36a-11e7-9684-000000000000', - duration: '1.079043ms', - }, -] diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index bbafe7f1b..07d638620 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -1,11 +1,14 @@ import React, {PropTypes, Component} from 'react' import {connect} from 'react-redux' import {bindActionCreators} from 'redux' +import uuid from 'node-uuid' 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 {getLogStreamByRuleID} from 'src/kapacitor/apis' +import {publishNotification} from 'shared/actions/notifications' class TickscriptPage extends Component { constructor(props) { @@ -29,26 +32,16 @@ class TickscriptPage extends Component { shouldFetch = null - logKey = j => (log, i) => ({ - ...log, - key: `${log.ts}-${j}-${i}`, - }) - fetchChunkedLogs = async (kapacitor, ruleID) => { + const {notify} = this.props + try { - const response = await fetch( - `${kapacitor.links.proxy}?path=/kapacitor/v1/logs?task=${ruleID}`, - { - method: 'GET', - headers: {'Content-Type': 'application/json'}, - } - ) + const response = await getLogStreamByRuleID(kapacitor, ruleID) const reader = await response.body.getReader() const decoder = new TextDecoder() let result - let j = 0 while (this.shouldFetch === true && !(result && result.done)) { result = await reader.read() @@ -57,20 +50,21 @@ class TickscriptPage extends Component { stream: !result.done, }) - const json = `[${chunk.split('}{').join('},{')}]` + const json = `[${chunk.split('}\n{').join('},{')}]` - const logs = JSON.parse(json).map(this.logKey(j)) + const logs = JSON.parse(json).map(log => ({ + ...log, + key: uuid.v4(), + })) this.setState({ - logs: [...this.state.logs, ...logs], + logs: [...logs, ...this.state.logs], }) - - j += 1 } } catch (error) { console.error(error) + notify('error', error) throw error - // TODO error handling } } @@ -205,6 +199,7 @@ TickscriptPage.propTypes = { ruleID: string, }).isRequired, rules: arrayOf(shape()), + notify: func.isRequired, } const mapStateToProps = state => { @@ -216,6 +211,7 @@ const mapStateToProps = state => { const mapDispatchToProps = dispatch => ({ kapacitorActions: bindActionCreators(kapactiorActionCreators, dispatch), errorActions: bindActionCreators(errorActionCreators, dispatch), + notify: bindActionCreators(publishNotification, dispatch), }) export default connect(mapStateToProps, mapDispatchToProps)(TickscriptPage) diff --git a/ui/src/shared/components/ResizeContainer.js b/ui/src/shared/components/ResizeContainer.js index 07fd59417..a67a40374 100644 --- a/ui/src/shared/components/ResizeContainer.js +++ b/ui/src/shared/components/ResizeContainer.js @@ -97,7 +97,7 @@ class ResizeContainer extends Component { render() { const {bottomHeightPixels, topHeight, bottomHeight, isDragging} = this.state - const {containerClass, children, isKapacitorTheme} = this.props + const {containerClass, children, theme} = this.props if (React.Children.count(children) > maximumNumChildren) { console.error( @@ -122,7 +122,7 @@ class ResizeContainer extends Component { })}
Date: Wed, 8 Nov 2017 11:27:35 -0600 Subject: [PATCH 025/138] Refactor and simplify auto group by (:interval:) template variable --- bolt/internal/internal.go | 11 +- chronograf.go | 272 ++---------------------------- chronograf_test.go | 63 ------- influx/influx.go | 6 +- influx/influx_test.go | 10 +- influx/query.go | 46 ++++++ influx/query_test.go | 41 +++++ influx/templates.go | 122 ++++++++++---- influx/templates_test.go | 336 ++++++++++++++++++++++++++------------ server/queries.go | 14 +- server/queries_test.go | 17 +- server/templates_test.go | 12 +- 12 files changed, 467 insertions(+), 483 deletions(-) delete mode 100644 chronograf_test.go diff --git a/bolt/internal/internal.go b/bolt/internal/internal.go index 8d79dde74..5bb729ecf 100644 --- a/bolt/internal/internal.go +++ b/bolt/internal/internal.go @@ -330,9 +330,9 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error { templates := make([]chronograf.Template, len(pb.Templates)) for i, t := range pb.Templates { - vals := make([]chronograf.BasicTemplateValue, len(t.Values)) + vals := make([]chronograf.TemplateValue, len(t.Values)) for j, v := range t.Values { - vals[j] = chronograf.BasicTemplateValue{ + vals[j] = chronograf.TemplateValue{ Selected: v.Selected, Type: v.Type, Value: v.Value, @@ -341,7 +341,7 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error { template := chronograf.Template{ ID: chronograf.TemplateID(t.ID), - BasicTemplateVar: chronograf.BasicTemplateVar{ + TemplateVar: chronograf.TemplateVar{ Var: t.TempVar, Values: vals, }, @@ -434,8 +434,5 @@ func UnmarshalUser(data []byte, u *chronograf.User) error { // UnmarshalUserPB decodes a user from binary protobuf data. // We are ignoring the password for now. func UnmarshalUserPB(data []byte, u *User) error { - if err := proto.Unmarshal(data, u); err != nil { - return err - } - return nil + return proto.Unmarshal(data, u) } diff --git a/chronograf.go b/chronograf.go index f5876c163..b6e314742 100644 --- a/chronograf.go +++ b/chronograf.go @@ -1,21 +1,10 @@ package chronograf import ( - "bytes" "context" - "encoding/json" - "errors" - "fmt" "io" "net/http" - "regexp" - "strconv" - "strings" "time" - "unicode" - "unicode/utf8" - - "github.com/influxdata/influxdb/influxql" ) // General errors. @@ -136,196 +125,17 @@ type Range struct { Lower int64 `json:"lower"` // Lower is the lower bound } -type TemplateVariable interface { - fmt.Stringer - Name() string // returns the variable name - Precedence() uint // ordinal indicating precedence level for replacement -} - -type ExecutableVar interface { - Exec(string) -} - // TemplateValue is a value use to replace a template in an InfluxQL query -type BasicTemplateValue struct { +type TemplateValue struct { Value string `json:"value"` // Value is the specific value used to replace a template in an InfluxQL query Type string `json:"type"` // Type can be tagKey, tagValue, fieldKey, csv, measurement, database, constant Selected bool `json:"selected"` // Selected states that this variable has been picked to use for replacement } // TemplateVar is a named variable within an InfluxQL query to be replaced with Values -type BasicTemplateVar struct { - Var string `json:"tempVar"` // Var is the string to replace within InfluxQL - Values []BasicTemplateValue `json:"values"` // Values are the replacement values within InfluxQL -} - -func (t BasicTemplateVar) Name() string { - return t.Var -} - -// String converts the template variable into a correct InfluxQL string based -// on its type -func (t BasicTemplateVar) String() string { - if len(t.Values) == 0 { - return "" - } - switch t.Values[0].Type { - case "tagKey", "fieldKey", "measurement", "database": - return `"` + t.Values[0].Value + `"` - case "tagValue", "timeStamp": - return `'` + t.Values[0].Value + `'` - case "csv", "constant": - return t.Values[0].Value - default: - return "" - } -} - -func (t BasicTemplateVar) Precedence() uint { - return 0 -} - -type GroupByVar struct { - Var string `json:"tempVar"` // the name of the variable as present in the query - Duration time.Duration `json:"duration,omitempty"` // the Duration supplied by the query - Resolution uint `json:"resolution"` // the available screen resolution to render the results of this query - ReportingInterval time.Duration `json:"reportingInterval,omitempty"` // the interval at which data is reported to this series -} - -// Exec is responsible for extracting the Duration from the query -func (g *GroupByVar) Exec(query string) { - whereClause := "WHERE" - start := strings.Index(query, whereClause) - if start == -1 { - // no where clause - return - } - - // reposition start to after the 'where' keyword - durStr := query[start+len(whereClause):] - - // attempt to parse out a relative time range - // locate duration literal start - prefix := "time > now() - " - lowerDuration, err := g.parseRelative(durStr, prefix) - if err == nil { - prefix := "time < now() - " - upperDuration, err := g.parseRelative(durStr, prefix) - if err != nil { - g.Duration = lowerDuration - return - } - g.Duration = lowerDuration - upperDuration - if g.Duration < 0 { - g.Duration = -g.Duration - } - } - - dur, err := g.parseAbsolute(durStr) - if err == nil { - // we found an absolute time range - g.Duration = dur - } -} - -// parseRelative locates and extracts a duration value from a fragment of an -// InfluxQL query following the "where" keyword. For example, in the fragment -// "time > now() - 180d GROUP BY :interval:", parseRelative would return a -// duration equal to 180d -func (g *GroupByVar) parseRelative(fragment string, prefix string) (time.Duration, error) { - start := strings.Index(fragment, prefix) - if start == -1 { - return time.Duration(0), errors.New("not a relative duration") - } - - // reposition to duration literal - durFragment := fragment[start+len(prefix):] - - // init counters - pos := 0 - - // locate end of duration literal - for pos < len(durFragment) { - rn, _ := utf8.DecodeRuneInString(durFragment[pos:]) - if unicode.IsSpace(rn) { - break - } - pos++ - } - - // attempt to parse what we suspect is a duration literal - dur, err := influxql.ParseDuration(durFragment[:pos]) - if err != nil { - return dur, err - } - - return dur, nil -} - -// parseAbsolute will determine the duration between two absolute timestamps -// found within an InfluxQL fragment following the "where" keyword. For -// example, the fragement "time > '1985-10-25T00:01:21-0800 and time < -// '1985-10-25T00:01:22-0800'" would yield a duration of 1m' -func (g *GroupByVar) parseAbsolute(fragment string) (time.Duration, error) { - timePtn := `time\s[>|<]\s'([0-9\-T\:\.Z]+)'` // Playground: http://gobular.com/x/208f66bd-1889-4269-ab47-1efdfeeb63f0 - re, err := regexp.Compile(timePtn) - if err != nil { - // this is a developer error and should complain loudly - panic("Bad Regex: err:" + err.Error()) - } - - if !re.Match([]byte(fragment)) { - return time.Duration(0), errors.New("absolute duration not found") - } - - // extract at most two times - matches := re.FindAll([]byte(fragment), 2) - - // parse out absolute times - durs := make([]time.Time, 0, 2) - for _, match := range matches { - durStr := re.FindSubmatch(match) - if tm, err := time.Parse(time.RFC3339Nano, string(durStr[1])); err == nil { - durs = append(durs, tm) - } - } - - if len(durs) == 1 { - durs = append(durs, time.Now()) - } - - // reject more than 2 times found - if len(durs) != 2 { - return time.Duration(0), errors.New("must provide exactly two absolute times") - } - - dur := durs[1].Sub(durs[0]) - - return dur, nil -} - -func (g *GroupByVar) String() string { - // The function is: ((total_seconds * millisecond_converstion) / group_by) = pixels / 3 - // Number of points given the pixels - pixels := float64(g.Resolution) / 3.0 - msPerPixel := float64(g.Duration/time.Millisecond) / pixels - secPerPixel := float64(g.Duration/time.Second) / pixels - if secPerPixel < 1.0 { - if msPerPixel < 1.0 { - msPerPixel = 1.0 - } - return "time(" + strconv.FormatInt(int64(msPerPixel), 10) + "ms)" - } - // If groupby is more than 1 second round to the second - return "time(" + strconv.FormatInt(int64(secPerPixel), 10) + "s)" -} - -func (g *GroupByVar) Name() string { - return g.Var -} - -func (g *GroupByVar) Precedence() uint { - return 1 +type TemplateVar struct { + Var string `json:"tempVar"` // Var is the string to replace within InfluxQL + Values []TemplateValue `json:"values"` // Values are the replacement values within InfluxQL } // TemplateID is the unique ID used to identify a template @@ -333,7 +143,7 @@ type TemplateID string // Template represents a series of choices to replace TemplateVars within InfluxQL type Template struct { - BasicTemplateVar + TemplateVar ID TemplateID `json:"id"` // ID is the unique ID associated with this template Type string `json:"type"` // Type can be fieldKeys, tagKeys, tagValues, CSV, constant, query, measurements, databases Label string `json:"label"` // Label is a user-facing description of the Template @@ -342,69 +152,15 @@ type Template struct { // Query retrieves a Response from a TimeSeries. type Query struct { - Command string `json:"query"` // Command is the query itself - DB string `json:"db,omitempty"` // DB is optional and if empty will not be used. - RP string `json:"rp,omitempty"` // RP is a retention policy and optional; if empty will not be used. - TemplateVars TemplateVars `json:"tempVars,omitempty"` // TemplateVars are template variables to replace within an InfluxQL query - Wheres []string `json:"wheres,omitempty"` // Wheres restricts the query to certain attributes - GroupBys []string `json:"groupbys,omitempty"` // GroupBys collate the query by these tags - Resolution uint `json:"resolution,omitempty"` // Resolution is the available screen resolution to render query results - Label string `json:"label,omitempty"` // Label is the Y-Axis label for the data - Range *Range `json:"range,omitempty"` // Range is the default Y-Axis range for the data -} - -// TemplateVars are a heterogeneous collection of different TemplateVariables -// with the capability to decode arbitrary JSON into the appropriate template -// variable type -type TemplateVars []TemplateVariable - -func (t *TemplateVars) UnmarshalJSON(text []byte) error { - // TODO: Need to test that server throws an error when :interval:'s Resolution or ReportingInterval or zero-value - rawVars := bytes.NewReader(text) - dec := json.NewDecoder(rawVars) - - // read open bracket - rawTok, err := dec.Token() - if err != nil { - return err - } - - tok, isDelim := rawTok.(json.Delim) - if !isDelim || tok != '[' { - return errors.New("Expected JSON array, but found " + tok.String()) - } - - for dec.More() { - var halfBakedVar json.RawMessage - err := dec.Decode(&halfBakedVar) - if err != nil { - return err - } - - var agb GroupByVar - err = json.Unmarshal(halfBakedVar, &agb) - if err != nil { - return err - } - - // ensure that we really have a GroupByVar - if agb.Resolution != 0 { - (*t) = append(*t, &agb) - continue - } - - var tvar BasicTemplateVar - err = json.Unmarshal(halfBakedVar, &tvar) - if err != nil { - return err - } - - // ensure that we really have a BasicTemplateVar - if len(tvar.Values) != 0 { - (*t) = append(*t, tvar) - } - } - return nil + Command string `json:"query"` // Command is the query itself + DB string `json:"db,omitempty"` // DB is optional and if empty will not be used. + RP string `json:"rp,omitempty"` // RP is a retention policy and optional; if empty will not be used. + TemplateVars []TemplateVar `json:"tempVars,omitempty"` // TemplateVars are template variables to replace within an InfluxQL query + Wheres []string `json:"wheres,omitempty"` // Wheres restricts the query to certain attributes + GroupBys []string `json:"groupbys,omitempty"` // GroupBys collate the query by these tags + Resolution uint `json:"resolution,omitempty"` // Resolution is the available screen resolution to render query results + Label string `json:"label,omitempty"` // Label is the Y-Axis label for the data + Range *Range `json:"range,omitempty"` // Range is the default Y-Axis range for the data } // DashboardQuery includes state for the query builder. This is a transition diff --git a/chronograf_test.go b/chronograf_test.go deleted file mode 100644 index 850674a4d..000000000 --- a/chronograf_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package chronograf_test - -import ( - "testing" - - "github.com/influxdata/chronograf" -) - -func Test_GroupByVar(t *testing.T) { - gbvTests := []struct { - name string - query string - want string - resolution uint // the screen resolution to render queries into - }{ - { - name: "relative time only lower bound with one day of duration", - query: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d GROUP BY :interval:", - resolution: 1000, - want: "time(259s)", - }, - { - name: "relative time with relative upper bound with one minute of duration", - query: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 3m AND time < now() - 2m GROUP BY :interval:", - resolution: 1000, - want: "time(180ms)", - }, - { - name: "relative time with relative lower bound and now upper with one day of duration", - query: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d AND time < now() GROUP BY :interval:", - resolution: 1000, - want: "time(259s)", - }, - { - name: "absolute time with one minute of duration", - query: "SELECT mean(usage_idle) FROM cpu WHERE time > '1985-10-25T00:01:00Z' and time < '1985-10-25T00:02:00Z' GROUP BY :interval:", - resolution: 1000, - want: "time(180ms)", - }, - { - name: "absolute time with nano seconds and zero duraiton", - query: "SELECT mean(usage_idle) FROM cpu WHERE time > '2017-07-24T15:33:42.994Z' and time < '2017-07-24T15:33:42.994Z' GROUP BY :interval:", - resolution: 1000, - want: "time(1ms)", - }, - } - - for _, test := range gbvTests { - t.Run(test.name, func(t *testing.T) { - gbv := chronograf.GroupByVar{ - Var: ":interval:", - Resolution: test.resolution, - } - - gbv.Exec(test.query) - got := gbv.String() - - if got != test.want { - t.Fatalf("%q - durations not equal! Want: %s, Got: %s", test.name, test.want, got) - } - }) - } -} diff --git a/influx/influx.go b/influx/influx.go index 520d2614b..1106b96ea 100644 --- a/influx/influx.go +++ b/influx/influx.go @@ -9,6 +9,7 @@ import ( "net/http" "net/url" "strings" + "time" "github.com/influxdata/chronograf" ) @@ -55,7 +56,10 @@ func (c *Client) query(u *url.URL, q chronograf.Query) (chronograf.Response, err command := q.Command // TODO(timraymond): move this upper Query() function if len(q.TemplateVars) > 0 { - command = TemplateReplace(q.Command, q.TemplateVars) + command, err = TemplateReplace(q.Command, q.TemplateVars, time.Now()) + if err != nil { + return nil, err + } } logs := c.Logger. WithField("component", "proxy"). diff --git a/influx/influx_test.go b/influx/influx_test.go index a6dce9e11..d165ccb86 100644 --- a/influx/influx_test.go +++ b/influx/influx_test.go @@ -276,11 +276,11 @@ func Test_Influx_HTTPS_InsecureSkipVerify(t *testing.T) { called = false q = "" query = chronograf.Query{ - Command: "select $field from cpu", - TemplateVars: chronograf.TemplateVars{ - chronograf.BasicTemplateVar{ - Var: "$field", - Values: []chronograf.BasicTemplateValue{ + Command: "select :field: from cpu", + TemplateVars: []chronograf.TemplateVar{ + chronograf.TemplateVar{ + Var: ":field:", + Values: []chronograf.TemplateValue{ { Value: "usage_user", Type: "fieldKey", diff --git a/influx/query.go b/influx/query.go index 2346fde7c..15b0a62bc 100644 --- a/influx/query.go +++ b/influx/query.go @@ -10,6 +10,52 @@ import ( "github.com/influxdata/influxdb/influxql" ) +func TimeRangeAsEpochNano(expr influxql.Expr, now time.Time) (min, max int64, err error) { + tmin, tmax, err := influxql.TimeRange(expr) + if err != nil { + return 0, 0, err + } + if tmin.IsZero() { + min = time.Unix(0, influxql.MinTime).UnixNano() + } else { + min = tmin.UnixNano() + } + if tmax.IsZero() { + max = now.UnixNano() + } else { + max = tmax.UnixNano() + } + return +} + +const WhereToken = "WHERE" + +func ParseTime(influxQL string, now time.Time) (time.Duration, error) { + start := strings.Index(strings.ToUpper(influxQL), WhereToken) + if start == -1 { + return 0, fmt.Errorf("not a relative duration") + } + start += len(WhereToken) + where := influxQL[start:] + cond, err := influxql.ParseExpr(where) + if err != nil { + return 0, err + } + nowVal := &influxql.NowValuer{ + Now: now, + } + cond = influxql.Reduce(cond, nowVal) + min, max, err := TimeRangeAsEpochNano(cond, now) + if err != nil { + return 0, err + } + dur := time.Duration(max - min) + if dur < 0 { + dur = 0 + } + return dur, nil +} + // Convert changes an InfluxQL query to a QueryConfig func Convert(influxQL string) (chronograf.QueryConfig, error) { itsDashboardTime := false diff --git a/influx/query_test.go b/influx/query_test.go index d01a46fb5..dba50dfc7 100644 --- a/influx/query_test.go +++ b/influx/query_test.go @@ -2,6 +2,7 @@ package influx import ( "testing" + "time" "github.com/google/go-cmp/cmp" "github.com/influxdata/chronograf" @@ -767,3 +768,43 @@ func TestConvert(t *testing.T) { }) } } + +func TestParseTime(t *testing.T) { + tests := []struct { + name string + influxQL string + now string + want time.Duration + wantErr bool + }{ + { + name: "time equal", + now: "2000-01-01T00:00:00Z", + influxQL: `SELECT mean("numSeries") AS "mean_numSeries" FROM "_internal"."monitor"."database" WHERE time > now() - 1h and time < now() - 1h GROUP BY :interval: FILL(null);`, + want: 0, + }, + { + name: "time shifted by one hour", + now: "2000-01-01T00:00:00Z", + influxQL: `SELECT mean("numSeries") AS "mean_numSeries" FROM "_internal"."monitor"."database" WHERE time > now() - 1h - 1h and time < now() - 1h GROUP BY :interval: FILL(null);`, + want: 3599999999998, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + now, err := time.Parse(time.RFC3339, tt.now) + if err != nil { + t.Fatalf("%v", err) + } + got, err := ParseTime(tt.influxQL, now) + if (err != nil) != tt.wantErr { + t.Errorf("ParseTime() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Logf("%d", got) + t.Errorf("ParseTime() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/influx/templates.go b/influx/templates.go index c006f7fa1..8c0ad0e28 100644 --- a/influx/templates.go +++ b/influx/templates.go @@ -1,40 +1,106 @@ package influx import ( + "sort" + "strconv" "strings" + "time" "github.com/influxdata/chronograf" ) -// TemplateReplace replaces templates with values within the query string -func TemplateReplace(query string, templates chronograf.TemplateVars) string { - tvarsByPrecedence := make(map[uint]chronograf.TemplateVars, len(templates)) - maxPrecedence := uint(0) - for _, tmp := range templates { - precedence := tmp.Precedence() - if precedence > maxPrecedence { - maxPrecedence = precedence - } - tvarsByPrecedence[precedence] = append(tvarsByPrecedence[precedence], tmp) - } - - replaced := query - for prc := uint(0); prc <= maxPrecedence; prc++ { - replacements := []string{} - - for _, v := range tvarsByPrecedence[prc] { - if evar, ok := v.(chronograf.ExecutableVar); ok { - evar.Exec(replaced) - } - newVal := v.String() - if newVal != "" { - replacements = append(replacements, v.Name(), newVal) - } +func SortTemplates(ts []chronograf.TemplateVar) []chronograf.TemplateVar { + sort.Slice(ts, func(i, j int) bool { + if len(ts[i].Values) != len(ts[j].Values) { + return len(ts[i].Values) < len(ts[j].Values) } - replacer := strings.NewReplacer(replacements...) - replaced = replacer.Replace(replaced) - } + if len(ts[i].Values) == 0 { + return i < j + } - return replaced + for k := range ts[i].Values { + if ts[i].Values[k].Type != ts[j].Values[k].Type { + return ts[i].Values[k].Type < ts[j].Values[k].Type + } + if ts[i].Values[k].Value != ts[j].Values[k].Value { + return ts[i].Values[k].Value < ts[j].Values[k].Value + } + } + return i < j + }) + return ts +} + +// RenderTemplate converts the template variable into a correct InfluxQL string based +// on its type +func RenderTemplate(query string, t chronograf.TemplateVar, now time.Time) (string, error) { + if len(t.Values) == 0 { + return query, nil + } + switch t.Values[0].Type { + case "tagKey", "fieldKey", "measurement", "database": + return strings.Replace(query, t.Var, `"`+t.Values[0].Value+`"`, -1), nil + case "tagValue", "timeStamp": + return strings.Replace(query, t.Var, `'`+t.Values[0].Value+`'`, -1), nil + case "csv", "constant": + return strings.Replace(query, t.Var, t.Values[0].Value, -1), nil + } + + tv := map[string]string{} + for i := range t.Values { + tv[t.Values[i].Type] = t.Values[i].Value + } + + if res, ok := tv["resolution"]; ok { + resolution, err := strconv.ParseInt(res, 0, 64) + if err != nil { + return "", err + } + ppp, ok := tv["pointsPerPixel"] + if !ok { + ppp = "3" + } + pixelsPerPoint, err := strconv.ParseInt(ppp, 0, 64) + if err != nil { + return "", err + } + + dur, err := ParseTime(query, now) + if err != nil { + return "", err + } + interval := AutoGroupBy(resolution, pixelsPerPoint, dur) + return strings.Replace(query, t.Var, interval, -1), nil + } + return query, nil +} + +func AutoGroupBy(resolution, pixelsPerPoint int64, duration time.Duration) string { + // The function is: ((total_seconds * millisecond_converstion) / group_by) = pixels / 3 + // Number of points given the pixels + pixels := float64(resolution) / float64(pixelsPerPoint) + msPerPixel := float64(duration/time.Millisecond) / pixels + secPerPixel := float64(duration/time.Second) / pixels + if secPerPixel < 1.0 { + if msPerPixel < 1.0 { + msPerPixel = 1.0 + } + return "time(" + strconv.FormatInt(int64(msPerPixel), 10) + "ms)" + } + // If groupby is more than 1 second round to the second + return "time(" + strconv.FormatInt(int64(secPerPixel), 10) + "s)" +} + +// TemplateReplace replaces templates with values within the query string +func TemplateReplace(query string, templates []chronograf.TemplateVar, now time.Time) (string, error) { + templates = SortTemplates(templates) + for i := range templates { + var err error + query, err = RenderTemplate(query, templates[i], now) + if err != nil { + return "", err + } + } + return query, nil } diff --git a/influx/templates_test.go b/influx/templates_test.go index bb6ffc4bb..482a16dc5 100644 --- a/influx/templates_test.go +++ b/influx/templates_test.go @@ -2,6 +2,7 @@ package influx import ( "encoding/json" + "fmt" "reflect" "testing" "time" @@ -13,43 +14,43 @@ func TestTemplateReplace(t *testing.T) { tests := []struct { name string query string - vars chronograf.TemplateVars + vars []chronograf.TemplateVar want string }{ { name: "select with parameters", - query: "$METHOD field1, $field FROM $measurement WHERE temperature > $temperature", - vars: chronograf.TemplateVars{ - chronograf.BasicTemplateVar{ - Var: "$temperature", - Values: []chronograf.BasicTemplateValue{ + query: ":method: field1, :field: FROM :measurement: WHERE temperature > :temperature:", + vars: []chronograf.TemplateVar{ + chronograf.TemplateVar{ + Var: ":temperature:", + Values: []chronograf.TemplateValue{ { Type: "csv", Value: "10", }, }, }, - chronograf.BasicTemplateVar{ - Var: "$field", - Values: []chronograf.BasicTemplateValue{ + chronograf.TemplateVar{ + Var: ":field:", + Values: []chronograf.TemplateValue{ { Type: "fieldKey", Value: "field2", }, }, }, - chronograf.BasicTemplateVar{ - Var: "$METHOD", - Values: []chronograf.BasicTemplateValue{ + chronograf.TemplateVar{ + Var: ":method:", + Values: []chronograf.TemplateValue{ { Type: "csv", Value: "SELECT", }, }, }, - chronograf.BasicTemplateVar{ - Var: "$measurement", - Values: []chronograf.BasicTemplateValue{ + chronograf.TemplateVar{ + Var: ":measurement:", + Values: []chronograf.TemplateValue{ { Type: "csv", Value: `"cpu"`, @@ -62,28 +63,28 @@ func TestTemplateReplace(t *testing.T) { { name: "select with parameters and aggregates", query: `SELECT mean($field) FROM "cpu" WHERE $tag = $value GROUP BY $tag`, - vars: chronograf.TemplateVars{ - chronograf.BasicTemplateVar{ + vars: []chronograf.TemplateVar{ + chronograf.TemplateVar{ Var: "$value", - Values: []chronograf.BasicTemplateValue{ + Values: []chronograf.TemplateValue{ { Type: "tagValue", Value: "howdy.com", }, }, }, - chronograf.BasicTemplateVar{ + chronograf.TemplateVar{ Var: "$tag", - Values: []chronograf.BasicTemplateValue{ + Values: []chronograf.TemplateValue{ { Type: "tagKey", Value: "host", }, }, }, - chronograf.BasicTemplateVar{ + chronograf.TemplateVar{ Var: "$field", - Values: []chronograf.BasicTemplateValue{ + Values: []chronograf.TemplateValue{ { Type: "fieldKey", Value: "field", @@ -101,8 +102,8 @@ func TestTemplateReplace(t *testing.T) { { name: "var without a value", query: `SELECT $field FROM "cpu"`, - vars: chronograf.TemplateVars{ - chronograf.BasicTemplateVar{ + vars: []chronograf.TemplateVar{ + chronograf.TemplateVar{ Var: "$field", }, }, @@ -111,10 +112,10 @@ func TestTemplateReplace(t *testing.T) { { name: "var with unknown type", query: `SELECT $field FROM "cpu"`, - vars: chronograf.TemplateVars{ - chronograf.BasicTemplateVar{ + vars: []chronograf.TemplateVar{ + chronograf.TemplateVar{ Var: "$field", - Values: []chronograf.BasicTemplateValue{ + Values: []chronograf.TemplateValue{ { Type: "who knows?", Value: "field", @@ -127,42 +128,63 @@ func TestTemplateReplace(t *testing.T) { { name: "auto group by", query: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by :interval:`, - vars: chronograf.TemplateVars{ - &chronograf.GroupByVar{ - Var: ":interval:", - Duration: 180 * 24 * time.Hour, - Resolution: 1000, - ReportingInterval: 10 * time.Second, + vars: []chronograf.TemplateVar{ + { + Var: ":interval:", + Values: []chronograf.TemplateValue{ + { + Value: "1000", + Type: "resolution", + }, + { + Value: "3", + Type: "pointsPerPixel", + }, + }, }, }, - want: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by time(46656s)`, + want: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by time(46655s)`, }, { name: "auto group by without duration", query: `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 4320h group by :interval:`, - vars: chronograf.TemplateVars{ - &chronograf.GroupByVar{ - Var: ":interval:", - Duration: 0 * time.Minute, - Resolution: 1000, - ReportingInterval: 10 * time.Second, + vars: []chronograf.TemplateVar{ + { + Var: ":interval:", + Values: []chronograf.TemplateValue{ + { + Value: "1000", + Type: "resolution", + }, + { + Value: "3", + Type: "pointsPerPixel", + }, + }, }, }, - want: `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 4320h group by time(46656s)`, + want: `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 4320h group by time(46655s)`, }, { name: "auto group by with :dashboardTime:", query: `SELECT mean(usage_idle) from "cpu" WHERE time > :dashboardTime: group by :interval:`, - vars: chronograf.TemplateVars{ - &chronograf.GroupByVar{ - Var: ":interval:", - Duration: 0 * time.Minute, - Resolution: 1000, - ReportingInterval: 10 * time.Second, + vars: []chronograf.TemplateVar{ + { + Var: ":interval:", + Values: []chronograf.TemplateValue{ + { + Value: "1000", + Type: "resolution", + }, + { + Value: "3", + Type: "pointsPerPixel", + }, + }, }, - &chronograf.BasicTemplateVar{ + { Var: ":dashboardTime:", - Values: []chronograf.BasicTemplateValue{ + Values: []chronograf.TemplateValue{ { Type: "constant", Value: "now() - 4320h", @@ -170,20 +192,28 @@ func TestTemplateReplace(t *testing.T) { }, }, }, - want: `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 4320h group by time(46656s)`, + want: `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 4320h group by time(46655s)`, }, { name: "auto group by failing condition", query: `SELECT mean(usage_idle) FROM "cpu" WHERE time > :dashboardTime: GROUP BY :interval:`, - vars: []chronograf.TemplateVariable{ - &chronograf.GroupByVar{ - Var: ":interval:", - Resolution: 115, - ReportingInterval: 10 * time.Second, + vars: []chronograf.TemplateVar{ + { + Var: ":interval:", + Values: []chronograf.TemplateValue{ + { + Value: "115", + Type: "resolution", + }, + { + Value: "3", + Type: "pointsPerPixel", + }, + }, }, - chronograf.BasicTemplateVar{ + { Var: ":dashboardTime:", - Values: []chronograf.BasicTemplateValue{ + Values: []chronograf.TemplateValue{ { Value: "now() - 1h", Type: "constant", @@ -197,7 +227,14 @@ func TestTemplateReplace(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := TemplateReplace(tt.query, tt.vars) + now, err := time.Parse(time.RFC3339, "1985-10-25T00:01:00Z") + if err != nil { + t.Fatal(err) + } + got, err := TemplateReplace(tt.query, tt.vars, now) + if err != nil { + t.Fatalf("TestParse unexpected TemplateReplace error: %v", err) + } if got != tt.want { t.Errorf("TestParse %s =\n%s\nwant\n%s", tt.name, got, tt.want) } @@ -209,8 +246,20 @@ func Test_TemplateVarsUnmarshalling(t *testing.T) { req := `[ { "tempVar": ":interval:", - "resolution": 1000, - "reportingInterval": 10 + "values": [ + { + "value": "1000", + "type": "resolution" + }, + { + "value": "3", + "type": "pointsPerPixel" + }, + { + "value": "10", + "type": "reportingInterval" + } + ] }, { "tempVar": ":cpu:", @@ -224,15 +273,27 @@ func Test_TemplateVarsUnmarshalling(t *testing.T) { } ]` - expected := []chronograf.TemplateVariable{ - &chronograf.GroupByVar{ - Var: ":interval:", - Resolution: 1000, - ReportingInterval: 10 * time.Nanosecond, + want := []chronograf.TemplateVar{ + { + Var: ":interval:", + Values: []chronograf.TemplateValue{ + { + Value: "1000", + Type: "resolution", + }, + { + Value: "3", + Type: "pointsPerPixel", + }, + { + Value: "10", + Type: "reportingInterval", + }, + }, }, - chronograf.BasicTemplateVar{ + { Var: ":cpu:", - Values: []chronograf.BasicTemplateValue{ + Values: []chronograf.TemplateValue{ { Value: "cpu-total", Type: "tagValue", @@ -242,65 +303,128 @@ func Test_TemplateVarsUnmarshalling(t *testing.T) { }, } - var tvars chronograf.TemplateVars - err := json.Unmarshal([]byte(req), &tvars) + var got []chronograf.TemplateVar + err := json.Unmarshal([]byte(req), &got) if err != nil { t.Fatal("Err unmarshaling:", err) } - if len(tvars) != len(expected) { - t.Fatal("Expected", len(expected), "vars but found", len(tvars)) - } - - if !reflect.DeepEqual(*(tvars[0].(*chronograf.GroupByVar)), *(expected[0].(*chronograf.GroupByVar))) { - t.Errorf("UnmarshalJSON() = \n%#v\n want \n%#v\n", *(tvars[0].(*chronograf.GroupByVar)), *(expected[0].(*chronograf.GroupByVar))) - } - - if !reflect.DeepEqual(tvars[1].(chronograf.BasicTemplateVar), expected[1].(chronograf.BasicTemplateVar)) { - t.Errorf("UnmarshalJSON() = \n%#v\n want \n%#v\n", tvars[1].(chronograf.BasicTemplateVar), expected[1].(chronograf.BasicTemplateVar)) + if !reflect.DeepEqual(got, want) { + t.Errorf("UnmarshalJSON() = \n%#v\n want \n%#v\n", got, want) } } -func TestGroupByVarString(t *testing.T) { +func TestAutoGroupBy(t *testing.T) { tests := []struct { - name string - tvar *chronograf.GroupByVar - want string + name string + resolution int64 + pixelsPerPoint int64 + duration time.Duration + want string }{ { - name: "String() calculates the GROUP BY interval", - tvar: &chronograf.GroupByVar{ - Resolution: 700, - ReportingInterval: 10 * time.Second, - Duration: 24 * time.Hour, - }, - want: "time(370s)", + name: "String() calculates the GROUP BY interval", + resolution: 700, + pixelsPerPoint: 3, + duration: 24 * time.Hour, + want: "time(370s)", }, { - name: "String() milliseconds if less than one second intervals", - tvar: &chronograf.GroupByVar{ - Resolution: 100000, - ReportingInterval: 10 * time.Second, - Duration: time.Hour, - }, - want: "time(107ms)", + name: "String() milliseconds if less than one second intervals", + resolution: 100000, + pixelsPerPoint: 3, + duration: time.Hour, + want: "time(107ms)", }, { - name: "String() milliseconds if less than one millisecond", - tvar: &chronograf.GroupByVar{ - Resolution: 100000, - ReportingInterval: 10 * time.Second, - Duration: time.Second, - }, - want: "time(1ms)", + name: "String() milliseconds if less than one millisecond", + resolution: 100000, + pixelsPerPoint: 3, + duration: time.Second, + want: "time(1ms)", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := tt.tvar.String() + got := AutoGroupBy(tt.resolution, tt.pixelsPerPoint, tt.duration) if got != tt.want { - t.Errorf("TestGroupByVarString %s =\n%s\nwant\n%s", tt.name, got, tt.want) + t.Errorf("TestAutoGroupBy %s =\n%s\nwant\n%s", tt.name, got, tt.want) } }) } } + +func Test_RenderTemplate(t *testing.T) { + gbvTests := []struct { + name string + query string + want string + resolution uint // the screen resolution to render queries into + }{ + { + name: "relative time only lower bound with one day of duration", + query: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d GROUP BY :interval:", + resolution: 1000, + want: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d GROUP BY time(259s)", + }, + { + name: "relative time offset by week", + query: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d - 7d AND time < now() - 7d GROUP BY :interval:", + resolution: 1000, + want: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d - 7d AND time < now() - 7d GROUP BY time(259s)", + }, + { + name: "relative time with relative upper bound with one minute of duration", + query: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 3m AND time < now() - 2m GROUP BY :interval:", + resolution: 1000, + want: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 3m AND time < now() - 2m GROUP BY time(179ms)", + }, + { + name: "relative time with relative lower bound and now upper with one day of duration", + query: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d AND time < now() GROUP BY :interval:", + resolution: 1000, + want: "SELECT mean(usage_idle) FROM cpu WHERE time > now() - 1d AND time < now() GROUP BY time(259s)", + }, + { + name: "absolute time with one minute of duration", + query: "SELECT mean(usage_idle) FROM cpu WHERE time > '1985-10-25T00:01:00Z' and time < '1985-10-25T00:02:00Z' GROUP BY :interval:", + resolution: 1000, + want: "SELECT mean(usage_idle) FROM cpu WHERE time > '1985-10-25T00:01:00Z' and time < '1985-10-25T00:02:00Z' GROUP BY time(179ms)", + }, + { + name: "absolute time with nano seconds and zero duraiton", + query: "SELECT mean(usage_idle) FROM cpu WHERE time > '2017-07-24T15:33:42.994Z' and time < '2017-07-24T15:33:42.994Z' GROUP BY :interval:", + resolution: 1000, + want: "SELECT mean(usage_idle) FROM cpu WHERE time > '2017-07-24T15:33:42.994Z' and time < '2017-07-24T15:33:42.994Z' GROUP BY time(1ms)", + }, + } + + for _, tt := range gbvTests { + t.Run(tt.name, func(t *testing.T) { + now, err := time.Parse(time.RFC3339, "1985-10-25T00:01:00Z") + if err != nil { + t.Fatal(err) + } + tvar := chronograf.TemplateVar{ + Var: ":interval:", + Values: []chronograf.TemplateValue{ + { + Value: fmt.Sprintf("%d", tt.resolution), + Type: "resolution", + }, + }, + } + + got, err := RenderTemplate(tt.query, tvar, now) + if err != nil { + t.Fatalf("unexpected error rendering template %v", err) + } + + if got != tt.want { + t.Fatalf("%q - durations not equal! Want: %s, Got: %s", tt.name, tt.want, got) + } + }) + } +} + +// SELECT mean("numSeries") AS "mean_numSeries" FROM "_internal"."monitor"."database" WHERE time > now() - 1h GROUP BY :interval: FILL(null);SELECT mean("numSeries") AS "mean_numSeries_shifted__1__h" FROM "_internal"."monitor"."database" WHERE time > now() - 1h - 1h AND time < now() - 1h GROUP BY :interval: FILL(null) diff --git a/server/queries.go b/server/queries.go index 16b2c5a7f..76e5ad4b5 100644 --- a/server/queries.go +++ b/server/queries.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net/http" + "time" "golang.org/x/net/context" @@ -21,8 +22,8 @@ type QueryRequest struct { // QueriesRequest converts all queries to queryConfigs with the help // of the template variables type QueriesRequest struct { - Queries []QueryRequest `json:"queries"` - TemplateVars chronograf.TemplateVars `json:"tempVars,omitempty"` + Queries []QueryRequest `json:"queries"` + TemplateVars []chronograf.TemplateVar `json:"tempVars,omitempty"` } // QueryResponse is the return result of a QueryRequest including @@ -33,7 +34,7 @@ type QueryResponse struct { QueryConfig chronograf.QueryConfig `json:"queryConfig"` QueryAST *queries.SelectStatement `json:"queryAST,omitempty"` QueryTemplated *string `json:"queryTemplated,omitempty"` - TemplateVars chronograf.TemplateVars `json:"tempVars,omitempty"` + TemplateVars []chronograf.TemplateVar `json:"tempVars,omitempty"` } // QueriesResponse is the response for a QueriesRequest @@ -72,7 +73,12 @@ func (s *Service) Queries(w http.ResponseWriter, r *http.Request) { Query: q.Query, } - query := influx.TemplateReplace(q.Query, req.TemplateVars) + query, err := influx.TemplateReplace(q.Query, req.TemplateVars, time.Now()) + if err != nil { + Error(w, http.StatusBadRequest, err.Error(), s.Logger) + return + } + qc := ToQueryConfig(query) if err := s.DefaultRP(ctx, &qc, &src); err != nil { Error(w, http.StatusBadRequest, err.Error(), s.Logger) diff --git a/server/queries_test.go b/server/queries_test.go index 3e409779f..83967a7e7 100644 --- a/server/queries_test.go +++ b/server/queries_test.go @@ -98,7 +98,7 @@ func TestService_Queries(t *testing.T) { r: httptest.NewRequest("POST", "/queries", bytes.NewReader([]byte(`{ "queries": [ { - "query": "SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time > now() - 1m", + "query": "SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time > :dashboardTime: AND time < :upperDashboardTime: GROUP BY :interval:", "id": "82b60d37-251e-4afe-ac93-ca20a3642b11" } ], @@ -153,13 +153,20 @@ func TestService_Queries(t *testing.T) { "id": "interval", "type": "constant", "tempVar": ":interval:", - "resolution": 1000, - "reportingInterval": 10000000000, - "values": [] + "values": [ + { + "value": "1000", + "type": "resolution" + }, + { + "value": "3", + "type": "pointsPerPixel" + } + ] } ] }`))), - want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e now() - 1m","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"_internal","measurement":"httpd","retentionPolicy":"monitor","fields":[{"value":"pingReq","type":"field","alias":""}],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e now() - 1m","range":{"upper":"","lower":"now() - 1m"}},"queryAST":{"condition":{"expr":"binary","op":"\u003e","lhs":{"expr":"reference","val":"time"},"rhs":{"expr":"binary","op":"-","lhs":{"expr":"call","name":"now"},"rhs":{"expr":"literal","val":"1m","type":"duration"}}},"fields":[{"column":{"expr":"reference","val":"pingReq"}}],"sources":[{"database":"_internal","retentionPolicy":"monitor","name":"httpd","type":"measurement"}]},"queryTemplated":"SELECT \"pingReq\" FROM \"_internal\".\"monitor\".\"httpd\" WHERE time \u003e now() - 1m","tempVars":[{"tempVar":":dbs:","values":[{"value":"_internal","type":"database","selected":true}]},{"tempVar":":dashboardTime:","values":[{"value":"now() - 15m","type":"constant","selected":true}]},{"tempVar":":upperDashboardTime:","values":[{"value":"now()","type":"constant","selected":true}]},{"tempVar":":interval:","duration":60000000000,"resolution":1000,"reportingInterval":10000000000}]}]} + want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e :dashboardTime: AND time \u003c :upperDashboardTime: GROUP BY :interval:","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"","measurement":"","retentionPolicy":"","fields":[],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e :dashboardTime: AND time \u003c :upperDashboardTime: GROUP BY :interval:","range":null},"queryTemplated":"SELECT \"pingReq\" FROM \"_internal\".\"monitor\".\"httpd\" WHERE time \u003e now() - 15m AND time \u003c now() GROUP BY time(2s)","tempVars":[{"tempVar":":upperDashboardTime:","values":[{"value":"now()","type":"constant","selected":true}]},{"tempVar":":dashboardTime:","values":[{"value":"now() - 15m","type":"constant","selected":true}]},{"tempVar":":dbs:","values":[{"value":"_internal","type":"database","selected":true}]},{"tempVar":":interval:","values":[{"value":"1000","type":"resolution","selected":false},{"value":"3","type":"pointsPerPixel","selected":false}]}]}]} `, }, } diff --git a/server/templates_test.go b/server/templates_test.go index afd220afe..8a9bec46f 100644 --- a/server/templates_test.go +++ b/server/templates_test.go @@ -16,8 +16,8 @@ func TestValidTemplateRequest(t *testing.T) { name: "Valid Template", template: &chronograf.Template{ Type: "fieldKeys", - BasicTemplateVar: chronograf.BasicTemplateVar{ - Values: []chronograf.BasicTemplateValue{ + TemplateVar: chronograf.TemplateVar{ + Values: []chronograf.TemplateValue{ { Type: "fieldKey", }, @@ -30,8 +30,8 @@ func TestValidTemplateRequest(t *testing.T) { wantErr: true, template: &chronograf.Template{ Type: "Unknown Type", - BasicTemplateVar: chronograf.BasicTemplateVar{ - Values: []chronograf.BasicTemplateValue{ + TemplateVar: chronograf.TemplateVar{ + Values: []chronograf.TemplateValue{ { Type: "fieldKey", }, @@ -44,8 +44,8 @@ func TestValidTemplateRequest(t *testing.T) { wantErr: true, template: &chronograf.Template{ Type: "csv", - BasicTemplateVar: chronograf.BasicTemplateVar{ - Values: []chronograf.BasicTemplateValue{ + TemplateVar: chronograf.TemplateVar{ + Values: []chronograf.TemplateValue{ { Type: "unknown value", }, From ad6192c4a8bcf138c7a6026bef6ae1ddb30ed6f8 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 8 Nov 2017 14:26:37 -0800 Subject: [PATCH 026/138] Update frontend to handle new :interval: shape --- ui/src/dashboards/containers/DashboardPage.js | 19 +++++++++++---- ui/src/shared/components/AutoRefresh.js | 23 +++++++++++++++---- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/ui/src/dashboards/containers/DashboardPage.js b/ui/src/dashboards/containers/DashboardPage.js index 6094b63ca..b28988985 100644 --- a/ui/src/dashboards/containers/DashboardPage.js +++ b/ui/src/dashboards/containers/DashboardPage.js @@ -263,14 +263,23 @@ class DashboardPage extends Component { ], } - // this controls the auto group by behavior const interval = { id: 'interval', - type: 'constant', + type: 'autoGroupBy', tempVar: ':interval:', - resolution: 1000, - reportingInterval: 10000000000, - values: [], + label: 'automatically determine the best group by time', + values: [ + { + value: '1000', // pixels + type: 'resolution', + selected: true, + }, + { + value: '3', + type: 'pointsPerPixel', + selected: true, + }, + ], } let templatesIncludingDashTime diff --git a/ui/src/shared/components/AutoRefresh.js b/ui/src/shared/components/AutoRefresh.js index 1c430091e..003f1c157 100644 --- a/ui/src/shared/components/AutoRefresh.js +++ b/ui/src/shared/components/AutoRefresh.js @@ -81,20 +81,35 @@ const AutoRefresh = ComposedComponent => { const templatesWithResolution = templates.map(temp => { if (temp.tempVar === ':interval:') { if (resolution) { - return {...temp, resolution} + return { + ...temp, + values: temp.values.map( + v => (temp.type === 'resolution' ? {...v, resolution} : v) + ), + } + } + + return { + ...temp, + values: [ + ...temp.values, + {value: '1000', type: 'resolution', selected: true}, + ], } - return {...temp, resolution: 1000} } - return {...temp} + + return temp }) + const tempVars = removeUnselectedTemplateValues(templatesWithResolution) + return fetchTimeSeriesAsync( { source: host, db: database, rp, query, - tempVars: removeUnselectedTemplateValues(templatesWithResolution), + tempVars, resolution, }, editQueryStatus From d47cd65487f72b297358540e37044a845cb9f184 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 3 Nov 2017 13:12:52 -0700 Subject: [PATCH 027/138] Move query options into QueryOptions component --- ui/src/shared/components/FieldList.js | 21 ++++++---------- ui/src/shared/components/QueryOptions.js | 32 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 13 deletions(-) create mode 100644 ui/src/shared/components/QueryOptions.js diff --git a/ui/src/shared/components/FieldList.js b/ui/src/shared/components/FieldList.js index b92623d3d..a0528d8dd 100644 --- a/ui/src/shared/components/FieldList.js +++ b/ui/src/shared/components/FieldList.js @@ -1,9 +1,8 @@ import React, {PropTypes, Component} from 'react' import _ from 'lodash' +import QueryOptions from 'shared/components/QueryOptions' import FieldListItem from 'src/data_explorer/components/FieldListItem' -import GroupByTimeDropdown from 'src/data_explorer/components/GroupByTimeDropdown' -import FillQuery from 'shared/components/FillQuery' import FancyScrollbar from 'shared/components/FancyScrollbar' import {showFieldKeys} from 'shared/apis/metaQuery' @@ -134,7 +133,6 @@ class FieldList extends Component { } = this.props const hasAggregates = numFunctions(fields) > 0 - const hasGroupByTime = groupBy.time const noDBorMeas = !database || !measurement return ( @@ -142,16 +140,13 @@ class FieldList extends Component {
Fields {hasAggregates - ?
- - {isKapacitorRule - ? null - : } -
+ ? : null}
{noDBorMeas diff --git a/ui/src/shared/components/QueryOptions.js b/ui/src/shared/components/QueryOptions.js new file mode 100644 index 000000000..ae2b52c1a --- /dev/null +++ b/ui/src/shared/components/QueryOptions.js @@ -0,0 +1,32 @@ +import React, {PropTypes} from 'react' +import GroupByTimeDropdown from 'src/data_explorer/components/GroupByTimeDropdown' +import FillQuery from 'shared/components/FillQuery' + +const QueryOptions = ({ + fill, + onFill, + groupBy, + onGroupByTime, + isKapacitorRule, +}) => +
+ + {isKapacitorRule ? null : } +
+ +const {bool, func, shape, string} = PropTypes + +QueryOptions.propTypes = { + fill: string, + onFill: func.isRequired, + groupBy: shape({ + time: string, + }).isRequired, + onGroupByTime: func.isRequired, + isKapacitorRule: bool.isRequired, +} + +export default QueryOptions From 32042db61e0aecc81579cbcd4e99b8ac26fa4bad Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 3 Nov 2017 13:33:14 -0700 Subject: [PATCH 028/138] WIP add TimeShiftDropdown component --- .../components/GroupByTimeDropdown.js | 4 ++-- ui/src/shared/components/FieldList.js | 7 ++++++ ui/src/shared/components/QueryOptions.js | 11 +++++++++ ui/src/shared/components/TimeShiftDropdown.js | 24 +++++++++++++++++++ ui/src/shared/constants/timeShift.js | 5 ++++ 5 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 ui/src/shared/components/TimeShiftDropdown.js create mode 100644 ui/src/shared/constants/timeShift.js diff --git a/ui/src/data_explorer/components/GroupByTimeDropdown.js b/ui/src/data_explorer/components/GroupByTimeDropdown.js index ebf1d8e5b..9219881c3 100644 --- a/ui/src/data_explorer/components/GroupByTimeDropdown.js +++ b/ui/src/data_explorer/components/GroupByTimeDropdown.js @@ -7,8 +7,6 @@ import Dropdown from 'shared/components/Dropdown' import {AUTO_GROUP_BY} from 'shared/constants' -const {func, string, shape} = PropTypes - const isInRuleBuilder = pathname => pathname.includes('alert-rules') const isInDataExplorer = pathname => pathname.includes('data-explorer') @@ -37,6 +35,8 @@ const GroupByTimeDropdown = ({ />
+const {func, string, shape} = PropTypes + GroupByTimeDropdown.propTypes = { location: shape({ pathname: string.isRequired, diff --git a/ui/src/shared/components/FieldList.js b/ui/src/shared/components/FieldList.js index a0528d8dd..833e0c9fd 100644 --- a/ui/src/shared/components/FieldList.js +++ b/ui/src/shared/components/FieldList.js @@ -106,6 +106,10 @@ class FieldList extends Component { applyFuncsToField(fieldFunc, groupBy) } + handleTimeShift = shift => { + console.log('you just got shifted: ', shift) + } + _getFields = () => { const {database, measurement, retentionPolicy} = this.props.query const {source} = this.context @@ -134,6 +138,7 @@ class FieldList extends Component { const hasAggregates = numFunctions(fields) > 0 const noDBorMeas = !database || !measurement + const shift = {duration: ''} return (
@@ -142,9 +147,11 @@ class FieldList extends Component { {hasAggregates ? : null} diff --git a/ui/src/shared/components/QueryOptions.js b/ui/src/shared/components/QueryOptions.js index ae2b52c1a..8cd387254 100644 --- a/ui/src/shared/components/QueryOptions.js +++ b/ui/src/shared/components/QueryOptions.js @@ -1,11 +1,14 @@ import React, {PropTypes} from 'react' import GroupByTimeDropdown from 'src/data_explorer/components/GroupByTimeDropdown' +import TimeShiftDropdown from 'src/shared/components/TimeShiftDropdown' import FillQuery from 'shared/components/FillQuery' const QueryOptions = ({ fill, + shift, onFill, groupBy, + onTimeShift, onGroupByTime, isKapacitorRule, }) => @@ -14,6 +17,10 @@ const QueryOptions = ({ selected={groupBy.time} onChooseGroupByTime={onGroupByTime} /> + {isKapacitorRule ? null : }
@@ -25,8 +32,12 @@ QueryOptions.propTypes = { groupBy: shape({ time: string, }).isRequired, + shift: shape({ + duration: string, + }), onGroupByTime: func.isRequired, isKapacitorRule: bool.isRequired, + onTimeShift: func.isRequired, } export default QueryOptions diff --git a/ui/src/shared/components/TimeShiftDropdown.js b/ui/src/shared/components/TimeShiftDropdown.js new file mode 100644 index 000000000..b16c0dcb5 --- /dev/null +++ b/ui/src/shared/components/TimeShiftDropdown.js @@ -0,0 +1,24 @@ +import React, {PropTypes} from 'react' +import Dropdown from 'shared/components/Dropdown' +import {TIME_SHIFTS} from 'shared/constants/timeShift' + +const TimeShiftDropdown = ({selected, onChooseTimeShift}) => +
+ + +
+ +const {func, string} = PropTypes + +TimeShiftDropdown.propTypes = { + selected: string, + onChooseTimeShift: func.isRequired, +} + +export default TimeShiftDropdown diff --git a/ui/src/shared/constants/timeShift.js b/ui/src/shared/constants/timeShift.js new file mode 100644 index 000000000..b35f7a9e5 --- /dev/null +++ b/ui/src/shared/constants/timeShift.js @@ -0,0 +1,5 @@ +export const TIME_SHIFTS = [ + {duration: '1d', text: '1d', multiple: 1, unit: 'd'}, + {duration: '7d', text: '7d', multiple: 7, unit: 'd'}, + {duration: '30d', text: '30d', multiple: 30, unit: 'd'}, +] From 87c164e63a8930eb736c1ff3cc37090323d037c6 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 3 Nov 2017 13:48:42 -0700 Subject: [PATCH 029/138] Be the change --- ui/src/utils/queryTransitions.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ui/src/utils/queryTransitions.js b/ui/src/utils/queryTransitions.js index 0519b83fa..40ad05423 100644 --- a/ui/src/utils/queryTransitions.js +++ b/ui/src/utils/queryTransitions.js @@ -108,7 +108,7 @@ export const toggleField = (query, {value}) => { } } -export function groupByTime(query, time) { +export const groupByTime = (query, time) => { return Object.assign({}, query, { groupBy: Object.assign({}, query.groupBy, { time, @@ -118,7 +118,7 @@ export function groupByTime(query, time) { export const fill = (query, value) => ({...query, fill: value}) -export function toggleTagAcceptance(query) { +export const toggleTagAcceptance = query => { return Object.assign({}, query, { areTagsAccepted: !query.areTagsAccepted, }) @@ -185,13 +185,13 @@ export const applyFuncsToField = (query, {field, funcs = []}, groupBy) => { } } -export function updateRawQuery(query, rawText) { +export const updateRawQuery = (query, rawText) => { return Object.assign({}, query, { rawText, }) } -export function groupByTag(query, tagKey) { +export const groupByTag = (query, tagKey) => { const oldTags = query.groupBy.tags let newTags @@ -209,7 +209,7 @@ export function groupByTag(query, tagKey) { }) } -export function chooseTag(query, tag) { +export const chooseTag = (query, tag) => { const tagValues = query.tags[tag.key] const shouldRemoveTag = tagValues && tagValues.length === 1 && tagValues[0] === tag.value @@ -219,6 +219,14 @@ export function chooseTag(query, tag) { return Object.assign({}, query, {tags: newTags}) } + const updateTagValues = newTagValues => { + return Object.assign({}, query, { + tags: Object.assign({}, query.tags, { + [tag.key]: newTagValues, + }), + }) + } + const oldTagValues = query.tags[tag.key] if (!oldTagValues) { return updateTagValues([tag.value]) @@ -233,12 +241,4 @@ export function chooseTag(query, tag) { } return updateTagValues(query.tags[tag.key].concat(tag.value)) - - function updateTagValues(newTagValues) { - return Object.assign({}, query, { - tags: Object.assign({}, query.tags, { - [tag.key]: newTagValues, - }), - }) - } } From 1aa4664f7d0a204f92eaf4cf1df3edc3f4bdc87d Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 3 Nov 2017 13:50:20 -0700 Subject: [PATCH 030/138] Add no time shift option --- ui/src/shared/components/TimeShiftDropdown.js | 2 +- ui/src/shared/constants/timeShift.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/src/shared/components/TimeShiftDropdown.js b/ui/src/shared/components/TimeShiftDropdown.js index b16c0dcb5..4b6c630ae 100644 --- a/ui/src/shared/components/TimeShiftDropdown.js +++ b/ui/src/shared/components/TimeShiftDropdown.js @@ -10,7 +10,7 @@ const TimeShiftDropdown = ({selected, onChooseTimeShift}) => buttonColor="btn-info" items={TIME_SHIFTS} onChoose={onChooseTimeShift} - selected={selected || 'Duration'} + selected={selected || 'none'} />
diff --git a/ui/src/shared/constants/timeShift.js b/ui/src/shared/constants/timeShift.js index b35f7a9e5..225734abb 100644 --- a/ui/src/shared/constants/timeShift.js +++ b/ui/src/shared/constants/timeShift.js @@ -1,4 +1,5 @@ export const TIME_SHIFTS = [ + {duration: null, text: 'none', multiple: 0, unit: null}, {duration: '1d', text: '1d', multiple: 1, unit: 'd'}, {duration: '7d', text: '7d', multiple: 7, unit: 'd'}, {duration: '30d', text: '30d', multiple: 30, unit: 'd'}, From 82e92a0598da81c849087b3d66a1030772385c18 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 3 Nov 2017 13:56:01 -0700 Subject: [PATCH 031/138] Make Id ID --- .../data_explorer/reducers/queryConfigSpec.js | 190 +++++++++--------- 1 file changed, 95 insertions(+), 95 deletions(-) diff --git a/ui/spec/data_explorer/reducers/queryConfigSpec.js b/ui/spec/data_explorer/reducers/queryConfigSpec.js index 7dfa1399f..b4162f0e7 100644 --- a/ui/spec/data_explorer/reducers/queryConfigSpec.js +++ b/ui/spec/data_explorer/reducers/queryConfigSpec.js @@ -26,63 +26,63 @@ const fakeAddQueryAction = (panelID, queryID) => { } } -function buildInitialState(queryId, params) { - return Object.assign({}, defaultQueryConfig({id: queryId}), params) +function buildInitialState(queryID, params) { + return Object.assign({}, defaultQueryConfig({id: queryID}), params) } describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { - const queryId = 123 + const queryID = 123 it('can add a query', () => { - const state = reducer({}, fakeAddQueryAction('blah', queryId)) + const state = reducer({}, fakeAddQueryAction('blah', queryID)) - const actual = state[queryId] - const expected = defaultQueryConfig({id: queryId}) + const actual = state[queryID] + const expected = defaultQueryConfig({id: queryID}) expect(actual).to.deep.equal(expected) }) describe('choosing db, rp, and measurement', () => { let state beforeEach(() => { - state = reducer({}, fakeAddQueryAction('any', queryId)) + state = reducer({}, fakeAddQueryAction('any', queryID)) }) it('sets the db and rp', () => { const newState = reducer( state, - chooseNamespace(queryId, { + chooseNamespace(queryID, { database: 'telegraf', retentionPolicy: 'monitor', }) ) - expect(newState[queryId].database).to.equal('telegraf') - expect(newState[queryId].retentionPolicy).to.equal('monitor') + expect(newState[queryID].database).to.equal('telegraf') + expect(newState[queryID].retentionPolicy).to.equal('monitor') }) it('sets the measurement', () => { - const newState = reducer(state, chooseMeasurement(queryId, 'mem')) + const newState = reducer(state, chooseMeasurement(queryID, 'mem')) - expect(newState[queryId].measurement).to.equal('mem') + expect(newState[queryID].measurement).to.equal('mem') }) }) describe('a query has measurements and fields', () => { let state beforeEach(() => { - const one = reducer({}, fakeAddQueryAction('any', queryId)) + const one = reducer({}, fakeAddQueryAction('any', queryID)) const two = reducer( one, - chooseNamespace(queryId, { + chooseNamespace(queryID, { database: '_internal', retentionPolicy: 'daily', }) ) - const three = reducer(two, chooseMeasurement(queryId, 'disk')) + const three = reducer(two, chooseMeasurement(queryID, 'disk')) state = reducer( three, - addInitialField(queryId, { + addInitialField(queryID, { value: 'a great field', type: 'field', }) @@ -92,91 +92,91 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { describe('choosing a new namespace', () => { it('clears out the old measurement and fields', () => { // what about tags? - expect(state[queryId].measurement).to.equal('disk') - expect(state[queryId].fields.length).to.equal(1) + expect(state[queryID].measurement).to.equal('disk') + expect(state[queryID].fields.length).to.equal(1) const newState = reducer( state, - chooseNamespace(queryId, { + chooseNamespace(queryID, { database: 'newdb', retentionPolicy: 'newrp', }) ) - expect(newState[queryId].measurement).to.be.null - expect(newState[queryId].fields.length).to.equal(0) + expect(newState[queryID].measurement).to.be.null + expect(newState[queryID].fields.length).to.equal(0) }) }) describe('choosing a new measurement', () => { it('leaves the namespace and clears out the old fields', () => { // what about tags? - expect(state[queryId].fields.length).to.equal(1) + expect(state[queryID].fields.length).to.equal(1) const newState = reducer( state, - chooseMeasurement(queryId, 'newmeasurement') + chooseMeasurement(queryID, 'newmeasurement') ) - expect(state[queryId].database).to.equal(newState[queryId].database) - expect(state[queryId].retentionPolicy).to.equal( - newState[queryId].retentionPolicy + expect(state[queryID].database).to.equal(newState[queryID].database) + expect(state[queryID].retentionPolicy).to.equal( + newState[queryID].retentionPolicy ) - expect(newState[queryId].fields.length).to.equal(0) + expect(newState[queryID].fields.length).to.equal(0) }) }) describe('DE_TOGGLE_FIELD', () => { it('can toggle multiple fields', () => { - expect(state[queryId].fields.length).to.equal(1) + expect(state[queryID].fields.length).to.equal(1) const newState = reducer( state, - toggleField(queryId, { + toggleField(queryID, { value: 'f2', type: 'field', }) ) - expect(newState[queryId].fields.length).to.equal(2) - expect(newState[queryId].fields[1].alias).to.deep.equal('mean_f2') - expect(newState[queryId].fields[1].args).to.deep.equal([ + expect(newState[queryID].fields.length).to.equal(2) + expect(newState[queryID].fields[1].alias).to.deep.equal('mean_f2') + expect(newState[queryID].fields[1].args).to.deep.equal([ {value: 'f2', type: 'field'}, ]) - expect(newState[queryId].fields[1].value).to.deep.equal('mean') + expect(newState[queryID].fields[1].value).to.deep.equal('mean') }) it('applies a func to newly selected fields', () => { - expect(state[queryId].fields.length).to.equal(1) - expect(state[queryId].fields[0].type).to.equal('func') - expect(state[queryId].fields[0].value).to.equal('mean') + expect(state[queryID].fields.length).to.equal(1) + expect(state[queryID].fields[0].type).to.equal('func') + expect(state[queryID].fields[0].value).to.equal('mean') const newState = reducer( state, - toggleField(queryId, { + toggleField(queryID, { value: 'f2', type: 'field', }) ) - expect(newState[queryId].fields[1].value).to.equal('mean') - expect(newState[queryId].fields[1].alias).to.equal('mean_f2') - expect(newState[queryId].fields[1].args).to.deep.equal([ + expect(newState[queryID].fields[1].value).to.equal('mean') + expect(newState[queryID].fields[1].alias).to.equal('mean_f2') + expect(newState[queryID].fields[1].args).to.deep.equal([ {value: 'f2', type: 'field'}, ]) - expect(newState[queryId].fields[1].type).to.equal('func') + expect(newState[queryID].fields[1].type).to.equal('func') }) it('adds the field property to query config if not found', () => { - delete state[queryId].fields - expect(state[queryId].fields).to.equal(undefined) + delete state[queryID].fields + expect(state[queryID].fields).to.equal(undefined) const newState = reducer( state, - toggleField(queryId, {value: 'fk1', type: 'field'}) + toggleField(queryID, {value: 'fk1', type: 'field'}) ) - expect(newState[queryId].fields.length).to.equal(1) + expect(newState[queryID].fields.length).to.equal(1) }) }) }) @@ -189,7 +189,7 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { const f4 = {value: 'f4', type: 'field'} const initialState = { - [queryId]: { + [queryID]: { id: 123, database: 'db1', measurement: 'm1', @@ -201,7 +201,7 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { }, } - const action = applyFuncsToField(queryId, { + const action = applyFuncsToField(queryID, { field: {value: 'f1', type: 'field'}, funcs: [ {value: 'fn3', type: 'func', args: []}, @@ -211,7 +211,7 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { const nextState = reducer(initialState, action) - expect(nextState[queryId].fields).to.deep.equal([ + expect(nextState[queryID].fields).to.deep.equal([ {value: 'fn3', type: 'func', args: [f1], alias: `fn3_${f1.value}`}, {value: 'fn4', type: 'func', args: [f1], alias: `fn4_${f1.value}`}, {value: 'fn1', type: 'func', args: [f2], alias: `fn1_${f2.value}`}, @@ -230,7 +230,7 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { const groupBy = {time: '1m', tags: []} const initialState = { - [queryId]: { + [queryID]: { id: 123, database: 'db1', measurement: 'm1', @@ -239,35 +239,35 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { }, } - const action = removeFuncs(queryId, fields, groupBy) + const action = removeFuncs(queryID, fields, groupBy) const nextState = reducer(initialState, action) - const actual = nextState[queryId].fields + const actual = nextState[queryID].fields const expected = [f1, f2] expect(actual).to.eql(expected) - expect(nextState[queryId].groupBy.time).to.equal(null) + expect(nextState[queryID].groupBy.time).to.equal(null) }) }) describe('DE_CHOOSE_TAG', () => { it('adds a tag key/value to the query', () => { const initialState = { - [queryId]: buildInitialState(queryId, { + [queryID]: buildInitialState(queryID, { tags: { k1: ['v0'], k2: ['foo'], }, }), } - const action = chooseTag(queryId, { + const action = chooseTag(queryID, { key: 'k1', value: 'v1', }) const nextState = reducer(initialState, action) - expect(nextState[queryId].tags).to.eql({ + expect(nextState[queryID].tags).to.eql({ k1: ['v0', 'v1'], k2: ['foo'], }) @@ -275,31 +275,31 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { it("creates a new entry if it's the first key", () => { const initialState = { - [queryId]: buildInitialState(queryId, { + [queryID]: buildInitialState(queryID, { tags: {}, }), } - const action = chooseTag(queryId, { + const action = chooseTag(queryID, { key: 'k1', value: 'v1', }) const nextState = reducer(initialState, action) - expect(nextState[queryId].tags).to.eql({ + expect(nextState[queryID].tags).to.eql({ k1: ['v1'], }) }) it('removes a value that is already in the list', () => { const initialState = { - [queryId]: buildInitialState(queryId, { + [queryID]: buildInitialState(queryID, { tags: { k1: ['v1'], }, }), } - const action = chooseTag(queryId, { + const action = chooseTag(queryID, { key: 'k1', value: 'v1', }) @@ -307,14 +307,14 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { const nextState = reducer(initialState, action) // TODO: this should probably remove the `k1` property entirely from the tags object - expect(nextState[queryId].tags).to.eql({}) + expect(nextState[queryID].tags).to.eql({}) }) }) describe('DE_GROUP_BY_TAG', () => { it('adds a tag key/value to the query', () => { const initialState = { - [queryId]: { + [queryID]: { id: 123, database: 'db1', measurement: 'm1', @@ -323,11 +323,11 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { groupBy: {tags: [], time: null}, }, } - const action = groupByTag(queryId, 'k1') + const action = groupByTag(queryID, 'k1') const nextState = reducer(initialState, action) - expect(nextState[queryId].groupBy).to.eql({ + expect(nextState[queryID].groupBy).to.eql({ time: null, tags: ['k1'], }) @@ -335,7 +335,7 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { it('removes a tag if the given tag key is already in the GROUP BY list', () => { const initialState = { - [queryId]: { + [queryID]: { id: 123, database: 'db1', measurement: 'm1', @@ -344,11 +344,11 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { groupBy: {tags: ['k1'], time: null}, }, } - const action = groupByTag(queryId, 'k1') + const action = groupByTag(queryID, 'k1') const nextState = reducer(initialState, action) - expect(nextState[queryId].groupBy).to.eql({ + expect(nextState[queryID].groupBy).to.eql({ time: null, tags: [], }) @@ -358,14 +358,14 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { describe('DE_TOGGLE_TAG_ACCEPTANCE', () => { it('it toggles areTagsAccepted', () => { const initialState = { - [queryId]: buildInitialState(queryId), + [queryID]: buildInitialState(queryID), } - const action = toggleTagAcceptance(queryId) + const action = toggleTagAcceptance(queryID) const nextState = reducer(initialState, action) - expect(nextState[queryId].areTagsAccepted).to.equal( - !initialState[queryId].areTagsAccepted + expect(nextState[queryID].areTagsAccepted).to.equal( + !initialState[queryID].areTagsAccepted ) }) }) @@ -374,99 +374,99 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { it('applys the appropriate group by time', () => { const time = '100y' const initialState = { - [queryId]: buildInitialState(queryId), + [queryID]: buildInitialState(queryID), } - const action = groupByTime(queryId, time) + const action = groupByTime(queryID, time) const nextState = reducer(initialState, action) - expect(nextState[queryId].groupBy.time).to.equal(time) + expect(nextState[queryID].groupBy.time).to.equal(time) }) }) it('updates entire config', () => { const initialState = { - [queryId]: buildInitialState(queryId), + [queryID]: buildInitialState(queryID), } - const expected = defaultQueryConfig({id: queryId}, {rawText: 'hello'}) + const expected = defaultQueryConfig({id: queryID}, {rawText: 'hello'}) const action = updateQueryConfig(expected) const nextState = reducer(initialState, action) - expect(nextState[queryId]).to.deep.equal(expected) + expect(nextState[queryID]).to.deep.equal(expected) }) it("updates a query's raw text", () => { const initialState = { - [queryId]: buildInitialState(queryId), + [queryID]: buildInitialState(queryID), } const text = 'foo' - const action = updateRawQuery(queryId, text) + const action = updateRawQuery(queryID, text) const nextState = reducer(initialState, action) - expect(nextState[queryId].rawText).to.equal('foo') + expect(nextState[queryID].rawText).to.equal('foo') }) it("updates a query's raw status", () => { const initialState = { - [queryId]: buildInitialState(queryId), + [queryID]: buildInitialState(queryID), } const status = 'your query was sweet' - const action = editQueryStatus(queryId, status) + const action = editQueryStatus(queryID, status) const nextState = reducer(initialState, action) - expect(nextState[queryId].status).to.equal(status) + expect(nextState[queryID].status).to.equal(status) }) describe('DE_FILL', () => { it('applies an explicit fill when group by time is used', () => { const initialState = { - [queryId]: buildInitialState(queryId), + [queryID]: buildInitialState(queryID), } const time = '10s' - const action = groupByTime(queryId, time) + const action = groupByTime(queryID, time) const nextState = reducer(initialState, action) - expect(nextState[queryId].fill).to.equal(NULL_STRING) + expect(nextState[queryID].fill).to.equal(NULL_STRING) }) it('updates fill to non-null-string non-number string value', () => { const initialState = { - [queryId]: buildInitialState(queryId), + [queryID]: buildInitialState(queryID), } - const action = fill(queryId, LINEAR) + const action = fill(queryID, LINEAR) const nextState = reducer(initialState, action) - expect(nextState[queryId].fill).to.equal(LINEAR) + expect(nextState[queryID].fill).to.equal(LINEAR) }) it('updates fill to string integer value', () => { const initialState = { - [queryId]: buildInitialState(queryId), + [queryID]: buildInitialState(queryID), } const INT_STRING = '1337' - const action = fill(queryId, INT_STRING) + const action = fill(queryID, INT_STRING) const nextState = reducer(initialState, action) - expect(nextState[queryId].fill).to.equal(INT_STRING) + expect(nextState[queryID].fill).to.equal(INT_STRING) }) it('updates fill to string float value', () => { const initialState = { - [queryId]: buildInitialState(queryId), + [queryID]: buildInitialState(queryID), } const FLOAT_STRING = '1.337' - const action = fill(queryId, FLOAT_STRING) + const action = fill(queryID, FLOAT_STRING) const nextState = reducer(initialState, action) - expect(nextState[queryId].fill).to.equal(FLOAT_STRING) + expect(nextState[queryID].fill).to.equal(FLOAT_STRING) }) }) }) From 2c89f6ebb1306600394db2983b6dfec5df04cb08 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 3 Nov 2017 13:57:06 -0700 Subject: [PATCH 032/138] One ID to rule them all and in the darkness bind them --- ui/spec/kapacitor/reducers/queryConfigSpec.js | 126 +++++++++--------- ui/src/data_explorer/actions/view/index.js | 40 +++--- ui/src/data_explorer/reducers/queryConfigs.js | 62 ++++----- ui/src/kapacitor/actions/queryConfigs.js | 32 ++--- ui/src/kapacitor/reducers/queryConfigs.js | 52 ++++---- ui/src/shared/apis/metaQuery.js | 4 +- 6 files changed, 158 insertions(+), 158 deletions(-) diff --git a/ui/spec/kapacitor/reducers/queryConfigSpec.js b/ui/spec/kapacitor/reducers/queryConfigSpec.js index f21b04649..6ecbf5160 100644 --- a/ui/spec/kapacitor/reducers/queryConfigSpec.js +++ b/ui/spec/kapacitor/reducers/queryConfigSpec.js @@ -18,142 +18,142 @@ const fakeAddQueryAction = (panelID, queryID) => { } } -function buildInitialState(queryId, params) { +function buildInitialState(queryID, params) { return Object.assign( {}, - defaultQueryConfig({id: queryId, isKapacitorRule: true}), + defaultQueryConfig({id: queryID, isKapacitorRule: true}), params ) } describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => { - const queryId = 123 + const queryID = 123 it('can add a query', () => { - const state = reducer({}, fakeAddQueryAction('blah', queryId)) + const state = reducer({}, fakeAddQueryAction('blah', queryID)) - const actual = state[queryId] - const expected = defaultQueryConfig({id: queryId, isKapacitorRule: true}) + const actual = state[queryID] + const expected = defaultQueryConfig({id: queryID, isKapacitorRule: true}) expect(actual).to.deep.equal(expected) }) describe('choosing db, rp, and measurement', () => { let state beforeEach(() => { - state = reducer({}, fakeAddQueryAction('any', queryId)) + state = reducer({}, fakeAddQueryAction('any', queryID)) }) it('sets the db and rp', () => { const newState = reducer( state, - chooseNamespace(queryId, { + chooseNamespace(queryID, { database: 'telegraf', retentionPolicy: 'monitor', }) ) - expect(newState[queryId].database).to.equal('telegraf') - expect(newState[queryId].retentionPolicy).to.equal('monitor') + expect(newState[queryID].database).to.equal('telegraf') + expect(newState[queryID].retentionPolicy).to.equal('monitor') }) it('sets the measurement', () => { - const newState = reducer(state, chooseMeasurement(queryId, 'mem')) + const newState = reducer(state, chooseMeasurement(queryID, 'mem')) - expect(newState[queryId].measurement).to.equal('mem') + expect(newState[queryID].measurement).to.equal('mem') }) }) describe('a query has measurements and fields', () => { let state beforeEach(() => { - const one = reducer({}, fakeAddQueryAction('any', queryId)) + const one = reducer({}, fakeAddQueryAction('any', queryID)) const two = reducer( one, - chooseNamespace(queryId, { + chooseNamespace(queryID, { database: '_internal', retentionPolicy: 'daily', }) ) - const three = reducer(two, chooseMeasurement(queryId, 'disk')) + const three = reducer(two, chooseMeasurement(queryID, 'disk')) state = reducer( three, - toggleField(queryId, {value: 'a great field', funcs: []}) + toggleField(queryID, {value: 'a great field', funcs: []}) ) }) describe('choosing a new namespace', () => { it('clears out the old measurement and fields', () => { // what about tags? - expect(state[queryId].measurement).to.exist - expect(state[queryId].fields.length).to.equal(1) + expect(state[queryID].measurement).to.exist + expect(state[queryID].fields.length).to.equal(1) const newState = reducer( state, - chooseNamespace(queryId, { + chooseNamespace(queryID, { database: 'newdb', retentionPolicy: 'newrp', }) ) - expect(newState[queryId].measurement).not.to.exist - expect(newState[queryId].fields.length).to.equal(0) + expect(newState[queryID].measurement).not.to.exist + expect(newState[queryID].fields.length).to.equal(0) }) }) describe('choosing a new measurement', () => { it('leaves the namespace and clears out the old fields', () => { // what about tags? - expect(state[queryId].fields.length).to.equal(1) + expect(state[queryID].fields.length).to.equal(1) const newState = reducer( state, - chooseMeasurement(queryId, 'newmeasurement') + chooseMeasurement(queryID, 'newmeasurement') ) - expect(state[queryId].database).to.equal(newState[queryId].database) - expect(state[queryId].retentionPolicy).to.equal( - newState[queryId].retentionPolicy + expect(state[queryID].database).to.equal(newState[queryID].database) + expect(state[queryID].retentionPolicy).to.equal( + newState[queryID].retentionPolicy ) - expect(newState[queryId].fields.length).to.equal(0) + expect(newState[queryID].fields.length).to.equal(0) }) }) describe('when the query is part of a kapacitor rule', () => { it('only allows one field', () => { - expect(state[queryId].fields.length).to.equal(1) + expect(state[queryID].fields.length).to.equal(1) const newState = reducer( state, - toggleField(queryId, {value: 'a different field', type: 'field'}) + toggleField(queryID, {value: 'a different field', type: 'field'}) ) - expect(newState[queryId].fields.length).to.equal(1) - expect(newState[queryId].fields[0].value).to.equal('a different field') + expect(newState[queryID].fields.length).to.equal(1) + expect(newState[queryID].fields[0].value).to.equal('a different field') }) }) describe('KAPA_TOGGLE_FIELD', () => { it('cannot toggle multiple fields', () => { - expect(state[queryId].fields.length).to.equal(1) + expect(state[queryID].fields.length).to.equal(1) const newState = reducer( state, - toggleField(queryId, {value: 'a different field', type: 'field'}) + toggleField(queryID, {value: 'a different field', type: 'field'}) ) - expect(newState[queryId].fields.length).to.equal(1) - expect(newState[queryId].fields[0].value).to.equal('a different field') + expect(newState[queryID].fields.length).to.equal(1) + expect(newState[queryID].fields[0].value).to.equal('a different field') }) it('applies no funcs to newly selected fields', () => { - expect(state[queryId].fields.length).to.equal(1) + expect(state[queryID].fields.length).to.equal(1) const newState = reducer( state, - toggleField(queryId, {value: 'a different field', type: 'field'}) + toggleField(queryID, {value: 'a different field', type: 'field'}) ) - expect(newState[queryId].fields[0].type).to.equal('field') + expect(newState[queryID].fields[0].type).to.equal('field') }) }) }) @@ -162,7 +162,7 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => { it('applies functions to a field without any existing functions', () => { const f1 = {value: 'f1', type: 'field'} const initialState = { - [queryId]: { + [queryID]: { id: 123, database: 'db1', measurement: 'm1', @@ -174,13 +174,13 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => { }, } - const action = applyFuncsToField(queryId, { + const action = applyFuncsToField(queryID, { field: {value: 'f1', type: 'field'}, funcs: [{value: 'fn3', type: 'func'}, {value: 'fn4', type: 'func'}], }) const nextState = reducer(initialState, action) - const actual = nextState[queryId].fields + const actual = nextState[queryID].fields const expected = [ {value: 'fn3', type: 'func', args: [f1], alias: `fn3_${f1.value}`}, {value: 'fn4', type: 'func', args: [f1], alias: `fn4_${f1.value}`}, @@ -193,21 +193,21 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => { describe('KAPA_CHOOSE_TAG', () => { it('adds a tag key/value to the query', () => { const initialState = { - [queryId]: buildInitialState(queryId, { + [queryID]: buildInitialState(queryID, { tags: { k1: ['v0'], k2: ['foo'], }, }), } - const action = chooseTag(queryId, { + const action = chooseTag(queryID, { key: 'k1', value: 'v1', }) const nextState = reducer(initialState, action) - expect(nextState[queryId].tags).to.eql({ + expect(nextState[queryID].tags).to.eql({ k1: ['v0', 'v1'], k2: ['foo'], }) @@ -215,31 +215,31 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => { it("creates a new entry if it's the first key", () => { const initialState = { - [queryId]: buildInitialState(queryId, { + [queryID]: buildInitialState(queryID, { tags: {}, }), } - const action = chooseTag(queryId, { + const action = chooseTag(queryID, { key: 'k1', value: 'v1', }) const nextState = reducer(initialState, action) - expect(nextState[queryId].tags).to.eql({ + expect(nextState[queryID].tags).to.eql({ k1: ['v1'], }) }) it('removes a value that is already in the list', () => { const initialState = { - [queryId]: buildInitialState(queryId, { + [queryID]: buildInitialState(queryID, { tags: { k1: ['v1'], }, }), } - const action = chooseTag(queryId, { + const action = chooseTag(queryID, { key: 'k1', value: 'v1', }) @@ -247,14 +247,14 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => { const nextState = reducer(initialState, action) // TODO: this should probably remove the `k1` property entirely from the tags object - expect(nextState[queryId].tags).to.eql({}) + expect(nextState[queryID].tags).to.eql({}) }) }) describe('KAPA_GROUP_BY_TAG', () => { it('adds a tag key/value to the query', () => { const initialState = { - [queryId]: { + [queryID]: { id: 123, database: 'db1', measurement: 'm1', @@ -263,11 +263,11 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => { groupBy: {tags: [], time: null}, }, } - const action = groupByTag(queryId, 'k1') + const action = groupByTag(queryID, 'k1') const nextState = reducer(initialState, action) - expect(nextState[queryId].groupBy).to.eql({ + expect(nextState[queryID].groupBy).to.eql({ time: null, tags: ['k1'], }) @@ -275,7 +275,7 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => { it('removes a tag if the given tag key is already in the GROUP BY list', () => { const initialState = { - [queryId]: { + [queryID]: { id: 123, database: 'db1', measurement: 'm1', @@ -284,11 +284,11 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => { groupBy: {tags: ['k1'], time: null}, }, } - const action = groupByTag(queryId, 'k1') + const action = groupByTag(queryID, 'k1') const nextState = reducer(initialState, action) - expect(nextState[queryId].groupBy).to.eql({ + expect(nextState[queryID].groupBy).to.eql({ time: null, tags: [], }) @@ -298,14 +298,14 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => { describe('KAPA_TOGGLE_TAG_ACCEPTANCE', () => { it('it toggles areTagsAccepted', () => { const initialState = { - [queryId]: buildInitialState(queryId), + [queryID]: buildInitialState(queryID), } - const action = toggleTagAcceptance(queryId) + const action = toggleTagAcceptance(queryID) const nextState = reducer(initialState, action) - expect(nextState[queryId].areTagsAccepted).to.equal( - !initialState[queryId].areTagsAccepted + expect(nextState[queryID].areTagsAccepted).to.equal( + !initialState[queryID].areTagsAccepted ) }) }) @@ -314,14 +314,14 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => { it('applys the appropriate group by time', () => { const time = '100y' const initialState = { - [queryId]: buildInitialState(queryId), + [queryID]: buildInitialState(queryID), } - const action = groupByTime(queryId, time) + const action = groupByTime(queryID, time) const nextState = reducer(initialState, action) - expect(nextState[queryId].groupBy.time).to.equal(time) + expect(nextState[queryID].groupBy.time).to.equal(time) }) }) }) diff --git a/ui/src/data_explorer/actions/view/index.js b/ui/src/data_explorer/actions/view/index.js index 0c12cb207..602f2cd9d 100644 --- a/ui/src/data_explorer/actions/view/index.js +++ b/ui/src/data_explorer/actions/view/index.js @@ -18,26 +18,26 @@ export const deleteQuery = queryID => ({ }, }) -export const toggleField = (queryId, fieldFunc) => ({ +export const toggleField = (queryID, fieldFunc) => ({ type: 'DE_TOGGLE_FIELD', payload: { - queryId, + queryID, fieldFunc, }, }) -export const groupByTime = (queryId, time) => ({ +export const groupByTime = (queryID, time) => ({ type: 'DE_GROUP_BY_TIME', payload: { - queryId, + queryID, time, }, }) -export const fill = (queryId, value) => ({ +export const fill = (queryID, value) => ({ type: 'DE_FILL', payload: { - queryId, + queryID, value, }, }) @@ -51,44 +51,44 @@ export const removeFuncs = (queryID, fields, groupBy) => ({ }, }) -export const applyFuncsToField = (queryId, fieldFunc, groupBy) => ({ +export const applyFuncsToField = (queryID, fieldFunc, groupBy) => ({ type: 'DE_APPLY_FUNCS_TO_FIELD', payload: { - queryId, + queryID, fieldFunc, groupBy, }, }) -export const chooseTag = (queryId, tag) => ({ +export const chooseTag = (queryID, tag) => ({ type: 'DE_CHOOSE_TAG', payload: { - queryId, + queryID, tag, }, }) -export const chooseNamespace = (queryId, {database, retentionPolicy}) => ({ +export const chooseNamespace = (queryID, {database, retentionPolicy}) => ({ type: 'DE_CHOOSE_NAMESPACE', payload: { - queryId, + queryID, database, retentionPolicy, }, }) -export const chooseMeasurement = (queryId, measurement) => ({ +export const chooseMeasurement = (queryID, measurement) => ({ type: 'DE_CHOOSE_MEASUREMENT', payload: { - queryId, + queryID, measurement, }, }) -export const editRawText = (queryId, rawText) => ({ +export const editRawText = (queryID, rawText) => ({ type: 'DE_EDIT_RAW_TEXT', payload: { - queryId, + queryID, rawText, }, }) @@ -100,18 +100,18 @@ export const setTimeRange = bounds => ({ }, }) -export const groupByTag = (queryId, tagKey) => ({ +export const groupByTag = (queryID, tagKey) => ({ type: 'DE_GROUP_BY_TAG', payload: { - queryId, + queryID, tagKey, }, }) -export const toggleTagAcceptance = queryId => ({ +export const toggleTagAcceptance = queryID => ({ type: 'DE_TOGGLE_TAG_ACCEPTANCE', payload: { - queryId, + queryID, }, }) diff --git a/ui/src/data_explorer/reducers/queryConfigs.js b/ui/src/data_explorer/reducers/queryConfigs.js index 3837ca36a..fe193f1bf 100644 --- a/ui/src/data_explorer/reducers/queryConfigs.js +++ b/ui/src/data_explorer/reducers/queryConfigs.js @@ -20,24 +20,24 @@ import { const queryConfigs = (state = {}, action) => { switch (action.type) { case 'DE_CHOOSE_NAMESPACE': { - const {queryId, database, retentionPolicy} = action.payload - const nextQueryConfig = chooseNamespace(state[queryId], { + const {queryID, database, retentionPolicy} = action.payload + const nextQueryConfig = chooseNamespace(state[queryID], { database, retentionPolicy, }) return Object.assign({}, state, { - [queryId]: Object.assign(nextQueryConfig, {rawText: null}), + [queryID]: Object.assign(nextQueryConfig, {rawText: null}), }) } case 'DE_CHOOSE_MEASUREMENT': { - const {queryId, measurement} = action.payload - const nextQueryConfig = chooseMeasurement(state[queryId], measurement) + const {queryID, measurement} = action.payload + const nextQueryConfig = chooseMeasurement(state[queryID], measurement) return Object.assign({}, state, { - [queryId]: Object.assign(nextQueryConfig, { - rawText: state[queryId].rawText, + [queryID]: Object.assign(nextQueryConfig, { + rawText: state[queryID].rawText, }), }) } @@ -64,78 +64,78 @@ const queryConfigs = (state = {}, action) => { } case 'DE_EDIT_RAW_TEXT': { - const {queryId, rawText} = action.payload - const nextQueryConfig = editRawText(state[queryId], rawText) + const {queryID, rawText} = action.payload + const nextQueryConfig = editRawText(state[queryID], rawText) return Object.assign({}, state, { - [queryId]: nextQueryConfig, + [queryID]: nextQueryConfig, }) } case 'DE_GROUP_BY_TIME': { - const {queryId, time} = action.payload - const nextQueryConfig = groupByTime(state[queryId], time) + const {queryID, time} = action.payload + const nextQueryConfig = groupByTime(state[queryID], time) return Object.assign({}, state, { - [queryId]: nextQueryConfig, + [queryID]: nextQueryConfig, }) } case 'DE_TOGGLE_TAG_ACCEPTANCE': { - const {queryId} = action.payload - const nextQueryConfig = toggleTagAcceptance(state[queryId]) + const {queryID} = action.payload + const nextQueryConfig = toggleTagAcceptance(state[queryID]) return Object.assign({}, state, { - [queryId]: nextQueryConfig, + [queryID]: nextQueryConfig, }) } case 'DE_TOGGLE_FIELD': { - const {queryId, fieldFunc} = action.payload - const nextQueryConfig = toggleField(state[queryId], fieldFunc) + const {queryID, fieldFunc} = action.payload + const nextQueryConfig = toggleField(state[queryID], fieldFunc) return Object.assign({}, state, { - [queryId]: {...nextQueryConfig, rawText: null}, + [queryID]: {...nextQueryConfig, rawText: null}, }) } case 'DE_APPLY_FUNCS_TO_FIELD': { - const {queryId, fieldFunc, groupBy} = action.payload + const {queryID, fieldFunc, groupBy} = action.payload const nextQueryConfig = applyFuncsToField( - state[queryId], + state[queryID], fieldFunc, groupBy ) return Object.assign({}, state, { - [queryId]: nextQueryConfig, + [queryID]: nextQueryConfig, }) } case 'DE_CHOOSE_TAG': { - const {queryId, tag} = action.payload - const nextQueryConfig = chooseTag(state[queryId], tag) + const {queryID, tag} = action.payload + const nextQueryConfig = chooseTag(state[queryID], tag) return Object.assign({}, state, { - [queryId]: nextQueryConfig, + [queryID]: nextQueryConfig, }) } case 'DE_GROUP_BY_TAG': { - const {queryId, tagKey} = action.payload - const nextQueryConfig = groupByTag(state[queryId], tagKey) + const {queryID, tagKey} = action.payload + const nextQueryConfig = groupByTag(state[queryID], tagKey) return Object.assign({}, state, { - [queryId]: nextQueryConfig, + [queryID]: nextQueryConfig, }) } case 'DE_FILL': { - const {queryId, value} = action.payload - const nextQueryConfig = fill(state[queryId], value) + const {queryID, value} = action.payload + const nextQueryConfig = fill(state[queryID], value) return { ...state, - [queryId]: nextQueryConfig, + [queryID]: nextQueryConfig, } } diff --git a/ui/src/kapacitor/actions/queryConfigs.js b/ui/src/kapacitor/actions/queryConfigs.js index f25564234..e579b31c3 100644 --- a/ui/src/kapacitor/actions/queryConfigs.js +++ b/ui/src/kapacitor/actions/queryConfigs.js @@ -1,63 +1,63 @@ -export const chooseNamespace = (queryId, {database, retentionPolicy}) => ({ +export const chooseNamespace = (queryID, {database, retentionPolicy}) => ({ type: 'KAPA_CHOOSE_NAMESPACE', payload: { - queryId, + queryID, database, retentionPolicy, }, }) -export const chooseMeasurement = (queryId, measurement) => ({ +export const chooseMeasurement = (queryID, measurement) => ({ type: 'KAPA_CHOOSE_MEASUREMENT', payload: { - queryId, + queryID, measurement, }, }) -export const chooseTag = (queryId, tag) => ({ +export const chooseTag = (queryID, tag) => ({ type: 'KAPA_CHOOSE_TAG', payload: { - queryId, + queryID, tag, }, }) -export const groupByTag = (queryId, tagKey) => ({ +export const groupByTag = (queryID, tagKey) => ({ type: 'KAPA_GROUP_BY_TAG', payload: { - queryId, + queryID, tagKey, }, }) -export const toggleTagAcceptance = queryId => ({ +export const toggleTagAcceptance = queryID => ({ type: 'KAPA_TOGGLE_TAG_ACCEPTANCE', payload: { - queryId, + queryID, }, }) -export const toggleField = (queryId, fieldFunc) => ({ +export const toggleField = (queryID, fieldFunc) => ({ type: 'KAPA_TOGGLE_FIELD', payload: { - queryId, + queryID, fieldFunc, }, }) -export const applyFuncsToField = (queryId, fieldFunc) => ({ +export const applyFuncsToField = (queryID, fieldFunc) => ({ type: 'KAPA_APPLY_FUNCS_TO_FIELD', payload: { - queryId, + queryID, fieldFunc, }, }) -export const groupByTime = (queryId, time) => ({ +export const groupByTime = (queryID, time) => ({ type: 'KAPA_GROUP_BY_TIME', payload: { - queryId, + queryID, time, }, }) diff --git a/ui/src/kapacitor/reducers/queryConfigs.js b/ui/src/kapacitor/reducers/queryConfigs.js index 69688f3ac..5ea652a99 100644 --- a/ui/src/kapacitor/reducers/queryConfigs.js +++ b/ui/src/kapacitor/reducers/queryConfigs.js @@ -34,9 +34,9 @@ const queryConfigs = (state = {}, action) => { } case 'KAPA_CHOOSE_NAMESPACE': { - const {queryId, database, retentionPolicy} = action.payload + const {queryID, database, retentionPolicy} = action.payload const nextQueryConfig = chooseNamespace( - state[queryId], + state[queryID], { database, retentionPolicy, @@ -45,75 +45,75 @@ const queryConfigs = (state = {}, action) => { ) return Object.assign({}, state, { - [queryId]: Object.assign(nextQueryConfig, {rawText: null}), + [queryID]: Object.assign(nextQueryConfig, {rawText: null}), }) } case 'KAPA_CHOOSE_MEASUREMENT': { - const {queryId, measurement} = action.payload + const {queryID, measurement} = action.payload const nextQueryConfig = chooseMeasurement( - state[queryId], + state[queryID], measurement, IS_KAPACITOR_RULE ) return Object.assign({}, state, { - [queryId]: Object.assign(nextQueryConfig, { - rawText: state[queryId].rawText, + [queryID]: Object.assign(nextQueryConfig, { + rawText: state[queryID].rawText, }), }) } case 'KAPA_CHOOSE_TAG': { - const {queryId, tag} = action.payload - const nextQueryConfig = chooseTag(state[queryId], tag) + const {queryID, tag} = action.payload + const nextQueryConfig = chooseTag(state[queryID], tag) return Object.assign({}, state, { - [queryId]: nextQueryConfig, + [queryID]: nextQueryConfig, }) } case 'KAPA_GROUP_BY_TAG': { - const {queryId, tagKey} = action.payload - const nextQueryConfig = groupByTag(state[queryId], tagKey) + const {queryID, tagKey} = action.payload + const nextQueryConfig = groupByTag(state[queryID], tagKey) return Object.assign({}, state, { - [queryId]: nextQueryConfig, + [queryID]: nextQueryConfig, }) } case 'KAPA_TOGGLE_TAG_ACCEPTANCE': { - const {queryId} = action.payload - const nextQueryConfig = toggleTagAcceptance(state[queryId]) + const {queryID} = action.payload + const nextQueryConfig = toggleTagAcceptance(state[queryID]) return Object.assign({}, state, { - [queryId]: nextQueryConfig, + [queryID]: nextQueryConfig, }) } case 'KAPA_TOGGLE_FIELD': { - const {queryId, fieldFunc} = action.payload - const nextQueryConfig = toggleKapaField(state[queryId], fieldFunc) + const {queryID, fieldFunc} = action.payload + const nextQueryConfig = toggleKapaField(state[queryID], fieldFunc) - return {...state, [queryId]: {...nextQueryConfig, rawText: null}} + return {...state, [queryID]: {...nextQueryConfig, rawText: null}} } case 'KAPA_APPLY_FUNCS_TO_FIELD': { - const {queryId, fieldFunc} = action.payload - const {groupBy} = state[queryId] - const nextQueryConfig = applyFuncsToField(state[queryId], fieldFunc, { + const {queryID, fieldFunc} = action.payload + const {groupBy} = state[queryID] + const nextQueryConfig = applyFuncsToField(state[queryID], fieldFunc, { ...groupBy, time: groupBy.time ? groupBy.time : '10s', }) - return {...state, [queryId]: nextQueryConfig} + return {...state, [queryID]: nextQueryConfig} } case 'KAPA_GROUP_BY_TIME': { - const {queryId, time} = action.payload - const nextQueryConfig = groupByTime(state[queryId], time) + const {queryID, time} = action.payload + const nextQueryConfig = groupByTime(state[queryID], time) return Object.assign({}, state, { - [queryId]: nextQueryConfig, + [queryID]: nextQueryConfig, }) } diff --git a/ui/src/shared/apis/metaQuery.js b/ui/src/shared/apis/metaQuery.js index 5e6733ff9..af5696ff7 100644 --- a/ui/src/shared/apis/metaQuery.js +++ b/ui/src/shared/apis/metaQuery.js @@ -24,8 +24,8 @@ export function showQueries(source, db) { return proxy({source, query, db}) } -export function killQuery(source, queryId) { - const query = `KILL QUERY ${queryId}` +export function killQuery(source, queryID) { + const query = `KILL QUERY ${queryID}` return proxy({source, query}) } From c4b3e8a2e84abfa01f573eacf9b9f7b051a13f99 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 3 Nov 2017 13:59:49 -0700 Subject: [PATCH 033/138] Remove yarn warnings --- ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/package.json b/ui/package.json index 13dc39531..2c627b0a2 100644 --- a/ui/package.json +++ b/ui/package.json @@ -17,7 +17,7 @@ "test": "karma start", "test:integration": "nightwatch tests --skip", "test:lint": "yarn run lint; yarn run test", - "test:dev": "concurrently \"yarn run lint -- --watch\" \"yarn run test -- --no-single-run --reporters=verbose\"", + "test:dev": "concurrently \"yarn run lint --watch\" \"yarn run test --no-single-run --reporters=verbose\"", "clean": "rm -rf build", "storybook": "node ./storybook.js", "prettier": "prettier --single-quote --trailing-comma es5 --bracket-spacing false --semi false --write \"{src,spec}/**/*.js\"; eslint src --fix" From 1f601fd6126051e5849bd1054e7002af6c46152d Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 3 Nov 2017 14:08:48 -0700 Subject: [PATCH 034/138] Add time shift to queryConfig reducer --- .../data_explorer/reducers/queryConfigSpec.js | 16 ++++++++++++++++ ui/src/data_explorer/actions/view/index.js | 8 ++++++++ ui/src/data_explorer/reducers/queryConfigs.js | 8 ++++++++ ui/src/utils/queryTransitions.js | 2 ++ 4 files changed, 34 insertions(+) diff --git a/ui/spec/data_explorer/reducers/queryConfigSpec.js b/ui/spec/data_explorer/reducers/queryConfigSpec.js index b4162f0e7..48670cef3 100644 --- a/ui/spec/data_explorer/reducers/queryConfigSpec.js +++ b/ui/spec/data_explorer/reducers/queryConfigSpec.js @@ -1,7 +1,9 @@ import reducer from 'src/data_explorer/reducers/queryConfigs' + import defaultQueryConfig from 'src/utils/defaultQueryConfig' import { fill, + timeShift, chooseTag, groupByTag, groupByTime, @@ -469,4 +471,18 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { expect(nextState[queryID].fill).to.equal(FLOAT_STRING) }) }) + + describe('DE_TIME_SHIFT', () => { + it('can shift the time', () => { + const initialState = { + [queryID]: buildInitialState(queryID), + } + + const shift = {multiple: 1, unit: 'd', duration: '1d'} + const action = timeShift(queryID, shift) + const nextState = reducer(initialState, action) + + expect(nextState[queryID].shift).to.deep.equal(shift) + }) + }) }) diff --git a/ui/src/data_explorer/actions/view/index.js b/ui/src/data_explorer/actions/view/index.js index 602f2cd9d..f01d120e0 100644 --- a/ui/src/data_explorer/actions/view/index.js +++ b/ui/src/data_explorer/actions/view/index.js @@ -147,6 +147,14 @@ export const editQueryStatus = (queryID, status) => ({ }, }) +export const timeShift = (queryID, shift) => ({ + type: 'DE_TIME_SHIFT', + payload: { + queryID, + shift, + }, +}) + // Async actions export const editRawTextAsync = (url, id, text) => async dispatch => { try { diff --git a/ui/src/data_explorer/reducers/queryConfigs.js b/ui/src/data_explorer/reducers/queryConfigs.js index fe193f1bf..f8c765907 100644 --- a/ui/src/data_explorer/reducers/queryConfigs.js +++ b/ui/src/data_explorer/reducers/queryConfigs.js @@ -3,6 +3,7 @@ import _ from 'lodash' import defaultQueryConfig from 'src/utils/defaultQueryConfig' import { fill, + timeShift, chooseTag, groupByTag, removeFuncs, @@ -171,6 +172,13 @@ const queryConfigs = (state = {}, action) => { return {...state, [queryID]: nextQuery} } + + case 'DE_TIME_SHIFT': { + const {queryID, shift} = action.payload + const nextQuery = timeShift(state[queryID], shift) + + return {...state, [queryID]: nextQuery} + } } return state } diff --git a/ui/src/utils/queryTransitions.js b/ui/src/utils/queryTransitions.js index 40ad05423..7353422c2 100644 --- a/ui/src/utils/queryTransitions.js +++ b/ui/src/utils/queryTransitions.js @@ -242,3 +242,5 @@ export const chooseTag = (query, tag) => { return updateTagValues(query.tags[tag.key].concat(tag.value)) } + +export const timeShift = (query, shift) => ({...query, shift}) From 851f6f40480ff51fc3458f91ae2a7fdcf522a4da Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 3 Nov 2017 15:54:30 -0700 Subject: [PATCH 035/138] Link timeShift to queryConfig in Dashboards --- ui/src/shared/components/FieldList.js | 9 ++++++--- ui/src/shared/components/QueryOptions.js | 2 +- ui/src/shared/components/SchemaExplorer.js | 10 ++++++---- ui/src/utils/defaultQueryConfig.js | 1 + 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/ui/src/shared/components/FieldList.js b/ui/src/shared/components/FieldList.js index 833e0c9fd..0a21f311c 100644 --- a/ui/src/shared/components/FieldList.js +++ b/ui/src/shared/components/FieldList.js @@ -107,7 +107,7 @@ class FieldList extends Component { } handleTimeShift = shift => { - console.log('you just got shifted: ', shift) + this.props.onTimeShift(shift) } _getFields = () => { @@ -132,13 +132,12 @@ class FieldList extends Component { render() { const { - query: {database, measurement, fields = [], groupBy, fill}, + query: {database, measurement, fields = [], groupBy, fill, shift}, isKapacitorRule, } = this.props const hasAggregates = numFunctions(fields) > 0 const noDBorMeas = !database || !measurement - const shift = {duration: ''} return (
@@ -214,7 +213,11 @@ FieldList.propTypes = { database: string, retentionPolicy: string, measurement: string, + shift: shape({ + duration: string, + }), }).isRequired, + onTimeShift: func.isRequired, onToggleField: func.isRequired, onGroupByTime: func.isRequired, onFill: func, diff --git a/ui/src/shared/components/QueryOptions.js b/ui/src/shared/components/QueryOptions.js index 8cd387254..05bbd011b 100644 --- a/ui/src/shared/components/QueryOptions.js +++ b/ui/src/shared/components/QueryOptions.js @@ -18,7 +18,7 @@ const QueryOptions = ({ onChooseGroupByTime={onGroupByTime} /> {isKapacitorRule ? null : } diff --git a/ui/src/shared/components/SchemaExplorer.js b/ui/src/shared/components/SchemaExplorer.js index 4683ad447..e3326b231 100644 --- a/ui/src/shared/components/SchemaExplorer.js +++ b/ui/src/shared/components/SchemaExplorer.js @@ -13,6 +13,7 @@ const SchemaExplorer = ({ initialGroupByTime, actions: { fill, + timeShift, chooseTag, groupByTag, groupByTime, @@ -44,13 +45,14 @@ const SchemaExplorer = ({ source={source} query={query} querySource={source} - initialGroupByTime={initialGroupByTime} - onToggleField={actionBinder(id, toggleField)} onFill={actionBinder(id, fill)} - onGroupByTime={actionBinder(id, groupByTime)} - applyFuncsToField={actionBinder(id, applyFuncsToField)} + initialGroupByTime={initialGroupByTime} + onTimeShift={actionBinder(id, timeShift)} removeFuncs={actionBinder(id, removeFuncs)} + onToggleField={actionBinder(id, toggleField)} + onGroupByTime={actionBinder(id, groupByTime)} addInitialField={actionBinder(id, addInitialField)} + applyFuncsToField={actionBinder(id, applyFuncsToField)} />
diff --git a/ui/src/utils/defaultQueryConfig.js b/ui/src/utils/defaultQueryConfig.js index 37b478448..deef0c042 100644 --- a/ui/src/utils/defaultQueryConfig.js +++ b/ui/src/utils/defaultQueryConfig.js @@ -15,6 +15,7 @@ const defaultQueryConfig = ({id, isKapacitorRule = false}) => { areTagsAccepted: true, rawText: null, status: null, + shift: null, } return isKapacitorRule ? queryConfig : {...queryConfig, fill: NULL_STRING} From ad55c4051b5ed11396c79a7f0c71ba94643dbccf Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 3 Nov 2017 16:53:16 -0700 Subject: [PATCH 036/138] Introduce timeRange query helpers --- ui/spec/shared/query/helpersSpec.js | 26 ++++++++++++++++++++++++++ ui/src/shared/constants/timeRange.js | 2 ++ ui/src/shared/query/helpers.js | 24 ++++++++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 ui/spec/shared/query/helpersSpec.js create mode 100644 ui/src/shared/constants/timeRange.js create mode 100644 ui/src/shared/query/helpers.js diff --git a/ui/spec/shared/query/helpersSpec.js b/ui/spec/shared/query/helpersSpec.js new file mode 100644 index 000000000..9c24c2b12 --- /dev/null +++ b/ui/spec/shared/query/helpersSpec.js @@ -0,0 +1,26 @@ +import {timeRangeType, shiftTimeRange} from 'shared/query/helpers' +import {ABSOLUTE} from 'shared/constants/timeRange' +const format = 'influxql' + +describe.only('Shared.Query.Helpers', () => { + describe('timeRangeTypes', () => { + it('return invlalid if no upper and lower', () => { + const upper = null + const lower = null + + const timeRange = {lower, upper} + + expect(timeRangeType(timeRange)).to.equal(ABSOLUTE) + }) + + it('it can detect absolute type', () => { + const tenMinutes = 600000 + const upper = Date.now() + const lower = upper - tenMinutes + + const timeRange = {lower, upper, format} + + expect(timeRangeType(timeRange)).to.equal(ABSOLUTE) + }) + }) +}) diff --git a/ui/src/shared/constants/timeRange.js b/ui/src/shared/constants/timeRange.js new file mode 100644 index 000000000..15294b750 --- /dev/null +++ b/ui/src/shared/constants/timeRange.js @@ -0,0 +1,2 @@ +export const ABSOLUTE = 'absolute' +export const INVALID = 'invalid' diff --git a/ui/src/shared/query/helpers.js b/ui/src/shared/query/helpers.js new file mode 100644 index 000000000..cb5f096c3 --- /dev/null +++ b/ui/src/shared/query/helpers.js @@ -0,0 +1,24 @@ +import moment from 'moment' +import {ABSOLUTE} from 'shared/constants/timeRange' + +// calc time range type +export const timeRangeType = ({upper, lower, type}) => { + if (!upper && !lower) { + return 'invalid' + } + + if (!type || type === 'influxql') { + const mUpper = moment(upper) + const mLower = moment(lower) + const isUpperValid = mUpper.isValid() + const isLowerValid = mLower.isValid() + + if (isUpperValid && isLowerValid) { + return ABSOLUTE + } + } + + return 'none' +} + +// based on time range type, calc the time shifted dates From 98ffb7d93f42b0f6d31598dc09a16aea93e3cfb7 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 6 Nov 2017 11:22:40 -0800 Subject: [PATCH 037/138] Add tests for all possible timeRange types --- ui/spec/shared/query/helpersSpec.js | 38 ++++++++++++++++++++---- ui/src/shared/constants/timeRange.js | 2 ++ ui/src/shared/query/helpers.js | 43 +++++++++++++++++++--------- 3 files changed, 64 insertions(+), 19 deletions(-) diff --git a/ui/spec/shared/query/helpersSpec.js b/ui/spec/shared/query/helpersSpec.js index 9c24c2b12..cff927dc0 100644 --- a/ui/spec/shared/query/helpersSpec.js +++ b/ui/spec/shared/query/helpersSpec.js @@ -1,19 +1,26 @@ import {timeRangeType, shiftTimeRange} from 'shared/query/helpers' -import {ABSOLUTE} from 'shared/constants/timeRange' -const format = 'influxql' +import moment from 'moment' +import { + INVALID, + ABSOLUTE, + INFLUXQL, + RELATIVE_LOWER, + RELATIVE_UPPER, +} from 'shared/constants/timeRange' +const format = INFLUXQL -describe.only('Shared.Query.Helpers', () => { +describe('Shared.Query.Helpers', () => { describe('timeRangeTypes', () => { - it('return invlalid if no upper and lower', () => { + it('returns invalid if no upper and lower', () => { const upper = null const lower = null const timeRange = {lower, upper} - expect(timeRangeType(timeRange)).to.equal(ABSOLUTE) + expect(timeRangeType(timeRange)).to.equal(INVALID) }) - it('it can detect absolute type', () => { + it('can detect absolute type', () => { const tenMinutes = 600000 const upper = Date.now() const lower = upper - tenMinutes @@ -22,5 +29,24 @@ describe.only('Shared.Query.Helpers', () => { expect(timeRangeType(timeRange)).to.equal(ABSOLUTE) }) + + it('can detect exclusive relative lower', () => { + const lower = 'now() - 15m' + const upper = null + + const timeRange = {lower, upper, format} + + expect(timeRangeType(timeRange)).to.equal(RELATIVE_LOWER) + }) + + it('can detect relative upper', () => { + const upper = 'now()' + const oneMinute = 60000 + const lower = Date.now() - oneMinute + + const timeRange = {lower, upper, format} + + expect(timeRangeType(timeRange)).to.equal(RELATIVE_UPPER) + }) }) }) diff --git a/ui/src/shared/constants/timeRange.js b/ui/src/shared/constants/timeRange.js index 15294b750..f9e4b083a 100644 --- a/ui/src/shared/constants/timeRange.js +++ b/ui/src/shared/constants/timeRange.js @@ -1,2 +1,4 @@ export const ABSOLUTE = 'absolute' export const INVALID = 'invalid' +export const RELATIVE_LOWER = 'relative lower' +export const RELATIVE_UPPER = 'relative upper' diff --git a/ui/src/shared/query/helpers.js b/ui/src/shared/query/helpers.js index cb5f096c3..d8a9f076e 100644 --- a/ui/src/shared/query/helpers.js +++ b/ui/src/shared/query/helpers.js @@ -1,24 +1,41 @@ import moment from 'moment' -import {ABSOLUTE} from 'shared/constants/timeRange' +import { + INFLUXQL, + ABSOLUTE, + INVALID, + RELATIVE_LOWER, + RELATIVE_UPPER, +} from 'shared/constants/timeRange' +const now = /^now/ -// calc time range type export const timeRangeType = ({upper, lower, type}) => { if (!upper && !lower) { - return 'invalid' + return INVALID } - if (!type || type === 'influxql') { - const mUpper = moment(upper) - const mLower = moment(lower) - const isUpperValid = mUpper.isValid() - const isLowerValid = mLower.isValid() - - if (isUpperValid && isLowerValid) { - return ABSOLUTE - } + if (type && type !== INFLUXQL) { + return INVALID } - return 'none' + const isUpperValid = moment(upper).isValid() + const isLowerValid = moment(lower).isValid() + + // {lower: , upper: } + if (isLowerValid && isUpperValid) { + return ABSOLUTE + } + + // {lower: now - , upper: } + if (now.test(lower) && !upper) { + return RELATIVE_LOWER + } + + // {lower: , upper: now() - } + if (isLowerValid && now.test(upper)) { + return RELATIVE_UPPER + } + + return INVALID } // based on time range type, calc the time shifted dates From 9b0603a556d5f92e12b0ae2609c1cdd84d46d290 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 7 Nov 2017 12:57:57 -0800 Subject: [PATCH 038/138] Add shiftTimeRange function --- ui/spec/shared/query/helpersSpec.js | 57 +++++++++++++++++++++++++++++ ui/src/shared/query/helpers.js | 30 ++++++++++++++- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/ui/spec/shared/query/helpersSpec.js b/ui/spec/shared/query/helpersSpec.js index cff927dc0..5dbed1831 100644 --- a/ui/spec/shared/query/helpersSpec.js +++ b/ui/spec/shared/query/helpersSpec.js @@ -49,4 +49,61 @@ describe('Shared.Query.Helpers', () => { expect(timeRangeType(timeRange)).to.equal(RELATIVE_UPPER) }) }) + + describe('timeRangeShift', () => { + it('can calculate the shift for absolute timeRanges', () => { + const upper = Date.now() + const oneMinute = 60000 + const lower = Date.now() - oneMinute + const shift = {multiple: 7, unit: 'd'} + const timeRange = {upper, lower} + + const type = timeRangeType(timeRange) + const actual = shiftTimeRange(timeRange, shift) + const expected = { + lower: `${lower} - 7d`, + upper: `${upper} - 7d`, + type: 'shifted', + } + + expect(type).to.equal(ABSOLUTE) + expect(actual).to.deep.equal(expected) + }) + + it('can calculate the shift for relative lower timeRanges', () => { + const shift = {multiple: 7, unit: 'd'} + const lower = 'now() - 15m' + const timeRange = {lower, upper: null} + + const type = timeRangeType(timeRange) + const actual = shiftTimeRange(timeRange, shift) + const expected = { + lower: `${lower} - 7d`, + upper: `now() - 7d`, + type: 'shifted', + } + + expect(type).to.equal(RELATIVE_LOWER) + expect(actual).to.deep.equal(expected) + }) + + it('can calculate the shift for relative upper timeRanges', () => { + const upper = Date.now() + const oneMinute = 60000 + const lower = Date.now() - oneMinute + const shift = {multiple: 7, unit: 'd'} + const timeRange = {upper, lower} + + const type = timeRangeType(timeRange) + const actual = shiftTimeRange(timeRange, shift) + const expected = { + lower: `${lower} - 7d`, + upper: `${upper} - 7d`, + type: 'shifted', + } + + expect(type).to.equal(ABSOLUTE) + expect(actual).to.deep.equal(expected) + }) + }) }) diff --git a/ui/src/shared/query/helpers.js b/ui/src/shared/query/helpers.js index d8a9f076e..54407062c 100644 --- a/ui/src/shared/query/helpers.js +++ b/ui/src/shared/query/helpers.js @@ -38,4 +38,32 @@ export const timeRangeType = ({upper, lower, type}) => { return INVALID } -// based on time range type, calc the time shifted dates +export const shiftTimeRange = (timeRange, shift) => { + const {upper, lower} = timeRange + const {multiple, unit} = shift + const trType = timeRangeType(timeRange) + const duration = `${multiple}${unit}` + const type = 'shifted' + + switch (trType) { + case ABSOLUTE: { + return { + lower: `${lower} - ${duration}`, + upper: `${upper} - ${duration}`, + type, + } + } + + case RELATIVE_LOWER: { + return { + lower: `${lower} - ${duration}`, + upper: `now() - ${duration}`, + type, + } + } + + default: { + return {lower, upper, type: 'unshifted'} + } + } +} From 4b1d61f5368c1eeffcad11b5c928040c2d76d902 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 7 Nov 2017 13:44:39 -0800 Subject: [PATCH 039/138] Add shifted query if query is shifted --- ui/src/shared/query/helpers.js | 1 + ui/src/utils/buildQueriesForGraphs.js | 49 ++++++++++++++++++--------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/ui/src/shared/query/helpers.js b/ui/src/shared/query/helpers.js index 54407062c..77eb36318 100644 --- a/ui/src/shared/query/helpers.js +++ b/ui/src/shared/query/helpers.js @@ -46,6 +46,7 @@ export const shiftTimeRange = (timeRange, shift) => { const type = 'shifted' switch (trType) { + case RELATIVE_UPPER: case ABSOLUTE: { return { lower: `${lower} - ${duration}`, diff --git a/ui/src/utils/buildQueriesForGraphs.js b/ui/src/utils/buildQueriesForGraphs.js index 46e5cfc6a..a3be70d00 100644 --- a/ui/src/utils/buildQueriesForGraphs.js +++ b/ui/src/utils/buildQueriesForGraphs.js @@ -1,28 +1,45 @@ import {buildQuery} from 'utils/influxql' +import {shiftTimeRange} from 'shared/query/helpers' import {TYPE_QUERY_CONFIG} from 'src/dashboards/constants' -const buildQueries = (proxy, queryConfigs, timeRange) => { +const buildQueries = (proxy, queryConfigs, tR) => { const statements = queryConfigs.map(query => { - const text = - query.rawText || - buildQuery(TYPE_QUERY_CONFIG, query.range || timeRange, query) - return {text, id: query.id, queryConfig: query} - }) + const {rawText, range, id, shift, database, measurement, fields} = query + const timeRange = range || tR + const text = rawText || buildQuery(TYPE_QUERY_CONFIG, timeRange, query) + const isParsable = database && measurement && fields.length - const queries = statements.filter(s => s.text !== null).map(s => { - let queryProxy = '' - if (s.queryConfig.source) { - queryProxy = `${s.queryConfig.source.links.proxy}` + if (shift && isParsable) { + const shiftedQuery = buildQuery( + TYPE_QUERY_CONFIG, + shiftTimeRange(timeRange, shift), + query + ) + + return {text: `${text};${shiftedQuery}`, id, queryConfig: query} } - return { - host: [queryProxy || proxy], - text: s.text, - id: s.id, - queryConfig: s.queryConfig, - } + return {text, id, queryConfig: query} }) + const queries = statements + .filter(s => s.text !== null) + .map(({queryConfig, text, id}) => { + let queryProxy = '' + if (queryConfig.source) { + queryProxy = `${queryConfig.source.links.proxy}` + } + + const host = [queryProxy || proxy] + + return { + host, + text, + id, + queryConfig, + } + }) + return queries } From 0eac04acb3794a99c681b2bbf4d5756c5932ce6b Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 7 Nov 2017 16:07:32 -0800 Subject: [PATCH 040/138] Get shifted --- ui/src/dashboards/constants/index.js | 8 +----- ui/src/shared/components/LineGraph.js | 9 ++---- ui/src/shared/constants/timeShift.js | 3 ++ ui/src/shared/query/helpers.js | 40 +++++++++++++++++++++++++++ ui/src/utils/buildQueriesForGraphs.js | 8 ++---- ui/src/utils/influxql.js | 34 ++++++++++++++++------- ui/src/utils/timeSeriesToDygraph.js | 15 +++++----- 7 files changed, 80 insertions(+), 37 deletions(-) diff --git a/ui/src/dashboards/constants/index.js b/ui/src/dashboards/constants/index.js index 8bc3dc51c..25d5293c9 100644 --- a/ui/src/dashboards/constants/index.js +++ b/ui/src/dashboards/constants/index.js @@ -79,35 +79,29 @@ export const applyMasks = query => { const maskForWholeTemplates = '😸$1😸' return query.replace(matchWholeTemplates, maskForWholeTemplates) } - export const insertTempVar = (query, tempVar) => { return query.replace(MATCH_INCOMPLETE_TEMPLATES, tempVar) } - export const unMask = query => { return query.replace(/😸/g, ':') } - export const removeUnselectedTemplateValues = templates => { return templates.map(template => { const selectedValues = template.values.filter(value => value.selected) return {...template, values: selectedValues} }) } - export const DISPLAY_OPTIONS = { LINEAR: 'linear', LOG: 'log', BASE_2: '2', BASE_10: '10', } - export const TOOLTIP_CONTENT = { FORMAT: '

K/M/B = Thousand / Million / Billion
K/M/G = Kilo / Mega / Giga

', } - export const TYPE_QUERY_CONFIG = 'queryConfig' +export const TYPE_SHIFTED = 'shifted queryConfig' export const TYPE_IFQL = 'ifql' - export const DASHBOARD_NAME_MAX_LENGTH = 50 diff --git a/ui/src/shared/components/LineGraph.js b/ui/src/shared/components/LineGraph.js index 21680ce2e..101ca72b9 100644 --- a/ui/src/shared/components/LineGraph.js +++ b/ui/src/shared/components/LineGraph.js @@ -17,12 +17,8 @@ class LineGraph extends Component { } componentWillMount() { - const {data, activeQueryIndex, isInDataExplorer} = this.props - this._timeSeries = timeSeriesToDygraph( - data, - activeQueryIndex, - isInDataExplorer - ) + const {data, isInDataExplorer} = this.props + this._timeSeries = timeSeriesToDygraph(data, isInDataExplorer) } componentWillUpdate(nextProps) { @@ -33,7 +29,6 @@ class LineGraph extends Component { ) { this._timeSeries = timeSeriesToDygraph( nextProps.data, - nextProps.activeQueryIndex, nextProps.isInDataExplorer ) } diff --git a/ui/src/shared/constants/timeShift.js b/ui/src/shared/constants/timeShift.js index 225734abb..90fddb328 100644 --- a/ui/src/shared/constants/timeShift.js +++ b/ui/src/shared/constants/timeShift.js @@ -1,5 +1,8 @@ export const TIME_SHIFTS = [ {duration: null, text: 'none', multiple: 0, unit: null}, + {duration: '1m', text: '1m', multiple: 1, unit: 'm'}, + {duration: '1h', text: '1h', multiple: 1, unit: 'h'}, + {duration: '12h', text: '12h', multiple: 12, unit: 'h'}, {duration: '1d', text: '1d', multiple: 1, unit: 'd'}, {duration: '7d', text: '7d', multiple: 7, unit: 'd'}, {duration: '30d', text: '30d', multiple: 30, unit: 'd'}, diff --git a/ui/src/shared/query/helpers.js b/ui/src/shared/query/helpers.js index 77eb36318..e881210ef 100644 --- a/ui/src/shared/query/helpers.js +++ b/ui/src/shared/query/helpers.js @@ -68,3 +68,43 @@ export const shiftTimeRange = (timeRange, shift) => { } } } + +export const shiftDate = (date, multiple, unit) => { + if (!date && !multiple && !unit) { + return moment(date) + } + + return moment(date).add(multiple, getMomentUnit(unit)) +} + +const getMomentUnit = unit => { + switch (unit) { + case 'ms': { + return 'milliseconds' // (1 thousandth of a second) + } + + case 's': { + return 'seconds' + } + + case 'm': { + return 'minute' + } + + case 'h': { + return 'hour' + } + + case 'd': { + return 'day' + } + + case 'w': { + return 'week' + } + + default: { + return unit + } + } +} diff --git a/ui/src/utils/buildQueriesForGraphs.js b/ui/src/utils/buildQueriesForGraphs.js index a3be70d00..83d49b96f 100644 --- a/ui/src/utils/buildQueriesForGraphs.js +++ b/ui/src/utils/buildQueriesForGraphs.js @@ -1,6 +1,6 @@ import {buildQuery} from 'utils/influxql' import {shiftTimeRange} from 'shared/query/helpers' -import {TYPE_QUERY_CONFIG} from 'src/dashboards/constants' +import {TYPE_QUERY_CONFIG, TYPE_SHIFTED} from 'src/dashboards/constants' const buildQueries = (proxy, queryConfigs, tR) => { const statements = queryConfigs.map(query => { @@ -10,11 +10,7 @@ const buildQueries = (proxy, queryConfigs, tR) => { const isParsable = database && measurement && fields.length if (shift && isParsable) { - const shiftedQuery = buildQuery( - TYPE_QUERY_CONFIG, - shiftTimeRange(timeRange, shift), - query - ) + const shiftedQuery = buildQuery(TYPE_SHIFTED, timeRange, query, shift) return {text: `${text};${shiftedQuery}`, id, queryConfig: query} } diff --git a/ui/src/utils/influxql.js b/ui/src/utils/influxql.js index 096bf7b97..aae6d4571 100644 --- a/ui/src/utils/influxql.js +++ b/ui/src/utils/influxql.js @@ -2,7 +2,12 @@ import _ from 'lodash' import {TEMP_VAR_INTERVAL, AUTO_GROUP_BY} from 'shared/constants' import {NULL_STRING} from 'shared/constants/queryFillOptions' -import {TYPE_QUERY_CONFIG, TYPE_IFQL} from 'src/dashboards/constants' +import { + TYPE_QUERY_CONFIG, + TYPE_SHIFTED, + TYPE_IFQL, +} from 'src/dashboards/constants' +import {shiftTimeRange} from 'shared/query/helpers' import timeRanges from 'hson!shared/data/timeRanges.hson' /* eslint-disable quotes */ @@ -19,11 +24,11 @@ export const quoteIfTimestamp = ({lower, upper}) => { } /* eslint-enable quotes */ -export default function buildInfluxQLQuery(timeRange, config) { +export default function buildInfluxQLQuery(timeRange, config, shift) { const {groupBy, fill = NULL_STRING, tags, areTagsAccepted} = config const {upper, lower} = quoteIfTimestamp(timeRange) - const select = _buildSelect(config) + const select = _buildSelect(config, shift) if (select === null) { return null } @@ -35,26 +40,35 @@ export default function buildInfluxQLQuery(timeRange, config) { return `${select}${condition}${dimensions}${fillClause}` } -function _buildSelect({fields, database, retentionPolicy, measurement}) { +function _buildSelect({fields, database, retentionPolicy, measurement}, shift) { if (!database || !measurement || !fields || !fields.length) { return null } const rpSegment = retentionPolicy ? `"${retentionPolicy}"` : '' - const fieldsClause = _buildFields(fields) + const fieldsClause = _buildFields(fields, shift) const fullyQualifiedMeasurement = `"${database}".${rpSegment}."${measurement}"` const statement = `SELECT ${fieldsClause} FROM ${fullyQualifiedMeasurement}` return statement } // type arg will reason about new query types i.e. IFQL, GraphQL, or queryConfig -export const buildQuery = (type, timeRange, config) => { +export const buildQuery = (type, timeRange, config, shift) => { switch (type) { - case `${TYPE_QUERY_CONFIG}`: { + case TYPE_QUERY_CONFIG: { return buildInfluxQLQuery(timeRange, config) } - case `${TYPE_IFQL}`: { + case TYPE_SHIFTED: { + const {multiple, unit} = shift + return buildInfluxQLQuery( + shiftTimeRange(timeRange, shift), + config, + `_shifted__${multiple}__${unit}` + ) + } + + case TYPE_IFQL: { // build query usining IFQL here } } @@ -66,7 +80,7 @@ export function buildSelectStatement(config) { return _buildSelect(config) } -function _buildFields(fieldFuncs) { +function _buildFields(fieldFuncs, shift = '') { if (!fieldFuncs) { return '' } @@ -79,7 +93,7 @@ function _buildFields(fieldFuncs) { } case 'func': { const args = _buildFields(f.args) - const alias = f.alias ? ` AS "${f.alias}"` : '' + const alias = f.alias ? ` AS "${f.alias}${shift}"` : '' return `${f.value}(${args})${alias}` } } diff --git a/ui/src/utils/timeSeriesToDygraph.js b/ui/src/utils/timeSeriesToDygraph.js index c848456b2..1de04cd64 100644 --- a/ui/src/utils/timeSeriesToDygraph.js +++ b/ui/src/utils/timeSeriesToDygraph.js @@ -1,4 +1,5 @@ import _ from 'lodash' +import {shiftDate} from 'shared/query/helpers' import {map, reduce, forEach, concat, clone} from 'fast.js' /** @@ -15,12 +16,7 @@ const cells = { responseIndex: new Array(DEFAULT_SIZE), } -// activeQueryIndex is an optional argument that indicated which query's series we want highlighted. -export default function timeSeriesToDygraph( - raw = [], - activeQueryIndex, - isInDataExplorer -) { +export default function timeSeriesToDygraph(raw = [], isInDataExplorer) { // collect results from each influx response const results = reduce( raw, @@ -115,11 +111,16 @@ export default function timeSeriesToDygraph( const timeSeries = [] for (let i = 0; i < size; i++) { - const time = cells.time[i] + let time = cells.time[i] const value = cells.value[i] const label = cells.label[i] const seriesIndex = cells.seriesIndex[i] + if (label.includes('_shifted__')) { + const [, multiple, duration] = label.split('__') + time = +shiftDate(time, multiple, duration).format('x') + } + let existingRowIndex = tsMemo[time] if (existingRowIndex === undefined) { From e151f102e0873ff34b9e539466e7ccf4937ebb1d Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 8 Nov 2017 15:23:05 -0800 Subject: [PATCH 041/138] Introduce TimeShift to DataExplorer --- .../data_explorer/components/Visualization.js | 20 ++++++------------- ui/src/shared/components/QueryOptions.js | 2 +- ui/src/shared/components/TimeShiftDropdown.js | 2 +- ui/src/shared/constants/timeShift.js | 15 +++++++------- ui/src/utils/buildQueriesForGraphs.js | 3 +-- 5 files changed, 17 insertions(+), 25 deletions(-) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 308084e84..25b947a6e 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -1,9 +1,9 @@ import React, {PropTypes, Component} from 'react' -import buildInfluxQLQuery from 'utils/influxql' import classnames from 'classnames' import VisHeader from 'src/data_explorer/components/VisHeader' import VisView from 'src/data_explorer/components/VisView' import {GRAPH, TABLE} from 'shared/constants' +import buildQueries from 'utils/buildQueriesForGraphs' import _ from 'lodash' const META_QUERY_REGEX = /^show/i @@ -61,19 +61,11 @@ class Visualization extends Component { resizerBottomHeight, errorThrown, } = this.props + const {source: {links: {proxy}}} = this.context const {view} = this.state - const statements = queryConfigs.map(query => { - const text = - query.rawText || buildInfluxQLQuery(query.range || timeRange, query) - return {text, id: query.id, queryConfig: query} - }) - - const queries = statements.filter(s => s.text !== null).map(s => { - return {host: [proxy], text: s.text, id: s.id, queryConfig: s.queryConfig} - }) - + const queries = buildQueries(proxy, queryConfigs, timeRange) const activeQuery = queries[activeQueryIndex] const defaultQuery = queries[0] const query = activeQuery || defaultQuery @@ -81,12 +73,12 @@ class Visualization extends Component { return (
{isKapacitorRule ? null : } diff --git a/ui/src/shared/components/TimeShiftDropdown.js b/ui/src/shared/components/TimeShiftDropdown.js index 4b6c630ae..066be7fd1 100644 --- a/ui/src/shared/components/TimeShiftDropdown.js +++ b/ui/src/shared/components/TimeShiftDropdown.js @@ -4,7 +4,7 @@ import {TIME_SHIFTS} from 'shared/constants/timeShift' const TimeShiftDropdown = ({selected, onChooseTimeShift}) =>
- + { @@ -9,7 +8,7 @@ const buildQueries = (proxy, queryConfigs, tR) => { const text = rawText || buildQuery(TYPE_QUERY_CONFIG, timeRange, query) const isParsable = database && measurement && fields.length - if (shift && isParsable) { + if (shift && shift.multiple && isParsable) { const shiftedQuery = buildQuery(TYPE_SHIFTED, timeRange, query, shift) return {text: `${text};${shiftedQuery}`, id, queryConfig: query} From a59c78f9454996d1f0593487ecd6eeadd915de79 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 8 Nov 2017 15:37:02 -0800 Subject: [PATCH 042/138] Change shift shape to array in test --- .../data_explorer/reducers/queryConfigSpec.js | 2 +- ui/src/shared/query/helpers.js | 16 ++++++++-------- ui/src/utils/defaultQueryConfig.js | 2 +- ui/src/utils/queryTransitions.js | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ui/spec/data_explorer/reducers/queryConfigSpec.js b/ui/spec/data_explorer/reducers/queryConfigSpec.js index 48670cef3..07199db0d 100644 --- a/ui/spec/data_explorer/reducers/queryConfigSpec.js +++ b/ui/spec/data_explorer/reducers/queryConfigSpec.js @@ -482,7 +482,7 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { const action = timeShift(queryID, shift) const nextState = reducer(initialState, action) - expect(nextState[queryID].shift).to.deep.equal(shift) + expect(nextState[queryID].shift).to.deep.equal([shift]) }) }) }) diff --git a/ui/src/shared/query/helpers.js b/ui/src/shared/query/helpers.js index e881210ef..67e2373d0 100644 --- a/ui/src/shared/query/helpers.js +++ b/ui/src/shared/query/helpers.js @@ -69,14 +69,6 @@ export const shiftTimeRange = (timeRange, shift) => { } } -export const shiftDate = (date, multiple, unit) => { - if (!date && !multiple && !unit) { - return moment(date) - } - - return moment(date).add(multiple, getMomentUnit(unit)) -} - const getMomentUnit = unit => { switch (unit) { case 'ms': { @@ -108,3 +100,11 @@ const getMomentUnit = unit => { } } } + +export const shiftDate = (date, multiple, unit) => { + if (!date && !multiple && !unit) { + return moment(date) + } + + return moment(date).add(multiple, getMomentUnit(unit)) +} diff --git a/ui/src/utils/defaultQueryConfig.js b/ui/src/utils/defaultQueryConfig.js index deef0c042..51f528186 100644 --- a/ui/src/utils/defaultQueryConfig.js +++ b/ui/src/utils/defaultQueryConfig.js @@ -15,7 +15,7 @@ const defaultQueryConfig = ({id, isKapacitorRule = false}) => { areTagsAccepted: true, rawText: null, status: null, - shift: null, + shift: [], } return isKapacitorRule ? queryConfig : {...queryConfig, fill: NULL_STRING} diff --git a/ui/src/utils/queryTransitions.js b/ui/src/utils/queryTransitions.js index 7353422c2..cc89f2351 100644 --- a/ui/src/utils/queryTransitions.js +++ b/ui/src/utils/queryTransitions.js @@ -243,4 +243,4 @@ export const chooseTag = (query, tag) => { return updateTagValues(query.tags[tag.key].concat(tag.value)) } -export const timeShift = (query, shift) => ({...query, shift}) +export const timeShift = (query, shift) => ({...query, shift: [shift]}) From 757fd7088c0f01aaf5e4e6a858dc5ae2a1acfce2 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 8 Nov 2017 15:37:16 -0800 Subject: [PATCH 043/138] Fix broken timeSeries spec --- ui/spec/utils/timeSeriesToDygraphSpec.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ui/spec/utils/timeSeriesToDygraphSpec.js b/ui/spec/utils/timeSeriesToDygraphSpec.js index 167ed2036..9dd78cfcb 100644 --- a/ui/spec/utils/timeSeriesToDygraphSpec.js +++ b/ui/spec/utils/timeSeriesToDygraphSpec.js @@ -228,11 +228,7 @@ describe('timeSeriesToDygraph', () => { ] const isInDataExplorer = true - const actual = timeSeriesToDygraph( - influxResponse, - undefined, - isInDataExplorer - ) + const actual = timeSeriesToDygraph(influxResponse, isInDataExplorer) const expected = {} From 3d9e6075026928cf1352bbcf4c087ee53b5e49e2 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 8 Nov 2017 15:55:55 -0800 Subject: [PATCH 044/138] Allow for n time shifts --- ui/src/shared/components/FieldList.js | 14 +++++++++----- ui/src/utils/buildQueriesForGraphs.js | 12 +++++++++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/ui/src/shared/components/FieldList.js b/ui/src/shared/components/FieldList.js index 0a21f311c..38cc736f4 100644 --- a/ui/src/shared/components/FieldList.js +++ b/ui/src/shared/components/FieldList.js @@ -146,7 +146,7 @@ class FieldList extends Component { {hasAggregates ? { const text = rawText || buildQuery(TYPE_QUERY_CONFIG, timeRange, query) const isParsable = database && measurement && fields.length - if (shift && shift.multiple && isParsable) { - const shiftedQuery = buildQuery(TYPE_SHIFTED, timeRange, query, shift) + if (shift && shift.length && isParsable) { + const shiftedQueries = shift + .filter(s => s.unit) + .map(s => buildQuery(TYPE_SHIFTED, timeRange, query, s)) - return {text: `${text};${shiftedQuery}`, id, queryConfig: query} + return { + text: `${text};${shiftedQueries.join(';')}`, + id, + queryConfig: query, + } } return {text, id, queryConfig: query} From 741c920809d944556ca264c3163ddd513c04549d Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 9 Nov 2017 09:56:22 -0800 Subject: [PATCH 045/138] Christmas came early --- ui/src/kapacitor/containers/KapacitorRulePage.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ui/src/kapacitor/containers/KapacitorRulePage.js b/ui/src/kapacitor/containers/KapacitorRulePage.js index 43ec68c88..6b1754840 100644 --- a/ui/src/kapacitor/containers/KapacitorRulePage.js +++ b/ui/src/kapacitor/containers/KapacitorRulePage.js @@ -80,17 +80,17 @@ class KapacitorRulePage extends Component { } return ( ) } From d63c347ba5a1c17382a5ff12dd405ddcef615f94 Mon Sep 17 00:00:00 2001 From: Luke Morris Date: Thu, 9 Nov 2017 17:17:37 -0800 Subject: [PATCH 046/138] After create/delete, refresh database list --- ui/src/data_explorer/components/Visualization.js | 2 +- ui/src/shared/components/DatabaseList.js | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 308084e84..7bfd7f73b 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -6,7 +6,7 @@ import VisView from 'src/data_explorer/components/VisView' import {GRAPH, TABLE} from 'shared/constants' import _ from 'lodash' -const META_QUERY_REGEX = /^show/i +const META_QUERY_REGEX = /^(show|create|drop)/i class Visualization extends Component { constructor(props) { diff --git a/ui/src/shared/components/DatabaseList.js b/ui/src/shared/components/DatabaseList.js index 3b20a501d..bfca91885 100644 --- a/ui/src/shared/components/DatabaseList.js +++ b/ui/src/shared/components/DatabaseList.js @@ -45,12 +45,18 @@ const DatabaseList = React.createClass({ this.getDbRp() }, - componentDidUpdate(prevProps) { - if (_.isEqual(prevProps.querySource, this.props.querySource)) { - return - } + componentDidUpdate({querySource: prevSource, query: prevQuery}) { + const {querySource: nextSource, query: nextQuery} = this.props + const differentSource = !_.isEqual(prevSource, nextSource) - this.getDbRp() + const newMetaQuery = + prevQuery.rawText !== nextQuery.rawText && + nextQuery.rawText && + nextQuery.rawText.match(/^(create|drop)/) + + if (differentSource || newMetaQuery) { + setTimeout(this.getDbRp, 100) + } }, getDbRp() { From e46b592f3d84145f18a18e57d1caa58e0a70a6b4 Mon Sep 17 00:00:00 2001 From: Luke Morris Date: Thu, 9 Nov 2017 17:21:34 -0800 Subject: [PATCH 047/138] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81b43ff39..eb61b342a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ 1. [#2291](https://github.com/influxdata/chronograf/pull/2291): Fix several kapacitor alert creation panics. 1. [#2303](https://github.com/influxdata/chronograf/pull/2303): Add shadow-utils to RPM release packages 1. [#2292](https://github.com/influxdata/chronograf/pull/2292): Source extra command line options from defaults file +1. [#2327](https://github.com/influxdata/chronograf/pull/2327): After CREATE/DELETE queries, refresh list of databases in Data Explorer +1. [#2327](https://github.com/influxdata/chronograf/pull/2327): Visualize CREATE/DELETE queries with Table view in Data Explorer ### Features ### UI Improvements From 0e1ab16bba98699c6dc5cc87ac48831cb94af46a Mon Sep 17 00:00:00 2001 From: Luke Morris Date: Thu, 9 Nov 2017 17:27:25 -0800 Subject: [PATCH 048/138] This needs to be case-sensitive --- ui/src/shared/components/DatabaseList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/shared/components/DatabaseList.js b/ui/src/shared/components/DatabaseList.js index bfca91885..9236bc501 100644 --- a/ui/src/shared/components/DatabaseList.js +++ b/ui/src/shared/components/DatabaseList.js @@ -52,7 +52,7 @@ const DatabaseList = React.createClass({ const newMetaQuery = prevQuery.rawText !== nextQuery.rawText && nextQuery.rawText && - nextQuery.rawText.match(/^(create|drop)/) + nextQuery.rawText.match(/^(create|drop)/i) if (differentSource || newMetaQuery) { setTimeout(this.getDbRp, 100) From 6093ddc52f876e07346c73858fe3183c680dfe04 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 10 Nov 2017 11:06:48 -0800 Subject: [PATCH 049/138] WIP Add shifts to queryConifg on backend --- bolt/dashboards.go | 8 +- bolt/internal/internal.go | 28 ++ bolt/internal/internal.pb.go | 728 +++++++++++++++++++++++++++++---- bolt/internal/internal.proto | 75 ++-- bolt/internal/internal_test.go | 11 + chronograf.go | 9 + server/cells.go | 3 +- server/cells_test.go | 12 +- server/dashboards_test.go | 14 + server/queries.go | 1 + server/queries_test.go | 6 +- 11 files changed, 768 insertions(+), 127 deletions(-) diff --git a/bolt/dashboards.go b/bolt/dashboards.go index 1f982359f..1a57d3da1 100644 --- a/bolt/dashboards.go +++ b/bolt/dashboards.go @@ -86,6 +86,7 @@ func (d *DashboardsStore) Add(ctx context.Context, src chronograf.Dashboard) (ch id, _ := b.NextSequence() src.ID = chronograf.DashboardID(id) + // TODO: use FormatInt strID := strconv.Itoa(int(id)) for i, cell := range src.Cells { cid, err := d.IDs.Generate() @@ -95,12 +96,11 @@ func (d *DashboardsStore) Add(ctx context.Context, src chronograf.Dashboard) (ch cell.ID = cid src.Cells[i] = cell } - if v, err := internal.MarshalDashboard(src); err != nil { - return err - } else if err := b.Put([]byte(strID), v); err != nil { + v, err := internal.MarshalDashboard(src) + if err != nil { return err } - return nil + return b.Put([]byte(strID), v) }); err != nil { return chronograf.Dashboard{}, err } diff --git a/bolt/internal/internal.go b/bolt/internal/internal.go index 5bb729ecf..4de7ef6df 100644 --- a/bolt/internal/internal.go +++ b/bolt/internal/internal.go @@ -191,12 +191,26 @@ func MarshalDashboard(d chronograf.Dashboard) ([]byte, error) { if q.Range != nil { r.Upper, r.Lower = q.Range.Upper, q.Range.Lower } + q.Shifts = q.QueryConfig.Shifts queries[j] = &Query{ Command: q.Command, Label: q.Label, Range: r, Source: q.Source, } + + shifts := make([]*TimeShift, len(q.Shifts)) + for k := range q.Shifts { + shift := &TimeShift{ + Label: q.Shifts[k].Label, + Unit: q.Shifts[k].Unit, + Quantity: q.Shifts[k].Quantity, + } + + shifts[k] = shift + } + + queries[j].Shifts = shifts } axes := make(map[string]*Axis, len(c.Axes)) @@ -277,12 +291,26 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error { Label: q.Label, Source: q.Source, } + if q.Range.Upper != q.Range.Lower { queries[j].Range = &chronograf.Range{ Upper: q.Range.Upper, Lower: q.Range.Lower, } } + + shifts := make([]chronograf.TimeShift, len(q.Shifts)) + for k := range q.Shifts { + shift := chronograf.TimeShift{ + Label: q.Shifts[k].Label, + Unit: q.Shifts[k].Unit, + Quantity: q.Shifts[k].Quantity, + } + + shifts[k] = shift + } + + queries[j].Shifts = shifts } axes := make(map[string]chronograf.Axis, len(c.Axes)) diff --git a/bolt/internal/internal.pb.go b/bolt/internal/internal.pb.go index 02d757fdc..0683ebe34 100644 --- a/bolt/internal/internal.pb.go +++ b/bolt/internal/internal.pb.go @@ -20,6 +20,7 @@ It has these top-level messages: Layout Cell Query + TimeShift Range AlertRule User @@ -60,6 +61,83 @@ func (m *Source) String() string { return proto.CompactTextString(m) func (*Source) ProtoMessage() {} func (*Source) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{0} } +func (m *Source) GetID() int64 { + if m != nil { + return m.ID + } + return 0 +} + +func (m *Source) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Source) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *Source) GetUsername() string { + if m != nil { + return m.Username + } + return "" +} + +func (m *Source) GetPassword() string { + if m != nil { + return m.Password + } + return "" +} + +func (m *Source) GetURL() string { + if m != nil { + return m.URL + } + return "" +} + +func (m *Source) GetDefault() bool { + if m != nil { + return m.Default + } + return false +} + +func (m *Source) GetTelegraf() string { + if m != nil { + return m.Telegraf + } + return "" +} + +func (m *Source) GetInsecureSkipVerify() bool { + if m != nil { + return m.InsecureSkipVerify + } + return false +} + +func (m *Source) GetMetaURL() string { + if m != nil { + return m.MetaURL + } + return "" +} + +func (m *Source) GetSharedSecret() string { + if m != nil { + return m.SharedSecret + } + return "" +} + type Dashboard struct { ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"` @@ -72,6 +150,20 @@ func (m *Dashboard) String() string { return proto.CompactTextString( func (*Dashboard) ProtoMessage() {} func (*Dashboard) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{1} } +func (m *Dashboard) GetID() int64 { + if m != nil { + return m.ID + } + return 0 +} + +func (m *Dashboard) GetName() string { + if m != nil { + return m.Name + } + return "" +} + func (m *Dashboard) GetCells() []*DashboardCell { if m != nil { return m.Cells @@ -103,6 +195,34 @@ func (m *DashboardCell) String() string { return proto.CompactTextStr func (*DashboardCell) ProtoMessage() {} func (*DashboardCell) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{2} } +func (m *DashboardCell) GetX() int32 { + if m != nil { + return m.X + } + return 0 +} + +func (m *DashboardCell) GetY() int32 { + if m != nil { + return m.Y + } + return 0 +} + +func (m *DashboardCell) GetW() int32 { + if m != nil { + return m.W + } + return 0 +} + +func (m *DashboardCell) GetH() int32 { + if m != nil { + return m.H + } + return 0 +} + func (m *DashboardCell) GetQueries() []*Query { if m != nil { return m.Queries @@ -110,6 +230,27 @@ func (m *DashboardCell) GetQueries() []*Query { return nil } +func (m *DashboardCell) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *DashboardCell) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *DashboardCell) GetID() string { + if m != nil { + return m.ID + } + return "" +} + func (m *DashboardCell) GetAxes() map[string]*Axis { if m != nil { return m.Axes @@ -118,7 +259,7 @@ func (m *DashboardCell) GetAxes() map[string]*Axis { } type Axis struct { - LegacyBounds []int64 `protobuf:"varint,1,rep,name=legacyBounds" json:"legacyBounds,omitempty"` + LegacyBounds []int64 `protobuf:"varint,1,rep,packed,name=legacyBounds" json:"legacyBounds,omitempty"` Bounds []string `protobuf:"bytes,2,rep,name=bounds" json:"bounds,omitempty"` Label string `protobuf:"bytes,3,opt,name=label,proto3" json:"label,omitempty"` Prefix string `protobuf:"bytes,4,opt,name=prefix,proto3" json:"prefix,omitempty"` @@ -132,6 +273,55 @@ func (m *Axis) String() string { return proto.CompactTextString(m) } func (*Axis) ProtoMessage() {} func (*Axis) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{3} } +func (m *Axis) GetLegacyBounds() []int64 { + if m != nil { + return m.LegacyBounds + } + return nil +} + +func (m *Axis) GetBounds() []string { + if m != nil { + return m.Bounds + } + return nil +} + +func (m *Axis) GetLabel() string { + if m != nil { + return m.Label + } + return "" +} + +func (m *Axis) GetPrefix() string { + if m != nil { + return m.Prefix + } + return "" +} + +func (m *Axis) GetSuffix() string { + if m != nil { + return m.Suffix + } + return "" +} + +func (m *Axis) GetBase() string { + if m != nil { + return m.Base + } + return "" +} + +func (m *Axis) GetScale() string { + if m != nil { + return m.Scale + } + return "" +} + type Template struct { ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` TempVar string `protobuf:"bytes,2,opt,name=temp_var,json=tempVar,proto3" json:"temp_var,omitempty"` @@ -146,6 +336,20 @@ func (m *Template) String() string { return proto.CompactTextString(m func (*Template) ProtoMessage() {} func (*Template) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{4} } +func (m *Template) GetID() string { + if m != nil { + return m.ID + } + return "" +} + +func (m *Template) GetTempVar() string { + if m != nil { + return m.TempVar + } + return "" +} + func (m *Template) GetValues() []*TemplateValue { if m != nil { return m.Values @@ -153,6 +357,20 @@ func (m *Template) GetValues() []*TemplateValue { return nil } +func (m *Template) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *Template) GetLabel() string { + if m != nil { + return m.Label + } + return "" +} + func (m *Template) GetQuery() *TemplateQuery { if m != nil { return m.Query @@ -171,6 +389,27 @@ func (m *TemplateValue) String() string { return proto.CompactTextStr func (*TemplateValue) ProtoMessage() {} func (*TemplateValue) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{5} } +func (m *TemplateValue) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *TemplateValue) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +func (m *TemplateValue) GetSelected() bool { + if m != nil { + return m.Selected + } + return false +} + type TemplateQuery struct { Command string `protobuf:"bytes,1,opt,name=command,proto3" json:"command,omitempty"` Db string `protobuf:"bytes,2,opt,name=db,proto3" json:"db,omitempty"` @@ -185,6 +424,48 @@ func (m *TemplateQuery) String() string { return proto.CompactTextStr func (*TemplateQuery) ProtoMessage() {} func (*TemplateQuery) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{6} } +func (m *TemplateQuery) GetCommand() string { + if m != nil { + return m.Command + } + return "" +} + +func (m *TemplateQuery) GetDb() string { + if m != nil { + return m.Db + } + return "" +} + +func (m *TemplateQuery) GetRp() string { + if m != nil { + return m.Rp + } + return "" +} + +func (m *TemplateQuery) GetMeasurement() string { + if m != nil { + return m.Measurement + } + return "" +} + +func (m *TemplateQuery) GetTagKey() string { + if m != nil { + return m.TagKey + } + return "" +} + +func (m *TemplateQuery) GetFieldKey() string { + if m != nil { + return m.FieldKey + } + return "" +} + type Server struct { ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"` @@ -200,6 +481,55 @@ func (m *Server) String() string { return proto.CompactTextString(m) func (*Server) ProtoMessage() {} func (*Server) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{7} } +func (m *Server) GetID() int64 { + if m != nil { + return m.ID + } + return 0 +} + +func (m *Server) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Server) GetUsername() string { + if m != nil { + return m.Username + } + return "" +} + +func (m *Server) GetPassword() string { + if m != nil { + return m.Password + } + return "" +} + +func (m *Server) GetURL() string { + if m != nil { + return m.URL + } + return "" +} + +func (m *Server) GetSrcID() int64 { + if m != nil { + return m.SrcID + } + return 0 +} + +func (m *Server) GetActive() bool { + if m != nil { + return m.Active + } + return false +} + type Layout struct { ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` Application string `protobuf:"bytes,2,opt,name=Application,proto3" json:"Application,omitempty"` @@ -213,6 +543,27 @@ func (m *Layout) String() string { return proto.CompactTextString(m) func (*Layout) ProtoMessage() {} func (*Layout) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{8} } +func (m *Layout) GetID() string { + if m != nil { + return m.ID + } + return "" +} + +func (m *Layout) GetApplication() string { + if m != nil { + return m.Application + } + return "" +} + +func (m *Layout) GetMeasurement() string { + if m != nil { + return m.Measurement + } + return "" +} + func (m *Layout) GetCells() []*Cell { if m != nil { return m.Cells @@ -220,6 +571,13 @@ func (m *Layout) GetCells() []*Cell { return nil } +func (m *Layout) GetAutoflow() bool { + if m != nil { + return m.Autoflow + } + return false +} + type Cell struct { X int32 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` Y int32 `protobuf:"varint,2,opt,name=y,proto3" json:"y,omitempty"` @@ -228,7 +586,7 @@ type Cell struct { Queries []*Query `protobuf:"bytes,5,rep,name=queries" json:"queries,omitempty"` I string `protobuf:"bytes,6,opt,name=i,proto3" json:"i,omitempty"` Name string `protobuf:"bytes,7,opt,name=name,proto3" json:"name,omitempty"` - Yranges []int64 `protobuf:"varint,8,rep,name=yranges" json:"yranges,omitempty"` + Yranges []int64 `protobuf:"varint,8,rep,packed,name=yranges" json:"yranges,omitempty"` Ylabels []string `protobuf:"bytes,9,rep,name=ylabels" json:"ylabels,omitempty"` Type string `protobuf:"bytes,10,opt,name=type,proto3" json:"type,omitempty"` Axes map[string]*Axis `protobuf:"bytes,11,rep,name=axes" json:"axes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` @@ -239,6 +597,34 @@ func (m *Cell) String() string { return proto.CompactTextString(m) } func (*Cell) ProtoMessage() {} func (*Cell) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{9} } +func (m *Cell) GetX() int32 { + if m != nil { + return m.X + } + return 0 +} + +func (m *Cell) GetY() int32 { + if m != nil { + return m.Y + } + return 0 +} + +func (m *Cell) GetW() int32 { + if m != nil { + return m.W + } + return 0 +} + +func (m *Cell) GetH() int32 { + if m != nil { + return m.H + } + return 0 +} + func (m *Cell) GetQueries() []*Query { if m != nil { return m.Queries @@ -246,6 +632,41 @@ func (m *Cell) GetQueries() []*Query { return nil } +func (m *Cell) GetI() string { + if m != nil { + return m.I + } + return "" +} + +func (m *Cell) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Cell) GetYranges() []int64 { + if m != nil { + return m.Yranges + } + return nil +} + +func (m *Cell) GetYlabels() []string { + if m != nil { + return m.Ylabels + } + return nil +} + +func (m *Cell) GetType() string { + if m != nil { + return m.Type + } + return "" +} + func (m *Cell) GetAxes() map[string]*Axis { if m != nil { return m.Axes @@ -254,14 +675,15 @@ func (m *Cell) GetAxes() map[string]*Axis { } type Query struct { - Command string `protobuf:"bytes,1,opt,name=Command,proto3" json:"Command,omitempty"` - DB string `protobuf:"bytes,2,opt,name=DB,proto3" json:"DB,omitempty"` - RP string `protobuf:"bytes,3,opt,name=RP,proto3" json:"RP,omitempty"` - GroupBys []string `protobuf:"bytes,4,rep,name=GroupBys" json:"GroupBys,omitempty"` - Wheres []string `protobuf:"bytes,5,rep,name=Wheres" json:"Wheres,omitempty"` - Label string `protobuf:"bytes,6,opt,name=Label,proto3" json:"Label,omitempty"` - Range *Range `protobuf:"bytes,7,opt,name=Range" json:"Range,omitempty"` - Source string `protobuf:"bytes,8,opt,name=Source,proto3" json:"Source,omitempty"` + Command string `protobuf:"bytes,1,opt,name=Command,proto3" json:"Command,omitempty"` + DB string `protobuf:"bytes,2,opt,name=DB,proto3" json:"DB,omitempty"` + RP string `protobuf:"bytes,3,opt,name=RP,proto3" json:"RP,omitempty"` + GroupBys []string `protobuf:"bytes,4,rep,name=GroupBys" json:"GroupBys,omitempty"` + Wheres []string `protobuf:"bytes,5,rep,name=Wheres" json:"Wheres,omitempty"` + Label string `protobuf:"bytes,6,opt,name=Label,proto3" json:"Label,omitempty"` + Range *Range `protobuf:"bytes,7,opt,name=Range" json:"Range,omitempty"` + Source string `protobuf:"bytes,8,opt,name=Source,proto3" json:"Source,omitempty"` + Shifts []*TimeShift `protobuf:"bytes,9,rep,name=Shifts" json:"Shifts,omitempty"` } func (m *Query) Reset() { *m = Query{} } @@ -269,6 +691,48 @@ func (m *Query) String() string { return proto.CompactTextString(m) } func (*Query) ProtoMessage() {} func (*Query) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{10} } +func (m *Query) GetCommand() string { + if m != nil { + return m.Command + } + return "" +} + +func (m *Query) GetDB() string { + if m != nil { + return m.DB + } + return "" +} + +func (m *Query) GetRP() string { + if m != nil { + return m.RP + } + return "" +} + +func (m *Query) GetGroupBys() []string { + if m != nil { + return m.GroupBys + } + return nil +} + +func (m *Query) GetWheres() []string { + if m != nil { + return m.Wheres + } + return nil +} + +func (m *Query) GetLabel() string { + if m != nil { + return m.Label + } + return "" +} + func (m *Query) GetRange() *Range { if m != nil { return m.Range @@ -276,6 +740,52 @@ func (m *Query) GetRange() *Range { return nil } +func (m *Query) GetSource() string { + if m != nil { + return m.Source + } + return "" +} + +func (m *Query) GetShifts() []*TimeShift { + if m != nil { + return m.Shifts + } + return nil +} + +type TimeShift struct { + Label string `protobuf:"bytes,1,opt,name=Label,proto3" json:"Label,omitempty"` + Unit string `protobuf:"bytes,2,opt,name=Unit,proto3" json:"Unit,omitempty"` + Quantity string `protobuf:"bytes,3,opt,name=Quantity,proto3" json:"Quantity,omitempty"` +} + +func (m *TimeShift) Reset() { *m = TimeShift{} } +func (m *TimeShift) String() string { return proto.CompactTextString(m) } +func (*TimeShift) ProtoMessage() {} +func (*TimeShift) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{11} } + +func (m *TimeShift) GetLabel() string { + if m != nil { + return m.Label + } + return "" +} + +func (m *TimeShift) GetUnit() string { + if m != nil { + return m.Unit + } + return "" +} + +func (m *TimeShift) GetQuantity() string { + if m != nil { + return m.Quantity + } + return "" +} + type Range struct { Upper int64 `protobuf:"varint,1,opt,name=Upper,proto3" json:"Upper,omitempty"` Lower int64 `protobuf:"varint,2,opt,name=Lower,proto3" json:"Lower,omitempty"` @@ -284,7 +794,21 @@ type Range struct { func (m *Range) Reset() { *m = Range{} } func (m *Range) String() string { return proto.CompactTextString(m) } func (*Range) ProtoMessage() {} -func (*Range) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{11} } +func (*Range) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{12} } + +func (m *Range) GetUpper() int64 { + if m != nil { + return m.Upper + } + return 0 +} + +func (m *Range) GetLower() int64 { + if m != nil { + return m.Lower + } + return 0 +} type AlertRule struct { ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` @@ -296,7 +820,35 @@ type AlertRule struct { func (m *AlertRule) Reset() { *m = AlertRule{} } func (m *AlertRule) String() string { return proto.CompactTextString(m) } func (*AlertRule) ProtoMessage() {} -func (*AlertRule) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{12} } +func (*AlertRule) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{13} } + +func (m *AlertRule) GetID() string { + if m != nil { + return m.ID + } + return "" +} + +func (m *AlertRule) GetJSON() string { + if m != nil { + return m.JSON + } + return "" +} + +func (m *AlertRule) GetSrcID() int64 { + if m != nil { + return m.SrcID + } + return 0 +} + +func (m *AlertRule) GetKapaID() int64 { + if m != nil { + return m.KapaID + } + return 0 +} type User struct { ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` @@ -306,7 +858,21 @@ type User struct { func (m *User) Reset() { *m = User{} } func (m *User) String() string { return proto.CompactTextString(m) } func (*User) ProtoMessage() {} -func (*User) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{13} } +func (*User) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{14} } + +func (m *User) GetID() uint64 { + if m != nil { + return m.ID + } + return 0 +} + +func (m *User) GetName() string { + if m != nil { + return m.Name + } + return "" +} func init() { proto.RegisterType((*Source)(nil), "internal.Source") @@ -320,6 +886,7 @@ func init() { proto.RegisterType((*Layout)(nil), "internal.Layout") proto.RegisterType((*Cell)(nil), "internal.Cell") proto.RegisterType((*Query)(nil), "internal.Query") + proto.RegisterType((*TimeShift)(nil), "internal.TimeShift") proto.RegisterType((*Range)(nil), "internal.Range") proto.RegisterType((*AlertRule)(nil), "internal.AlertRule") proto.RegisterType((*User)(nil), "internal.User") @@ -328,70 +895,73 @@ func init() { func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) } var fileDescriptorInternal = []byte{ - // 1028 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x56, 0x4f, 0x6f, 0xe3, 0x44, - 0x14, 0xd7, 0xf8, 0x4f, 0x12, 0xbf, 0x74, 0x0b, 0x1a, 0xad, 0x58, 0xb3, 0x5c, 0x82, 0x05, 0x52, - 0x40, 0x6c, 0x41, 0xbb, 0x42, 0x42, 0xdc, 0xd2, 0x06, 0xad, 0x4a, 0xbb, 0x4b, 0x99, 0xb4, 0xe5, - 0x84, 0x56, 0x13, 0xe7, 0xa5, 0xb5, 0xd6, 0x89, 0xcd, 0xd8, 0x6e, 0xe3, 0x6f, 0xc1, 0x27, 0x40, - 0x42, 0xe2, 0xc4, 0x81, 0x03, 0x5f, 0x80, 0xfb, 0x7e, 0x2a, 0xf4, 0x66, 0xc6, 0x8e, 0xc3, 0x76, - 0xd1, 0x5e, 0xe0, 0x36, 0xbf, 0xf7, 0xc6, 0x6f, 0x66, 0xde, 0xef, 0xfd, 0x7e, 0x09, 0xec, 0x27, - 0xeb, 0x12, 0xd5, 0x5a, 0xa6, 0x07, 0xb9, 0xca, 0xca, 0x8c, 0x0f, 0x1a, 0x1c, 0xfd, 0xe1, 0x40, - 0x6f, 0x96, 0x55, 0x2a, 0x46, 0xbe, 0x0f, 0xce, 0xf1, 0x34, 0x64, 0x23, 0x36, 0x76, 0x85, 0x73, - 0x3c, 0xe5, 0x1c, 0xbc, 0xe7, 0x72, 0x85, 0xa1, 0x33, 0x62, 0xe3, 0x40, 0xe8, 0x35, 0xc5, 0xce, - 0xeb, 0x1c, 0x43, 0xd7, 0xc4, 0x68, 0xcd, 0x1f, 0xc2, 0xe0, 0xa2, 0xa0, 0x6a, 0x2b, 0x0c, 0x3d, - 0x1d, 0x6f, 0x31, 0xe5, 0xce, 0x64, 0x51, 0xdc, 0x66, 0x6a, 0x11, 0xfa, 0x26, 0xd7, 0x60, 0xfe, - 0x2e, 0xb8, 0x17, 0xe2, 0x34, 0xec, 0xe9, 0x30, 0x2d, 0x79, 0x08, 0xfd, 0x29, 0x2e, 0x65, 0x95, - 0x96, 0x61, 0x7f, 0xc4, 0xc6, 0x03, 0xd1, 0x40, 0xaa, 0x73, 0x8e, 0x29, 0x5e, 0x29, 0xb9, 0x0c, - 0x07, 0xa6, 0x4e, 0x83, 0xf9, 0x01, 0xf0, 0xe3, 0x75, 0x81, 0x71, 0xa5, 0x70, 0xf6, 0x32, 0xc9, - 0x2f, 0x51, 0x25, 0xcb, 0x3a, 0x0c, 0x74, 0x81, 0x3b, 0x32, 0x74, 0xca, 0x33, 0x2c, 0x25, 0x9d, - 0x0d, 0xba, 0x54, 0x03, 0x79, 0x04, 0x7b, 0xb3, 0x6b, 0xa9, 0x70, 0x31, 0xc3, 0x58, 0x61, 0x19, - 0x0e, 0x75, 0x7a, 0x27, 0x16, 0xfd, 0xcc, 0x20, 0x98, 0xca, 0xe2, 0x7a, 0x9e, 0x49, 0xb5, 0x78, - 0xab, 0x9e, 0x3d, 0x02, 0x3f, 0xc6, 0x34, 0x2d, 0x42, 0x77, 0xe4, 0x8e, 0x87, 0x8f, 0x1f, 0x1c, - 0xb4, 0x64, 0xb4, 0x75, 0x8e, 0x30, 0x4d, 0x85, 0xd9, 0xc5, 0xbf, 0x80, 0xa0, 0xc4, 0x55, 0x9e, - 0xca, 0x12, 0x8b, 0xd0, 0xd3, 0x9f, 0xf0, 0xed, 0x27, 0xe7, 0x36, 0x25, 0xb6, 0x9b, 0xa2, 0xdf, - 0x1d, 0xb8, 0xb7, 0x53, 0x8a, 0xef, 0x01, 0xdb, 0xe8, 0x5b, 0xf9, 0x82, 0x6d, 0x08, 0xd5, 0xfa, - 0x46, 0xbe, 0x60, 0x35, 0xa1, 0x5b, 0xcd, 0x9f, 0x2f, 0xd8, 0x2d, 0xa1, 0x6b, 0xcd, 0x9a, 0x2f, - 0xd8, 0x35, 0xff, 0x04, 0xfa, 0x3f, 0x55, 0xa8, 0x12, 0x2c, 0x42, 0x5f, 0x9f, 0xfc, 0xce, 0xf6, - 0xe4, 0xef, 0x2b, 0x54, 0xb5, 0x68, 0xf2, 0xf4, 0x52, 0xcd, 0xb8, 0xa1, 0x4f, 0xaf, 0x29, 0x56, - 0xd2, 0x74, 0xf4, 0x4d, 0x8c, 0xd6, 0xb6, 0x43, 0x86, 0x33, 0xea, 0xd0, 0x97, 0xe0, 0xc9, 0x0d, - 0x16, 0x61, 0xa0, 0xeb, 0x7f, 0xf8, 0x86, 0x66, 0x1c, 0x4c, 0x36, 0x58, 0x7c, 0xb3, 0x2e, 0x55, - 0x2d, 0xf4, 0xf6, 0x87, 0x4f, 0x21, 0x68, 0x43, 0x34, 0x39, 0x2f, 0xb1, 0xd6, 0x0f, 0x0c, 0x04, - 0x2d, 0xf9, 0x47, 0xe0, 0xdf, 0xc8, 0xb4, 0x32, 0x8d, 0x1f, 0x3e, 0xde, 0xdf, 0x96, 0x9d, 0x6c, - 0x92, 0x42, 0x98, 0xe4, 0xd7, 0xce, 0x57, 0x2c, 0xfa, 0x93, 0x81, 0x47, 0x31, 0x22, 0x3b, 0xc5, - 0x2b, 0x19, 0xd7, 0x87, 0x59, 0xb5, 0x5e, 0x14, 0x21, 0x1b, 0xb9, 0x63, 0x57, 0xec, 0xc4, 0xf8, - 0x7b, 0xd0, 0x9b, 0x9b, 0xac, 0x33, 0x72, 0xc7, 0x81, 0xb0, 0x88, 0xdf, 0x07, 0x3f, 0x95, 0x73, - 0x4c, 0xad, 0x0e, 0x0c, 0xa0, 0xdd, 0xb9, 0xc2, 0x65, 0xb2, 0xb1, 0x32, 0xb0, 0x88, 0xe2, 0x45, - 0xb5, 0xa4, 0xb8, 0x91, 0x80, 0x45, 0xd4, 0xae, 0xb9, 0x2c, 0xda, 0x16, 0xd2, 0x9a, 0x2a, 0x17, - 0xb1, 0x4c, 0x9b, 0x1e, 0x1a, 0x10, 0xfd, 0xc5, 0x68, 0xfe, 0x0d, 0xdf, 0x9d, 0x99, 0x33, 0x1d, - 0x7d, 0x1f, 0x06, 0x34, 0x0b, 0x2f, 0x6e, 0xa4, 0xb2, 0x73, 0xd7, 0x27, 0x7c, 0x29, 0x15, 0xff, - 0x1c, 0x7a, 0xfa, 0xe5, 0x77, 0xcc, 0x5e, 0x53, 0xee, 0x92, 0xf2, 0xc2, 0x6e, 0x6b, 0x19, 0xf4, - 0x3a, 0x0c, 0xb6, 0x8f, 0xf5, 0xbb, 0x8f, 0x7d, 0x04, 0x3e, 0x8d, 0x42, 0xad, 0x6f, 0x7f, 0x67, - 0x65, 0x33, 0x30, 0x66, 0x57, 0x74, 0x01, 0xf7, 0x76, 0x4e, 0x6c, 0x4f, 0x62, 0xbb, 0x27, 0x6d, - 0x59, 0x0c, 0x2c, 0x6b, 0xa4, 0xfd, 0x02, 0x53, 0x8c, 0x4b, 0x5c, 0xe8, 0x7e, 0x0f, 0x44, 0x8b, - 0xa3, 0x5f, 0xd9, 0xb6, 0xae, 0x3e, 0x8f, 0xd4, 0x1d, 0x67, 0xab, 0x95, 0x5c, 0x2f, 0x6c, 0xe9, - 0x06, 0x52, 0xdf, 0x16, 0x73, 0x5b, 0xda, 0x59, 0xcc, 0x09, 0xab, 0xdc, 0x32, 0xe8, 0xa8, 0x9c, - 0x8f, 0x60, 0xb8, 0x42, 0x59, 0x54, 0x0a, 0x57, 0xb8, 0x2e, 0x6d, 0x0b, 0xba, 0x21, 0xfe, 0x00, - 0xfa, 0xa5, 0xbc, 0x7a, 0x41, 0xb3, 0x67, 0x99, 0x2c, 0xe5, 0xd5, 0x09, 0xd6, 0xfc, 0x03, 0x08, - 0x96, 0x09, 0xa6, 0x0b, 0x9d, 0x32, 0x74, 0x0e, 0x74, 0xe0, 0x04, 0xeb, 0xe8, 0x37, 0x06, 0xbd, - 0x19, 0xaa, 0x1b, 0x54, 0x6f, 0x65, 0x17, 0x5d, 0x3b, 0x75, 0xff, 0xc5, 0x4e, 0xbd, 0xbb, 0xed, - 0xd4, 0xdf, 0xda, 0xe9, 0x7d, 0xf0, 0x67, 0x2a, 0x3e, 0x9e, 0xea, 0x1b, 0xb9, 0xc2, 0x00, 0x9a, - 0xc6, 0x49, 0x5c, 0x26, 0x37, 0x68, 0x3d, 0xd6, 0xa2, 0xe8, 0x17, 0x06, 0xbd, 0x53, 0x59, 0x67, - 0x55, 0xf9, 0xda, 0x84, 0x8d, 0x60, 0x38, 0xc9, 0xf3, 0x34, 0x89, 0x65, 0x99, 0x64, 0x6b, 0x7b, - 0xdb, 0x6e, 0x88, 0x76, 0x3c, 0xeb, 0xf4, 0xce, 0xdc, 0xbb, 0x1b, 0x22, 0x85, 0x1e, 0x69, 0x17, - 0x34, 0x96, 0xd6, 0x51, 0xa8, 0x31, 0x3f, 0x9d, 0xa4, 0x07, 0x4e, 0xaa, 0x32, 0x5b, 0xa6, 0xd9, - 0xad, 0x7e, 0xc9, 0x40, 0xb4, 0x38, 0x7a, 0xe5, 0x80, 0xf7, 0x7f, 0xb9, 0xdb, 0x1e, 0xb0, 0xc4, - 0x12, 0xc9, 0x92, 0xd6, 0xeb, 0xfa, 0x1d, 0xaf, 0x0b, 0xa1, 0x5f, 0x2b, 0xb9, 0xbe, 0xc2, 0x22, - 0x1c, 0x68, 0xe7, 0x68, 0xa0, 0xce, 0x68, 0x8d, 0x18, 0x93, 0x0b, 0x44, 0x03, 0xdb, 0x99, 0x87, - 0xce, 0xcc, 0x7f, 0x66, 0xfd, 0x70, 0xa8, 0x6f, 0x14, 0xee, 0xb6, 0xe5, 0xbf, 0xb3, 0xc1, 0x57, - 0x0c, 0xfc, 0x56, 0x30, 0x47, 0xbb, 0x82, 0x39, 0xda, 0x0a, 0x66, 0x7a, 0xd8, 0x08, 0x66, 0x7a, - 0x48, 0x58, 0x9c, 0x35, 0x82, 0x11, 0x67, 0x44, 0xd6, 0x53, 0x95, 0x55, 0xf9, 0x61, 0x6d, 0x58, - 0x0d, 0x44, 0x8b, 0x69, 0xca, 0x7e, 0xb8, 0x46, 0x65, 0x5b, 0x1d, 0x08, 0x8b, 0x68, 0x26, 0x4f, - 0xb5, 0x99, 0x98, 0xe6, 0x1a, 0xc0, 0x3f, 0x06, 0x5f, 0x50, 0xf3, 0x74, 0x87, 0x77, 0x78, 0xd1, - 0x61, 0x61, 0xb2, 0x54, 0xd4, 0xfc, 0x57, 0xb1, 0xbf, 0x27, 0x16, 0x45, 0x4f, 0xec, 0xe7, 0x54, - 0xfd, 0x22, 0xcf, 0x51, 0x59, 0x89, 0x19, 0xa0, 0xcf, 0xcc, 0x6e, 0xd1, 0xb8, 0xa3, 0x2b, 0x0c, - 0x88, 0x7e, 0x84, 0x60, 0x92, 0xa2, 0x2a, 0x45, 0x95, 0xbe, 0xee, 0xa9, 0x1c, 0xbc, 0x6f, 0x67, - 0xdf, 0x3d, 0x6f, 0x84, 0x49, 0xeb, 0xad, 0x9c, 0xdc, 0x7f, 0xc8, 0xe9, 0x44, 0xe6, 0xf2, 0x78, - 0xaa, 0xe7, 0xcc, 0x15, 0x16, 0x45, 0x9f, 0x82, 0x47, 0xb2, 0xed, 0x54, 0xf6, 0xde, 0x24, 0xf9, - 0x79, 0x4f, 0xff, 0x2b, 0x7b, 0xf2, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb7, 0x59, 0x2e, 0xc0, - 0xa7, 0x09, 0x00, 0x00, + // 1082 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0xcf, 0x8e, 0xe3, 0xc4, + 0x13, 0x96, 0x63, 0x3b, 0x89, 0x2b, 0xb3, 0xf3, 0xfb, 0xa9, 0x59, 0xb1, 0x66, 0xb9, 0x04, 0x0b, + 0xa4, 0xf0, 0x67, 0x07, 0xb4, 0x2b, 0x24, 0xc4, 0x2d, 0x33, 0x41, 0xab, 0x61, 0x66, 0x97, 0x99, + 0xce, 0xcc, 0x70, 0x42, 0xab, 0x4e, 0x52, 0x99, 0x58, 0xeb, 0xd8, 0xa6, 0xdd, 0x9e, 0x89, 0xdf, + 0x82, 0x27, 0x40, 0x42, 0xe2, 0xc4, 0x81, 0x03, 0x2f, 0xc0, 0x9d, 0x17, 0xe2, 0x8a, 0xaa, 0xbb, + 0xed, 0x38, 0xec, 0x2c, 0xda, 0x0b, 0xdc, 0xfa, 0xab, 0xea, 0x54, 0x55, 0xd7, 0x57, 0xf5, 0xc5, + 0xb0, 0x1f, 0xa7, 0x0a, 0x65, 0x2a, 0x92, 0x83, 0x5c, 0x66, 0x2a, 0x63, 0xfd, 0x1a, 0x47, 0xbf, + 0x76, 0xa0, 0x3b, 0xcd, 0x4a, 0x39, 0x47, 0xb6, 0x0f, 0x9d, 0xe3, 0x49, 0xe8, 0x0c, 0x9d, 0x91, + 0xcb, 0x3b, 0xc7, 0x13, 0xc6, 0xc0, 0x7b, 0x2e, 0xd6, 0x18, 0x76, 0x86, 0xce, 0x28, 0xe0, 0xfa, + 0x4c, 0xb6, 0x8b, 0x2a, 0xc7, 0xd0, 0x35, 0x36, 0x3a, 0xb3, 0x87, 0xd0, 0xbf, 0x2c, 0x28, 0xda, + 0x1a, 0x43, 0x4f, 0xdb, 0x1b, 0x4c, 0xbe, 0x33, 0x51, 0x14, 0xb7, 0x99, 0x5c, 0x84, 0xbe, 0xf1, + 0xd5, 0x98, 0xfd, 0x1f, 0xdc, 0x4b, 0x7e, 0x1a, 0x76, 0xb5, 0x99, 0x8e, 0x2c, 0x84, 0xde, 0x04, + 0x97, 0xa2, 0x4c, 0x54, 0xd8, 0x1b, 0x3a, 0xa3, 0x3e, 0xaf, 0x21, 0xc5, 0xb9, 0xc0, 0x04, 0xaf, + 0xa5, 0x58, 0x86, 0x7d, 0x13, 0xa7, 0xc6, 0xec, 0x00, 0xd8, 0x71, 0x5a, 0xe0, 0xbc, 0x94, 0x38, + 0x7d, 0x19, 0xe7, 0x57, 0x28, 0xe3, 0x65, 0x15, 0x06, 0x3a, 0xc0, 0x1d, 0x1e, 0xca, 0xf2, 0x0c, + 0x95, 0xa0, 0xdc, 0xa0, 0x43, 0xd5, 0x90, 0x45, 0xb0, 0x37, 0x5d, 0x09, 0x89, 0x8b, 0x29, 0xce, + 0x25, 0xaa, 0x70, 0xa0, 0xdd, 0x3b, 0xb6, 0xe8, 0x07, 0x07, 0x82, 0x89, 0x28, 0x56, 0xb3, 0x4c, + 0xc8, 0xc5, 0x1b, 0xf5, 0xec, 0x11, 0xf8, 0x73, 0x4c, 0x92, 0x22, 0x74, 0x87, 0xee, 0x68, 0xf0, + 0xf8, 0xc1, 0x41, 0x43, 0x46, 0x13, 0xe7, 0x08, 0x93, 0x84, 0x9b, 0x5b, 0xec, 0x33, 0x08, 0x14, + 0xae, 0xf3, 0x44, 0x28, 0x2c, 0x42, 0x4f, 0xff, 0x84, 0x6d, 0x7f, 0x72, 0x61, 0x5d, 0x7c, 0x7b, + 0x29, 0xfa, 0xa5, 0x03, 0xf7, 0x76, 0x42, 0xb1, 0x3d, 0x70, 0x36, 0xba, 0x2a, 0x9f, 0x3b, 0x1b, + 0x42, 0x95, 0xae, 0xc8, 0xe7, 0x4e, 0x45, 0xe8, 0x56, 0xf3, 0xe7, 0x73, 0xe7, 0x96, 0xd0, 0x4a, + 0xb3, 0xe6, 0x73, 0x67, 0xc5, 0x3e, 0x84, 0xde, 0xf7, 0x25, 0xca, 0x18, 0x8b, 0xd0, 0xd7, 0x99, + 0xff, 0xb7, 0xcd, 0x7c, 0x5e, 0xa2, 0xac, 0x78, 0xed, 0xa7, 0x97, 0x6a, 0xc6, 0x0d, 0x7d, 0xfa, + 0x4c, 0x36, 0x45, 0xd3, 0xd1, 0x33, 0x36, 0x3a, 0xdb, 0x0e, 0x19, 0xce, 0xa8, 0x43, 0x9f, 0x83, + 0x27, 0x36, 0x58, 0x84, 0x81, 0x8e, 0xff, 0xde, 0x6b, 0x9a, 0x71, 0x30, 0xde, 0x60, 0xf1, 0x55, + 0xaa, 0x64, 0xc5, 0xf5, 0xf5, 0x87, 0x4f, 0x21, 0x68, 0x4c, 0x34, 0x39, 0x2f, 0xb1, 0xd2, 0x0f, + 0x0c, 0x38, 0x1d, 0xd9, 0xfb, 0xe0, 0xdf, 0x88, 0xa4, 0x34, 0x8d, 0x1f, 0x3c, 0xde, 0xdf, 0x86, + 0x1d, 0x6f, 0xe2, 0x82, 0x1b, 0xe7, 0x97, 0x9d, 0x2f, 0x9c, 0xe8, 0x37, 0x07, 0x3c, 0xb2, 0x11, + 0xd9, 0x09, 0x5e, 0x8b, 0x79, 0x75, 0x98, 0x95, 0xe9, 0xa2, 0x08, 0x9d, 0xa1, 0x3b, 0x72, 0xf9, + 0x8e, 0x8d, 0xbd, 0x0d, 0xdd, 0x99, 0xf1, 0x76, 0x86, 0xee, 0x28, 0xe0, 0x16, 0xb1, 0xfb, 0xe0, + 0x27, 0x62, 0x86, 0x89, 0xdd, 0x03, 0x03, 0xe8, 0x76, 0x2e, 0x71, 0x19, 0x6f, 0xec, 0x1a, 0x58, + 0x44, 0xf6, 0xa2, 0x5c, 0x92, 0xdd, 0xac, 0x80, 0x45, 0xd4, 0xae, 0x99, 0x28, 0x9a, 0x16, 0xd2, + 0x99, 0x22, 0x17, 0x73, 0x91, 0xd4, 0x3d, 0x34, 0x20, 0xfa, 0xdd, 0xa1, 0xf9, 0x37, 0x7c, 0xb7, + 0x66, 0xce, 0x74, 0xf4, 0x1d, 0xe8, 0xd3, 0x2c, 0xbc, 0xb8, 0x11, 0xd2, 0xce, 0x5d, 0x8f, 0xf0, + 0x95, 0x90, 0xec, 0x53, 0xe8, 0xea, 0x97, 0xdf, 0x31, 0x7b, 0x75, 0xb8, 0x2b, 0xf2, 0x73, 0x7b, + 0xad, 0x61, 0xd0, 0x6b, 0x31, 0xd8, 0x3c, 0xd6, 0x6f, 0x3f, 0xf6, 0x11, 0xf8, 0x34, 0x0a, 0x95, + 0xae, 0xfe, 0xce, 0xc8, 0x66, 0x60, 0xcc, 0xad, 0xe8, 0x12, 0xee, 0xed, 0x64, 0x6c, 0x32, 0x39, + 0xbb, 0x99, 0xb6, 0x2c, 0x06, 0x96, 0x35, 0xda, 0xfd, 0x02, 0x13, 0x9c, 0x2b, 0x5c, 0xe8, 0x7e, + 0xf7, 0x79, 0x83, 0xa3, 0x9f, 0x9c, 0x6d, 0x5c, 0x9d, 0x8f, 0xb6, 0x7b, 0x9e, 0xad, 0xd7, 0x22, + 0x5d, 0xd8, 0xd0, 0x35, 0xa4, 0xbe, 0x2d, 0x66, 0x36, 0x74, 0x67, 0x31, 0x23, 0x2c, 0x73, 0xcb, + 0x60, 0x47, 0xe6, 0x6c, 0x08, 0x83, 0x35, 0x8a, 0xa2, 0x94, 0xb8, 0xc6, 0x54, 0xd9, 0x16, 0xb4, + 0x4d, 0xec, 0x01, 0xf4, 0x94, 0xb8, 0x7e, 0x41, 0xb3, 0x67, 0x99, 0x54, 0xe2, 0xfa, 0x04, 0x2b, + 0xf6, 0x2e, 0x04, 0xcb, 0x18, 0x93, 0x85, 0x76, 0x19, 0x3a, 0xfb, 0xda, 0x70, 0x82, 0x55, 0xf4, + 0xb3, 0x03, 0xdd, 0x29, 0xca, 0x1b, 0x94, 0x6f, 0x24, 0x17, 0x6d, 0x39, 0x75, 0xff, 0x41, 0x4e, + 0xbd, 0xbb, 0xe5, 0xd4, 0xdf, 0xca, 0xe9, 0x7d, 0xf0, 0xa7, 0x72, 0x7e, 0x3c, 0xd1, 0x15, 0xb9, + 0xdc, 0x00, 0x9a, 0xc6, 0xf1, 0x5c, 0xc5, 0x37, 0x68, 0x35, 0xd6, 0xa2, 0xe8, 0x47, 0x07, 0xba, + 0xa7, 0xa2, 0xca, 0x4a, 0xf5, 0xca, 0x84, 0x0d, 0x61, 0x30, 0xce, 0xf3, 0x24, 0x9e, 0x0b, 0x15, + 0x67, 0xa9, 0xad, 0xb6, 0x6d, 0xa2, 0x1b, 0xcf, 0x5a, 0xbd, 0x33, 0x75, 0xb7, 0x4d, 0xb4, 0xa1, + 0x47, 0x5a, 0x05, 0x8d, 0xa4, 0xb5, 0x36, 0xd4, 0x88, 0x9f, 0x76, 0xd2, 0x03, 0xc7, 0xa5, 0xca, + 0x96, 0x49, 0x76, 0xab, 0x5f, 0xd2, 0xe7, 0x0d, 0x8e, 0xfe, 0xe8, 0x80, 0xf7, 0x5f, 0xa9, 0xdb, + 0x1e, 0x38, 0xb1, 0x25, 0xd2, 0x89, 0x1b, 0xad, 0xeb, 0xb5, 0xb4, 0x2e, 0x84, 0x5e, 0x25, 0x45, + 0x7a, 0x8d, 0x45, 0xd8, 0xd7, 0xca, 0x51, 0x43, 0xed, 0xd1, 0x3b, 0x62, 0x44, 0x2e, 0xe0, 0x35, + 0x6c, 0x66, 0x1e, 0x5a, 0x33, 0xff, 0x89, 0xd5, 0xc3, 0x81, 0xae, 0x28, 0xdc, 0x6d, 0xcb, 0xbf, + 0x27, 0x83, 0x7f, 0x3a, 0xe0, 0x37, 0x0b, 0x73, 0xb4, 0xbb, 0x30, 0x47, 0xdb, 0x85, 0x99, 0x1c, + 0xd6, 0x0b, 0x33, 0x39, 0x24, 0xcc, 0xcf, 0xea, 0x85, 0xe1, 0x67, 0x44, 0xd6, 0x53, 0x99, 0x95, + 0xf9, 0x61, 0x65, 0x58, 0x0d, 0x78, 0x83, 0x69, 0xca, 0xbe, 0x5d, 0xa1, 0xb4, 0xad, 0x0e, 0xb8, + 0x45, 0x34, 0x93, 0xa7, 0x5a, 0x4c, 0x4c, 0x73, 0x0d, 0x60, 0x1f, 0x80, 0xcf, 0xa9, 0x79, 0xba, + 0xc3, 0x3b, 0xbc, 0x68, 0x33, 0x37, 0x5e, 0x0a, 0x6a, 0xbe, 0x55, 0xec, 0xff, 0x49, 0xfd, 0xe5, + 0xf2, 0x31, 0x74, 0xa7, 0xab, 0x78, 0xa9, 0xea, 0x7f, 0x95, 0xb7, 0x5a, 0x62, 0x14, 0xaf, 0x51, + 0xfb, 0xb8, 0xbd, 0x12, 0x9d, 0x43, 0xd0, 0x18, 0xb7, 0xe5, 0x38, 0xed, 0x72, 0x18, 0x78, 0x97, + 0x69, 0xac, 0xea, 0xb5, 0xa4, 0x33, 0x3d, 0xf6, 0xbc, 0x14, 0xa9, 0x8a, 0x55, 0x55, 0xaf, 0x65, + 0x8d, 0xa3, 0x27, 0xb6, 0x7c, 0x0a, 0x77, 0x99, 0xe7, 0x28, 0xed, 0x8a, 0x1b, 0xa0, 0x93, 0x64, + 0xb7, 0x68, 0xd4, 0xd9, 0xe5, 0x06, 0x44, 0xdf, 0x41, 0x30, 0x4e, 0x50, 0x2a, 0x5e, 0x26, 0xaf, + 0x6a, 0x3a, 0x03, 0xef, 0xeb, 0xe9, 0x37, 0xcf, 0xeb, 0x0a, 0xe8, 0xbc, 0x5d, 0x67, 0xf7, 0x6f, + 0xeb, 0x7c, 0x22, 0x72, 0x71, 0x3c, 0xd1, 0x73, 0xee, 0x72, 0x8b, 0xa2, 0x8f, 0xc0, 0x23, 0xd9, + 0x68, 0x45, 0xf6, 0x5e, 0x27, 0x39, 0xb3, 0xae, 0xfe, 0x2a, 0x7c, 0xf2, 0x57, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xda, 0x20, 0xfc, 0x99, 0x27, 0x0a, 0x00, 0x00, } diff --git a/bolt/internal/internal.proto b/bolt/internal/internal.proto index 9cd762d39..660a9c198 100644 --- a/bolt/internal/internal.proto +++ b/bolt/internal/internal.proto @@ -23,15 +23,15 @@ message Dashboard { } message DashboardCell { - int32 x = 1; // X-coordinate of Cell in the Dashboard - int32 y = 2; // Y-coordinate of Cell in the Dashboard - int32 w = 3; // Width of Cell in the Dashboard - int32 h = 4; // Height of Cell in the Dashboard - repeated Query queries = 5; // Time-series data queries for Dashboard - string name = 6; // User-facing name for this Dashboard - string type = 7; // Dashboard visualization type - string ID = 8; // id is the unique id of the dashboard. MIGRATED FIELD added in 1.2.0-beta6 - map axes = 9; // Axes represent the graphical viewport for a cell's visualizations + int32 x = 1; // X-coordinate of Cell in the Dashboard + int32 y = 2; // Y-coordinate of Cell in the Dashboard + int32 w = 3; // Width of Cell in the Dashboard + int32 h = 4; // Height of Cell in the Dashboard + repeated Query queries = 5; // Time-series data queries for Dashboard + string name = 6; // User-facing name for this Dashboard + string type = 7; // Dashboard visualization type + string ID = 8; // id is the unique id of the dashboard. MIGRATED FIELD added in 1.2.0-beta6 + map axes = 9; // Axes represent the graphical viewport for a cell's visualizations } message Axis { @@ -54,18 +54,18 @@ message Template { } message TemplateValue { - string type = 1; // Type can be tagKey, tagValue, fieldKey, csv, measurement, database, constant - string value = 2; // Value is the specific value used to replace a template in an InfluxQL query - bool selected = 3; // Selected states that this variable has been picked to use for replacement + string type = 1; // Type can be tagKey, tagValue, fieldKey, csv, measurement, database, constant + string value = 2; // Value is the specific value used to replace a template in an InfluxQL query + bool selected = 3; // Selected states that this variable has been picked to use for replacement } message TemplateQuery { - string command = 1; // Command is the query itself - string db = 2; // DB the database for the query (optional) - string rp = 3; // RP is a retention policy and optional; - string measurement = 4; // Measurement is the optinally selected measurement for the query - string tag_key = 5; // TagKey is the optionally selected tag key for the query - string field_key = 6; // FieldKey is the optionally selected field key for the query + string command = 1; // Command is the query itself + string db = 2; // DB the database for the query (optional) + string rp = 3; // RP is a retention policy and optional; + string measurement = 4; // Measurement is the optinally selected measurement for the query + string tag_key = 5; // TagKey is the optionally selected tag key for the query + string field_key = 6; // FieldKey is the optionally selected field key for the query } message Server { @@ -101,31 +101,38 @@ message Cell { } message Query { - string Command = 1; // Command is the query itself - string DB = 2; // DB the database for the query (optional) - string RP = 3; // RP is a retention policy and optional; - repeated string GroupBys= 4; // GroupBys define the groups to combine in the query - repeated string Wheres = 5; // Wheres define the restrictions on the query - string Label = 6; // Label is the name of the Y-Axis - Range Range = 7; // Range is the upper and lower bound of the Y-Axis - string Source = 8; // Source is the optional URI to the data source + string Command = 1; // Command is the query itself + string DB = 2; // DB the database for the query (optional) + string RP = 3; // RP is a retention policy and optional; + repeated string GroupBys = 4; // GroupBys define the groups to combine in the query + repeated string Wheres = 5; // Wheres define the restrictions on the query + string Label = 6; // Label is the name of the Y-Axis + Range Range = 7; // Range is the upper and lower bound of the Y-Axis + string Source = 8; // Source is the optional URI to the data source + repeated TimeShift Shifts = 9; // TimeShift represents a shift to apply to an influxql query's time range +} + +message TimeShift { + string Label = 1; // user facing description + string Unit = 2; // influxql time unit representation i.e. ms, s, m, h, d + string Quantity = 3; // number of units } message Range { - int64 Upper = 1; // Upper is the upper-bound of the range - int64 Lower = 2; // Lower is the lower-bound of the range + int64 Upper = 1; // Upper is the upper-bound of the range + int64 Lower = 2; // Lower is the lower-bound of the range } message AlertRule { - string ID = 1; // ID is the unique ID of this alert rule - string JSON = 2; // JSON byte representation of the alert - int64 SrcID = 3; // SrcID is the id of the source this alert is associated with - int64 KapaID = 4; // KapaID is the id of the kapacitor this alert is associated with + string ID = 1; // ID is the unique ID of this alert rule + string JSON = 2; // JSON byte representation of the alert + int64 SrcID = 3; // SrcID is the id of the source this alert is associated with + int64 KapaID = 4; // KapaID is the id of the kapacitor this alert is associated with } message User { - uint64 ID = 1; // ID is the unique ID of this user - string Name = 2; // Name is the user's login name + uint64 ID = 1; // ID is the unique ID of this user + string Name = 2; // Name is the user's login name } // The following is a vim modeline, it autoconfigures vim to have the diff --git a/bolt/internal/internal_test.go b/bolt/internal/internal_test.go index 244dd471c..4ca7ae420 100644 --- a/bolt/internal/internal_test.go +++ b/bolt/internal/internal_test.go @@ -163,6 +163,13 @@ func Test_MarshalDashboard(t *testing.T) { Upper: int64(100), }, Source: "/chronograf/v1/sources/1", + Shifts: []chronograf.TimeShift{ + { + Label: "Best Week Ever", + Unit: "d", + Quantity: "7", + }, + }, }, }, Axes: map[string]chronograf.Axis{ @@ -210,6 +217,7 @@ func Test_MarshalDashboard_WithLegacyBounds(t *testing.T) { Range: &chronograf.Range{ Upper: int64(100), }, + Shifts: []chronograf.TimeShift{}, }, }, Axes: map[string]chronograf.Axis{ @@ -241,6 +249,7 @@ func Test_MarshalDashboard_WithLegacyBounds(t *testing.T) { Range: &chronograf.Range{ Upper: int64(100), }, + Shifts: []chronograf.TimeShift{}, }, }, Axes: map[string]chronograf.Axis{ @@ -285,6 +294,7 @@ func Test_MarshalDashboard_WithEmptyLegacyBounds(t *testing.T) { Range: &chronograf.Range{ Upper: int64(100), }, + Shifts: []chronograf.TimeShift{}, }, }, Axes: map[string]chronograf.Axis{ @@ -316,6 +326,7 @@ func Test_MarshalDashboard_WithEmptyLegacyBounds(t *testing.T) { Range: &chronograf.Range{ Upper: int64(100), }, + Shifts: []chronograf.TimeShift{}, }, }, Axes: map[string]chronograf.Axis{ diff --git a/chronograf.go b/chronograf.go index b6e314742..0ea3592d6 100644 --- a/chronograf.go +++ b/chronograf.go @@ -171,6 +171,7 @@ type DashboardQuery struct { Range *Range `json:"range,omitempty"` // Range is the default Y-Axis range for the data QueryConfig QueryConfig `json:"queryConfig,omitempty"` // QueryConfig represents the query state that is understood by the data explorer Source string `json:"source"` // Source is the optional URI to the data source for this queryConfig + Shifts []TimeShift `json:"-"` // Shifts represents shifts to apply to an influxql query's time range. Clients expect the shift to be in the generated QueryConfig } // TemplateQuery is used to retrieve choices for template replacement @@ -284,6 +285,13 @@ type DurationRange struct { Lower string `json:"lower"` } +// TimeShift represents a shift to apply to an influxql query's time range +type TimeShift struct { + Label string `json:"label"` // user facing description + Unit string `json:"unit"` // influxql time unit representation i.e. ms, s, m, h, d + Quantity string `json:"quantity"` // number of units +} + // QueryConfig represents UI query from the data explorer type QueryConfig struct { ID string `json:"id,omitempty"` @@ -297,6 +305,7 @@ type QueryConfig struct { Fill string `json:"fill,omitempty"` RawText *string `json:"rawText"` Range *DurationRange `json:"range"` + Shifts []TimeShift `json:"shifts"` } // KapacitorNode adds arguments and properties to an alert diff --git a/server/cells.go b/server/cells.go index 2d01b406c..0bed1a4d9 100644 --- a/server/cells.go +++ b/server/cells.go @@ -31,7 +31,6 @@ func newCellResponses(dID chronograf.DashboardID, dcells []chronograf.DashboardC cells := make([]dashboardCellResponse, len(dcells)) for i, cell := range dcells { newCell := chronograf.DashboardCell{} - newCell.Queries = make([]chronograf.DashboardQuery, len(cell.Queries)) copy(newCell.Queries, cell.Queries) @@ -121,6 +120,8 @@ func CorrectWidthHeight(c *chronograf.DashboardCell) { func AddQueryConfig(c *chronograf.DashboardCell) { for i, q := range c.Queries { qc := ToQueryConfig(q.Command) + qc.Shifts = append([]chronograf.TimeShift(nil), q.Shifts...) + q.Shifts = nil q.QueryConfig = qc c.Queries[i] = q } diff --git a/server/cells_test.go b/server/cells_test.go index 12b109679..2f6060bf1 100644 --- a/server/cells_test.go +++ b/server/cells_test.go @@ -162,14 +162,14 @@ func Test_Service_DashboardCells(t *testing.T) { http.StatusOK, }, { - "cell axes should always be \"x\", \"y\", and \"y2\"", - &url.URL{ + name: "cell axes should always be \"x\", \"y\", and \"y2\"", + reqURL: &url.URL{ Path: "/chronograf/v1/dashboards/1/cells", }, - map[string]string{ + ctxParams: map[string]string{ "id": "1", }, - []chronograf.DashboardCell{ + mockResponse: []chronograf.DashboardCell{ { ID: "3899be5a-f6eb-4347-b949-de2f4fbea859", X: 0, @@ -182,7 +182,7 @@ func Test_Service_DashboardCells(t *testing.T) { Axes: map[string]chronograf.Axis{}, }, }, - []chronograf.DashboardCell{ + expected: []chronograf.DashboardCell{ { ID: "3899be5a-f6eb-4347-b949-de2f4fbea859", X: 0, @@ -205,7 +205,7 @@ func Test_Service_DashboardCells(t *testing.T) { }, }, }, - http.StatusOK, + expectedCode: http.StatusOK, }, } diff --git a/server/dashboards_test.go b/server/dashboards_test.go index 622393757..04f72fda4 100644 --- a/server/dashboards_test.go +++ b/server/dashboards_test.go @@ -219,6 +219,13 @@ func Test_newDashboardResponse(t *testing.T) { { Source: "/chronograf/v1/sources/1", Command: "SELECT donors from hill_valley_preservation_society where time > '1985-10-25 08:00:00'", + Shifts: []chronograf.TimeShift{ + { + Label: "Best Week Evar", + Unit: "d", + Quantity: "7", + }, + }, }, }, Axes: map[string]chronograf.Axis{ @@ -267,6 +274,13 @@ func Test_newDashboardResponse(t *testing.T) { }, Tags: make(map[string][]string, 0), AreTagsAccepted: false, + Shifts: []chronograf.TimeShift{ + { + Label: "Best Week Evar", + Unit: "d", + Quantity: "7", + }, + }, }, }, }, diff --git a/server/queries.go b/server/queries.go index 76e5ad4b5..38e937b34 100644 --- a/server/queries.go +++ b/server/queries.go @@ -84,6 +84,7 @@ func (s *Service) Queries(w http.ResponseWriter, r *http.Request) { Error(w, http.StatusBadRequest, err.Error(), s.Logger) return } + qc.Shifts = []chronograf.TimeShift{} qr.QueryConfig = qc if stmt, err := queries.ParseSelect(query); err == nil { diff --git a/server/queries_test.go b/server/queries_test.go index 83967a7e7..14e0c794a 100644 --- a/server/queries_test.go +++ b/server/queries_test.go @@ -60,7 +60,7 @@ func TestService_Queries(t *testing.T) { "id": "82b60d37-251e-4afe-ac93-ca20a3642b11" } ]}`))), - want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SELECT \"pingReq\" FROM db.\"monitor\".\"httpd\" WHERE time \u003e now() - 1m","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"db","measurement":"httpd","retentionPolicy":"monitor","fields":[{"value":"pingReq","type":"field","alias":""}],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":null,"range":{"upper":"","lower":"now() - 1m"}},"queryAST":{"condition":{"expr":"binary","op":"\u003e","lhs":{"expr":"reference","val":"time"},"rhs":{"expr":"binary","op":"-","lhs":{"expr":"call","name":"now"},"rhs":{"expr":"literal","val":"1m","type":"duration"}}},"fields":[{"column":{"expr":"reference","val":"pingReq"}}],"sources":[{"database":"db","retentionPolicy":"monitor","name":"httpd","type":"measurement"}]}}]} + want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SELECT \"pingReq\" FROM db.\"monitor\".\"httpd\" WHERE time \u003e now() - 1m","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"db","measurement":"httpd","retentionPolicy":"monitor","fields":[{"value":"pingReq","type":"field","alias":""}],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":null,"range":{"upper":"","lower":"now() - 1m"},"shifts":[]},"queryAST":{"condition":{"expr":"binary","op":"\u003e","lhs":{"expr":"reference","val":"time"},"rhs":{"expr":"binary","op":"-","lhs":{"expr":"call","name":"now"},"rhs":{"expr":"literal","val":"1m","type":"duration"}}},"fields":[{"column":{"expr":"reference","val":"pingReq"}}],"sources":[{"database":"db","retentionPolicy":"monitor","name":"httpd","type":"measurement"}]}}]} `, }, { @@ -81,7 +81,7 @@ func TestService_Queries(t *testing.T) { "id": "82b60d37-251e-4afe-ac93-ca20a3642b11" } ]}`))), - want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SHOW DATABASES","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"","measurement":"","retentionPolicy":"","fields":[],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":"SHOW DATABASES","range":null}}]} + want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SHOW DATABASES","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"","measurement":"","retentionPolicy":"","fields":[],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":"SHOW DATABASES","range":null,"shifts":[]}}]} `, }, { @@ -166,7 +166,7 @@ func TestService_Queries(t *testing.T) { } ] }`))), - want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e :dashboardTime: AND time \u003c :upperDashboardTime: GROUP BY :interval:","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"","measurement":"","retentionPolicy":"","fields":[],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e :dashboardTime: AND time \u003c :upperDashboardTime: GROUP BY :interval:","range":null},"queryTemplated":"SELECT \"pingReq\" FROM \"_internal\".\"monitor\".\"httpd\" WHERE time \u003e now() - 15m AND time \u003c now() GROUP BY time(2s)","tempVars":[{"tempVar":":upperDashboardTime:","values":[{"value":"now()","type":"constant","selected":true}]},{"tempVar":":dashboardTime:","values":[{"value":"now() - 15m","type":"constant","selected":true}]},{"tempVar":":dbs:","values":[{"value":"_internal","type":"database","selected":true}]},{"tempVar":":interval:","values":[{"value":"1000","type":"resolution","selected":false},{"value":"3","type":"pointsPerPixel","selected":false}]}]}]} + want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e :dashboardTime: AND time \u003c :upperDashboardTime: GROUP BY :interval:","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"","measurement":"","retentionPolicy":"","fields":[],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e :dashboardTime: AND time \u003c :upperDashboardTime: GROUP BY :interval:","range":null,"shifts":[]},"queryTemplated":"SELECT \"pingReq\" FROM \"_internal\".\"monitor\".\"httpd\" WHERE time \u003e now() - 15m AND time \u003c now() GROUP BY time(2s)","tempVars":[{"tempVar":":upperDashboardTime:","values":[{"value":"now()","type":"constant","selected":true}]},{"tempVar":":dashboardTime:","values":[{"value":"now() - 15m","type":"constant","selected":true}]},{"tempVar":":dbs:","values":[{"value":"_internal","type":"database","selected":true}]},{"tempVar":":interval:","values":[{"value":"1000","type":"resolution","selected":false},{"value":"3","type":"pointsPerPixel","selected":false}]}]}]} `, }, } From b9b40a16590037daf99099093f1ab5717867670b Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 10 Nov 2017 11:09:47 -0800 Subject: [PATCH 050/138] Change multiple to quantity in shift + misc cleanup --- .../data_explorer/reducers/queryConfigSpec.js | 2 +- ui/spec/kapacitor/reducers/queryConfigSpec.js | 11 +- ui/spec/shared/query/helpersSpec.js | 6 +- ui/src/kapacitor/actions/view/index.js | 212 ++++++++---------- .../kapacitor/containers/KapacitorRulePage.js | 8 +- ui/src/kapacitor/reducers/queryConfigs.js | 7 +- ui/src/shared/components/FieldList.js | 10 +- ui/src/shared/components/QueryOptions.js | 2 +- ui/src/shared/constants/timeShift.js | 16 +- ui/src/shared/query/helpers.js | 10 +- ui/src/utils/buildQueriesForGraphs.js | 6 +- ui/src/utils/defaultQueryConfig.js | 2 +- ui/src/utils/influxql.js | 4 +- ui/src/utils/queryTransitions.js | 2 +- ui/src/utils/timeSeriesToDygraph.js | 4 +- 15 files changed, 141 insertions(+), 161 deletions(-) diff --git a/ui/spec/data_explorer/reducers/queryConfigSpec.js b/ui/spec/data_explorer/reducers/queryConfigSpec.js index 07199db0d..dd1e7f63f 100644 --- a/ui/spec/data_explorer/reducers/queryConfigSpec.js +++ b/ui/spec/data_explorer/reducers/queryConfigSpec.js @@ -478,7 +478,7 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { [queryID]: buildInitialState(queryID), } - const shift = {multiple: 1, unit: 'd', duration: '1d'} + const shift = {quantity: 1, unit: 'd', duration: '1d'} const action = timeShift(queryID, shift) const nextState = reducer(initialState, action) diff --git a/ui/spec/kapacitor/reducers/queryConfigSpec.js b/ui/spec/kapacitor/reducers/queryConfigSpec.js index 6ecbf5160..ea6d560c6 100644 --- a/ui/spec/kapacitor/reducers/queryConfigSpec.js +++ b/ui/spec/kapacitor/reducers/queryConfigSpec.js @@ -1,14 +1,15 @@ import reducer from 'src/kapacitor/reducers/queryConfigs' import defaultQueryConfig from 'src/utils/defaultQueryConfig' import { + chooseTag, + timeShift, + groupByTag, + toggleField, + groupByTime, chooseNamespace, chooseMeasurement, - chooseTag, - groupByTag, - toggleTagAcceptance, - toggleField, applyFuncsToField, - groupByTime, + toggleTagAcceptance, } from 'src/kapacitor/actions/queryConfigs' const fakeAddQueryAction = (panelID, queryID) => { diff --git a/ui/spec/shared/query/helpersSpec.js b/ui/spec/shared/query/helpersSpec.js index 5dbed1831..c21c248a7 100644 --- a/ui/spec/shared/query/helpersSpec.js +++ b/ui/spec/shared/query/helpersSpec.js @@ -55,7 +55,7 @@ describe('Shared.Query.Helpers', () => { const upper = Date.now() const oneMinute = 60000 const lower = Date.now() - oneMinute - const shift = {multiple: 7, unit: 'd'} + const shift = {quantity: 7, unit: 'd'} const timeRange = {upper, lower} const type = timeRangeType(timeRange) @@ -71,7 +71,7 @@ describe('Shared.Query.Helpers', () => { }) it('can calculate the shift for relative lower timeRanges', () => { - const shift = {multiple: 7, unit: 'd'} + const shift = {quantity: 7, unit: 'd'} const lower = 'now() - 15m' const timeRange = {lower, upper: null} @@ -91,7 +91,7 @@ describe('Shared.Query.Helpers', () => { const upper = Date.now() const oneMinute = 60000 const lower = Date.now() - oneMinute - const shift = {multiple: 7, unit: 'd'} + const shift = {quantity: 7, unit: 'd'} const timeRange = {upper, lower} const type = timeRangeType(timeRange) diff --git a/ui/src/kapacitor/actions/view/index.js b/ui/src/kapacitor/actions/view/index.js index 20914ac60..395f81768 100644 --- a/ui/src/kapacitor/actions/view/index.js +++ b/ui/src/kapacitor/actions/view/index.js @@ -66,7 +66,7 @@ export const getRule = (kapacitor, ruleID) => async dispatch => { } } -export function loadDefaultRule() { +export const loadDefaultRule = () => { return dispatch => { const queryID = uuid.v4() dispatch({ @@ -88,15 +88,13 @@ export const fetchRules = kapacitor => async dispatch => { } } -export function chooseTrigger(ruleID, trigger) { - return { - type: 'CHOOSE_TRIGGER', - payload: { - ruleID, - trigger, - }, - } -} +export const chooseTrigger = (ruleID, trigger) => ({ + type: 'CHOOSE_TRIGGER', + payload: { + ruleID, + trigger, + }, +}) export const addEvery = (ruleID, frequency) => ({ type: 'ADD_EVERY', @@ -113,36 +111,30 @@ export const removeEvery = ruleID => ({ }, }) -export function updateRuleValues(ruleID, trigger, values) { - return { - type: 'UPDATE_RULE_VALUES', - payload: { - ruleID, - trigger, - values, - }, - } -} +export const updateRuleValues = (ruleID, trigger, values) => ({ + type: 'UPDATE_RULE_VALUES', + payload: { + ruleID, + trigger, + values, + }, +}) -export function updateMessage(ruleID, message) { - return { - type: 'UPDATE_RULE_MESSAGE', - payload: { - ruleID, - message, - }, - } -} +export const updateMessage = (ruleID, message) => ({ + type: 'UPDATE_RULE_MESSAGE', + payload: { + ruleID, + message, + }, +}) -export function updateDetails(ruleID, details) { - return { - type: 'UPDATE_RULE_DETAILS', - payload: { - ruleID, - details, - }, - } -} +export const updateDetails = (ruleID, details) => ({ + type: 'UPDATE_RULE_DETAILS', + payload: { + ruleID, + details, + }, +}) export const updateAlertProperty = (ruleID, alertNodeName, alertProperty) => ({ type: 'UPDATE_RULE_ALERT_PROPERTY', @@ -153,87 +145,73 @@ export const updateAlertProperty = (ruleID, alertNodeName, alertProperty) => ({ }, }) -export function updateAlerts(ruleID, alerts) { - return { - type: 'UPDATE_RULE_ALERTS', - payload: { - ruleID, - alerts, - }, - } +export const updateAlerts = (ruleID, alerts) => ({ + type: 'UPDATE_RULE_ALERTS', + payload: { + ruleID, + alerts, + }, +}) + +export const updateAlertNodes = (ruleID, alertNodeName, alertNodesText) => ({ + type: 'UPDATE_RULE_ALERT_NODES', + payload: { + ruleID, + alertNodeName, + alertNodesText, + }, +}) + +export const updateRuleName = (ruleID, name) => ({ + type: 'UPDATE_RULE_NAME', + payload: { + ruleID, + name, + }, +}) + +export const deleteRuleSuccess = ruleID => ({ + type: 'DELETE_RULE_SUCCESS', + payload: { + ruleID, + }, +}) + +export const updateRuleStatusSuccess = (ruleID, status) => ({ + type: 'UPDATE_RULE_STATUS_SUCCESS', + payload: { + ruleID, + status, + }, +}) + +export const deleteRule = rule => dispatch => { + deleteRuleAPI(rule) + .then(() => { + dispatch(deleteRuleSuccess(rule.id)) + dispatch( + publishNotification('success', `${rule.name} deleted successfully`) + ) + }) + .catch(() => { + dispatch( + publishNotification('error', `${rule.name} could not be deleted`) + ) + }) } -export function updateAlertNodes(ruleID, alertNodeName, alertNodesText) { - return { - type: 'UPDATE_RULE_ALERT_NODES', - payload: { - ruleID, - alertNodeName, - alertNodesText, - }, - } -} - -export function updateRuleName(ruleID, name) { - return { - type: 'UPDATE_RULE_NAME', - payload: { - ruleID, - name, - }, - } -} - -export function deleteRuleSuccess(ruleID) { - return { - type: 'DELETE_RULE_SUCCESS', - payload: { - ruleID, - }, - } -} - -export function updateRuleStatusSuccess(ruleID, status) { - return { - type: 'UPDATE_RULE_STATUS_SUCCESS', - payload: { - ruleID, - status, - }, - } -} - -export function deleteRule(rule) { - return dispatch => { - deleteRuleAPI(rule) - .then(() => { - dispatch(deleteRuleSuccess(rule.id)) - dispatch( - publishNotification('success', `${rule.name} deleted successfully`) - ) - }) - .catch(() => { - dispatch( - publishNotification('error', `${rule.name} could not be deleted`) - ) - }) - } -} - -export function updateRuleStatus(rule, status) { - return dispatch => { - updateRuleStatusAPI(rule, status) - .then(() => { - dispatch( - publishNotification('success', `${rule.name} ${status} successfully`) - ) - }) - .catch(() => { - dispatch( - publishNotification('error', `${rule.name} could not be ${status}`) - ) - }) - } +export const updateRuleStatus = (rule, status) => dispatch => { + updateRuleStatusAPI(rule, status) + .then(() => { + dispatch( + publishNotification('success', `${rule.name} ${status} successfully`) + ) + }) + .catch(() => { + dispatch( + publishNotification('error', `${rule.name} could not be ${status}`) + ) + }) } export const createTask = ( diff --git a/ui/src/kapacitor/containers/KapacitorRulePage.js b/ui/src/kapacitor/containers/KapacitorRulePage.js index 6b1754840..4537d7fb6 100644 --- a/ui/src/kapacitor/containers/KapacitorRulePage.js +++ b/ui/src/kapacitor/containers/KapacitorRulePage.js @@ -61,13 +61,13 @@ class KapacitorRulePage extends Component { render() { const { rules, - queryConfigs, params, - ruleActions, source, - queryConfigActions, - addFlashMessage, router, + ruleActions, + queryConfigs, + addFlashMessage, + queryConfigActions, } = this.props const {enabledAlerts, kapacitor} = this.state const rule = this.isEditing() diff --git a/ui/src/kapacitor/reducers/queryConfigs.js b/ui/src/kapacitor/reducers/queryConfigs.js index 5ea652a99..1de4284bf 100644 --- a/ui/src/kapacitor/reducers/queryConfigs.js +++ b/ui/src/kapacitor/reducers/queryConfigs.js @@ -1,13 +1,14 @@ import defaultQueryConfig from 'src/utils/defaultQueryConfig' import { - applyFuncsToField, - chooseMeasurement, - chooseNamespace, + timeShift, chooseTag, groupByTag, groupByTime, removeFuncs, + chooseNamespace, toggleKapaField, + applyFuncsToField, + chooseMeasurement, toggleTagAcceptance, } from 'src/utils/queryTransitions' diff --git a/ui/src/shared/components/FieldList.js b/ui/src/shared/components/FieldList.js index 38cc736f4..bfb06f2e3 100644 --- a/ui/src/shared/components/FieldList.js +++ b/ui/src/shared/components/FieldList.js @@ -132,7 +132,7 @@ class FieldList extends Component { render() { const { - query: {database, measurement, fields = [], groupBy, fill, shift}, + query: {database, measurement, fields = [], groupBy, fill, shifts}, isKapacitorRule, } = this.props @@ -146,7 +146,7 @@ class FieldList extends Component { {hasAggregates ? { export const shiftTimeRange = (timeRange, shift) => { const {upper, lower} = timeRange - const {multiple, unit} = shift + const {quantity, unit} = shift const trType = timeRangeType(timeRange) - const duration = `${multiple}${unit}` + const duration = `${quantity}${unit}` const type = 'shifted' switch (trType) { @@ -101,10 +101,10 @@ const getMomentUnit = unit => { } } -export const shiftDate = (date, multiple, unit) => { - if (!date && !multiple && !unit) { +export const shiftDate = (date, quantity, unit) => { + if (!date && !quantity && !unit) { return moment(date) } - return moment(date).add(multiple, getMomentUnit(unit)) + return moment(date).add(quantity, getMomentUnit(unit)) } diff --git a/ui/src/utils/buildQueriesForGraphs.js b/ui/src/utils/buildQueriesForGraphs.js index 16dfe9b13..3031f9cff 100644 --- a/ui/src/utils/buildQueriesForGraphs.js +++ b/ui/src/utils/buildQueriesForGraphs.js @@ -3,13 +3,13 @@ import {TYPE_QUERY_CONFIG, TYPE_SHIFTED} from 'src/dashboards/constants' const buildQueries = (proxy, queryConfigs, tR) => { const statements = queryConfigs.map(query => { - const {rawText, range, id, shift, database, measurement, fields} = query + const {rawText, range, id, shifts, database, measurement, fields} = query const timeRange = range || tR const text = rawText || buildQuery(TYPE_QUERY_CONFIG, timeRange, query) const isParsable = database && measurement && fields.length - if (shift && shift.length && isParsable) { - const shiftedQueries = shift + if (shifts && shifts.length && isParsable) { + const shiftedQueries = shifts .filter(s => s.unit) .map(s => buildQuery(TYPE_SHIFTED, timeRange, query, s)) diff --git a/ui/src/utils/defaultQueryConfig.js b/ui/src/utils/defaultQueryConfig.js index 51f528186..16915dff9 100644 --- a/ui/src/utils/defaultQueryConfig.js +++ b/ui/src/utils/defaultQueryConfig.js @@ -15,7 +15,7 @@ const defaultQueryConfig = ({id, isKapacitorRule = false}) => { areTagsAccepted: true, rawText: null, status: null, - shift: [], + shifts: [], } return isKapacitorRule ? queryConfig : {...queryConfig, fill: NULL_STRING} diff --git a/ui/src/utils/influxql.js b/ui/src/utils/influxql.js index aae6d4571..dc434f491 100644 --- a/ui/src/utils/influxql.js +++ b/ui/src/utils/influxql.js @@ -60,11 +60,11 @@ export const buildQuery = (type, timeRange, config, shift) => { } case TYPE_SHIFTED: { - const {multiple, unit} = shift + const {quantity, unit} = shift return buildInfluxQLQuery( shiftTimeRange(timeRange, shift), config, - `_shifted__${multiple}__${unit}` + `_shifted__${quantity}__${unit}` ) } diff --git a/ui/src/utils/queryTransitions.js b/ui/src/utils/queryTransitions.js index cc89f2351..1452cab22 100644 --- a/ui/src/utils/queryTransitions.js +++ b/ui/src/utils/queryTransitions.js @@ -243,4 +243,4 @@ export const chooseTag = (query, tag) => { return updateTagValues(query.tags[tag.key].concat(tag.value)) } -export const timeShift = (query, shift) => ({...query, shift: [shift]}) +export const timeShift = (query, shift) => ({...query, shifts: [shift]}) diff --git a/ui/src/utils/timeSeriesToDygraph.js b/ui/src/utils/timeSeriesToDygraph.js index 1de04cd64..aef7530c1 100644 --- a/ui/src/utils/timeSeriesToDygraph.js +++ b/ui/src/utils/timeSeriesToDygraph.js @@ -117,8 +117,8 @@ export default function timeSeriesToDygraph(raw = [], isInDataExplorer) { const seriesIndex = cells.seriesIndex[i] if (label.includes('_shifted__')) { - const [, multiple, duration] = label.split('__') - time = +shiftDate(time, multiple, duration).format('x') + const [, quantity, duration] = label.split('__') + time = +shiftDate(time, quantity, duration).format('x') } let existingRowIndex = tsMemo[time] From c8e80753efe02588498172d1e0ce438c40dfeb64 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 10 Nov 2017 11:10:19 -0800 Subject: [PATCH 051/138] Add timeShift actions + spec to kapa queryConfig --- ui/spec/kapacitor/reducers/queryConfigSpec.js | 14 ++++++++++++++ ui/src/kapacitor/actions/queryConfigs.js | 8 ++++++++ ui/src/kapacitor/reducers/queryConfigs.js | 7 +++++++ 3 files changed, 29 insertions(+) diff --git a/ui/spec/kapacitor/reducers/queryConfigSpec.js b/ui/spec/kapacitor/reducers/queryConfigSpec.js index ea6d560c6..16a02a2ab 100644 --- a/ui/spec/kapacitor/reducers/queryConfigSpec.js +++ b/ui/spec/kapacitor/reducers/queryConfigSpec.js @@ -325,4 +325,18 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => { expect(nextState[queryID].groupBy.time).to.equal(time) }) }) + + describe('KAPA_TIME_SHIFT', () => { + it('can shift the time', () => { + const initialState = { + [queryID]: buildInitialState(queryID), + } + + const shift = {quantity: 1, unit: 'd', duration: '1d'} + const action = timeShift(queryID, shift) + const nextState = reducer(initialState, action) + + expect(nextState[queryID].shift).to.deep.equal([shift]) + }) + }) }) diff --git a/ui/src/kapacitor/actions/queryConfigs.js b/ui/src/kapacitor/actions/queryConfigs.js index e579b31c3..ddffb4f9e 100644 --- a/ui/src/kapacitor/actions/queryConfigs.js +++ b/ui/src/kapacitor/actions/queryConfigs.js @@ -69,3 +69,11 @@ export const removeFuncs = (queryID, fields) => ({ fields, }, }) + +export const timeShift = (queryID, shift) => ({ + type: 'KAPA_TIME_SHIFT', + payload: { + queryID, + shift, + }, +}) diff --git a/ui/src/kapacitor/reducers/queryConfigs.js b/ui/src/kapacitor/reducers/queryConfigs.js index 1de4284bf..8435e7d2a 100644 --- a/ui/src/kapacitor/reducers/queryConfigs.js +++ b/ui/src/kapacitor/reducers/queryConfigs.js @@ -125,6 +125,13 @@ const queryConfigs = (state = {}, action) => { // fields with no functions cannot have a group by time return {...state, [queryID]: nextQuery} } + + case 'KAPA_TIME_SHIFT': { + const {queryID, shift} = action.payload + const nextQuery = timeShift(state[queryID], shift) + + return {...state, [queryID]: nextQuery} + } } return state } From c3a4696174f692c20ab91317daa088ad366ad1e3 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 10 Nov 2017 16:02:08 -0800 Subject: [PATCH 052/138] Add processing of incoming queryConfig --- server/cells.go | 14 ++++++++++++++ server/queryconfig.go | 27 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/server/cells.go b/server/cells.go index 0bed1a4d9..d1791e18f 100644 --- a/server/cells.go +++ b/server/cells.go @@ -70,6 +70,12 @@ func newCellResponses(dID chronograf.DashboardID, dcells []chronograf.DashboardC // have the correct axes specified func ValidDashboardCellRequest(c *chronograf.DashboardCell) error { CorrectWidthHeight(c) + for _, q := range c.Queries { + if err := ValidateQueryConfig(&q.QueryConfig); err != nil { + return err + } + } + MoveTimeShift(c) return HasCorrectAxes(c) } @@ -114,6 +120,14 @@ func CorrectWidthHeight(c *chronograf.DashboardCell) { } } +// MoveTimeShift moves TimeShift from the QueryConfig to the DashboardQuery +func MoveTimeShift(c *chronograf.DashboardCell) { + for i, query := range c.Queries { + query.Shifts = query.QueryConfig.Shifts + c.Queries[i] = query + } +} + // AddQueryConfig updates a cell by converting InfluxQL into queryconfigs // If influxql cannot be represented by a full query config, then, the // query config's raw text is set to the command. diff --git a/server/queryconfig.go b/server/queryconfig.go index 8ec22be8f..6370986ce 100644 --- a/server/queryconfig.go +++ b/server/queryconfig.go @@ -1,6 +1,8 @@ package server import ( + "fmt" + "github.com/influxdata/chronograf" "github.com/influxdata/chronograf/influx" ) @@ -22,3 +24,28 @@ func ToQueryConfig(query string) chronograf.QueryConfig { Tags: make(map[string][]string, 0), } } + +var validFieldTypes = map[string]bool{ + "func": true, + "field": true, + "integer": true, + "number": true, + "regex": true, + "wildcard": true, +} + +// ValidateQueryConfig checks any query config input +func ValidateQueryConfig(q *chronograf.QueryConfig) error { + for _, fld := range q.Fields { + invalid := fmt.Errorf(`invalid field type "%s" ; expect func, field, integer, number, regex, wildcard`, fld.Type) + if !validFieldTypes[fld.Type] { + return invalid + } + for _, arg := range fld.Args { + if !validFieldTypes[arg.Type] { + return invalid + } + } + } + return nil +} From c6fd7058ba970ebbe8bf95ffb672db48230de50f Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 10 Nov 2017 16:02:35 -0800 Subject: [PATCH 053/138] Add test for queryConfig validation --- server/queryconfig_test.go | 50 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 server/queryconfig_test.go diff --git a/server/queryconfig_test.go b/server/queryconfig_test.go new file mode 100644 index 000000000..03b84558e --- /dev/null +++ b/server/queryconfig_test.go @@ -0,0 +1,50 @@ +package server + +import ( + "testing" + + "github.com/influxdata/chronograf" +) + +func TestValidateQueryConfig(t *testing.T) { + tests := []struct { + name string + q *chronograf.QueryConfig + wantErr bool + }{ + { + name: "invalid field type", + q: &chronograf.QueryConfig{ + Fields: []chronograf.Field{ + { + Type: "invalid", + }, + }, + }, + wantErr: true, + }, + { + name: "invalid field args", + q: &chronograf.QueryConfig{ + Fields: []chronograf.Field{ + { + Type: "func", + Args: []chronograf.Field{ + { + Type: "invalid", + }, + }, + }, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := ValidateQueryConfig(tt.q); (err != nil) != tt.wantErr { + t.Errorf("ValidateQueryConfig() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} From 481a56be559e22e135812bdbbc5fb9bf7dc8f11c Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 10 Nov 2017 16:44:06 -0800 Subject: [PATCH 054/138] Make timeShift appear in dashboard layouts --- ui/src/shared/components/Layout.js | 2 +- ui/src/utils/buildQueriesForLayouts.js | 72 ++++++++++++++++++++++++++ ui/src/utils/influxql.js | 57 -------------------- 3 files changed, 73 insertions(+), 58 deletions(-) create mode 100644 ui/src/utils/buildQueriesForLayouts.js diff --git a/ui/src/shared/components/Layout.js b/ui/src/shared/components/Layout.js index ea69fe6c6..e11d2ad65 100644 --- a/ui/src/shared/components/Layout.js +++ b/ui/src/shared/components/Layout.js @@ -2,7 +2,7 @@ import React, {Component, PropTypes} from 'react' import WidgetCell from 'shared/components/WidgetCell' import LayoutCell from 'shared/components/LayoutCell' import RefreshingGraph from 'shared/components/RefreshingGraph' -import {buildQueriesForLayouts} from 'utils/influxql' +import {buildQueriesForLayouts} from 'utils/buildQueriesForLayouts' import _ from 'lodash' diff --git a/ui/src/utils/buildQueriesForLayouts.js b/ui/src/utils/buildQueriesForLayouts.js new file mode 100644 index 000000000..c6d160a68 --- /dev/null +++ b/ui/src/utils/buildQueriesForLayouts.js @@ -0,0 +1,72 @@ +import {buildQuery} from 'utils/influxql' +import {TYPE_SHIFTED, TYPE_QUERY_CONFIG} from 'src/dashboards/constants' +import timeRanges from 'hson!shared/data/timeRanges.hson' + +const buildCannedDashboardQuery = (query, {lower, upper}, host) => { + const {defaultGroupBy} = timeRanges.find(range => range.lower === lower) || { + defaultGroupBy: '5m', + } + const {wheres, groupbys} = query + + let text = query.text + + if (upper) { + text += ` where time > '${lower}' AND time < '${upper}'` + } else { + text += ` where time > ${lower}` + } + + if (host) { + text += ` and \"host\" = '${host}'` + } + + if (wheres && wheres.length > 0) { + text += ` and ${wheres.join(' and ')}` + } + + if (groupbys) { + if (groupbys.find(g => g.includes('time'))) { + text += ` group by ${groupbys.join(',')}` + } else if (groupbys.length > 0) { + text += ` group by time(${defaultGroupBy}),${groupbys.join(',')}` + } else { + text += ` group by time(${defaultGroupBy})` + } + } else { + text += ` group by time(${defaultGroupBy})` + } + + return text +} + +export const buildQueriesForLayouts = (cell, source, timeRange, host) => { + return cell.queries.map(query => { + let queryText + // Canned dashboards use an different a schema different from queryConfig. + if (query.queryConfig) { + const { + queryConfig: {database, measurement, fields, shifts, rawText, range}, + } = query + const tR = range || { + upper: ':upperDashboardTime:', + lower: ':dashboardTime:', + } + + queryText = + rawText || buildQuery(TYPE_QUERY_CONFIG, tR, query.queryConfig) + const isParsable = database && measurement && fields.length + + if (shifts && shifts.length && isParsable) { + const shiftedQueries = shifts + .filter(s => s.unit) + .map(s => buildQuery(TYPE_SHIFTED, timeRange, query.queryConfig, s)) + + queryText = `${queryText};${shiftedQueries.join(';')}` + } + } else { + queryText = buildCannedDashboardQuery(query, timeRange, host) + } + + return {...query, host: source.links.proxy, text: queryText} + }) +} diff --git a/ui/src/utils/influxql.js b/ui/src/utils/influxql.js index dc434f491..e5176074d 100644 --- a/ui/src/utils/influxql.js +++ b/ui/src/utils/influxql.js @@ -8,7 +8,6 @@ import { TYPE_IFQL, } from 'src/dashboards/constants' import {shiftTimeRange} from 'shared/query/helpers' -import timeRanges from 'hson!shared/data/timeRanges.hson' /* eslint-disable quotes */ export const quoteIfTimestamp = ({lower, upper}) => { @@ -169,61 +168,5 @@ function _buildFill(fill) { return ` FILL(${fill})` } -const buildCannedDashboardQuery = (query, {lower, upper}, host) => { - const {defaultGroupBy} = timeRanges.find(range => range.lower === lower) || { - defaultGroupBy: '5m', - } - const {wheres, groupbys} = query - - let text = query.text - - if (upper) { - text += ` where time > '${lower}' AND time < '${upper}'` - } else { - text += ` where time > ${lower}` - } - - if (host) { - text += ` and \"host\" = '${host}'` - } - - if (wheres && wheres.length > 0) { - text += ` and ${wheres.join(' and ')}` - } - - if (groupbys) { - if (groupbys.find(g => g.includes('time'))) { - text += ` group by ${groupbys.join(',')}` - } else if (groupbys.length > 0) { - text += ` group by time(${defaultGroupBy}),${groupbys.join(',')}` - } else { - text += ` group by time(${defaultGroupBy})` - } - } else { - text += ` group by time(${defaultGroupBy})` - } - - return text -} - -export const buildQueriesForLayouts = (cell, source, timeRange, host) => { - return cell.queries.map(query => { - let queryText - // Canned dashboards use an different a schema different from queryConfig. - if (query.queryConfig) { - const {queryConfig: {rawText, range}} = query - const tR = range || { - upper: ':upperDashboardTime:', - lower: ':dashboardTime:', - } - queryText = rawText || buildInfluxQLQuery(tR, query.queryConfig) - } else { - queryText = buildCannedDashboardQuery(query, timeRange, host) - } - - return {...query, host: source.links.proxy, text: queryText} - }) -} - export const buildRawText = (q, timeRange) => q.rawText || buildInfluxQLQuery(timeRange, q) || '' From 2e553cfa762cf0e3689169acb3054e2f29dec564 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 13 Nov 2017 11:18:48 -0800 Subject: [PATCH 055/138] Refactor log table items into individual components --- ui/src/kapacitor/components/LogItemHTTP.js | 32 +++++ .../kapacitor/components/LogItemHTTPError.js | 32 +++++ .../components/LogItemInfluxDBDebug.js | 34 +++++ .../components/LogItemKapacitorDebug.js | 31 +++++ .../components/LogItemKapacitorError.js | 31 +++++ .../components/LogItemKapacitorPoint.js | 51 ++++++++ ui/src/kapacitor/components/LogItemSession.js | 28 +++++ ui/src/kapacitor/components/LogsTable.js | 119 ++++-------------- 8 files changed, 265 insertions(+), 93 deletions(-) create mode 100644 ui/src/kapacitor/components/LogItemHTTP.js create mode 100644 ui/src/kapacitor/components/LogItemHTTPError.js create mode 100644 ui/src/kapacitor/components/LogItemInfluxDBDebug.js create mode 100644 ui/src/kapacitor/components/LogItemKapacitorDebug.js create mode 100644 ui/src/kapacitor/components/LogItemKapacitorError.js create mode 100644 ui/src/kapacitor/components/LogItemKapacitorPoint.js create mode 100644 ui/src/kapacitor/components/LogItemSession.js diff --git a/ui/src/kapacitor/components/LogItemHTTP.js b/ui/src/kapacitor/components/LogItemHTTP.js new file mode 100644 index 000000000..0d8b7a775 --- /dev/null +++ b/ui/src/kapacitor/components/LogItemHTTP.js @@ -0,0 +1,32 @@ +import React, {PropTypes} from 'react' + +const LogItemHTTP = ({logItem}) => +
+
+
+
+ {logItem.ts} +
+
+
+
HTTP Request
+
+ {logItem.method} {logItem.username}@{logItem.host} ({logItem.duration}) +
+
+
+ +const {shape, string} = PropTypes + +LogItemHTTP.propTypes = { + logItem: shape({ + lvl: string.isRequired, + ts: string.isRequired, + method: string.isRequired, + username: string.isRequired, + host: string.isRequired, + duration: string.isRequired, + }), +} + +export default LogItemHTTP diff --git a/ui/src/kapacitor/components/LogItemHTTPError.js b/ui/src/kapacitor/components/LogItemHTTPError.js new file mode 100644 index 000000000..fa0e79694 --- /dev/null +++ b/ui/src/kapacitor/components/LogItemHTTPError.js @@ -0,0 +1,32 @@ +import React, {PropTypes} from 'react' + +const LogItemHTTPError = ({logItem}) => +
+
+
+
+ {logItem.ts} +
+
+
+
HTTP Server
+
+
+ ERROR: {logItem.msg} +
+
+
+
+ +const {shape, string} = PropTypes + +LogItemHTTPError.propTypes = { + logItem: shape({ + key: string.isRequired, + lvl: string.isRequired, + ts: string.isRequired, + msg: string.isRequired, + }), +} + +export default LogItemHTTPError diff --git a/ui/src/kapacitor/components/LogItemInfluxDBDebug.js b/ui/src/kapacitor/components/LogItemInfluxDBDebug.js new file mode 100644 index 000000000..b6be11705 --- /dev/null +++ b/ui/src/kapacitor/components/LogItemInfluxDBDebug.js @@ -0,0 +1,34 @@ +import React, {PropTypes} from 'react' + +const LogItemInfluxDBDebug = ({logItem}) => +
+
+
+
+ {logItem.ts} +
+
+
+
InfluxDB
+
+
+ DEBUG: {logItem.msg} +
+ Cluster: {logItem.cluster} +
+
+
+
+ +const {shape, string} = PropTypes + +LogItemInfluxDBDebug.propTypes = { + logItem: shape({ + lvl: string.isRequired, + ts: string.isRequired, + msg: string.isRequired, + cluster: string.isRequired, + }), +} + +export default LogItemInfluxDBDebug diff --git a/ui/src/kapacitor/components/LogItemKapacitorDebug.js b/ui/src/kapacitor/components/LogItemKapacitorDebug.js new file mode 100644 index 000000000..9b99d5129 --- /dev/null +++ b/ui/src/kapacitor/components/LogItemKapacitorDebug.js @@ -0,0 +1,31 @@ +import React, {PropTypes} from 'react' + +const LogItemKapacitorDebug = ({logItem}) => +
+
+
+
+ {logItem.ts} +
+
+
+
Kapacitor
+
+
+ DEBUG: {logItem.msg} +
+
+
+
+ +const {shape, string} = PropTypes + +LogItemKapacitorDebug.propTypes = { + logItem: shape({ + lvl: string.isRequired, + ts: string.isRequired, + msg: string.isRequired, + }), +} + +export default LogItemKapacitorDebug diff --git a/ui/src/kapacitor/components/LogItemKapacitorError.js b/ui/src/kapacitor/components/LogItemKapacitorError.js new file mode 100644 index 000000000..1d4ca573d --- /dev/null +++ b/ui/src/kapacitor/components/LogItemKapacitorError.js @@ -0,0 +1,31 @@ +import React, {PropTypes} from 'react' + +const LogItemKapacitorError = ({logItem}) => +
+
+
+
+ {logItem.ts} +
+
+
+
Kapacitor
+
+
+ ERROR: {logItem.msg} +
+
+
+
+ +const {shape, string} = PropTypes + +LogItemKapacitorError.propTypes = { + logItem: shape({ + lvl: string.isRequired, + ts: string.isRequired, + msg: string.isRequired, + }), +} + +export default LogItemKapacitorError diff --git a/ui/src/kapacitor/components/LogItemKapacitorPoint.js b/ui/src/kapacitor/components/LogItemKapacitorPoint.js new file mode 100644 index 000000000..6a7639330 --- /dev/null +++ b/ui/src/kapacitor/components/LogItemKapacitorPoint.js @@ -0,0 +1,51 @@ +import React, {PropTypes} from 'react' + +const renderKeysAndValues = object => { + if (!object) { + return -- + } + const objKeys = Object.keys(object) + const objValues = Object.values(object) + + const objElements = objKeys.map((objKey, i) => +
+ {objKey}: {objValues[i]} +
+ ) + return objElements +} +const LogItemKapacitorPoint = ({logItem}) => +
+
+
+
+ {logItem.ts} +
+
+
+
Kapacitor Point
+
+
+ TAGS
+ {renderKeysAndValues(logItem.tag)} +
+
+ FIELDS
+ {renderKeysAndValues(logItem.field)} +
+
+
+
+ +const {shape, string} = PropTypes + +LogItemKapacitorPoint.propTypes = { + logItem: shape({ + lvl: string.isRequired, + ts: string.isRequired, + tag: shape.isRequired, + field: shape.isRequired, + }), +} + +export default LogItemKapacitorPoint diff --git a/ui/src/kapacitor/components/LogItemSession.js b/ui/src/kapacitor/components/LogItemSession.js new file mode 100644 index 000000000..c5c1cb286 --- /dev/null +++ b/ui/src/kapacitor/components/LogItemSession.js @@ -0,0 +1,28 @@ +import React, {PropTypes} from 'react' + +const LogItemSession = ({logItem}) => +
+
+
+
+ {logItem.ts} +
+
+
+
+ {logItem.msg} +
+
+
+ +const {shape, string} = PropTypes + +LogItemSession.propTypes = { + logItem: shape({ + lvl: string.isRequired, + ts: string.isRequired, + msg: string.isRequired, + }), +} + +export default LogItemSession diff --git a/ui/src/kapacitor/components/LogsTable.js b/ui/src/kapacitor/components/LogsTable.js index b16de6f40..60497499f 100644 --- a/ui/src/kapacitor/components/LogsTable.js +++ b/ui/src/kapacitor/components/LogsTable.js @@ -1,124 +1,57 @@ import React, {Component, PropTypes} from 'react' +import LogItemSession from 'src/kapacitor/components/LogItemSession' +import LogItemHTTP from 'src/kapacitor/components/LogItemHTTP' +import LogItemHTTPError from 'src/kapacitor/components/LogItemHTTPError' +import LogItemKapacitorPoint from 'src/kapacitor/components/LogItemKapacitorPoint' +import LogItemKapacitorError from 'src/kapacitor/components/LogItemKapacitorError' +import LogItemKapacitorDebug from 'src/kapacitor/components/LogItemKapacitorDebug' +import LogItemInfluxDBDebug from 'src/kapacitor/components/LogItemInfluxDBDebug' + class LogsTable extends Component { constructor(props) { super(props) } - renderKeysAndValues = object => { - if (!object) { - return -- - } - const objKeys = Object.keys(object) - const objValues = Object.values(object) - - const objElements = objKeys.map((objKey, i) => -
- {objKey}: {objValues[i]} -
- ) - return objElements - } - - renderTableRow = logItem => { - let rowDetails - + renderTableRow = (logItem, index) => { if (logItem.service === 'sessions') { - rowDetails = ( -
-
- {logItem.msg} -
-
- ) + return } if (logItem.service === 'http' && logItem.msg === 'http request') { - rowDetails = ( -
-
HTTP Request
-
- {logItem.method} {logItem.username}@{logItem.host} ({logItem.duration}) -
-
- ) + return } if (logItem.service === 'kapacitor' && logItem.msg === 'point') { - rowDetails = ( -
-
Kapacitor Point
-
-
- TAGS
- {this.renderKeysAndValues(logItem.tag)} -
-
- FIELDS
- {this.renderKeysAndValues(logItem.field)} -
-
-
- ) + return } if (logItem.service === 'httpd_server_errors' && logItem.lvl === 'error') { - rowDetails = ( -
-
HTTP Server
-
-
- ERROR: {logItem.msg} -
-
-
- ) + return } if (logItem.service === 'kapacitor' && logItem.lvl === 'error') { - rowDetails = ( -
-
Kapacitor
-
-
- ERROR: {logItem.msg} -
-
-
- ) + return } if (logItem.service === 'kapacitor' && logItem.lvl === 'debug') { - rowDetails = ( -
-
Kapacitor
-
-
- DEBUG: {logItem.msg} -
-
-
- ) + return } if (logItem.service === 'influxdb' && logItem.lvl === 'debug') { - rowDetails = ( -
-
InfluxDB
-
-
- DEBUG: {logItem.msg} -
- Cluster: {logItem.cluster} -
-
-
- ) + return } return ( -
+
{logItem.ts}
- {rowDetails} +
+
Thing
+
+
+

Thang

+
+
+
) } @@ -140,7 +73,7 @@ class LogsTable extends Component {
- {logs.map(l => this.renderTableRow(l))} + {logs.map((log, i) => this.renderTableRow(log, i))}
From 17bf971833db45dccc2032425bf6734b259bd776 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 13 Nov 2017 11:47:49 -0800 Subject: [PATCH 056/138] Render spinner in logs panel when awaiting logs --- ui/src/kapacitor/components/LogsTable.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/src/kapacitor/components/LogsTable.js b/ui/src/kapacitor/components/LogsTable.js index 60497499f..84b046c1b 100644 --- a/ui/src/kapacitor/components/LogsTable.js +++ b/ui/src/kapacitor/components/LogsTable.js @@ -73,7 +73,9 @@ class LogsTable extends Component {
- {logs.map((log, i) => this.renderTableRow(log, i))} + {logs.length + ? logs.map((log, i) => this.renderTableRow(log, i)) + :
}
From f890f4b59b64d67aca9d6573fc6df60f3e8fe305 Mon Sep 17 00:00:00 2001 From: Luke Morris Date: Mon, 13 Nov 2017 16:48:04 -0800 Subject: [PATCH 057/138] Be the change --- .../dashboards/containers/DashboardsPage.js | 54 +++++++++---------- ui/src/hosts/containers/HostsPage.js | 44 ++++++++------- 2 files changed, 51 insertions(+), 47 deletions(-) diff --git a/ui/src/dashboards/containers/DashboardsPage.js b/ui/src/dashboards/containers/DashboardsPage.js index 0a1d2d634..3cb2183a1 100644 --- a/ui/src/dashboards/containers/DashboardsPage.js +++ b/ui/src/dashboards/containers/DashboardsPage.js @@ -1,4 +1,4 @@ -import React, {PropTypes} from 'react' +import React, {PropTypes, Component} from 'react' import {withRouter} from 'react-router' import {connect} from 'react-redux' import {bindActionCreators} from 'redux' @@ -11,40 +11,20 @@ import {getDashboardsAsync, deleteDashboardAsync} from 'src/dashboards/actions' import {NEW_DASHBOARD} from 'src/dashboards/constants' -const {arrayOf, func, string, shape} = PropTypes - -const DashboardsPage = React.createClass({ - propTypes: { - source: shape({ - id: string.isRequired, - name: string.isRequired, - type: string, - links: shape({ - proxy: string.isRequired, - }).isRequired, - telegraf: string.isRequired, - }), - router: shape({ - push: func.isRequired, - }).isRequired, - handleGetDashboards: func.isRequired, - handleDeleteDashboard: func.isRequired, - dashboards: arrayOf(shape()), - }, - +class DashboardsPage extends Component { componentDidMount() { this.props.handleGetDashboards() - }, + } async handleCreateDashbord() { const {source: {id}, router: {push}} = this.props const {data} = await createDashboard(NEW_DASHBOARD) push(`/sources/${id}/dashboards/${data.id}`) - }, + } handleDeleteDashboard(dashboard) { this.props.handleDeleteDashboard(dashboard) - }, + } render() { const {dashboards} = this.props @@ -61,8 +41,28 @@ const DashboardsPage = React.createClass({ />
) - }, -}) + } +} + +const {arrayOf, func, string, shape} = PropTypes + +DashboardsPage.propTypes = { + source: shape({ + id: string.isRequired, + name: string.isRequired, + type: string, + links: shape({ + proxy: string.isRequired, + }).isRequired, + telegraf: string.isRequired, + }), + router: shape({ + push: func.isRequired, + }).isRequired, + handleGetDashboards: func.isRequired, + handleDeleteDashboard: func.isRequired, + dashboards: arrayOf(shape()), +} const mapStateToProps = ({dashboardUI: {dashboards, dashboard}}) => ({ dashboards, diff --git a/ui/src/hosts/containers/HostsPage.js b/ui/src/hosts/containers/HostsPage.js index 4c4e8eeb6..9d4be6bf4 100644 --- a/ui/src/hosts/containers/HostsPage.js +++ b/ui/src/hosts/containers/HostsPage.js @@ -1,4 +1,4 @@ -import React, {PropTypes} from 'react' +import React, {PropTypes, Component} from 'react' import _ from 'lodash' import HostsTable from 'src/hosts/components/HostsTable' @@ -7,27 +7,16 @@ import SourceIndicator from 'shared/components/SourceIndicator' import {getCpuAndLoadForHosts, getMappings, getAppsForHosts} from '../apis' -export const HostsPage = React.createClass({ - propTypes: { - source: PropTypes.shape({ - id: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - type: PropTypes.string, // 'influx-enterprise' - links: PropTypes.shape({ - proxy: PropTypes.string.isRequired, - }).isRequired, - telegraf: PropTypes.string.isRequired, - }), - addFlashMessage: PropTypes.func, - }, +class HostsPage extends Component { + constructor(props) { + super(props) - getInitialState() { - return { + this.state = { hosts: {}, hostsLoading: true, hostsError: '', } - }, + } componentDidMount() { const {source, addFlashMessage} = this.props @@ -71,7 +60,7 @@ export const HostsPage = React.createClass({ // (like with a bogus proxy link). We should provide better messaging to the user in this catch after that's fixed. console.error(reason) // eslint-disable-line no-console }) - }, + } render() { const {source} = this.props @@ -104,7 +93,22 @@ export const HostsPage = React.createClass({
) - }, -}) + } +} + +const {func, shape, string} = PropTypes + +HostsPage.propTypes = { + source: shape({ + id: string.isRequired, + name: string.isRequired, + type: string, // 'influx-enterprise' + links: shape({ + proxy: string.isRequired, + }).isRequired, + telegraf: string.isRequired, + }), + addFlashMessage: func, +} export default HostsPage From 7724527fafb6743f1a7a90ae3f0d2c6593060a7e Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 13 Nov 2017 16:49:47 -0800 Subject: [PATCH 058/138] Use fancy scrollbars on logs table --- ui/src/kapacitor/components/LogsTable.js | 8 ++++++-- ui/src/style/components/kapacitor-logs-table.scss | 5 ++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ui/src/kapacitor/components/LogsTable.js b/ui/src/kapacitor/components/LogsTable.js index 84b046c1b..cfe5f588d 100644 --- a/ui/src/kapacitor/components/LogsTable.js +++ b/ui/src/kapacitor/components/LogsTable.js @@ -1,5 +1,6 @@ import React, {Component, PropTypes} from 'react' +import FancyScrollbar from 'shared/components/FancyScrollbar' import LogItemSession from 'src/kapacitor/components/LogItemSession' import LogItemHTTP from 'src/kapacitor/components/LogItemHTTP' import LogItemHTTPError from 'src/kapacitor/components/LogItemHTTPError' @@ -71,13 +72,16 @@ class LogsTable extends Component { />
-
+
{logs.length ? logs.map((log, i) => this.renderTableRow(log, i)) :
}
-
+
) } diff --git a/ui/src/style/components/kapacitor-logs-table.scss b/ui/src/style/components/kapacitor-logs-table.scss index f90730326..360ca03d5 100644 --- a/ui/src/style/components/kapacitor-logs-table.scss +++ b/ui/src/style/components/kapacitor-logs-table.scss @@ -25,12 +25,11 @@ $logs-margin: 4px; background-color: $g4-onyx; } .logs-table--panel { - position: absolute; + position: absolute !important; top: $logs-table-header-height; left: 0; width: 100%; - height: calc(100% - #{$logs-table-header-height}); - overflow: auto; + height: calc(100% - #{$logs-table-header-height}) !important; } .logs-table, From d28416d150eabb0a0ee5184dc16cd0c342099a13 Mon Sep 17 00:00:00 2001 From: Luke Morris Date: Mon, 13 Nov 2017 16:51:32 -0800 Subject: [PATCH 059/138] Convert DashboardsPageContent to class --- .../components/DashboardsPageContents.js | 88 ++++++++++--------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/ui/src/dashboards/components/DashboardsPageContents.js b/ui/src/dashboards/components/DashboardsPageContents.js index 987a75ae4..5f3bb7fe6 100644 --- a/ui/src/dashboards/components/DashboardsPageContents.js +++ b/ui/src/dashboards/components/DashboardsPageContents.js @@ -1,54 +1,58 @@ -import React, {PropTypes} from 'react' +import React, {PropTypes, Component} from 'react' import DashboardsTable from 'src/dashboards/components/DashboardsTable' import FancyScrollbar from 'shared/components/FancyScrollbar' -const DashboardsPageContents = ({ - dashboards, - onDeleteDashboard, - onCreateDashboard, - dashboardLink, -}) => { - let tableHeader - if (dashboards === null) { - tableHeader = 'Loading Dashboards...' - } else if (dashboards.length === 1) { - tableHeader = '1 Dashboard' - } else { - tableHeader = `${dashboards.length} Dashboards` - } +class DashboardsPageContents extends Component { + render() { + const { + dashboards, + onDeleteDashboard, + onCreateDashboard, + dashboardLink, + } = this.props - return ( - -
-
-
-
-
-

- {tableHeader} -

- -
-
- + let tableHeader + if (dashboards === null) { + tableHeader = 'Loading Dashboards...' + } else if (dashboards.length === 1) { + tableHeader = '1 Dashboard' + } else { + tableHeader = `${dashboards.length} Dashboards` + } + + return ( + +
+
+
+
+
+

+ {tableHeader} +

+ +
+
+ +
-
- - ) + + ) + } } const {arrayOf, func, shape, string} = PropTypes From bfb288985cf8aa61295e1539f71617ed20c806a0 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 13 Nov 2017 16:52:45 -0800 Subject: [PATCH 060/138] Sort logs table newest to oldest --- ui/src/style/components/kapacitor-logs-table.scss | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/src/style/components/kapacitor-logs-table.scss b/ui/src/style/components/kapacitor-logs-table.scss index 360ca03d5..7f0208d3e 100644 --- a/ui/src/style/components/kapacitor-logs-table.scss +++ b/ui/src/style/components/kapacitor-logs-table.scss @@ -38,6 +38,9 @@ $logs-margin: 4px; align-items: stretch; flex-direction: column; } +.logs-table { + flex-direction: column-reverse; +} .logs-table--row { padding: 8px ($logs-table-padding - 16px) 8px ($logs-table-padding / 2); border-bottom: 2px solid $g3-castle; @@ -46,7 +49,7 @@ $logs-margin: 4px; &:hover { background-color: $g4-onyx; } - &:last-child { + &:first-child { border-bottom: none; } } From dd17704b943a2fe32a2dfcc3f0398839d8a0cc0a Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 13 Nov 2017 17:11:43 -0800 Subject: [PATCH 061/138] Polish TICKscript editor controls --- .../components/TickscriptEditorControls.js | 2 +- ui/src/kapacitor/components/TickscriptID.js | 7 ++----- ui/src/style/pages/tickscript-editor.scss | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/ui/src/kapacitor/components/TickscriptEditorControls.js b/ui/src/kapacitor/components/TickscriptEditorControls.js index 9520001f0..aff096104 100644 --- a/ui/src/kapacitor/components/TickscriptEditorControls.js +++ b/ui/src/kapacitor/components/TickscriptEditorControls.js @@ -18,7 +18,7 @@ const TickscriptEditorControls = ({ {isNewTickscript ? : } -
+
-

+

{id}

diff --git a/ui/src/style/pages/tickscript-editor.scss b/ui/src/style/pages/tickscript-editor.scss index b56cadedd..3f70bb942 100644 --- a/ui/src/style/pages/tickscript-editor.scss +++ b/ui/src/style/pages/tickscript-editor.scss @@ -27,6 +27,21 @@ $tickscript-console-height: 60px; padding: 0 60px; background-color: $g3-castle; } +.tickscript-controls--name { + margin: 0; + letter-spacing: 0; + @include no-user-select(); + font-size: 17px; + font-weight: 400; + color: $g13-mist; +} +.tickscript-controls--right { + display: flex; + align-items: center; + flex-wrap: nowrap; + + > * {margin-left: 8px;} +} .tickscript-console--output { padding: 0 60px; font-family: $code-font; From 2b429885df78631db23840f2b2cabd9b9686930b Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 13 Nov 2017 18:15:53 -0800 Subject: [PATCH 062/138] WIP Make new log items fade in --- ui/src/style/components/kapacitor-logs-table.scss | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ui/src/style/components/kapacitor-logs-table.scss b/ui/src/style/components/kapacitor-logs-table.scss index 7f0208d3e..fed167ff9 100644 --- a/ui/src/style/components/kapacitor-logs-table.scss +++ b/ui/src/style/components/kapacitor-logs-table.scss @@ -38,12 +38,24 @@ $logs-margin: 4px; align-items: stretch; flex-direction: column; } +@keyframes LogsFadeIn { + from { + background-color: $g6-smoke; + } + to { + background-color: transparent; + } +} .logs-table { flex-direction: column-reverse; } .logs-table--row { padding: 8px ($logs-table-padding - 16px) 8px ($logs-table-padding / 2); border-bottom: 2px solid $g3-castle; + animation-name: LogsFadeIn; + animation-duration: 2.5s; + animation-iteration-count: 1; + animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); transition: background-color 0.25s ease; &:hover { From afb6b9b8d5760c74c06f34d8ddf26158a0e02100 Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 14 Nov 2017 14:23:26 -0800 Subject: [PATCH 063/138] Fix for 2250 --- ui/src/style/components/graph.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/style/components/graph.scss b/ui/src/style/components/graph.scss index 29f31aa7a..843b53f5f 100644 --- a/ui/src/style/components/graph.scss +++ b/ui/src/style/components/graph.scss @@ -138,7 +138,7 @@ $graph-gutter: 16px; font-size: 20px; font-weight: 400; margin: 0; - text-align: left; + text-align: center; color: $g8-storm; white-space: pre-wrap; } From 152fb9b319cc78744e01be6d07917a4994ae9670 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 15 Nov 2017 09:40:21 -0800 Subject: [PATCH 064/138] Remove non-functional filter from DOM Can re-introduce when it is functional --- ui/src/kapacitor/components/LogsTable.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ui/src/kapacitor/components/LogsTable.js b/ui/src/kapacitor/components/LogsTable.js index cfe5f588d..4cb7316f4 100644 --- a/ui/src/kapacitor/components/LogsTable.js +++ b/ui/src/kapacitor/components/LogsTable.js @@ -64,13 +64,6 @@ class LogsTable extends Component {

Logs

-
- -
Date: Wed, 15 Nov 2017 09:41:23 -0800 Subject: [PATCH 065/138] Remove unused prop --- ui/src/kapacitor/components/LogsTable.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/src/kapacitor/components/LogsTable.js b/ui/src/kapacitor/components/LogsTable.js index 4cb7316f4..eefdd697e 100644 --- a/ui/src/kapacitor/components/LogsTable.js +++ b/ui/src/kapacitor/components/LogsTable.js @@ -80,10 +80,9 @@ class LogsTable extends Component { } } -const {arrayOf, bool, shape, string} = PropTypes +const {arrayOf, shape, string} = PropTypes LogsTable.propTypes = { - isWidget: bool, logs: arrayOf( shape({ key: string.isRequired, From e9073d0d848049aa9bc4439f892a08e0500376a2 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 15 Nov 2017 09:44:44 -0800 Subject: [PATCH 066/138] Improve catchall template for log items --- ui/src/kapacitor/components/LogsTable.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/src/kapacitor/components/LogsTable.js b/ui/src/kapacitor/components/LogsTable.js index eefdd697e..ae1379c9f 100644 --- a/ui/src/kapacitor/components/LogsTable.js +++ b/ui/src/kapacitor/components/LogsTable.js @@ -46,10 +46,12 @@ class LogsTable extends Component {
-
Thing
+
+ {logItem.service || '--'} +
-

Thang

+ {logItem.msg || '--'}
From 34948bb70806c324faffe9aa01c0d749c381d4a2 Mon Sep 17 00:00:00 2001 From: Luke Morris Date: Thu, 16 Nov 2017 10:04:56 -0800 Subject: [PATCH 067/138] Add filter to DashboardsPage --- .../components/DashboardsPageContents.js | 20 ++++++++++++++++++- ui/src/hosts/components/SearchBar.js | 3 +-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/ui/src/dashboards/components/DashboardsPageContents.js b/ui/src/dashboards/components/DashboardsPageContents.js index 5f3bb7fe6..358d99e35 100644 --- a/ui/src/dashboards/components/DashboardsPageContents.js +++ b/ui/src/dashboards/components/DashboardsPageContents.js @@ -1,9 +1,22 @@ import React, {PropTypes, Component} from 'react' import DashboardsTable from 'src/dashboards/components/DashboardsTable' +import SearchBar from 'src/hosts/components/SearchBar' import FancyScrollbar from 'shared/components/FancyScrollbar' class DashboardsPageContents extends Component { + constructor(props) { + super(props) + + this.state = { + searchTerm: '', + } + } + + filterDashboards = searchTerm => { + this.setState({searchTerm}) + } + render() { const { dashboards, @@ -21,6 +34,10 @@ class DashboardsPageContents extends Component { tableHeader = `${dashboards.length} Dashboards` } + const filteredDashboards = dashboards.filter(d => + d.name.includes(this.state.searchTerm) + ) + return (
@@ -31,6 +48,7 @@ class DashboardsPageContents extends Component {

{tableHeader}

+ +
+ + +
Date: Thu, 16 Nov 2017 10:56:41 -0800 Subject: [PATCH 069/138] Shift filter box to the left --- ui/src/dashboards/components/DashboardsPageContents.js | 7 +++++-- ui/src/hosts/components/HostsTable.js | 5 ++++- ui/src/hosts/components/SearchBar.js | 6 ++++-- ui/src/style/pages/dashboards.scss | 8 ++++++++ 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/ui/src/dashboards/components/DashboardsPageContents.js b/ui/src/dashboards/components/DashboardsPageContents.js index dd55b31c3..6cf4e5073 100644 --- a/ui/src/dashboards/components/DashboardsPageContents.js +++ b/ui/src/dashboards/components/DashboardsPageContents.js @@ -48,8 +48,11 @@ class DashboardsPageContents extends Component {

{tableHeader}

-
- +
+
{hostCount > 0 && !hostsError.length diff --git a/ui/src/hosts/components/SearchBar.js b/ui/src/hosts/components/SearchBar.js index a0c998787..f362acc35 100644 --- a/ui/src/hosts/components/SearchBar.js +++ b/ui/src/hosts/components/SearchBar.js @@ -22,12 +22,13 @@ class SearchBar extends Component { } render() { + const {placeholder} = this.props return (
@@ -39,10 +40,11 @@ class SearchBar extends Component { } } -const {func} = PropTypes +const {func, string} = PropTypes SearchBar.propTypes = { onSearch: func.isRequired, + placeholder: string.isRequired, } export default SearchBar diff --git a/ui/src/style/pages/dashboards.scss b/ui/src/style/pages/dashboards.scss index aae882209..c57d57b4a 100644 --- a/ui/src/style/pages/dashboards.scss +++ b/ui/src/style/pages/dashboards.scss @@ -56,6 +56,14 @@ $dash-graph-options-arrow: 8px; } } +/* + Dashboard Index Page + ------------------------------------------------------ +*/ +.dashboards-page--actions .users__search-widget { + margin-right: 8px; +} + /* Default Dashboard Mode ------------------------------------------------------ From a4853433bf36f6e451599ce90227759f6315900b Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 16 Nov 2017 16:19:51 -0800 Subject: [PATCH 070/138] Remove timeShift from query options in kapacitor --- ui/src/shared/components/FieldList.js | 2 +- ui/src/shared/components/QueryOptions.js | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ui/src/shared/components/FieldList.js b/ui/src/shared/components/FieldList.js index bfb06f2e3..dd2db7486 100644 --- a/ui/src/shared/components/FieldList.js +++ b/ui/src/shared/components/FieldList.js @@ -221,7 +221,7 @@ FieldList.propTypes = { }) ), }).isRequired, - onTimeShift: func.isRequired, + onTimeShift: func, onToggleField: func.isRequired, onGroupByTime: func.isRequired, onFill: func, diff --git a/ui/src/shared/components/QueryOptions.js b/ui/src/shared/components/QueryOptions.js index e0d23dba7..d02ee0240 100644 --- a/ui/src/shared/components/QueryOptions.js +++ b/ui/src/shared/components/QueryOptions.js @@ -17,10 +17,12 @@ const QueryOptions = ({ selected={groupBy.time} onChooseGroupByTime={onGroupByTime} /> - + {isKapacitorRule + ? null + : } {isKapacitorRule ? null : }
From 7818c319acbcbbf703c0bfb50653523310611298 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 16 Nov 2017 16:29:44 -0800 Subject: [PATCH 071/138] Fix tests to shifts --- ui/spec/data_explorer/reducers/queryConfigSpec.js | 2 +- ui/spec/kapacitor/reducers/queryConfigSpec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/spec/data_explorer/reducers/queryConfigSpec.js b/ui/spec/data_explorer/reducers/queryConfigSpec.js index dd1e7f63f..7830393d9 100644 --- a/ui/spec/data_explorer/reducers/queryConfigSpec.js +++ b/ui/spec/data_explorer/reducers/queryConfigSpec.js @@ -482,7 +482,7 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => { const action = timeShift(queryID, shift) const nextState = reducer(initialState, action) - expect(nextState[queryID].shift).to.deep.equal([shift]) + expect(nextState[queryID].shifts).to.deep.equal([shift]) }) }) }) diff --git a/ui/spec/kapacitor/reducers/queryConfigSpec.js b/ui/spec/kapacitor/reducers/queryConfigSpec.js index 16a02a2ab..a654e4efd 100644 --- a/ui/spec/kapacitor/reducers/queryConfigSpec.js +++ b/ui/spec/kapacitor/reducers/queryConfigSpec.js @@ -336,7 +336,7 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => { const action = timeShift(queryID, shift) const nextState = reducer(initialState, action) - expect(nextState[queryID].shift).to.deep.equal([shift]) + expect(nextState[queryID].shifts).to.deep.equal([shift]) }) }) }) From 60c95b600a88c01faa95f83dc1a913e79651a642 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 16 Nov 2017 16:33:14 -0800 Subject: [PATCH 072/138] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81b43ff39..87088fb44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ 1. [#2292](https://github.com/influxdata/chronograf/pull/2292): Source extra command line options from defaults file ### Features +1. [#2385](https://github.com/influxdata/chronograf/pull/2385): Add time shift feature to DataExplorer and Dashboards + ### UI Improvements ## v1.3.10.0 [2017-10-24] From d8c2b2b8ebd3d54f20a7190b0a9e66a35b296b5b Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 17 Nov 2017 10:51:50 -0800 Subject: [PATCH 073/138] Update test --- bolt/internal/internal_test.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/bolt/internal/internal_test.go b/bolt/internal/internal_test.go index 4ca7ae420..b94ea5c7e 100644 --- a/bolt/internal/internal_test.go +++ b/bolt/internal/internal_test.go @@ -163,13 +163,7 @@ func Test_MarshalDashboard(t *testing.T) { Upper: int64(100), }, Source: "/chronograf/v1/sources/1", - Shifts: []chronograf.TimeShift{ - { - Label: "Best Week Ever", - Unit: "d", - Quantity: "7", - }, - }, + Shifts: []chronograf.TimeShift{}, }, }, Axes: map[string]chronograf.Axis{ From 86a58a32ea864b81b02fdd4f7e1285e403b996cb Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Fri, 17 Nov 2017 14:11:05 -0600 Subject: [PATCH 074/138] Update queryConfig render to support wildcard, re, int, num --- ui/src/utils/influxql.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ui/src/utils/influxql.js b/ui/src/utils/influxql.js index 096bf7b97..6ff9d9747 100644 --- a/ui/src/utils/influxql.js +++ b/ui/src/utils/influxql.js @@ -77,6 +77,18 @@ function _buildFields(fieldFuncs) { case 'field': { return f.value === '*' ? '*' : `"${f.value}"` } + case 'wildcard': { + return '*' + } + case 'regex': { + return `/${f.value}/` + } + case 'number': { + return `${f.value}` + } + case 'integer': { + return `${f.value}` + } case 'func': { const args = _buildFields(f.args) const alias = f.alias ? ` AS "${f.alias}"` : '' From de6c1746ef88ad305272a80bbd354a2c7920e0fa Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Fri, 17 Nov 2017 14:16:08 -0600 Subject: [PATCH 075/138] Update CHANGELOG to mention support for wildcard, number and regex queries --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 191ae6f28..80a5ad8a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ 1. [#2303](https://github.com/influxdata/chronograf/pull/2303): Add shadow-utils to RPM release packages 1. [#2292](https://github.com/influxdata/chronograf/pull/2292): Source extra command line options from defaults file 1. [#2329](https://github.com/influxdata/chronograf/pull/2329): Include tag values alongside measurement name in Data Explorer result tabs +1. [#2386](https://github.com/influxdata/chronograf/pull/2386): Fix queries that include regex, numbers and wildcard ### Features ### UI Improvements From 83e11909768952249dfb2fbc0e67fee292a763b7 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Fri, 17 Nov 2017 15:51:10 -0600 Subject: [PATCH 076/138] Update Makefile --- Makefile | 53 +++++++++++++++++------------------------------------ 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/Makefile b/Makefile index 29cc23061..d8dab4fae 100644 --- a/Makefile +++ b/Makefile @@ -23,42 +23,23 @@ ${BINARY}: $(SOURCES) .bindata .jsdep .godep go build -o ${BINARY} ${LDFLAGS} ./cmd/chronograf/main.go define CHRONOGIRAFFE - tLf iCf. - .CCC. tCC: - CGG; CGG: -tG0Gt: GGGGGGGGGGGGGGGG1 .,:, -LG1,,:1CC: .GGL;iLC1iii1LCi;GG1 .1GCL1iGG1 - LG1:::;i1CGGt;;;;;;L0t;;;;;;GGGC1;;::,iGC - ,ii:. 1GG1iiii;;tfiC;;;;;;;GGCfCGCGGC, - fGCiiiiGi1Lt;;iCLL,i;;;CGt - fGG11iiii1C1iiiiiGt1;;;;;CGf - .GGLLL1i1CitfiiL1iCi;;iLCGGt - .CGL11LGCCCCCCCLLCGG1;1GG; - CGL1tf1111iiiiiiL1ifGG, - LGCff1fCt1tCfiiCiCGC - LGGf111111111iCGGt - fGGGGGGGGGGGGGGi - ifii111111itL - ;f1i11111iitf - ;f1iiiiiii1tf - :fi111iii11tf - :fi111ii1i1tf - :f111111ii1tt - ,L111111ii1tt - .Li1111i1111CCCCCCCCCCCCCCLt; - L111ii11111ittttt1tttttittti1fC; - f1111ii111i1ttttt1;iii1ittt1ttttCt. - tt11ii111tti1ttt1tt1;11;;;;iitttifCCCL, - 11i1i11ttttti;1t1;;;ttt1;;ii;itti;L,;CCL - ;f;;;;1tttti;;ttti;;;;;;;;;;;1tt1ifi .CCi - ,L;itti;;;it;;;;;tt1;;;t1;;;;;;ii;t; :CC, - L;;;;iti;;;;;;;;;;;;;;;;;;;;;;;i;L, ;CC. - ti;;;iLLfffi;;;;;ittt11i;;;;;;;;;L tCCfff; - it;;;;;;L,ti;;;;;1Ltttft1t;;;;;;1t ;CCCL; - :f;;;;;;L.ti;;;;;tftttf1,f;;;;;;f: ;CC1: - .L;;;;;;L.t1;;;;;tt111fi,f;;;;;;L. - 1Li;;iL1 :Ci;;;tL1i1fC, Lt;;;;Li - .;tt; ifLt:;fLf; ;LCCt, + .-. .-. + | \/ | + /, ,_ `'-. + .-|\ /`\ '. + .' 0/ | 0\ \_ `". + .-' _,/ '--'.'|#''---' + `--' | / \# + | / \# + \ ;|\ .\# + |' ' // \ ::\# + \ /` \ ':\# + `"` \.. \# + \::. \# + \:: \# + \' .:\# + \ :::\# + \ '::\# endef export CHRONOGIRAFFE chronogiraffe: ${BINARY} From 9a72902fa3f58d08bb7dbecc2b301cf7917aeab0 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Mon, 20 Nov 2017 22:22:40 -0700 Subject: [PATCH 077/138] Add Kapacitor version detection. --- ui/src/kapacitor/apis/index.js | 14 ++++++++++ ui/src/kapacitor/components/LogsToggle.js | 16 +++++++----- ui/src/kapacitor/components/Tickscript.js | 3 +++ .../kapacitor/components/TickscriptHeader.js | 3 +++ ui/src/kapacitor/containers/TickscriptPage.js | 26 ++++++++++++++++--- 5 files changed, 51 insertions(+), 11 deletions(-) diff --git a/ui/src/kapacitor/apis/index.js b/ui/src/kapacitor/apis/index.js index 7b218c03b..5056365da 100644 --- a/ui/src/kapacitor/apis/index.js +++ b/ui/src/kapacitor/apis/index.js @@ -112,3 +112,17 @@ export const getLogStreamByRuleID = (kapacitor, ruleID) => method: 'GET', headers: {'Content-Type': 'application/json'}, }) + +export const pingKapacitorVersion = async kapacitor => { + try { + const result = await AJAX({ + method: 'GET', + url: `${kapacitor.links.proxy}?path=/kapacitor/v1/ping`, + }) + const kapVersion = result.headers['x-kapacitor-version'] + return kapVersion === '' ? null : kapVersion + } catch (error) { + console.error(error) + throw error + } +} diff --git a/ui/src/kapacitor/components/LogsToggle.js b/ui/src/kapacitor/components/LogsToggle.js index eedddafbf..92bc5f5f1 100644 --- a/ui/src/kapacitor/components/LogsToggle.js +++ b/ui/src/kapacitor/components/LogsToggle.js @@ -1,6 +1,6 @@ import React, {PropTypes} from 'react' -const LogsToggle = ({areLogsVisible, onToggleLogsVisbility}) => +const LogsToggle = ({areLogsVisible, areLogsEnabled, onToggleLogsVisbility}) =>
  • > Editor
  • -
  • - Editor + Logs -
  • + {areLogsEnabled && +
  • + Editor + Logs +
  • }
const {bool, func} = PropTypes LogsToggle.propTypes = { areLogsVisible: bool, + areLogsEnabled: bool, onToggleLogsVisbility: func.isRequired, } diff --git a/ui/src/kapacitor/components/Tickscript.js b/ui/src/kapacitor/components/Tickscript.js index 4083ee761..2a54dd093 100644 --- a/ui/src/kapacitor/components/Tickscript.js +++ b/ui/src/kapacitor/components/Tickscript.js @@ -17,6 +17,7 @@ const Tickscript = ({ onChangeID, isNewTickscript, areLogsVisible, + areLogsEnabled, onToggleLogsVisbility, }) =>
@@ -24,6 +25,7 @@ const Tickscript = ({ task={task} onSave={onSave} areLogsVisible={areLogsVisible} + areLogsEnabled={areLogsEnabled} onToggleLogsVisbility={onToggleLogsVisbility} isNewTickscript={isNewTickscript} /> @@ -55,6 +57,7 @@ Tickscript.propTypes = { id: string, }), areLogsVisible: bool, + areLogsEnabled: bool, onToggleLogsVisbility: func.isRequired, task: shape({ id: string, diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js index 658da68af..1d13b5003 100644 --- a/ui/src/kapacitor/components/TickscriptHeader.js +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -7,6 +7,7 @@ const TickscriptHeader = ({ task: {id}, onSave, areLogsVisible, + areLogsEnabled, isNewTickscript, onToggleLogsVisbility, }) => @@ -17,6 +18,7 @@ const TickscriptHeader = ({
@@ -39,6 +41,7 @@ TickscriptHeader.propTypes = { isNewTickscript: bool, onSave: func, areLogsVisible: bool, + areLogsEnabled: bool, onToggleLogsVisbility: func.isRequired, task: shape({ dbrps: arrayOf( diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 07d638620..2ad6b4659 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -7,7 +7,7 @@ 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 {getLogStreamByRuleID} from 'src/kapacitor/apis' +import {getLogStreamByRuleID, pingKapacitorVersion} from 'src/kapacitor/apis' import {publishNotification} from 'shared/actions/notifications' class TickscriptPage extends Component { @@ -27,6 +27,7 @@ class TickscriptPage extends Component { validation: '', isEditingID: true, logs: [], + areLogsEnabled: false, } } @@ -92,9 +93,25 @@ class TickscriptPage extends Component { this.setState({task: {tickscript, dbrps, type, status, name, id}}) } - this.shouldFetch = true + try { + const version = await pingKapacitorVersion(kapacitor) - this.fetchChunkedLogs(kapacitor, ruleID) + // Check minor version number for Kapacitor 1.4 + if (version && version.split('.')[1] === '4') { + this.shouldFetch = true + this.fetchChunkedLogs(kapacitor, ruleID) + this.setState({ + areLogsEnabled: true, + }) + } else { + this.setState({ + areLogsEnabled: false, + }) + } + } catch (err) { + console.error(err) + this.props.notify('error', err) + } this.setState({kapacitor}) } @@ -152,7 +169,7 @@ class TickscriptPage extends Component { render() { const {source} = this.props - const {task, validation, logs, areLogsVisible} = this.state + const {task, validation, logs, areLogsVisible, areLogsEnabled} = this.state return ( ) From 93ff34affcf557e0173c52dc241fea31cc9e4776 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Nov 2017 10:33:21 -0800 Subject: [PATCH 078/138] Update proto TimeShift property descriptions --- bolt/internal/internal.proto | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bolt/internal/internal.proto b/bolt/internal/internal.proto index 660a9c198..b9c5c86bd 100644 --- a/bolt/internal/internal.proto +++ b/bolt/internal/internal.proto @@ -113,9 +113,9 @@ message Query { } message TimeShift { - string Label = 1; // user facing description - string Unit = 2; // influxql time unit representation i.e. ms, s, m, h, d - string Quantity = 3; // number of units + string Label = 1; // Label user facing description + string Unit = 2; // Unit influxql time unit representation i.e. ms, s, m, h, d + string Quantity = 3; // Quantity number of units } message Range { From 208575b0cfff1c9f8c7bc48a97e9069e79d3605f Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Nov 2017 10:37:07 -0800 Subject: [PATCH 079/138] Format TimeShift struct property descriptions --- chronograf.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chronograf.go b/chronograf.go index 0ea3592d6..11fe9c109 100644 --- a/chronograf.go +++ b/chronograf.go @@ -287,9 +287,9 @@ type DurationRange struct { // TimeShift represents a shift to apply to an influxql query's time range type TimeShift struct { - Label string `json:"label"` // user facing description - Unit string `json:"unit"` // influxql time unit representation i.e. ms, s, m, h, d - Quantity string `json:"quantity"` // number of units + Label string `json:"label"` // Label user facing description + Unit string `json:"unit"` // Unit influxql time unit representation i.e. ms, s, m, h, d + Quantity string `json:"quantity"` // Quantity number of units } // QueryConfig represents UI query from the data explorer From 33e83254e5e9c34eed9a50b3ee0f6474f46405b5 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Nov 2017 11:27:48 -0800 Subject: [PATCH 080/138] Handle nil cell in validateDashboardRequest --- server/cells.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/cells.go b/server/cells.go index d1791e18f..de5c72b7b 100644 --- a/server/cells.go +++ b/server/cells.go @@ -69,6 +69,10 @@ func newCellResponses(dID chronograf.DashboardID, dcells []chronograf.DashboardC // ValidDashboardCellRequest verifies that the dashboard cells have a query and // have the correct axes specified func ValidDashboardCellRequest(c *chronograf.DashboardCell) error { + if c == nil { + return fmt.Errorf("Chronograf dashboard cell was nil") + } + CorrectWidthHeight(c) for _, q := range c.Queries { if err := ValidateQueryConfig(&q.QueryConfig); err != nil { From 59fb33387b7badf5c30ba6ef912129b4ea453fbe Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Nov 2017 11:34:26 -0800 Subject: [PATCH 081/138] Fix broken test due to stupid new line --- server/queries_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/queries_test.go b/server/queries_test.go index 58ab9c1a4..14e0c794a 100644 --- a/server/queries_test.go +++ b/server/queries_test.go @@ -167,6 +167,7 @@ func TestService_Queries(t *testing.T) { ] }`))), want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e :dashboardTime: AND time \u003c :upperDashboardTime: GROUP BY :interval:","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"","measurement":"","retentionPolicy":"","fields":[],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e :dashboardTime: AND time \u003c :upperDashboardTime: GROUP BY :interval:","range":null,"shifts":[]},"queryTemplated":"SELECT \"pingReq\" FROM \"_internal\".\"monitor\".\"httpd\" WHERE time \u003e now() - 15m AND time \u003c now() GROUP BY time(2s)","tempVars":[{"tempVar":":upperDashboardTime:","values":[{"value":"now()","type":"constant","selected":true}]},{"tempVar":":dashboardTime:","values":[{"value":"now() - 15m","type":"constant","selected":true}]},{"tempVar":":dbs:","values":[{"value":"_internal","type":"database","selected":true}]},{"tempVar":":interval:","values":[{"value":"1000","type":"resolution","selected":false},{"value":"3","type":"pointsPerPixel","selected":false}]}]}]} +`, }, } for _, tt := range tests { From 27ca493cfba11d6760f2c681f58a9ca55e076c9b Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Tue, 21 Nov 2017 13:51:02 -0700 Subject: [PATCH 082/138] Add null guard to tag parsing function to prevent parsing values that don't match tag RegEx. --- ui/src/hosts/apis/index.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/ui/src/hosts/apis/index.js b/ui/src/hosts/apis/index.js index 39caf9fbf..f8815f07d 100644 --- a/ui/src/hosts/apis/index.js +++ b/ui/src/hosts/apis/index.js @@ -196,17 +196,21 @@ function parseSeries(series) { function parseTag(s, obj) { const match = tag.exec(s) - const kv = match[0] - const key = match[1] - const value = match[2] + if (match) { + const kv = match[0] + const key = match[1] + const value = match[2] - if (key) { - if (!obj.tags) { - obj.tags = {} + if (key) { + if (!obj.tags) { + obj.tags = {} + } + obj.tags[key] = value } - obj.tags[key] = value + return s.slice(match.index + kv.length) } - return s.slice(match.index + kv.length) + + return '' } let workStr = series.slice() From 8b2bac550bfe020a1a0157afffc55ea68c44f9a4 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Tue, 21 Nov 2017 13:56:02 -0700 Subject: [PATCH 083/138] Changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80a5ad8a2..7944fc156 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ 1. [#2292](https://github.com/influxdata/chronograf/pull/2292): Source extra command line options from defaults file 1. [#2329](https://github.com/influxdata/chronograf/pull/2329): Include tag values alongside measurement name in Data Explorer result tabs 1. [#2386](https://github.com/influxdata/chronograf/pull/2386): Fix queries that include regex, numbers and wildcard +1. [#2398](https://github.com/influxdata/chronograf/pull/2398): Fix apps on hosts page from parsing tags with null values ### Features ### UI Improvements From 51e7e8fe3c798d2b65a50e4f7e514a205cc5dd8b Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Tue, 21 Nov 2017 14:46:54 -0700 Subject: [PATCH 084/138] Refactor to use feature detection instead of version detection. Add logs upgrade message. --- ui/src/kapacitor/apis/index.js | 18 +-------- ui/src/kapacitor/constants/index.js | 2 + ui/src/kapacitor/containers/TickscriptPage.js | 38 +++++++++---------- 3 files changed, 22 insertions(+), 36 deletions(-) diff --git a/ui/src/kapacitor/apis/index.js b/ui/src/kapacitor/apis/index.js index 5056365da..5f2e83838 100644 --- a/ui/src/kapacitor/apis/index.js +++ b/ui/src/kapacitor/apis/index.js @@ -102,27 +102,13 @@ export const updateTask = async ( } export const getLogStream = kapacitor => - fetch(`${kapacitor.links.proxy}?path=/kapacitor/v1/logs`, { + fetch(`${kapacitor.links.proxy}?path=/kapacitor/v1preview/logs`, { method: 'GET', headers: {'Content-Type': 'application/json'}, }) export const getLogStreamByRuleID = (kapacitor, ruleID) => - fetch(`${kapacitor.links.proxy}?path=/kapacitor/v1/logs?task=${ruleID}`, { + fetch(`${kapacitor.links.proxy}?path=/kapacitor/v1preview/logs?task=${ruleID}`, { method: 'GET', headers: {'Content-Type': 'application/json'}, }) - -export const pingKapacitorVersion = async kapacitor => { - try { - const result = await AJAX({ - method: 'GET', - url: `${kapacitor.links.proxy}?path=/kapacitor/v1/ping`, - }) - const kapVersion = result.headers['x-kapacitor-version'] - return kapVersion === '' ? null : kapVersion - } catch (error) { - console.error(error) - throw error - } -} diff --git a/ui/src/kapacitor/constants/index.js b/ui/src/kapacitor/constants/index.js index 181543e68..92815bd86 100644 --- a/ui/src/kapacitor/constants/index.js +++ b/ui/src/kapacitor/constants/index.js @@ -219,3 +219,5 @@ export const ALERT_NODES_ACCESSORS = { ) .join('.'), } + +export const KAPACITOR_LOGS_NOT_FOUND = 404 diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 2ad6b4659..15ec313ac 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -7,7 +7,8 @@ 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 {getLogStreamByRuleID, pingKapacitorVersion} from 'src/kapacitor/apis' +import {getLogStreamByRuleID} from 'src/kapacitor/apis' +import {KAPACITOR_LOGS_NOT_FOUND} from 'src/kapacitor/constants' import {publishNotification} from 'shared/actions/notifications' class TickscriptPage extends Component { @@ -39,6 +40,21 @@ class TickscriptPage extends Component { try { const response = await getLogStreamByRuleID(kapacitor, ruleID) + if (response.status === KAPACITOR_LOGS_NOT_FOUND) { + this.setState({ + areLogsEnabled: false, + }) + this.shouldFetch = false + notify('warning', 'Could not use logging, requires Kapacitor version 1.4') + return + } + else { + this.setState({ + areLogsEnabled: true, + }) + this.shouldFetch = true + } + const reader = await response.body.getReader() const decoder = new TextDecoder() @@ -93,25 +109,7 @@ class TickscriptPage extends Component { this.setState({task: {tickscript, dbrps, type, status, name, id}}) } - try { - const version = await pingKapacitorVersion(kapacitor) - - // Check minor version number for Kapacitor 1.4 - if (version && version.split('.')[1] === '4') { - this.shouldFetch = true - this.fetchChunkedLogs(kapacitor, ruleID) - this.setState({ - areLogsEnabled: true, - }) - } else { - this.setState({ - areLogsEnabled: false, - }) - } - } catch (err) { - console.error(err) - this.props.notify('error', err) - } + this.fetchChunkedLogs(kapacitor, ruleID) this.setState({kapacitor}) } From c87bfe16786a935a99db35d77ba18e250efba307 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Tue, 21 Nov 2017 16:53:04 -0600 Subject: [PATCH 085/138] Add GENERIC_API_KEY to override of oauth2 responses to support azure --- oauth2/generic.go | 11 +++++++---- oauth2/generic_test.go | 1 + server/server.go | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/oauth2/generic.go b/oauth2/generic.go index aa18e716f..0172c70af 100644 --- a/oauth2/generic.go +++ b/oauth2/generic.go @@ -27,6 +27,7 @@ type Generic struct { AuthURL string TokenURL string APIURL string // APIURL returns OpenID Userinfo + APIKey string // APIKey is the JSON key to lookup email address in APIURL response Logger chronograf.Logger } @@ -69,9 +70,7 @@ func (g *Generic) Config() *oauth2.Config { // PrincipalID returns the email address of the user. func (g *Generic) PrincipalID(provider *http.Client) (string, error) { - res := struct { - Email string `json:"email"` - }{} + res := map[string]interface{}{} r, err := provider.Get(g.APIURL) if err != nil { @@ -83,7 +82,11 @@ func (g *Generic) PrincipalID(provider *http.Client) (string, error) { return "", err } - email := res.Email + email := "" + value := res[g.APIKey] + if e, ok := value.(string); ok { + email = e + } // If we did not receive an email address, try to lookup the email // in a similar way as github diff --git a/oauth2/generic_test.go b/oauth2/generic_test.go index a773c686a..f33cc9ef4 100644 --- a/oauth2/generic_test.go +++ b/oauth2/generic_test.go @@ -34,6 +34,7 @@ func TestGenericPrincipalID(t *testing.T) { prov := oauth2.Generic{ Logger: logger, APIURL: mockAPI.URL, + APIKey: "email", } tt, err := oauth2.NewTestTripper(logger, mockAPI, http.DefaultTransport) if err != nil { diff --git a/server/server.go b/server/server.go index d51d8148c..fc3354cb9 100644 --- a/server/server.go +++ b/server/server.go @@ -79,6 +79,7 @@ type Server struct { GenericAuthURL string `long:"generic-auth-url" description:"OAuth 2.0 provider's authorization endpoint URL" env:"GENERIC_AUTH_URL"` GenericTokenURL string `long:"generic-token-url" description:"OAuth 2.0 provider's token endpoint URL" env:"GENERIC_TOKEN_URL"` GenericAPIURL string `long:"generic-api-url" description:"URL that returns OpenID UserInfo compatible information." env:"GENERIC_API_URL"` + GenericAPIKey string `long:"generic-api-key" description:"JSON lookup key into OpenID UserInfo. (Azure should be userPrincipalName)" default:"email" env:"GENERIC_API_KEY"` Auth0Domain string `long:"auth0-domain" description:"Subdomain of auth0.com used for Auth0 OAuth2 authentication" env:"AUTH0_DOMAIN"` Auth0ClientID string `long:"auth0-client-id" description:"Auth0 Client ID for OAuth2 support" env:"AUTH0_CLIENT_ID"` @@ -181,6 +182,7 @@ func (s *Server) genericOAuth(logger chronograf.Logger, auth oauth2.Authenticato AuthURL: s.GenericAuthURL, TokenURL: s.GenericTokenURL, APIURL: s.GenericAPIURL, + APIKey: s.GenericAPIKey, Logger: logger, } jwt := oauth2.NewJWT(s.TokenSecret) From 5a22288b3962c5d1f2205d8d6cf59f4bcdd85cad Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Tue, 21 Nov 2017 17:09:24 -0600 Subject: [PATCH 086/138] Update CHANGELOG to override generic oauth email JSON key --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aceef616..b79509197 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ### Features 1. [#2385](https://github.com/influxdata/chronograf/pull/2385): Add time shift feature to DataExplorer and Dashboards +1. [#2400](https://github.com/influxdata/chronograf/pull/2400): Allow override of generic oauth2 keys for email ### UI Improvements From f2dc4ffb797493774bf3b1aaffd2cf1b6fc56b77 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Mon, 27 Nov 2017 04:01:39 -0700 Subject: [PATCH 087/138] Linter. --- ui/src/kapacitor/apis/index.js | 11 +++++++---- ui/src/kapacitor/containers/TickscriptPage.js | 16 +++++++++------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/ui/src/kapacitor/apis/index.js b/ui/src/kapacitor/apis/index.js index 5f2e83838..41220be6f 100644 --- a/ui/src/kapacitor/apis/index.js +++ b/ui/src/kapacitor/apis/index.js @@ -108,7 +108,10 @@ export const getLogStream = kapacitor => }) export const getLogStreamByRuleID = (kapacitor, ruleID) => - fetch(`${kapacitor.links.proxy}?path=/kapacitor/v1preview/logs?task=${ruleID}`, { - method: 'GET', - headers: {'Content-Type': 'application/json'}, - }) + fetch( + `${kapacitor.links.proxy}?path=/kapacitor/v1preview/logs?task=${ruleID}`, + { + method: 'GET', + headers: {'Content-Type': 'application/json'}, + } + ) diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 15ec313ac..3f2fef6c7 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -45,15 +45,17 @@ class TickscriptPage extends Component { areLogsEnabled: false, }) this.shouldFetch = false - notify('warning', 'Could not use logging, requires Kapacitor version 1.4') + notify( + 'warning', + 'Could not use logging, requires Kapacitor version 1.4' + ) return } - else { - this.setState({ - areLogsEnabled: true, - }) - this.shouldFetch = true - } + + this.setState({ + areLogsEnabled: true, + }) + this.shouldFetch = true const reader = await response.body.getReader() const decoder = new TextDecoder() From 498297f21bba9ae73f4d900b3a57d11f07d4ba9f Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 27 Nov 2017 10:13:02 -0500 Subject: [PATCH 088/138] Put Chrony back --- Makefile | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index d8dab4fae..6ec6c6050 100644 --- a/Makefile +++ b/Makefile @@ -23,23 +23,14 @@ ${BINARY}: $(SOURCES) .bindata .jsdep .godep go build -o ${BINARY} ${LDFLAGS} ./cmd/chronograf/main.go define CHRONOGIRAFFE - .-. .-. - | \/ | - /, ,_ `'-. - .-|\ /`\ '. - .' 0/ | 0\ \_ `". - .-' _,/ '--'.'|#''---' - `--' | / \# - | / \# - \ ;|\ .\# - |' ' // \ ::\# - \ /` \ ':\# - `"` \.. \# - \::. \# - \:: \# - \' .:\# - \ :::\# - \ '::\# + ._ o o + \_`-)|_ + ,"" _\_ + ," ## | 0 0. + ," ## ,-\__ `. + ," / `--._;) - "HAI, I'm Chronogiraffe. Let's be friends!" + ," ## / +," ## / endef export CHRONOGIRAFFE chronogiraffe: ${BINARY} From 90b138cb536b0153efb6364ab8190f825a700fa7 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 27 Nov 2017 10:37:38 -0500 Subject: [PATCH 089/138] Use props instead of state for Dashboard name list --- ui/src/dashboards/containers/DashboardPage.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/ui/src/dashboards/containers/DashboardPage.js b/ui/src/dashboards/containers/DashboardPage.js index b28988985..2fc75ae7f 100644 --- a/ui/src/dashboards/containers/DashboardPage.js +++ b/ui/src/dashboards/containers/DashboardPage.js @@ -39,13 +39,12 @@ class DashboardPage extends Component { selectedCell: null, isTemplating: false, zoomedTimeRange: {zoomedLower: null, zoomedUpper: null}, - names: [], } } async componentDidMount() { const { - params: {dashboardID, sourceID}, + params: {dashboardID}, dashboardActions: { getDashboardsAsync, updateTempVarValues, @@ -62,13 +61,6 @@ class DashboardPage extends Component { // Refresh and persists influxql generated template variable values await updateTempVarValues(source, dashboard) await putDashboardByID(dashboardID) - - const names = dashboards.map(d => ({ - name: d.name, - link: `/sources/${sourceID}/dashboards/${d.id}`, - })) - - this.setState({names}) } handleOpenTemplateManager = () => { @@ -294,7 +286,11 @@ class DashboardPage extends Component { templatesIncludingDashTime = [] } - const {selectedCell, isEditMode, isTemplating, names} = this.state + const {selectedCell, isEditMode, isTemplating} = this.state + const names = dashboards.map(d => ({ + name: d.name, + link: `/sources/${sourceID}/dashboards/${d.id}`, + })) return (
From e739a142cd1fd0f7a74effe9b935b627878b0465 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 27 Nov 2017 10:51:58 -0500 Subject: [PATCH 090/138] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aceef616..38ecc526e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ 1. [#2292](https://github.com/influxdata/chronograf/pull/2292): Source extra command line options from defaults file 1. [#2329](https://github.com/influxdata/chronograf/pull/2329): Include tag values alongside measurement name in Data Explorer result tabs 1. [#2386](https://github.com/influxdata/chronograf/pull/2386): Fix queries that include regex, numbers and wildcard +1. [#2408](https://github.com/influxdata/chronograf/pull/2408): Fix updated Dashboard names not updating dashboard list ### Features 1. [#2385](https://github.com/influxdata/chronograf/pull/2385): Add time shift feature to DataExplorer and Dashboards From 9cb9e0bcfb9e7b480ae694751c31fb06810fe452 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 27 Nov 2017 17:03:14 -0500 Subject: [PATCH 091/138] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 191ae6f28..d87f122b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ 1. [#2329](https://github.com/influxdata/chronograf/pull/2329): Include tag values alongside measurement name in Data Explorer result tabs ### Features +1. [#2384](https://github.com/influxdata/chronograf/pull/2384): Add filtering by name to Dashboard index page ### UI Improvements ## v1.3.10.0 [2017-10-24] From 4b509448dc181a417a12becc6457e11751b9b057 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 27 Nov 2017 17:14:52 -0500 Subject: [PATCH 092/138] Remove redundant prop check --- ui/src/shared/components/DatabaseList.js | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/src/shared/components/DatabaseList.js b/ui/src/shared/components/DatabaseList.js index 9236bc501..6300e8154 100644 --- a/ui/src/shared/components/DatabaseList.js +++ b/ui/src/shared/components/DatabaseList.js @@ -51,7 +51,6 @@ const DatabaseList = React.createClass({ const newMetaQuery = prevQuery.rawText !== nextQuery.rawText && - nextQuery.rawText && nextQuery.rawText.match(/^(create|drop)/i) if (differentSource || newMetaQuery) { From f80a6d8f03a03019c5b456bf7cfc8bcb47e210f6 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 28 Nov 2017 11:17:59 -0500 Subject: [PATCH 093/138] Stop 'cannot communicate with server' spam --- ui/src/shared/middleware/errors.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/src/shared/middleware/errors.js b/ui/src/shared/middleware/errors.js index 56d64dd03..73c0e6102 100644 --- a/ui/src/shared/middleware/errors.js +++ b/ui/src/shared/middleware/errors.js @@ -37,7 +37,8 @@ const errorsMiddleware = store => next => action => { } else if (altText) { store.dispatch(notify(alertType, altText)) } else { - store.dispatch(notify(alertType, 'Cannot communicate with server.')) + // TODO: actually do proper error handling + // store.dispatch(notify(alertType, 'Cannot communicate with server.')) } } From 1a1a74a1d2bd4dbac48b45a77c8b4a524998be4e Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 28 Nov 2017 12:46:01 -0500 Subject: [PATCH 094/138] Update label to handle new field shapes --- ui/src/shared/presenters/index.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ui/src/shared/presenters/index.js b/ui/src/shared/presenters/index.js index dd23a5e04..d9984206a 100644 --- a/ui/src/shared/presenters/index.js +++ b/ui/src/shared/presenters/index.js @@ -110,11 +110,9 @@ function getRolesForUser(roles, user) { } export const buildDefaultYLabel = queryConfig => { - return queryConfig.rawText - ? '' - : `${queryConfig.measurement}.${_.get( - queryConfig, - ['fields', '0', 'field'], - '' - )}` + const {measurement, fields} = queryConfig + const fieldAlias = `${_.get(fields, ['0', 'alias'], '')}` + const field = `${_.get(fields, ['0', 'value'], '')}` + + return `${measurement}.${fieldAlias || field}` } From 8471c829de9b3f10d8bb8b8fe4bacef084418816 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Tue, 28 Nov 2017 10:47:14 -0700 Subject: [PATCH 095/138] Resolve some PR issues. Improve Logging UX. Switch back to version detection due to poor UX (lag) of feature detection. Split out LogsTableRow to SFC. LogsTable also becomes an SFC. --- ui/src/kapacitor/apis/index.js | 14 +++ ui/src/kapacitor/components/LogsTable.js | 98 ++++--------------- ui/src/kapacitor/components/LogsTableRow.js | 68 +++++++++++++ ui/src/kapacitor/components/LogsToggle.js | 16 ++- .../kapacitor/components/TickscriptHeader.js | 11 ++- ui/src/kapacitor/constants/index.js | 2 - ui/src/kapacitor/containers/TickscriptPage.js | 34 ++++--- 7 files changed, 136 insertions(+), 107 deletions(-) create mode 100644 ui/src/kapacitor/components/LogsTableRow.js diff --git a/ui/src/kapacitor/apis/index.js b/ui/src/kapacitor/apis/index.js index 41220be6f..4465f8aca 100644 --- a/ui/src/kapacitor/apis/index.js +++ b/ui/src/kapacitor/apis/index.js @@ -115,3 +115,17 @@ export const getLogStreamByRuleID = (kapacitor, ruleID) => headers: {'Content-Type': 'application/json'}, } ) + +export const pingKapacitorVersion = async kapacitor => { + try { + const result = await AJAX({ + method: 'GET', + url: `${kapacitor.links.proxy}?path=/kapacitor/v1preview/ping`, + }) + const kapVersion = result.headers['x-kapacitor-version'] + return kapVersion === '' ? null : kapVersion + } catch (error) { + console.error(error) + throw error + } +} diff --git a/ui/src/kapacitor/components/LogsTable.js b/ui/src/kapacitor/components/LogsTable.js index ae1379c9f..0e49badd3 100644 --- a/ui/src/kapacitor/components/LogsTable.js +++ b/ui/src/kapacitor/components/LogsTable.js @@ -1,86 +1,26 @@ -import React, {Component, PropTypes} from 'react' +import React, {PropTypes} from 'react' import FancyScrollbar from 'shared/components/FancyScrollbar' -import LogItemSession from 'src/kapacitor/components/LogItemSession' -import LogItemHTTP from 'src/kapacitor/components/LogItemHTTP' -import LogItemHTTPError from 'src/kapacitor/components/LogItemHTTPError' -import LogItemKapacitorPoint from 'src/kapacitor/components/LogItemKapacitorPoint' -import LogItemKapacitorError from 'src/kapacitor/components/LogItemKapacitorError' -import LogItemKapacitorDebug from 'src/kapacitor/components/LogItemKapacitorDebug' -import LogItemInfluxDBDebug from 'src/kapacitor/components/LogItemInfluxDBDebug' +import LogsTableRow from 'src/kapacitor/components/LogsTableRow' -class LogsTable extends Component { - constructor(props) { - super(props) - } - - renderTableRow = (logItem, index) => { - if (logItem.service === 'sessions') { - return - } - if (logItem.service === 'http' && logItem.msg === 'http request') { - return - } - if (logItem.service === 'kapacitor' && logItem.msg === 'point') { - return - } - if (logItem.service === 'httpd_server_errors' && logItem.lvl === 'error') { - return - } - if (logItem.service === 'kapacitor' && logItem.lvl === 'error') { - return - } - if (logItem.service === 'kapacitor' && logItem.lvl === 'debug') { - return - } - if (logItem.service === 'influxdb' && logItem.lvl === 'debug') { - return - } - - return ( -
-
-
-
- {logItem.ts} -
-
-
-
- {logItem.service || '--'} -
-
-
- {logItem.msg || '--'} -
-
-
+const LogsTable = ({logs}) => +
+
+

Logs

+
+ +
+ {logs.length + ? logs.map((log, i) => + + ) + :
}
- ) - } - - render() { - const {logs} = this.props - - return ( -
-
-

Logs

-
- -
- {logs.length - ? logs.map((log, i) => this.renderTableRow(log, i)) - :
} -
- -
- ) - } -} +
+
const {arrayOf, shape, string} = PropTypes diff --git a/ui/src/kapacitor/components/LogsTableRow.js b/ui/src/kapacitor/components/LogsTableRow.js new file mode 100644 index 000000000..32b658c67 --- /dev/null +++ b/ui/src/kapacitor/components/LogsTableRow.js @@ -0,0 +1,68 @@ +import React, {PropTypes} from 'react' + +import LogItemSession from 'src/kapacitor/components/LogItemSession' +import LogItemHTTP from 'src/kapacitor/components/LogItemHTTP' +import LogItemHTTPError from 'src/kapacitor/components/LogItemHTTPError' +import LogItemKapacitorPoint from 'src/kapacitor/components/LogItemKapacitorPoint' +import LogItemKapacitorError from 'src/kapacitor/components/LogItemKapacitorError' +import LogItemKapacitorDebug from 'src/kapacitor/components/LogItemKapacitorDebug' +import LogItemInfluxDBDebug from 'src/kapacitor/components/LogItemInfluxDBDebug' + +const LogsTableRow = ({logItem, index}) => { + if (logItem.service === 'sessions') { + return + } + if (logItem.service === 'http' && logItem.msg === 'http request') { + return + } + if (logItem.service === 'kapacitor' && logItem.msg === 'point') { + return + } + if (logItem.service === 'httpd_server_errors' && logItem.lvl === 'error') { + return + } + if (logItem.service === 'kapacitor' && logItem.lvl === 'error') { + return + } + if (logItem.service === 'kapacitor' && logItem.lvl === 'debug') { + return + } + if (logItem.service === 'influxdb' && logItem.lvl === 'debug') { + return + } + + return ( +
+
+
+
+ {logItem.ts} +
+
+
+
+ {logItem.service || '--'} +
+
+
+ {logItem.msg || '--'} +
+
+
+
+ ) +} + +const {number, shape, string} = PropTypes + +LogsTableRow.propTypes = { + logItem: shape({ + key: string.isRequired, + ts: string.isRequired, + lvl: string.isRequired, + msg: string.isRequired, + }).isRequired, + index: number, +} + +export default LogsTableRow diff --git a/ui/src/kapacitor/components/LogsToggle.js b/ui/src/kapacitor/components/LogsToggle.js index 92bc5f5f1..eedddafbf 100644 --- a/ui/src/kapacitor/components/LogsToggle.js +++ b/ui/src/kapacitor/components/LogsToggle.js @@ -1,6 +1,6 @@ import React, {PropTypes} from 'react' -const LogsToggle = ({areLogsVisible, areLogsEnabled, onToggleLogsVisbility}) => +const LogsToggle = ({areLogsVisible, onToggleLogsVisbility}) =>
  • > Editor
  • - {areLogsEnabled && -
  • - Editor + Logs -
  • } +
  • + Editor + Logs +
const {bool, func} = PropTypes LogsToggle.propTypes = { areLogsVisible: bool, - areLogsEnabled: bool, onToggleLogsVisbility: func.isRequired, } diff --git a/ui/src/kapacitor/components/TickscriptHeader.js b/ui/src/kapacitor/components/TickscriptHeader.js index 1d13b5003..c830887ae 100644 --- a/ui/src/kapacitor/components/TickscriptHeader.js +++ b/ui/src/kapacitor/components/TickscriptHeader.js @@ -16,11 +16,12 @@ const TickscriptHeader = ({

TICKscript Editor

- + {areLogsEnabled && + }
class DeleteConfirmButtons extends Component { @@ -37,7 +38,7 @@ class DeleteConfirmButtons extends Component { } render() { - const {onDelete, item, buttonSize} = this.props + const {onDelete, item, buttonSize, icon, square} = this.props const {isConfirming} = this.state return isConfirming @@ -50,21 +51,27 @@ class DeleteConfirmButtons extends Component { : } } -const {func, oneOfType, shape, string} = PropTypes +const {bool, func, oneOfType, shape, string} = PropTypes DeleteButton.propTypes = { onClickDelete: func.isRequired, buttonSize: string, + icon: string, + square: bool, } DeleteConfirmButtons.propTypes = { item: oneOfType([(string, shape())]), onDelete: func.isRequired, buttonSize: string, + square: bool, + icon: string, } DeleteConfirmButtons.defaultProps = { From eb40728aff7c96c482d12a915a8075b4deb091cc Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 28 Nov 2017 12:26:46 -0800 Subject: [PATCH 097/138] Refactor styles for template variable rows to withstand smaller resolutions --- .../template_variables/RowButtons.js | 23 ++++++++----------- .../template-variables-manager.scss | 20 ++++++++++------ 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/ui/src/dashboards/components/template_variables/RowButtons.js b/ui/src/dashboards/components/template_variables/RowButtons.js index 549b7a257..7bc69bc75 100644 --- a/ui/src/dashboards/components/template_variables/RowButtons.js +++ b/ui/src/dashboards/components/template_variables/RowButtons.js @@ -1,33 +1,30 @@ import React, {PropTypes} from 'react' import DeleteConfirmButtons from 'shared/components/DeleteConfirmButtons' -const RowButtons = ({ - onStartEdit, - isEditing, - onCancelEdit, - onDelete, - id, - selectedType, -}) => { +const RowButtons = ({onStartEdit, isEditing, onCancelEdit, onDelete, id}) => { if (isEditing) { return (
-
) } return (
- +