diff --git a/app/package-lock.json b/app/package-lock.json index 7ee63c1..3d808ef 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -4934,6 +4934,12 @@ "throttleit": "0.0.2" } }, + "number-abbreviate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/number-abbreviate/-/number-abbreviate-2.0.0.tgz", + "integrity": "sha1-6cstGNuADhU88HKClVPEr+DfeJg=", + "dev": true + }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", diff --git a/app/package.json b/app/package.json index b99b020..85e16b0 100644 --- a/app/package.json +++ b/app/package.json @@ -16,6 +16,7 @@ "css-loader": "^2.1.0", "electron-nucleus": "^1.11.0", "electron-telemetry": "git+https://github.com/thomasnordquist/electron-telemetry.git", + "number-abbreviate": "^2.0.0", "react": "^16.8.0-alpha.1", "react-dom": "^16.7.0", "react-redux": "^6.0.0", diff --git a/app/src/components/BrokerStatistics.tsx b/app/src/components/BrokerStatistics.tsx new file mode 100644 index 0000000..29eacc0 --- /dev/null +++ b/app/src/components/BrokerStatistics.tsx @@ -0,0 +1,131 @@ +import * as React from 'react' +import * as q from '../../../backend/src/Model' + +import { AppState } from '../reducers' +import { Typography } from '@material-ui/core' +import { StyleRulesCallback, withStyles } from '@material-ui/core/styles' + +import { connect } from 'react-redux' +const abbreviate = require('number-abbreviate') + +interface Stats { + topic: string + title: string +} + +const styles: StyleRulesCallback = theme => ({ + flex: { + display: 'flex', + width: '100%', + }, + container: { + width: '100%', + height: '224px', + backgroundColor: 'rebeccapurple', + marginBottom: 0, + marginTop: 'auto', + padding: '8px', + }, +}) + +interface Props { + classes: any + tree?: q.Tree +} + +class BrokerStatistics extends React.Component { + constructor(props: any) { + super(props) + this.state = {} + } + + public render() { + const { tree, classes } = this.props + if (!tree) { + return null + } + + const stats: any = { + broker: { + topic: '$SYS/broker/version', + title: 'Broker', + }, + clients: { + topic: '$SYS/broker/clients/total', + title: 'Clients', + }, + subscriptions: { + topic: '$SYS/broker/subscriptions/count', + title: 'Subscriptions', + }, + received: { + topic: '$SYS/broker/messages/received', + title: 'Received', + }, + sent: { + topic: '$SYS/broker/messages/sent', + title: 'Sent', + }, + received5m: { + topic: '$SYS/broker/load/messages/received/5min', + title: 'Received last 5min', + }, + sent5m: { + topic: '$SYS/broker/load/messages/sent/5min', + title: 'Sent 5m', + }, + heap: { + topic: '$SYS/broker/heap/current', + title: 'Memory', + }, + heapMax: { + topic: '$SYS/broker/heap/maximum', + title: 'Memory (max)', + }, + } + + return ( +
+ {this.renderStat(tree, stats.broker)} + {this.renderPair(tree, stats.sent, stats.received)} + {this.renderPair(tree, stats.clients, stats.subscriptions)} + {this.renderPair(tree, stats.sent5m, stats.received5m)} + {this.renderPair(tree, stats.heap, stats.heapMax)} +
+ ) + } + + private renderPair(tree: q.Tree, a: Stats, b: Stats) { + return ( +
+
{this.renderStat(tree, a)}
+
{this.renderStat(tree, b)}
+
+ ) + } + + public renderStat(tree: q.Tree, stat: Stats) { + const node = tree.findNode(stat.topic) + if (!node) { + return null + } + + let value = node.message && node.message.value + value = !isNaN(value) ? abbreviate(value) : value + + return ( +
+ {stat.title} + {value} +
+ ) + } +} + +const mapStateToProps = (state: AppState) => { + return { + tree: state.connection.tree, + } +} + +export default withStyles(styles)(connect(mapStateToProps)(BrokerStatistics)) diff --git a/app/src/components/Settings.tsx b/app/src/components/Settings.tsx index 5de9d70..7938e34 100644 --- a/app/src/components/Settings.tsx +++ b/app/src/components/Settings.tsx @@ -16,8 +16,9 @@ import { StyleRulesCallback, withStyles } from '@material-ui/core/styles' import ChevronRight from '@material-ui/icons/ChevronRight' import { bindActionCreators } from 'redux' import { connect } from 'react-redux' -import { settingsActions } from '../actions' +import { settingsActions, treeActions } from '../actions' import { TopicOrder } from '../reducers/Settings' +import BrokerStatistics from './BrokerStatistics' const styles: StyleRulesCallback = theme => ({ drawer: { @@ -39,12 +40,12 @@ const styles: StyleRulesCallback = theme => ({ }) interface Props { - actions: typeof settingsActions autoExpandLimit: number visible: boolean store?: any - classes: any topicOrder: TopicOrder + classes: any + actions: typeof settingsActions } class Settings extends React.Component { @@ -79,6 +80,7 @@ class Settings extends React.Component { {this.renderAutoExpand()} {this.renderNodeOrder()} + ) } @@ -115,8 +117,8 @@ class Settings extends React.Component { return (
- Topic order - } @@ -124,12 +126,12 @@ class Settings extends React.Component { name="node-order" className={classes.input} style={{ flex: '1' }} - > - default - a-z - {TopicOrder.messages} - {TopicOrder.topics} - + > + default + a-z + {TopicOrder.messages} + {TopicOrder.topics} +
) } diff --git a/app/src/components/TitleBar.tsx b/app/src/components/TitleBar.tsx index 050f36f..666b3fb 100644 --- a/app/src/components/TitleBar.tsx +++ b/app/src/components/TitleBar.tsx @@ -1,5 +1,4 @@ import * as React from 'react' -import * as q from '../../../backend/src/Model' import { AppBar, Button, IconButton, InputBase, Toolbar, Typography } from '@material-ui/core' import { StyleRulesCallback, withStyles } from '@material-ui/core/styles' diff --git a/app/src/components/Tree/TreeNodeTitle.tsx b/app/src/components/Tree/TreeNodeTitle.tsx index 00ab003..af8faf4 100644 --- a/app/src/components/Tree/TreeNodeTitle.tsx +++ b/app/src/components/Tree/TreeNodeTitle.tsx @@ -56,7 +56,6 @@ class TreeNodeTitle extends React.Component { private renderValue() { const style: React.CSSProperties = { - maxWidth: '15em', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', diff --git a/backend/src/Model/TreeNode.ts b/backend/src/Model/TreeNode.ts index 5aaeb3e..c53ab23 100644 --- a/backend/src/Model/TreeNode.ts +++ b/backend/src/Model/TreeNode.ts @@ -148,6 +148,25 @@ export class TreeNode { return this.cachedChildTopics } + public findNode (path: String): TreeNode | undefined { + const topics = path.split('/') + + return this.findChild(topics) + } + + private findChild(edges: string[]): TreeNode | undefined { + if (edges.length === 0) { + return this + } + + const nextEdge = this.edges[edges[0]] + if (!nextEdge) { + return undefined + } + + return nextEdge.target.findChild(edges.slice(1)) + } + private mergeEdges(node: TreeNode) { const edgeKeys = Object.keys(node.edges) let edgesDidUpdate = false diff --git a/backend/src/Model/spec/EventDispatcher.spec.ts b/backend/src/Model/spec/EventDispatcher.spec.ts index d9ab80a..441d886 100644 --- a/backend/src/Model/spec/EventDispatcher.spec.ts +++ b/backend/src/Model/spec/EventDispatcher.spec.ts @@ -2,7 +2,6 @@ import 'mocha' import { EventDispatcher } from '../../../../events' import { expect } from 'chai' -import { doesNotReject } from 'assert' describe('EventDispatcher', async () => { it('should dispatch', async function () { diff --git a/backend/src/Model/spec/Tree.spec.ts b/backend/src/Model/spec/Tree.spec.ts index 172212a..e5ea6b9 100644 --- a/backend/src/Model/spec/Tree.spec.ts +++ b/backend/src/Model/spec/Tree.spec.ts @@ -1,5 +1,4 @@ import 'mocha' -import './TreeNode.findNode' import { Tree, TreeNodeFactory } from '../' diff --git a/backend/src/Model/spec/TreeNode.findNode.spec.ts b/backend/src/Model/spec/TreeNode.findNode.spec.ts index 0b6c219..bb26849 100644 --- a/backend/src/Model/spec/TreeNode.findNode.spec.ts +++ b/backend/src/Model/spec/TreeNode.findNode.spec.ts @@ -1,5 +1,4 @@ import 'mocha' -import './TreeNode.findNode' import { TreeNodeFactory } from '../' import { expect } from 'chai' diff --git a/backend/src/Model/spec/TreeNode.findNode.ts b/backend/src/Model/spec/TreeNode.findNode.ts deleted file mode 100644 index 6feb70a..0000000 --- a/backend/src/Model/spec/TreeNode.findNode.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { TreeNode } from '../' -declare module '../' { - interface TreeNode { - findNode(path: String): TreeNode | undefined - } -} - -TreeNode.prototype.findNode = function (path: String): TreeNode | undefined { - const topics = path.split('/') - const edge = this.edges[topics[0]] - const remainingTopics = topics.slice(1, topics.length) - if (edge && remainingTopics.length === 0) { - return edge.target - } else if (edge) { - return edge.target.findNode(remainingTopics.join('/')) - } - - return undefined -} diff --git a/backend/src/Model/spec/TreeNode.spec.ts b/backend/src/Model/spec/TreeNode.spec.ts index dfafe19..6db49fb 100644 --- a/backend/src/Model/spec/TreeNode.spec.ts +++ b/backend/src/Model/spec/TreeNode.spec.ts @@ -1,4 +1,3 @@ -import './TreeNode.findNode' import 'mocha' import { TreeNodeFactory } from '../' diff --git a/backend/src/Model/spec/TreeNodeFactory.spec.ts b/backend/src/Model/spec/TreeNodeFactory.spec.ts index 3ba64b4..a7f488a 100644 --- a/backend/src/Model/spec/TreeNodeFactory.spec.ts +++ b/backend/src/Model/spec/TreeNodeFactory.spec.ts @@ -1,5 +1,4 @@ import 'mocha' -import './TreeNode.findNode' import { TreeNodeFactory } from '../' import { expect } from 'chai'