Skip to content

Commit

Permalink
Merge pull request #141 from BornaP/125-objects-sql-definitions
Browse files Browse the repository at this point in the history
#125 objects sql definitions
  • Loading branch information
BornaP committed May 12, 2016
2 parents f0201f5 + 9073f25 commit d79069f
Show file tree
Hide file tree
Showing 10 changed files with 329 additions and 86 deletions.
2 changes: 1 addition & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@
"babel-core": "^5.8.29",
"babel-runtime": "^5.8.29",
"debug": "^2.2.0",
"sqlectron-core": "^3.4.0"
"sqlectron-core": "^3.6.0"
}
}
72 changes: 72 additions & 0 deletions src/renderer/actions/sqlscripts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { getDBConnByName } from './connections';
import { updateQueryIfNeeded } from './queries';


export const GET_SCRIPT_REQUEST = 'GET_SCRIPT_REQUEST';
export const GET_SCRIPT_SUCCESS = 'GET_SCRIPT_SUCCESS';
export const GET_SCRIPT_FAILURE = 'GET_SCRIPT_FAILURE';


export function getSQLScriptIfNeeded(database, item, actionType, objectType) {
return (dispatch, getState) => {
const state = getState();
if (shouldFetchScript(state, database, item, actionType)) {
return dispatch(getSQLScript(database, item, actionType, objectType));
} else if (isScriptAlreadyFetched(state, database, item, actionType)) {
const script = getAlreadyFetchedScript(state, database, item, actionType);
return dispatch(updateQueryIfNeeded(script));
}
};
}

function shouldFetchScript (state, database, item, actionType) {
const scripts = state.sqlscripts;
if (!scripts) return true;
if (scripts.isFetching) return false;
if (!scripts.scriptsByObject[database]) return true;
if (!scripts.scriptsByObject[database][item]) return true;
if (!scripts.scriptsByObject[database][item][actionType]) return true;
return scripts.didInvalidate;
}

function isScriptAlreadyFetched (state, database, item, actionType) {
const scripts = state.sqlscripts;
if (!scripts.scriptsByObject[database]) return false;
if (!scripts.scriptsByObject[database][item]) return false;
if (scripts.scriptsByObject[database][item][actionType]) return true;
return false;
}

function getAlreadyFetchedScript (state, database, item, actionType) {
return state.sqlscripts.scriptsByObject[database][item][actionType];
}


function getSQLScript (database, item, actionType, objectType) {
return async (dispatch) => {
dispatch({ type: GET_SCRIPT_REQUEST, database, item, actionType, objectType });
try {
const dbConn = getDBConnByName(database);
let script;
if (actionType === 'CREATE') {
[script] = objectType === 'Table'
? await dbConn.getTableCreateScript(item)
: (objectType === 'View')
? await dbConn.getViewCreateScript(item)
: await dbConn.getRoutineCreateScript(item, objectType);
} else if (actionType === 'SELECT') {
script = await dbConn.getTableSelectScript(item);
} else if (actionType === 'INSERT') {
script = await dbConn.getTableInsertScript(item);
} else if (actionType === 'UPDATE') {
script = await dbConn.getTableUpdateScript(item);
} else if (actionType === 'DELETE') {
script = await dbConn.getTableDeleteScript(item);
}
dispatch({ type: GET_SCRIPT_SUCCESS, database, item, script, actionType, objectType });
dispatch(updateQueryIfNeeded(script));
} catch (error) {
dispatch({ type: GET_SCRIPT_FAILURE, error });
}
};
}
135 changes: 135 additions & 0 deletions src/renderer/components/database-item.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import React, { Component, PropTypes } from 'react';
import TableSubmenu from './table-submenu.jsx';
import { Menu, MenuItem } from 'remote';


const STYLE = {
item: { wordBreak: 'break-all', cursor: 'default' },
};


export default class DatabaseItem extends Component {
static propTypes = {
database: PropTypes.object.isRequired,
item: PropTypes.object.isRequired,
dbObjectType: PropTypes.string.isRequired,
style: PropTypes.object,
columnsByTable: PropTypes.object,
triggersByTable: PropTypes.object,
onSelectItem: PropTypes.func,
onExecuteDefaultQuery: PropTypes.func,
onGetSQLScript: PropTypes.func,
}

constructor(props, context) {
super(props, context);
this.state = {};
this.contextMenu;
}

// Context menu is built dinamically on click (if it does not exist), because building
// menu onComponentDidMount or onComponentWillMount slows table listing when database
// has a loads of tables, because menu will be created (unnecessarily) for every table shown
onContextMenu(e){
e.preventDefault();
if (!this.contextMenu){
this.buildContextMenu();
}
this.contextMenu.popup(e.clientX, e.clientY);
}

buildContextMenu(){
const {
database,
item,
dbObjectType,
onExecuteDefaultQuery,
onGetSQLScript,
} = this.props;
const actionTypes = ['SELECT', 'INSERT', 'UPDATE', 'DELETE'];

this.contextMenu = new Menu();
if (dbObjectType === 'Table' || dbObjectType === 'View') {
this.contextMenu.append(new MenuItem({
label: 'Execute default query',
click: onExecuteDefaultQuery.bind(this, database, item)
}));
}
this.contextMenu.append(new MenuItem({
label: 'CREATE script',
click: onGetSQLScript.bind(this, database, item, 'CREATE', dbObjectType)
}));
if (dbObjectType === 'Table') {
actionTypes.map((actionType, index) => {
this.contextMenu.append(new MenuItem({
label: `${actionType} script`,
click: onGetSQLScript.bind(this, database, item, actionType, dbObjectType)
}));
});
}
}

toggleTableCollapse() {
this.setState({ tableCollapsed: !this.state.tableCollapsed });
}

renderSubItems(table) {
const { columnsByTable, triggersByTable, database } = this.props;

if (!columnsByTable || !columnsByTable[table]) {
return null;
}

const displayStyle = {};
if (!this.state.tableCollapsed) {
displayStyle.display = 'none';
}

return (
<div style={displayStyle}>
<TableSubmenu
title="Columns"
table={table}
itemsByTable={columnsByTable}
database={database}/>
<TableSubmenu
collapsed
title="Triggers"
table={table}
itemsByTable={triggersByTable}
database={database}/>
</div>
);
}

render() {
const { database, item, style, onSelectItem, dbObjectType } = this.props;
const hasChildElements = !!onSelectItem;
const onSingleClick = hasChildElements
? () => {onSelectItem(database, item); this.toggleTableCollapse();}
: () => {};

const collapseCssClass = this.state.tableCollapsed ? 'down' : 'right';
const collapseIcon = (
<i className={`${collapseCssClass} triangle icon`} style={{float: 'left', margin: '0 0.15em 0 -1em'}}></i>
);
const tableIcon = (
<i className="table icon" style={{float: 'left', margin: '0 0.3em 0 0'}}></i>
);

return (
<div>
<span
style={style}
className="item"
onClick={onSingleClick}
onContextMenu={::this.onContextMenu}>
{ dbObjectType === 'Table' ? collapseIcon : null }
{ dbObjectType === 'Table' ? tableIcon : null }
{item.name}
</span>
{this.renderSubItems(item.name)}
</div>
);
}
}
89 changes: 22 additions & 67 deletions src/renderer/components/database-list-item-metadata.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { Component, PropTypes } from 'react';
import TableSubmenu from './table-submenu.jsx';
import DatabaseItem from './database-item.jsx';


const STYLE = {
header: { fontSize: '0.85em', color: '#636363' },
Expand All @@ -16,8 +17,9 @@ export default class DbMetadataList extends Component {
triggersByTable: PropTypes.object,
collapsed: PropTypes.bool,
database: PropTypes.object.isRequired,
onDoubleClickItem: PropTypes.func,
onExecuteDefaultQuery: PropTypes.func,
onSelectItem: PropTypes.func,
onGetSQLScript: PropTypes.func,
}

constructor(props, context) {
Expand All @@ -35,10 +37,6 @@ export default class DbMetadataList extends Component {
this.setState({ collapsed: !this.state.collapsed });
}

toggleTableCollapse(tableName) {
this.setState({ [tableName]: !this.state[tableName] });
}

renderHeader() {
const title = this.state.collapsed ? 'Expand' : 'Collapse';
const cssClass = this.state.collapsed ? 'right' : 'down';
Expand All @@ -56,7 +54,13 @@ export default class DbMetadataList extends Component {
}

renderItems() {
const { onDoubleClickItem, onSelectItem, items, database } = this.props;
const {
onExecuteDefaultQuery,
onSelectItem,
items,
database,
onGetSQLScript,
} = this.props;

if (!items || this.state.collapsed) {
return null;
Expand All @@ -69,79 +73,30 @@ export default class DbMetadataList extends Component {
}

return items.map(item => {
const isClickable = !!onDoubleClickItem;
const hasChildElements = !!onSelectItem;
const title = isClickable ? 'Click twice to select default query' : '';
const onDoubleClick = isClickable
? onDoubleClickItem.bind(this, database, item)
: () => {};
const onSingleClick = hasChildElements
? () => {onSelectItem(database, item); this.toggleTableCollapse(item.name);}
: () => {};

const cssStyle = {...STYLE.item};
if (this.state.collapsed) {
cssStyle.display = 'none';
}
cssStyle.cursor = hasChildElements ? 'pointer' : 'default';
const collapseCssClass = this.state[item.name] ? 'down' : 'right';
const collapseIcon = (
<i className={`${collapseCssClass} triangle icon`} style={{float: 'left', margin: '0 0.15em 0 -1em'}}></i>
);
const tableIcon = (
<i className="table icon" style={{float: 'left', margin: '0 0.3em 0 0'}}></i>
);

/*
TODO: Move standard table query to context menu
*/
return (
<div key={item.name}>
<span
title={title}
style={cssStyle}
className="item"
onDoubleClick={onDoubleClick}
onClick={onSingleClick}>
{ this.props.title === 'Tables' ? collapseIcon : null }
{ this.props.title === 'Tables' ? tableIcon : null }
{item.name}
</span>
{this.renderSubItems(item.name)}
</div>
<DatabaseItem
key={item.name}
database={database}
item={item}
dbObjectType={this.props.title.slice(0, -1)}
style={cssStyle}
columnsByTable={this.props.columnsByTable}
triggersByTable={this.props.triggersByTable}
onSelectItem={onSelectItem}
onExecuteDefaultQuery={onExecuteDefaultQuery}
onGetSQLScript={onGetSQLScript} />
);
});
}

renderSubItems(table) {
const { columnsByTable, triggersByTable, database } = this.props;

if (!columnsByTable || !columnsByTable[table]) {
return null;
}

const displayStyle = {};
if (!this.state[table]) {
displayStyle.display = 'none';
}

return (
<div style={displayStyle}>
<TableSubmenu
title="Columns"
table={table}
itemsByTable={columnsByTable}
database={database}/>
<TableSubmenu
collapsed
title="Triggers"
table={table}
itemsByTable={triggersByTable}
database={database}/>
</div>
);
}

render() {
return (
<div className="item">
Expand Down
Loading

0 comments on commit d79069f

Please sign in to comment.