Merge pull request #1212 from influxdata/feature/meta-query-builder

Feature/meta query builder
pull/1207/head^2
Andrew Watkins 2017-04-07 10:50:19 -06:00 committed by GitHub
commit e2c0715745
8 changed files with 100 additions and 11 deletions

View File

@ -33,6 +33,7 @@
1. [#1113](https://github.com/influxdata/chronograf/issues/1113): Add Slack channel per Kapacitor alert. 1. [#1113](https://github.com/influxdata/chronograf/issues/1113): Add Slack channel per Kapacitor alert.
1. [#1095](https://github.com/influxdata/chronograf/pull/1095): Add new auth duration CLI option; add client heartbeat 1. [#1095](https://github.com/influxdata/chronograf/pull/1095): Add new auth duration CLI option; add client heartbeat
1. [#1168](https://github.com/influxdata/chronograf/issue/1168): Expand support for --basepath on some load balancers 1. [#1168](https://github.com/influxdata/chronograf/issue/1168): Expand support for --basepath on some load balancers
1. [#1212](https://github.com/influxdata/chronograf/issue/1212): Add query templates and loading animation to the RawQueryEditor
1. [#1221](https://github.com/influxdata/chronograf/issue/1221): More sensical Cell and Dashboard defaults 1. [#1221](https://github.com/influxdata/chronograf/issue/1221): More sensical Cell and Dashboard defaults
### UI Improvements ### UI Improvements

View File

@ -35,13 +35,6 @@ const QueryEditor = React.createClass({
}).isRequired, }).isRequired,
}, },
getInitialState() {
return {
database: null,
measurement: null,
}
},
handleChooseNamespace(namespace) { handleChooseNamespace(namespace) {
this.props.actions.chooseNamespace(this.props.query.id, namespace) this.props.actions.chooseNamespace(this.props.query.id, namespace)
}, },

View File

@ -1,5 +1,8 @@
import React, {PropTypes} from 'react' import React, {PropTypes} from 'react'
import classNames from 'classnames' import classNames from 'classnames'
import Dropdown from 'src/shared/components/Dropdown'
import LoadingDots from 'src/shared/components/LoadingDots'
import {QUERY_TEMPLATES} from 'src/data_explorer/constants'
const ENTER = 13 const ENTER = 13
const ESCAPE = 27 const ESCAPE = 27
@ -45,6 +48,10 @@ const RawQueryEditor = React.createClass({
this.props.onUpdate(this.state.value) this.props.onUpdate(this.state.value)
}, },
handleChooseTemplate(template) {
this.setState({value: template.query})
},
render() { render() {
const {query: {rawStatus}} = this.props const {query: {rawStatus}} = this.props
const {value} = this.state const {value} = this.state
@ -63,6 +70,7 @@ const RawQueryEditor = React.createClass({
spellCheck="false" spellCheck="false"
/> />
{this.renderStatus(rawStatus)} {this.renderStatus(rawStatus)}
<Dropdown items={QUERY_TEMPLATES} selected={'Query Templates'} onChoose={this.handleChooseTemplate} className="query-template"/>
</div> </div>
) )
}, },
@ -74,6 +82,14 @@ const RawQueryEditor = React.createClass({
) )
} }
if (rawStatus.loading) {
return (
<div className="raw-text--status">
<LoadingDots />
</div>
)
}
return ( return (
<div className={classNames("raw-text--status", {"raw-text--error": rawStatus.error, "raw-text--success": rawStatus.success, "raw-text--warning": rawStatus.warn})}> <div className={classNames("raw-text--status", {"raw-text--error": rawStatus.error, "raw-text--success": rawStatus.success, "raw-text--warning": rawStatus.warn})}>
<span className={classNames("icon", {stop: rawStatus.error, checkmark: rawStatus.success, "alert-triangle": rawStatus.warn})}></span> <span className={classNames("icon", {stop: rawStatus.error, checkmark: rawStatus.success, "alert-triangle": rawStatus.warn})}></span>

View File

@ -82,12 +82,15 @@ const ChronoTable = React.createClass({
return return
} }
this.setState({isLoading: true})
const {onEditRawStatus} = this.props const {onEditRawStatus} = this.props
onEditRawStatus(query.id, {loading: true})
this.setState({isLoading: true})
// second param is db, we want to leave this blank // second param is db, we want to leave this blank
try { try {
const {data} = await fetchTimeSeries(query.host, undefined, query.text) const {data} = await fetchTimeSeries(query.host, undefined, query.text)
this.setState({isLoading: false}) this.setState({isLoading: false})
onEditRawStatus(query.id, {loading: false})
const results = _.get(data, ['results', '0'], false) const results = _.get(data, ['results', '0'], false)
if (!results) { if (!results) {

View File

@ -10,3 +10,23 @@ export const INFLUXQL_FUNCTIONS = [
'spread', 'spread',
'stddev', 'stddev',
] ]
export const QUERY_TEMPLATES = [
{text: 'Show Databases', query: 'SHOW DATABASES'},
{text: 'Create Database', query: 'CREATE DATABASE "db_name"'},
{text: 'Drop Database', query: 'DROP DATABASE "db_name"'},
{text: 'Show Measurements', query: 'SHOW MEASUREMENTS ON "db_name"'},
{text: 'Show Tag Keys', query: 'SHOW TAG KEYS ON "db_name" FROM "measurement_name"'},
{text: 'Show Tag Values', query: 'SHOW TAG VALUES ON "db_name" FROM "measurement_name" WITH KEY = "tag_key"'},
{text: 'Show Retention Policies', query: 'SHOW RETENTION POLICIES on "db_name"'},
{text: 'Create Retention Policy', query: 'CREATE RETENTION POLICY "rp_name" ON "db_name" DURATION 30d REPLICATION 1 DEFAULT'},
{text: 'Drop Retention Policy', query: 'DROP RETENTION POLICY "rp_name" ON "db_name"'},
{text: 'Create Continuous Query', query: 'CREATE CONTINUOUS QUERY "cq_name" ON "db_name" BEGIN SELECT min("field") INTO "target_measurement" FROM "current_measurement" GROUP BY time(30m) END'},
{text: 'Drop Continuous Query', query: 'DROP CONTINUOUS QUERY "cq_name" ON "db_name"'},
{text: 'Show Users', query: 'SHOW USERS'},
{text: 'Create User', query: `CREATE USER "username" WITH PASSWORD 'password'`},
{text: 'Create Admin User', query: `CREATE USER "username" WITH PASSWORD 'password' WITH ALL PRIVILEGES`},
{text: 'Drop User', query: 'DROP USER "username"'},
{text: 'Show Stats', query: 'SHOW STATS'},
{text: 'Show Diagnostics', query: 'SHOW DIAGNOSTICS'},
]

View File

@ -0,0 +1,19 @@
import React, {PropTypes} from 'react'
const LoadingDots = ({className}) => (
<div className={`loading-dots ${className}`}>
<div></div>
<div></div>
<div></div>
</div>
)
const {
string,
} = PropTypes
LoadingDots.propTypes = {
className: string,
}
export default LoadingDots

View File

@ -26,7 +26,7 @@
} }
$raw-text-color: $c-pool; $raw-text-color: $c-pool;
$raw-text-height: 38px; $raw-text-height: 42px;
.raw-text--field { .raw-text--field {
@include custom-scrollbar($g2-kevlar, $raw-text-color); @include custom-scrollbar($g2-kevlar, $raw-text-color);
@ -72,7 +72,7 @@ $raw-text-height: 38px;
} }
.raw-text--status { .raw-text--status {
width: 100%; width: 100%;
height: ($query-builder--preview-height - 2px - $raw-text-height); height: ($query-builder--preview-height - $raw-text-height);
line-height: 12px; line-height: 12px;
font-size: 12px; font-size: 12px;
background-color: $g2-kevlar; background-color: $g2-kevlar;
@ -108,4 +108,13 @@ $raw-text-height: 38px;
&.raw-text--success { &.raw-text--success {
color: $c-rainforest; color: $c-rainforest;
} }
} }
.dropdown.query-template {
position: absolute;
top: 8px;
right: 7px;
.dropdown-toggle {
width: 135px;
}
}

View File

@ -90,3 +90,31 @@
margin-bottom: 11px; margin-bottom: 11px;
} }
} }
/*
Loading Dots
----------------------------------------------
*/
.loading-dots {
position: absolute;
transform: translate(0,0);
transform: translateX(50%);
width: 16px;
height: 18px;
div {
width: 4px;
height: 4px;
background-color: $g6-smoke;
border-radius: 50%;
position: absolute;
top: 50%;
transform: translate(-50%,-50%);
}
div:nth-child(1) {left: 0; animation: refreshingSpinnerA 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) infinite;}
div:nth-child(2) {left: 50%; animation: refreshingSpinnerB 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) infinite;}
div:nth-child(3) {left: 100%; animation: refreshingSpinnerC 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) infinite;}
}