Skip to content

Commit

Permalink
Numerous improvements to SQL Lab (#1088)
Browse files Browse the repository at this point in the history
* Improving the Visualize flow

* Fixed the timer

* CTAS

* Expiclit engine handling

* make tab full height, stretch for longer content (#1081)

* Better error handling for queries

* Hooked and fixed CSV export

* Linting

* Tying in the dttm in the viz flow

* Indicator showing when going offline

* Addressing comments, fixing the build

* Fixing unit tests
  • Loading branch information
mistercrunch authored Sep 11, 2016
1 parent c20ee0c commit 1971bf6
Show file tree
Hide file tree
Showing 19 changed files with 298 additions and 205 deletions.
5 changes: 5 additions & 0 deletions caravel/assets/javascripts/SqlLab/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const SET_ACTIVE_QUERY_EDITOR = 'SET_ACTIVE_QUERY_EDITOR';
export const ADD_ALERT = 'ADD_ALERT';
export const REMOVE_ALERT = 'REMOVE_ALERT';
export const REFRESH_QUERIES = 'REFRESH_QUERIES';
export const SET_NETWORK_STATUS = 'SET_NETWORK_STATUS';

export function resetState() {
return { type: RESET_STATE };
Expand All @@ -36,6 +37,10 @@ export function addQueryEditor(queryEditor) {
return { type: ADD_QUERY_EDITOR, queryEditor };
}

export function setNetworkStatus(networkOn) {
return { type: SET_NETWORK_STATUS, networkOn };
}

export function addAlert(alert) {
return { type: ADD_ALERT, alert };
}
Expand Down
60 changes: 0 additions & 60 deletions caravel/assets/javascripts/SqlLab/components/LeftPane.jsx

This file was deleted.

28 changes: 21 additions & 7 deletions caravel/assets/javascripts/SqlLab/components/QueryAutoRefresh.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { connect } from 'react-redux';
import * as Actions from '../actions';

const $ = require('jquery');

const QUERY_UPDATE_FREQ = 1000;
const QUERY_UPDATE_BUFFER_MS = 5000;

class QueryAutoRefresh extends React.Component {
componentWillMount() {
Expand All @@ -15,20 +16,28 @@ class QueryAutoRefresh extends React.Component {
}
startTimer() {
if (!(this.timer)) {
this.timer = setInterval(this.stopwatch.bind(this), 1000);
this.timer = setInterval(this.stopwatch.bind(this), QUERY_UPDATE_FREQ);
}
}
stopTimer() {
clearInterval(this.timer);
this.timer = null;
}
stopwatch() {
const url = '/caravel/queries/0';
const url = '/caravel/queries/' + (this.props.queriesLastUpdate - QUERY_UPDATE_BUFFER_MS);
// No updates in case of failure.
$.getJSON(url, (data, status) => {
if (status === 'success') {
$.getJSON(url, (data) => {
if (Object.keys(data).length > 0) {
this.props.actions.refreshQueries(data);
}
if (!this.props.networkOn) {
this.props.actions.setNetworkStatus(true);
}
})
.fail(() => {
if (this.props.networkOn) {
this.props.actions.setNetworkStatus(false);
}
});
}
render() {
Expand All @@ -37,13 +46,18 @@ class QueryAutoRefresh extends React.Component {
}
QueryAutoRefresh.propTypes = {
actions: React.PropTypes.object,
queriesLastUpdate: React.PropTypes.integer,
networkOn: React.PropTypes.boolean,
};
QueryAutoRefresh.defaultProps = {
// queries: null,
};

function mapStateToProps() {
return {};
function mapStateToProps(state) {
return {
queriesLastUpdate: state.queriesLastUpdate,
networkOn: state.networkOn,
};
}

function mapDispatchToProps(dispatch) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class QueryTable extends React.Component {
q.duration = fDuration(q.startDttm, q.endDttm);
}
q.started = moment.utc(q.startDttm).format('HH:mm:ss');
const source = q.ctas ? q.executedSql : q.sql;
const source = (q.ctas) ? q.executedSql : q.sql;
q.sql = (
<SqlShrink sql={source} />
);
Expand Down
2 changes: 1 addition & 1 deletion caravel/assets/javascripts/SqlLab/components/ResultSet.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class ResultSet extends React.Component {
>
<i className="fa fa-line-chart m-l-1" /> Visualize
</Button>
<Button bsSize="small">
<Button bsSize="small" href={'/caravel/csv/' + this.props.query.id}>
<i className="fa fa-file-text-o" /> .CSV
</Button>
</ButtonGroup>
Expand Down
101 changes: 71 additions & 30 deletions caravel/assets/javascripts/SqlLab/components/SouthPane.jsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,85 @@
import { Alert, Tab, Tabs } from 'react-bootstrap';
import { Alert, Button, Tab, Tabs } from 'react-bootstrap';
import QueryHistory from './QueryHistory';
import ResultSet from './ResultSet';
import React from 'react';

const SouthPane = function (props) {
let results = <div />;
if (props.latestQuery) {
if (props.latestQuery.state === 'running') {
results = (
<img className="loading" alt="Loading.." src="/static/assets/images/loading.gif" />
);
} else if (props.latestQuery.state === 'failed') {
results = <Alert bsStyle="danger">{props.latestQuery.msg}</Alert>;
} else if (props.latestQuery.state === 'success') {
results = <ResultSet showControls query={props.latestQuery} />;
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import shortid from 'shortid';

class SouthPane extends React.Component {
popSelectStar() {
const qe = {
id: shortid.generate(),
title: this.props.latestQuery.tempTable,
autorun: false,
dbId: this.props.latestQuery.dbId,
sql: `SELECT * FROM ${this.props.latestQuery.tempTable}`,
};
this.props.actions.addQueryEditor(qe);
}
render() {
let results = <div />;
const latestQuery = this.props.latestQuery;
if (latestQuery) {
if (['running', 'pending'].includes(latestQuery.state)) {
results = (
<img className="loading" alt="Loading.." src="/static/assets/images/loading.gif" />
);
} else if (latestQuery.state === 'failed') {
results = <Alert bsStyle="danger">{latestQuery.errorMessage}</Alert>;
} else if (latestQuery.state === 'success' && latestQuery.ctas) {
results = (
<div>
<Alert bsStyle="info">
Table [<strong>{latestQuery.tempTable}</strong>] was created
</Alert>
<p>
<Button
bsSize="small"
className="m-r-5"
onClick={this.popSelectStar.bind(this)}
>
Query in a new tab
</Button>
<Button bsSize="small">Visualize</Button>
</p>
</div>);
} else if (latestQuery.state === 'success') {
results = <ResultSet showControls search query={latestQuery} />;
}
} else {
results = <Alert bsStyle="info">Run a query to display results here</Alert>;
}
} else {
results = <Alert bsStyle="info">Run a query to display results here</Alert>;
return (
<div className="SouthPane">
<Tabs bsStyle="tabs">
<Tab title="Results" eventKey={1}>
<div style={{ overflow: 'auto' }}>
{results}
</div>
</Tab>
<Tab title="Query History" eventKey={2}>
<QueryHistory />
</Tab>
</Tabs>
</div>
);
}
return (
<div className="SouthPane">
<Tabs bsStyle="tabs">
<Tab title="Results" eventKey={1}>
<div style={{ overflow: 'auto' }}>
{results}
</div>
</Tab>
<Tab title="Query History" eventKey={2}>
<QueryHistory />
</Tab>
</Tabs>
</div>
);
};
}

SouthPane.propTypes = {
latestQuery: React.PropTypes.object,
actions: React.PropTypes.object,
};

SouthPane.defaultProps = {
};

export default SouthPane;
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}
export default connect(null, mapDispatchToProps)(SouthPane);
73 changes: 41 additions & 32 deletions caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,19 @@ class SqlEditor extends React.Component {
that.props.actions.querySuccess(query, results);
}
},
error(err) {
error(err, textStatus, errorThrown) {
let msg;
try {
msg = err.responseJSON.error;
} catch (e) {
msg = (err.responseText) ? err.responseText : e;
if (err.responseText !== undefined) {
msg = err.responseText;
}
}
if (typeof(msg) !== 'string') {
msg = JSON.stringify(msg);
if (textStatus === 'error' && errorThrown === '') {
msg = 'Could not connect to server';
} else if (msg === null) {
msg = `[${textStatus}] ${errorThrown}`;
}
that.props.actions.queryFailed(query, msg);
},
Expand Down Expand Up @@ -135,6 +139,15 @@ class SqlEditor extends React.Component {
ctasChanged(event) {
this.setState({ ctas: event.target.value });
}

sqlEditorHeight() {
// quick hack to make the white bg of the tab stretch full height.
const tabNavHeight = 40;
const navBarHeight = 56;
const mysteryVerticalHeight = 50;
return window.innerHeight - tabNavHeight - navBarHeight - mysteryVerticalHeight;
}

render() {
let runButtons = (
<ButtonGroup bsSize="small" className="inline m-r-5 pull-left">
Expand Down Expand Up @@ -248,34 +261,30 @@ class SqlEditor extends React.Component {
</div>
);
return (
<div className="SqlEditor">
<div>
<div>
<Row>
<Col md={3}>
<SqlEditorLeft queryEditor={this.props.queryEditor} />
</Col>
<Col md={9}>
<AceEditor
mode="sql"
name={this.props.queryEditor.id}
theme="github"
minLines={7}
maxLines={30}
onChange={this.textChange.bind(this)}
height="200px"
width="100%"
editorProps={{ $blockScrolling: true }}
enableBasicAutocompletion
value={this.props.queryEditor.sql}
/>
{editorBottomBar}
<br />
<SouthPane latestQuery={this.props.latestQuery} sqlEditor={this} />
</Col>
</Row>
</div>
</div>
<div className="SqlEditor" style={{ minHeight: this.sqlEditorHeight() }}>
<Row>
<Col md={3}>
<SqlEditorLeft queryEditor={this.props.queryEditor} />
</Col>
<Col md={9}>
<AceEditor
mode="sql"
name={this.props.queryEditor.id}
theme="github"
minLines={7}
maxLines={30}
onChange={this.textChange.bind(this)}
height="200px"
width="100%"
editorProps={{ $blockScrolling: true }}
enableBasicAutocompletion
value={this.props.queryEditor.sql}
/>
{editorBottomBar}
<br />
<SouthPane latestQuery={this.props.latestQuery} sqlEditor={this} />
</Col>
</Row>
</div>
);
}
Expand Down
Loading

0 comments on commit 1971bf6

Please sign in to comment.