From c255e89219c209b5a5371134fbb9a9b90036ded4 Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Wed, 19 Oct 2016 09:17:08 -0700 Subject: [PATCH] [sqllab] show partition metadata for Presto (#1342) --- caravel/assets/javascripts/SqlLab/actions.js | 6 +- .../SqlLab/components/SouthPane.jsx | 2 +- .../SqlLab/components/SqlEditor.jsx | 4 +- ...torTopToolbar.jsx => SqlEditorLeftBar.jsx} | 26 ++- .../SqlLab/components/TabbedSqlEditors.jsx | 1 - .../SqlLab/components/TableElement.jsx | 120 +++++++++---- caravel/assets/javascripts/SqlLab/main.css | 6 + caravel/assets/javascripts/SqlLab/reducers.js | 19 ++- .../components/CopyToClipboard.jsx | 6 +- .../javascripts/components/ModalTrigger.jsx | 2 +- .../javascripts/sqllab/TableElement_spec.jsx | 158 ++++++++++++++++++ caravel/db_engine_specs.py | 49 +++++- caravel/views.py | 15 ++ tests/base_tests.py | 13 ++ tests/core_tests.py | 50 +++--- 15 files changed, 390 insertions(+), 87 deletions(-) rename caravel/assets/javascripts/SqlLab/components/{SqlEditorTopToolbar.jsx => SqlEditorLeftBar.jsx} (89%) create mode 100644 caravel/assets/spec/javascripts/sqllab/TableElement_spec.jsx diff --git a/caravel/assets/javascripts/SqlLab/actions.js b/caravel/assets/javascripts/SqlLab/actions.js index dab04aace6bad..b03a30d259623 100644 --- a/caravel/assets/javascripts/SqlLab/actions.js +++ b/caravel/assets/javascripts/SqlLab/actions.js @@ -2,7 +2,7 @@ export const RESET_STATE = 'RESET_STATE'; export const ADD_QUERY_EDITOR = 'ADD_QUERY_EDITOR'; export const CLONE_QUERY_TO_NEW_TAB = 'CLONE_QUERY_TO_NEW_TAB'; export const REMOVE_QUERY_EDITOR = 'REMOVE_QUERY_EDITOR'; -export const ADD_TABLE = 'ADD_TABLE'; +export const MERGE_TABLE = 'MERGE_TABLE'; export const REMOVE_TABLE = 'REMOVE_TABLE'; export const START_QUERY = 'START_QUERY'; export const STOP_QUERY = 'STOP_QUERY'; @@ -86,8 +86,8 @@ export function queryEditorSetSql(queryEditor, sql) { return { type: QUERY_EDITOR_SET_SQL, queryEditor, sql }; } -export function addTable(table) { - return { type: ADD_TABLE, table }; +export function mergeTable(table) { + return { type: MERGE_TABLE, table }; } export function expandTable(table) { diff --git a/caravel/assets/javascripts/SqlLab/components/SouthPane.jsx b/caravel/assets/javascripts/SqlLab/components/SouthPane.jsx index 72cf9e6572cc5..9723899d37a41 100644 --- a/caravel/assets/javascripts/SqlLab/components/SouthPane.jsx +++ b/caravel/assets/javascripts/SqlLab/components/SouthPane.jsx @@ -55,7 +55,7 @@ class SouthPane extends React.Component { return (
- +
{results}
diff --git a/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx b/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx index 70c81318cf1e4..4f69b9ce253ed 100644 --- a/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx +++ b/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx @@ -28,7 +28,7 @@ import shortid from 'shortid'; import SouthPane from './SouthPane'; import Timer from './Timer'; -import SqlEditorTopToolbar from './SqlEditorTopToolbar'; +import SqlEditorLeftBar from './SqlEditorLeftBar'; class SqlEditor extends React.Component { constructor(props) { @@ -252,7 +252,7 @@ class SqlEditor extends React.Component {
- + { - this.props.actions.addTable({ - id: shortid.generate(), + this.props.actions.mergeTable({ dbId: this.props.queryEditor.dbId, queryEditorId: this.props.queryEditor.id, name: data.name, @@ -102,6 +100,18 @@ class SqlEditorTopToolbar extends React.Component { }); this.setState({ tableLoading: false }); }); + + url = `/caravel/extra_table_metadata/${qe.dbId}/${tableName}/${qe.schema}/`; + $.get(url, (data) => { + const table = { + dbId: this.props.queryEditor.dbId, + queryEditorId: this.props.queryEditor.id, + schema: qe.schema, + name: tableName, + }; + Object.assign(table, data); + this.props.actions.mergeTable(table); + }); } render() { let networkAlert = null; @@ -158,14 +168,14 @@ class SqlEditorTopToolbar extends React.Component { } } -SqlEditorTopToolbar.propTypes = { +SqlEditorLeftBar.propTypes = { queryEditor: React.PropTypes.object, tables: React.PropTypes.array, actions: React.PropTypes.object, networkOn: React.PropTypes.bool, }; -SqlEditorTopToolbar.defaultProps = { +SqlEditorLeftBar.defaultProps = { tables: [], }; @@ -182,4 +192,4 @@ function mapDispatchToProps(dispatch) { }; } -export default connect(mapStateToProps, mapDispatchToProps)(SqlEditorTopToolbar); +export default connect(mapStateToProps, mapDispatchToProps)(SqlEditorLeftBar); diff --git a/caravel/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx b/caravel/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx index 2ae4ac550b5ad..e8e0bffe44fe0 100644 --- a/caravel/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx +++ b/caravel/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx @@ -116,7 +116,6 @@ class TabbedSqlEditors extends React.Component { key={qe.id} title={tabTitle} eventKey={qe.id} - id={`a11y-query-editor-${qe.id}`} >
diff --git a/caravel/assets/javascripts/SqlLab/components/TableElement.jsx b/caravel/assets/javascripts/SqlLab/components/TableElement.jsx index abc84a8fcc34b..70c2a73cc876d 100644 --- a/caravel/assets/javascripts/SqlLab/components/TableElement.jsx +++ b/caravel/assets/javascripts/SqlLab/components/TableElement.jsx @@ -1,11 +1,23 @@ import React from 'react'; -import { ButtonGroup } from 'react-bootstrap'; +import { ButtonGroup, Well } from 'react-bootstrap'; import Link from './Link'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import * as Actions from '../actions'; import shortid from 'shortid'; -import ModalTrigger from '../../components/ModalTrigger.jsx'; +import ModalTrigger from '../../components/ModalTrigger'; +import CopyToClipboard from '../../components/CopyToClipboard'; + +const propTypes = { + table: React.PropTypes.object, + queryEditor: React.PropTypes.object, + actions: React.PropTypes.object, +}; + +const defaultProps = { + table: null, + actions: {}, +}; class TableElement extends React.Component { setSelectStar() { @@ -40,40 +52,85 @@ class TableElement extends React.Component { collapseTable(e) { e.preventDefault(); - this.props.actions.collapseTable.bind(this, this.props.table)(); + this.props.actions.collapseTable(this.props.table); } expandTable(e) { e.preventDefault(); - this.props.actions.expandTable.bind(this, this.props.table)(); + this.props.actions.expandTable(this.props.table); + } + + removeTable() { + this.props.actions.removeTable(this.props.table); } render() { + const table = this.props.table; let metadata = null; let buttonToggle; - if (this.props.table.expanded) { + + let header; + if (table.partitions) { + let partitionQuery; + let partitionClipBoard; + if (table.partitions.partitionQuery) { + partitionQuery = table.partitions.partitionQuery; + const tt = 'Copy partition query to clipboard'; + partitionClipBoard = ( + } + /> + ); + } + let latest = []; + for (const k in table.partitions.latest) { + latest.push(`${k}=${table.partitions.latest[k]}`); + } + latest = latest.join('/'); + header = ( + +
+ + latest partition: {latest} + {partitionClipBoard} +
+
+ ); + } + if (table.expanded) { buttonToggle = ( { this.collapseTable(e); }} > - {this.props.table.name} + {table.name} ); metadata = (
- {this.props.table.columns.map((col) => ( -
-
- {col.name} -
-
- {col.type} -
-
- ))} -
+ {header} +
+ {table.columns.map((col) => { + let name = col.name; + if (col.indexed) { + name = {col.name}; + } + return ( +
+
+ {name} +
+
+ {col.type} +
+
); + })} +
+
); } else { @@ -82,34 +139,34 @@ class TableElement extends React.Component { href="#" onClick={(e) => { this.expandTable(e); }} > - {this.props.table.name} + {table.name} ); } let keyLink; - if (this.props.table.indexes && this.props.table.indexes.length > 0) { + if (table.indexes && table.indexes.length > 0) { keyLink = ( - Keys for table {this.props.table.name} + Keys for table {table.name}
} modalBody={ -
{JSON.stringify(this.props.table.indexes, null, 4)}
+
{JSON.stringify(table.indexes, null, 4)}
} triggerNode={ } /> ); } return ( -
+
{buttonToggle} @@ -131,26 +188,22 @@ class TableElement extends React.Component { />
- {metadata} +
+ {metadata} +
); } } -TableElement.propTypes = { - table: React.PropTypes.object, - queryEditor: React.PropTypes.object, - actions: React.PropTypes.object, -}; -TableElement.defaultProps = { - table: null, -}; +TableElement.propTypes = propTypes; +TableElement.defaultProps = defaultProps; function mapDispatchToProps(dispatch) { return { @@ -158,3 +211,4 @@ function mapDispatchToProps(dispatch) { }; } export default connect(null, mapDispatchToProps)(TableElement); +export { TableElement }; diff --git a/caravel/assets/javascripts/SqlLab/main.css b/caravel/assets/javascripts/SqlLab/main.css index c42c73283b2de..4ef7876394832 100644 --- a/caravel/assets/javascripts/SqlLab/main.css +++ b/caravel/assets/javascripts/SqlLab/main.css @@ -232,3 +232,9 @@ div.tablePopover:hover { .SouthPane .tab-content { padding-top: 10px; } + +.TableElement .well { + margin-top: 5px; + margin-bottom: 5px; + padding: 5px 10px; +} diff --git a/caravel/assets/javascripts/SqlLab/reducers.js b/caravel/assets/javascripts/SqlLab/reducers.js index ff1dabd9e92a0..4f768c63ac287 100644 --- a/caravel/assets/javascripts/SqlLab/reducers.js +++ b/caravel/assets/javascripts/SqlLab/reducers.js @@ -72,8 +72,23 @@ export const sqlLabReducer = function (state, action) { [actions.RESET_STATE]() { return Object.assign({}, initialState); }, - [actions.ADD_TABLE]() { - return addToArr(state, 'tables', action.table); + [actions.MERGE_TABLE]() { + const at = Object.assign({}, action.table); + let existingTable; + state.tables.forEach((t) => { + if ( + t.dbId === at.dbId && + t.queryEditorId === at.queryEditorId && + t.schema === at.schema && + t.name === at.name) { + existingTable = t; + } + }); + if (existingTable) { + return alterInArr(state, 'tables', existingTable, at); + } + at.id = shortid.generate(); + return addToArr(state, 'tables', at); }, [actions.EXPAND_TABLE]() { return alterInArr(state, 'tables', action.table, { expanded: true }); diff --git a/caravel/assets/javascripts/components/CopyToClipboard.jsx b/caravel/assets/javascripts/components/CopyToClipboard.jsx index 5330f8de7a1f2..5b149fe78f919 100644 --- a/caravel/assets/javascripts/components/CopyToClipboard.jsx +++ b/caravel/assets/javascripts/components/CopyToClipboard.jsx @@ -76,9 +76,11 @@ export default class CopyToClipboard extends React.Component { return ( {this.props.shouldShowText && - {this.props.text} + + {this.props.text} +      + } -