From 8646221b419f397da712729176dfaf37ec41211a Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sat, 25 Jan 2020 13:50:48 +0100 Subject: [PATCH] TableDataTab --- README.md | 1 + api/package.json | 3 +- api/src/controllers/databaseConnections.js | 17 +++ api/src/controllers/tables.js | 11 ++ api/src/index.js | 2 + api/src/proc/databaseConnectionProcess.js | 7 ++ api/src/types.ts | 5 +- api/yarn.lock | 5 + web/src/Screen.js | 5 +- web/src/TabContent.js | 18 +++ web/src/TabsPanel.js | 18 +-- web/src/appobj/tableAppObject.js | 14 ++- web/src/icons.js | 128 +++++++++------------ web/src/tabs/TableDataTab.js | 5 + web/src/tabs/index.js | 5 + web/src/widgets/DatabaseWidget.js | 2 +- 16 files changed, 159 insertions(+), 87 deletions(-) create mode 100644 api/src/controllers/tables.js create mode 100644 web/src/TabContent.js create mode 100644 web/src/tabs/TableDataTab.js create mode 100644 web/src/tabs/index.js diff --git a/README.md b/README.md index 497a54a41..8fafb2423 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,4 @@ Open http://localhost:5000 in your browser [![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=NJLMGV9DXYFCE) + diff --git a/api/package.json b/api/package.json index 29bbd9767..f8a7bde05 100644 --- a/api/package.json +++ b/api/package.json @@ -15,7 +15,8 @@ "mysql": "^2.17.1", "nedb-promises": "^4.0.1", "pg": "^7.17.0", - "socket.io": "^2.3.0" + "socket.io": "^2.3.0", + "uuid": "^3.4.0" }, "scripts": { "start": "nodemon src/index.js", diff --git a/api/src/controllers/databaseConnections.js b/api/src/controllers/databaseConnections.js index 51eeb1f45..6be6b2d07 100644 --- a/api/src/controllers/databaseConnections.js +++ b/api/src/controllers/databaseConnections.js @@ -1,4 +1,5 @@ const _ = require('lodash'); +const uuidv1 = require('uuid/v1'); const connections = require('./connections'); const socket = require('../utility/socket'); const { fork } = require('child_process'); @@ -7,6 +8,7 @@ const DatabaseAnalyser = require('../engines/default/DatabaseAnalyser'); module.exports = { /** @type {import('../types').OpenedDatabaseConnection[]} */ opened: [], + requests: {}, handle_structure(id, database, { structure }) { const existing = this.opened.find(x => x.id == id && x.database == database); @@ -17,6 +19,11 @@ module.exports = { handle_error(id, { error }) { console.log(error); }, + handle_response(id, database, { msgid, ...response }) { + const [resolve, reject] = this.requests[msgid]; + resolve(response); + delete this.requests[msgid]; + }, async ensureOpened(id, database) { const existing = this.opened.find(x => x.id == id && x.database == database); @@ -39,6 +46,16 @@ module.exports = { return newOpened; }, + /** @param {import('../types').OpenedDatabaseConnection} conn */ + async sendRequest(conn, message) { + const msgid = uuidv1(); + const promise = new Promise((resolve, reject) => { + this.requests[msgid] = [resolve, reject]; + conn.subprocess.send({ msgid, ...message }); + }); + return promise; + }, + listObjects_meta: 'get', async listObjects({ id, database }) { const opened = await this.ensureOpened(id, database); diff --git a/api/src/controllers/tables.js b/api/src/controllers/tables.js new file mode 100644 index 000000000..729c12827 --- /dev/null +++ b/api/src/controllers/tables.js @@ -0,0 +1,11 @@ +const _ = require('lodash'); +const databaseConnections = require('./databaseConnections'); + +module.exports = { + tableData_meta: 'get', + async tableData({ id, database, schemaName, pureName }) { + const opened = await databaseConnections.ensureOpened(id, database); + const res = opened.sendRequest({ msgtype: 'tableData', schemaName, pureName }); + return res; + }, +}; diff --git a/api/src/index.js b/api/src/index.js index 213dc5237..dd1d5c1a2 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -8,6 +8,7 @@ const useController = require('./utility/useController'); const connections = require('./controllers/connections'); const serverConnections = require('./controllers/serverConnections'); const databaseConnections = require('./controllers/databaseConnections'); +const tables = require('./controllers/tables'); const socket = require('./utility/socket'); const app = express(); @@ -25,5 +26,6 @@ app.get('/', (req, res) => { useController(app, '/connections', connections); useController(app, '/server-connections', serverConnections); useController(app, '/database-connections', databaseConnections); +useController(app, '/tables', tables); server.listen(3000); diff --git a/api/src/proc/databaseConnectionProcess.js b/api/src/proc/databaseConnectionProcess.js index 0f28e47cb..60d42bf1e 100644 --- a/api/src/proc/databaseConnectionProcess.js +++ b/api/src/proc/databaseConnectionProcess.js @@ -18,8 +18,15 @@ async function handleConnect(connection) { setInterval(handleFullRefresh, 30 * 1000); } +async function handleTableData({ msgid, schemaName, pureName }) { + const driver = engines(storedConnection); + const res = await driver.query(systemConnection, `SELECT TOP(100) FROM ${pureName}`); + process.send({ msgtype: 'response', msgid, rows: res }); +} + const messageHandlers = { connect: handleConnect, + tableData: handleTableData, }; async function handleMessage({ msgtype, ...other }) { diff --git a/api/src/types.ts b/api/src/types.ts index 02af3e82c..12b24a932 100644 --- a/api/src/types.ts +++ b/api/src/types.ts @@ -1,3 +1,5 @@ +import { ChildProcess } from 'child_process'; + export interface EngineDriver { connect({ server, port, user, password }); query(pool, sql: string): Promise; @@ -23,4 +25,5 @@ export interface OpenedDatabaseConnection { id: string; database: string; structure: DatabaseInfo; -} \ No newline at end of file + subprocess: ChildProcess; +} diff --git a/api/yarn.lock b/api/yarn.lock index 514ce0b24..5ea6eba80 100644 --- a/api/yarn.lock +++ b/api/yarn.lock @@ -2655,6 +2655,11 @@ uuid@^3.1.0, uuid@^3.2.1, uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== +uuid@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + v8-compile-cache@^2.0.3: version "2.1.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" diff --git a/web/src/Screen.js b/web/src/Screen.js index 42ac95e06..fac24f832 100644 --- a/web/src/Screen.js +++ b/web/src/Screen.js @@ -4,6 +4,7 @@ import React from 'react'; import theme from './theme'; import styled from 'styled-components'; import TabsPanel from './TabsPanel'; +import TabContent from './TabContent'; import WidgetIconPanel from './widgets/WidgetIconPanel'; import { useCurrentWidget } from './utility/globalState'; import WidgetContainer from './widgets/WidgetContainer'; @@ -55,7 +56,7 @@ const StausBar = styled.div` background-color: ${theme.statusBar.background}; `; -export default function Screen({ children = undefined }) { +export default function Screen() { const currentWidget = useCurrentWidget(); const leftPanelWidth = currentWidget ? theme.leftPanel.width : 0; return ( @@ -71,7 +72,7 @@ export default function Screen({ children = undefined }) { - {children} + ); diff --git a/web/src/TabContent.js b/web/src/TabContent.js new file mode 100644 index 000000000..8d1b48d3c --- /dev/null +++ b/web/src/TabContent.js @@ -0,0 +1,18 @@ +import React from 'react'; +import styled from 'styled-components'; +import theme from './theme'; +import tabs from './tabs'; + +import { useOpenedTabs } from './utility/globalState'; + +export default function TabContent() { + const files = useOpenedTabs(); + + const selectedTab = files.find(x => x.selected); + if (!selectedTab) return null; + + const TabComponent = tabs[selectedTab.tabComponent]; + if (TabComponent) return ; + + return null; +} diff --git a/web/src/TabsPanel.js b/web/src/TabsPanel.js index 63c51b7df..9f1c7debc 100644 --- a/web/src/TabsPanel.js +++ b/web/src/TabsPanel.js @@ -2,8 +2,8 @@ import React from 'react'; import styled from 'styled-components'; import theme from './theme'; -import { TableIcon } from './icons'; import { useOpenedTabs, useSetOpenedTabs } from './utility/globalState'; +import { getIconImage } from './icons'; // const files = [ // { name: 'app.js' }, @@ -31,7 +31,7 @@ const FileNameWrapper = styled.span` `; export default function TabsPanel() { - const files = useOpenedTabs(); + const tabs = useOpenedTabs(); const setOpenedTabs = useSetOpenedTabs(); const handleTabClick = id => { @@ -49,15 +49,15 @@ export default function TabsPanel() { }; return ( <> - {files.map(file => ( + {tabs.map(tab => ( handleTabClick(file.id)} - onMouseUp={e => handleMouseUp(e, file.id)} + {...tab} + key={tab.id} + onClick={() => handleTabClick(tab.id)} + onMouseUp={e => handleMouseUp(e, tab.id)} > - - {file.name} + {getIconImage(tab.icon)} + {tab.title} ))} diff --git a/web/src/appobj/tableAppObject.js b/web/src/appobj/tableAppObject.js index f584e66ab..182135d07 100644 --- a/web/src/appobj/tableAppObject.js +++ b/web/src/appobj/tableAppObject.js @@ -27,7 +27,19 @@ export default function tableAppObject({ pureName, schemaName }, { setOpenedTabs const Icon = TableIcon; const onClick = ({ schemaName, pureName }) => { const id = uuidv1(); - setOpenedTabs(files => [...files, { id, name: pureName }]); + setOpenedTabs(files => [ + ...files, + { + id, + title: pureName, + icon: 'table2.svg', + tabComponent: 'TableDataTab', + props: { + schemaName, + pureName, + }, + }, + ]); }; return { title, key, Icon, Menu, onClick }; diff --git a/web/src/icons.js b/web/src/icons.js index a0ec71c87..6b4fd7840 100644 --- a/web/src/icons.js +++ b/web/src/icons.js @@ -1,113 +1,97 @@ -import React from "react"; -import _ from "lodash"; +import React from 'react'; +import _ from 'lodash'; -function getIconImage(src, { size = 16, style = {}, className, title }) { - if (src.endsWith(".svg")) { - src = "/icons/" + src; +export function getIconImage(src, props) { + const { size = 16, style = {}, className, title } = props || {}; + if (!src) return null; + if (src.endsWith('.svg')) { + src = '/icons/' + src; } // if (props.alignToLine) { // style["position"] = "relative"; // style["top"] = "-2px"; // style["marginRight"] = "4px"; // } - return ( - - ); + return ; } export function getFontIcon(fontIconSpec, props = {}) { let iconClass = fontIconSpec; if (!iconClass) return null; - var parts = iconClass.split(" "); + var parts = iconClass.split(' '); var name = parts[0]; parts = parts.slice(1); - var className = props.className || ""; + var className = props.className || ''; // if (_.startsWith(name, 'bs-')) className += ` glyphicon glyphicon-${name.substr(3)}`; - if (_.startsWith(name, "fa-")) className += ` fas fa-${name.substr(3)}`; + if (_.startsWith(name, 'fa-')) className += ` fas fa-${name.substr(3)}`; - if (_.includes(parts, "spin")) className += " fa-spin"; + if (_.includes(parts, 'spin')) className += ' fa-spin'; var style = props.style || {}; var last = parts[parts.length - 1]; - if (last && last != "spin") { - style["color"] = last; + if (last && last != 'spin') { + style['color'] = last; } return ; } -export const TableIcon = props => getIconImage("table2.svg", props); -export const ViewIcon = props => getIconImage("view2.svg", props); -export const DatabaseIcon = props => getIconImage("database.svg", props); -export const ServerIcon = props => getIconImage("server.svg", props); +export const TableIcon = props => getIconImage('table2.svg', props); +export const ViewIcon = props => getIconImage('view2.svg', props); +export const DatabaseIcon = props => getIconImage('database.svg', props); +export const ServerIcon = props => getIconImage('server.svg', props); -export const MicrosoftIcon = props => getIconImage("microsoft.svg", props); -export const MySqlIcon = props => getIconImage("mysql.svg", props); -export const PostgreSqlIcon = props => getIconImage("postgresql.svg", props); -export const SqliteIcon = props => getIconImage("sqlite.svg", props); +export const MicrosoftIcon = props => getIconImage('microsoft.svg', props); +export const MySqlIcon = props => getIconImage('mysql.svg', props); +export const PostgreSqlIcon = props => getIconImage('postgresql.svg', props); +export const SqliteIcon = props => getIconImage('sqlite.svg', props); -export const ProcedureIcon = props => getIconImage("procedure2.svg", props); -export const FunctionIcon = props => getIconImage("function.svg", props); -export const TriggerIcon = props => getIconImage("trigger.svg", props); +export const ProcedureIcon = props => getIconImage('procedure2.svg', props); +export const FunctionIcon = props => getIconImage('function.svg', props); +export const TriggerIcon = props => getIconImage('trigger.svg', props); -export const HomeIcon = props => getIconImage("home.svg", props); -export const PrimaryKeyIcon = props => getIconImage("primarykey.svg", props); -export const ForeignKeyIcon = props => getIconImage("foreignkey.svg", props); -export const ComplexKeyIcon = props => getIconImage("complexkey.svg", props); -export const VariableIcon = props => getIconImage("variable.svg", props); -export const UniqueIcon = props => getIconImage("unique.svg", props); -export const IndexIcon = props => getIconImage("index.svg", props); +export const HomeIcon = props => getIconImage('home.svg', props); +export const PrimaryKeyIcon = props => getIconImage('primarykey.svg', props); +export const ForeignKeyIcon = props => getIconImage('foreignkey.svg', props); +export const ComplexKeyIcon = props => getIconImage('complexkey.svg', props); +export const VariableIcon = props => getIconImage('variable.svg', props); +export const UniqueIcon = props => getIconImage('unique.svg', props); +export const IndexIcon = props => getIconImage('index.svg', props); -export const StartIcon = props => getIconImage("start.svg", props); -export const DownCircleIcon = props => getIconImage("down_circle.svg", props); +export const StartIcon = props => getIconImage('start.svg', props); +export const DownCircleIcon = props => getIconImage('down_circle.svg', props); -export const ColumnIcon = props => getIconImage("column.svg", props); +export const ColumnIcon = props => getIconImage('column.svg', props); -export const SqlIcon = props => getIconImage("sql.svg", props); -export const ExcelIcon = props => getIconImage("excel.svg", props); -export const DiagramIcon = props => getIconImage("diagram.svg", props); -export const QueryDesignIcon = props => getIconImage("querydesign.svg", props); -export const LocalDbIcon = props => getIconImage("localdb.svg", props); -export const CsvIcon = props => getIconImage("csv.svg", props); -export const ChangeSetIcon = props => getIconImage("changeset.svg", props); -export const BinaryFileIcon = props => getIconImage("binaryfile.svg", props); +export const SqlIcon = props => getIconImage('sql.svg', props); +export const ExcelIcon = props => getIconImage('excel.svg', props); +export const DiagramIcon = props => getIconImage('diagram.svg', props); +export const QueryDesignIcon = props => getIconImage('querydesign.svg', props); +export const LocalDbIcon = props => getIconImage('localdb.svg', props); +export const CsvIcon = props => getIconImage('csv.svg', props); +export const ChangeSetIcon = props => getIconImage('changeset.svg', props); +export const BinaryFileIcon = props => getIconImage('binaryfile.svg', props); -export const ReferenceIcon = props => getIconImage("reference.svg", props); -export const LinkIcon = props => getIconImage("link.svg", props); +export const ReferenceIcon = props => getIconImage('reference.svg', props); +export const LinkIcon = props => getIconImage('link.svg', props); -export const SequenceIcon = props => getIconImage("sequence.svg", props); -export const CheckIcon = props => getIconImage("check.svg", props); +export const SequenceIcon = props => getIconImage('sequence.svg', props); +export const CheckIcon = props => getIconImage('check.svg', props); -export const LinkedServerIcon = props => - getIconImage("linkedserver.svg", props); +export const LinkedServerIcon = props => getIconImage('linkedserver.svg', props); -export const EmptyIcon = props => - getIconImage( - "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=", - props - ); +export const EmptyIcon = props => getIconImage('data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=', props); -export const TimesRedIcon = props => getFontIcon("fa-times red", props); -export const TimesGreenCircleIcon = props => - getFontIcon("fa-times-circle green", props); -export const GrayFilterIcon = props => - getFontIcon("fa-filter lightgray", props); -export const ExclamationTriangleIcon = props => - getFontIcon("fa-exclamation-triangle", props); -export const HourGlassIcon = props => getFontIcon("fa-hourglass", props); -export const InfoBlueCircleIcon = props => - getFontIcon("fa-info-circle blue", props); +export const TimesRedIcon = props => getFontIcon('fa-times red', props); +export const TimesGreenCircleIcon = props => getFontIcon('fa-times-circle green', props); +export const GrayFilterIcon = props => getFontIcon('fa-filter lightgray', props); +export const ExclamationTriangleIcon = props => getFontIcon('fa-exclamation-triangle', props); +export const HourGlassIcon = props => getFontIcon('fa-hourglass', props); +export const InfoBlueCircleIcon = props => getFontIcon('fa-info-circle blue', props); -export const SpinnerIcon = props => getFontIcon("fa-spinner spin", props); +export const SpinnerIcon = props => getFontIcon('fa-spinner spin', props); export const FontIcon = ({ name }) => ; diff --git a/web/src/tabs/TableDataTab.js b/web/src/tabs/TableDataTab.js new file mode 100644 index 000000000..bb553fb43 --- /dev/null +++ b/web/src/tabs/TableDataTab.js @@ -0,0 +1,5 @@ +import React from 'react'; + +export default function TableDataTab({ schemaName, pureName }) { + return
{pureName}
; +} diff --git a/web/src/tabs/index.js b/web/src/tabs/index.js new file mode 100644 index 000000000..5c41b3a1a --- /dev/null +++ b/web/src/tabs/index.js @@ -0,0 +1,5 @@ +import TableDataTab from './TableDataTab'; + +export default { + TableDataTab, +}; diff --git a/web/src/widgets/DatabaseWidget.js b/web/src/widgets/DatabaseWidget.js index c2d3c7794..b7c7342ae 100644 --- a/web/src/widgets/DatabaseWidget.js +++ b/web/src/widgets/DatabaseWidget.js @@ -63,7 +63,7 @@ function SqlObjectList({ id, database }) { const { tables } = objects || {}; return ( <> - + ({ ...x, id, database }))} makeAppObj={tableAppObject} /> ); }