Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ dist
caravel.egg-info/
app.db
*.bak
.idea
*.sqllite

# Node.js, webpack artifacts
*.entry.js
Expand Down
1 change: 1 addition & 0 deletions caravel/assets/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
"prefer-arrow-callback": 0,
"func-names": 0,
"react/jsx-no-bind": 0,
"no-confusing-arrow": 0,
}
}
19 changes: 19 additions & 0 deletions caravel/assets/javascripts/SqlLab/TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

# TODO
* Figure out how to organize the left panel, integrate Search
* collapse sql beyond 10 lines
* Security per-database (dropdown)
* Get a to work

## Cosmetic
* Result set font is too big
* lmiit/timer/buttons wrap
* table label is transparent
* SqlEditor buttons
* use react-bootstrap-prompt for query title input
* Make tabs look great

# PROJECT
* Write Runbook
* Confirm backups
* merge chef branch
116 changes: 116 additions & 0 deletions caravel/assets/javascripts/SqlLab/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
export const RESET_STATE = 'RESET_STATE';
export const ADD_QUERY_EDITOR = 'ADD_QUERY_EDITOR';
export const REMOVE_QUERY_EDITOR = 'REMOVE_QUERY_EDITOR';
export const ADD_TABLE = 'ADD_TABLE';
export const REMOVE_TABLE = 'REMOVE_TABLE';
export const START_QUERY = 'START_QUERY';
export const STOP_QUERY = 'STOP_QUERY';
export const END_QUERY = 'END_QUERY';
export const REMOVE_QUERY = 'REMOVE_QUERY';
export const EXPAND_TABLE = 'EXPAND_TABLE';
export const COLLAPSE_TABLE = 'COLLAPSE_TABLE';
export const QUERY_SUCCESS = 'QUERY_SUCCESS';
export const QUERY_FAILED = 'QUERY_FAILED';
export const QUERY_EDITOR_SETDB = 'QUERY_EDITOR_SETDB';
export const QUERY_EDITOR_SET_SCHEMA = 'QUERY_EDITOR_SET_SCHEMA';
export const QUERY_EDITOR_SET_TITLE = 'QUERY_EDITOR_SET_TITLE';
export const QUERY_EDITOR_SET_AUTORUN = 'QUERY_EDITOR_SET_AUTORUN';
export const QUERY_EDITOR_SET_SQL = 'QUERY_EDITOR_SET_SQL';
export const SET_WORKSPACE_DB = 'SET_WORKSPACE_DB';
export const ADD_WORKSPACE_QUERY = 'ADD_WORKSPACE_QUERY';
export const REMOVE_WORKSPACE_QUERY = 'REMOVE_WORKSPACE_QUERY';
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 function resetState() {
return { type: RESET_STATE };
}

export function addQueryEditor(queryEditor) {
return { type: ADD_QUERY_EDITOR, queryEditor };
}

export function addAlert(alert) {
return { type: ADD_ALERT, alert };
}

export function removeAlert(alert) {
return { type: REMOVE_ALERT, alert };
}

export function setActiveQueryEditor(queryEditor) {
return { type: SET_ACTIVE_QUERY_EDITOR, queryEditor };
}

export function removeQueryEditor(queryEditor) {
return { type: REMOVE_QUERY_EDITOR, queryEditor };
}

export function removeQuery(query) {
return { type: REMOVE_QUERY, query };
}

export function queryEditorSetDb(queryEditor, dbId) {
return { type: QUERY_EDITOR_SETDB, queryEditor, dbId };
}

export function queryEditorSetSchema(queryEditor, schema) {
return { type: QUERY_EDITOR_SET_SCHEMA, queryEditor, schema };
}

export function queryEditorSetAutorun(queryEditor, autorun) {
return { type: QUERY_EDITOR_SET_AUTORUN, queryEditor, autorun };
}

export function queryEditorSetTitle(queryEditor, title) {
return { type: QUERY_EDITOR_SET_TITLE, queryEditor, title };
}

export function queryEditorSetSql(queryEditor, sql) {
return { type: QUERY_EDITOR_SET_SQL, queryEditor, sql };
}

export function addTable(table) {
return { type: ADD_TABLE, table };
}

export function expandTable(table) {
return { type: EXPAND_TABLE, table };
}

export function collapseTable(table) {
return { type: COLLAPSE_TABLE, table };
}

export function removeTable(table) {
return { type: REMOVE_TABLE, table };
}

export function startQuery(query) {
return { type: START_QUERY, query };
}

export function stopQuery(query) {
return { type: STOP_QUERY, query };
}

export function querySuccess(query, results) {
return { type: QUERY_SUCCESS, query, results };
}

export function queryFailed(query, msg) {
return { type: QUERY_FAILED, query, msg };
}

export function addWorkspaceQuery(query) {
return { type: ADD_WORKSPACE_QUERY, query };
}

export function removeWorkspaceQuery(query) {
return { type: REMOVE_WORKSPACE_QUERY, query };
}
export function refreshQueries(alteredQueries) {
return { type: REFRESH_QUERIES, alteredQueries };
}
5 changes: 5 additions & 0 deletions caravel/assets/javascripts/SqlLab/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const STATE_BSSTYLE_MAP = {
failed: 'danger',
running: 'warning',
success: 'success',
};
41 changes: 41 additions & 0 deletions caravel/assets/javascripts/SqlLab/components/Alerts.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import { Alert } from 'react-bootstrap';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';

class Alerts extends React.Component {
removeAlert(alert) {
this.props.actions.removeAlert(alert);
}
render() {
const alerts = this.props.alerts.map((alert) =>
<Alert
bsStyle={alert.bsStyle}
style={{ width: '500px', textAlign: 'midddle', margin: '10px auto' }}
>
{alert.msg}
<i
className="fa fa-close pull-right"
onClick={this.removeAlert.bind(this, alert)}
style={{ cursor: 'pointer' }}
/>
</Alert>
);
return (
<div>{alerts}</div>
);
}
}

Alerts.propTypes = {
alerts: React.PropTypes.array,
actions: React.PropTypes.object,
};

function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}
export default connect(null, mapDispatchToProps)(Alerts);
46 changes: 46 additions & 0 deletions caravel/assets/javascripts/SqlLab/components/ButtonWithTooltip.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react';
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap';

const ButtonWithTooltip = (props) => {
let tooltip = (
<Tooltip id="tooltip">
{props.tooltip}
</Tooltip>
);
return (
<OverlayTrigger
overlay={tooltip}
delayShow={300}
placement={props.placement}
delayHide={150}
>
<Button
onClick={props.onClick}
bsStyle={props.bsStyle}
disabled={props.disabled}
className={props.className}
>
{props.children}
</Button>
</OverlayTrigger>
);
};

ButtonWithTooltip.defaultProps = {
onClick: () => {},
disabled: false,
placement: 'top',
bsStyle: 'default',
};

ButtonWithTooltip.propTypes = {
bsStyle: React.PropTypes.string,
children: React.PropTypes.element,
className: React.PropTypes.string,
disabled: React.PropTypes.bool,
onClick: React.PropTypes.func,
placement: React.PropTypes.string,
tooltip: React.PropTypes.string,
};

export default ButtonWithTooltip;
65 changes: 65 additions & 0 deletions caravel/assets/javascripts/SqlLab/components/LeftPane.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react';
import { Alert, Button, Label } from 'react-bootstrap';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import QueryLink from './QueryLink';

import 'react-select/dist/react-select.css';

const LeftPane = (props) => {
let queryElements;
if (props.workspaceQueries.length > 0) {
queryElements = props.workspaceQueries.map((q) => <QueryLink query={q} />);
} else {
queryElements = (
<Alert bsStyle="info">
Use the save button on the SQL editor to save a query
into this section for future reference.
</Alert>
);
}
return (
<div>
<div className="panel panel-default LeftPane">
<div className="panel-heading">
<div className="panel-title">
Saved Queries
<div className="pull-right">
<Label bsStyle="danger">ALPHA</Label>
</div>
</div>
</div>
<div className="panel-body">
{queryElements}
</div>
</div>
<br /><br />
<Button onClick={props.actions.resetState.bind(this)} bsStyle="danger">
Reset State
</Button>
</div>
);
};

LeftPane.propTypes = {
workspaceQueries: React.PropTypes.array,
actions: React.PropTypes.object,
};

LeftPane.defaultProps = {
workspaceQueries: [],
};

function mapStateToProps(state) {
return {
workspaceQueries: state.workspaceQueries,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}

export default connect(mapStateToProps, mapDispatchToProps)(LeftPane);
54 changes: 54 additions & 0 deletions caravel/assets/javascripts/SqlLab/components/Link.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react';
import { OverlayTrigger, Tooltip } from 'react-bootstrap';


class Link extends React.Component {
render() {
let tooltip = (
<Tooltip id="tooltip">
{this.props.tooltip}
</Tooltip>
);
const link = (
<a
href={this.props.href}
onClick={this.props.onClick}
style={this.props.style}
className={'Link ' + this.props.className}
>
{this.props.children}
</a>
);
if (this.props.tooltip) {
return (
<OverlayTrigger
overlay={tooltip}
placement={this.props.placement}
delayShow={300}
delayHide={150}
>
{link}
</OverlayTrigger>
);
}
return link;
}
}
Link.propTypes = {
children: React.PropTypes.object,
className: React.PropTypes.string,
href: React.PropTypes.string,
onClick: React.PropTypes.func,
placement: React.PropTypes.string,
style: React.PropTypes.object,
tooltip: React.PropTypes.string,
};
Link.defaultProps = {
disabled: false,
href: '#',
tooltip: null,
placement: 'top',
onClick: () => {},
};

export default Link;
Loading