This is an automated email from the ASF dual-hosted git repository. eallen pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/qpid-dispatch.git
The following commit(s) were added to refs/heads/master by this push: new 681c330 DISPATCH-1592: Grey out unreachable routers. Added ability to suppress notification popups. Set browser window title. 681c330 is described below commit 681c330c172595507457215b9bd3253dfcdd5181 Author: Ernest Allen <eal...@redhat.com> AuthorDate: Thu Mar 12 19:36:08 2020 -0400 DISPATCH-1592: Grey out unreachable routers. Added ability to suppress notification popups. Set browser window title. --- console/react/src/App.css | 24 ++++++ console/react/src/common/DropdownMenu.js | 15 +++- console/react/src/common/amqp/connection.js | 20 +++-- console/react/src/common/amqp/topology.js | 5 ++ console/react/src/common/contextMenuComponent.js | 5 ++ console/react/src/overview/dashboard/layout.js | 19 ++++- .../src/overview/dashboard/notificationDrawer.js | 2 +- console/react/src/topology/svgUtils.js | 3 +- console/react/src/topology/topoUtils.js | 20 +++++ console/react/src/topology/topologyViewer.js | 96 ++++++++++++---------- 10 files changed, 154 insertions(+), 55 deletions(-) diff --git a/console/react/src/App.css b/console/react/src/App.css index 7c25676..03270d4 100644 --- a/console/react/src/App.css +++ b/console/react/src/App.css @@ -82,9 +82,16 @@ g.selected line { .link { stroke: #000; } +.link.dropped { + stroke: #888888; + opacity: 0.25; +} .topology-header path.link { stroke: #fff; } +.topology-header ul.context-menu.layout-dropdown { + right: 0; +} #topologyPage, #messageFlowPage { @@ -545,7 +552,20 @@ circle.node.route-container { circle.node.edge.multiple { fill: #e0e0ff; } +circle.node.dropped, +circle.node.normal.in.dropped, +circle.node.normal.out.dropped { + fill: #f0f0f0; + stroke: #dddddd; + stroke-dasharray: 5 5; +} +circle.node.inter-router.dropped { + fill: #f0f0f0; +} +circle.node.dropped + text { + stroke: #dddddd; +} text.console, text.on-demand, text.normal, @@ -1504,3 +1524,7 @@ button.dropdown-panel-toggle.pf-c-accordion__toggle span.pf-c-accordion__toggle- .node-name { text-decoration: underline; } + +.menu-item-icon { + padding-right: 0.5em; +} diff --git a/console/react/src/common/DropdownMenu.js b/console/react/src/common/DropdownMenu.js index 5f58b9d..9d82827 100644 --- a/console/react/src/common/DropdownMenu.js +++ b/console/react/src/common/DropdownMenu.js @@ -27,7 +27,9 @@ class DropdownMenu extends Component { handleDropdownLogout: PropTypes.func.isRequired, isConnected: PropTypes.func.isRequired, handleContextHide: PropTypes.func.isRequired, - parentClass: PropTypes.string.isRequired + parentClass: PropTypes.string.isRequired, + handleSuppress: PropTypes.func.isRequired, + suppress: PropTypes.any.isRequired }; constructor(props) { super(props); @@ -40,6 +42,12 @@ class DropdownMenu extends Component { title: "Logout", action: this.logout, enabled: this.isLoggedIn + }, + { + title: "Suppress notifications", + action: this.suppress, + enabled: () => true, + suppressIcon: true } ]; } @@ -48,6 +56,10 @@ class DropdownMenu extends Component { this.props.handleDropdownLogout(); }; + suppress = () => { + this.props.handleSuppress(); + }; + isLoggedIn = () => { return this.props.isConnected(); }; @@ -64,6 +76,7 @@ class DropdownMenu extends Component { contextEventPosition={[-1, -1]} // show in-place handleContextHide={this.props.handleContextHide} menuItems={this.contextMenuItems} + suppress={this.props.suppress} parentClass={this.props.parentClass} /> ) diff --git a/console/react/src/common/amqp/connection.js b/console/react/src/common/amqp/connection.js index 773810e..748e647 100644 --- a/console/react/src/common/amqp/connection.js +++ b/console/react/src/common/amqp/connection.js @@ -357,14 +357,18 @@ class ConnectionManager { var _correlationId = this.correlator.corr(); var self = this; return new Promise((resolve, reject) => { - self.correlator.register(_correlationId, resolve, reject); - self.sender.send({ - body: body, - to: to, - reply_to: self.receiver.remote.attach.source.address, - correlation_id: _correlationId, - application_properties: application_properties - }); + try { + self.correlator.register(_correlationId, resolve, reject); + self.sender.send({ + body: body, + to: to, + reply_to: self.receiver.remote.attach.source.address, + correlation_id: _correlationId, + application_properties: application_properties + }); + } catch (error) { + console.log(error); + } }); }; _fullAddr = toAddr => { diff --git a/console/react/src/common/amqp/topology.js b/console/react/src/common/amqp/topology.js index 7c86e7d..693fc04 100644 --- a/console/react/src/common/amqp/topology.js +++ b/console/react/src/common/amqp/topology.js @@ -141,8 +141,13 @@ class Topology { this._nodeInfo[rId][entity] = utils.copy(workInfo[rId][entity]); } } + // find routers that have no connection info + if (!workInfo[rId].connection) { + this._nodeInfo[rId].connection.stale = true; + } } else if (all) { changes.lostRouters.push(rId); + delete this._nodeInfo[rId]; } } // add any new routers diff --git a/console/react/src/common/contextMenuComponent.js b/console/react/src/common/contextMenuComponent.js index 8e660fa..62292fe 100644 --- a/console/react/src/common/contextMenuComponent.js +++ b/console/react/src/common/contextMenuComponent.js @@ -62,6 +62,11 @@ class ContextMenuComponent extends React.Component { this.proxyClick(item, e); }} > + {this.props.suppress && item.suppressIcon && ( + <span + className={`pficon pficon-${this.props.suppress} menu-item-icon`} + ></span> + )} {item.title} </li> ); diff --git a/console/react/src/overview/dashboard/layout.js b/console/react/src/overview/dashboard/layout.js index e49ee26..1a0eef2 100644 --- a/console/react/src/overview/dashboard/layout.js +++ b/console/react/src/overview/dashboard/layout.js @@ -57,6 +57,8 @@ import { utils } from "../../common/amqp/utilities"; import throughputData from "./throughputData"; import inflightData from "./inflightData"; +const SUPPRESS_NOTIFICATIONS = "noNotify"; + class PageLayout extends React.PureComponent { constructor(props) { super(props); @@ -70,7 +72,8 @@ class PageLayout extends React.PureComponent { isNavOpenMobile: false, isMobileView: false, user: "anonymous", - timePeriod: 60 + timePeriod: 60, + suppress: JSON.parse(localStorage.getItem(SUPPRESS_NOTIFICATIONS)) || false }; this.isDropdownOpen = false; @@ -97,6 +100,7 @@ class PageLayout extends React.PureComponent { this.throughputChartData = new throughputData(this.service); this.inflightChartData = new inflightData(this.service); this.updateCharts(); + document.title = this.props.config.title; }; componentWillUnmount = () => { @@ -273,6 +277,12 @@ class PageLayout extends React.PureComponent { } }; + handleSuppress = () => { + this.setState({ suppress: !this.state.suppress ? "ok" : false }, () => { + localStorage.setItem(SUPPRESS_NOTIFICATIONS, JSON.stringify(this.state.suppress)); + }); + }; + render() { const { activeItem, activeGroup } = this.state; const { isNavOpenDesktop, isNavOpenMobile, isMobileView } = this.state; @@ -327,7 +337,10 @@ class PageLayout extends React.PureComponent { </Button> </ToolbarItem> <ToolbarItem className="notification-button"> - <NotificationDrawer ref={el => (this.notificationRef = el)} /> + <NotificationDrawer + ref={el => (this.notificationRef = el)} + suppress={this.state.suppress} + /> </ToolbarItem> </ToolbarGroup> <ToolbarGroup> @@ -341,6 +354,8 @@ class PageLayout extends React.PureComponent { ref={el => (this.dropdownRef = el)} handleContextHide={this.handleUserMenuHide} handleDropdownLogout={this.handleDropdownLogout} + handleSuppress={this.handleSuppress} + suppress={this.state.suppress} isConnected={this.isConnected} parentClass="user-button" /> diff --git a/console/react/src/overview/dashboard/notificationDrawer.js b/console/react/src/overview/dashboard/notificationDrawer.js index 81c8540..b6411e8 100644 --- a/console/react/src/overview/dashboard/notificationDrawer.js +++ b/console/react/src/overview/dashboard/notificationDrawer.js @@ -93,7 +93,7 @@ class NotificationDrawer extends React.Component { }); event.isRead = false; accordionSections[section].events.unshift(event); - if (this.alertListRef && !silent) { + if (this.alertListRef && !silent && this.props.suppress === false) { this.alertListRef.addAlert(severity, message); } this.setState({ diff --git a/console/react/src/topology/svgUtils.js b/console/react/src/topology/svgUtils.js index d441bcf..66ae160 100644 --- a/console/react/src/topology/svgUtils.js +++ b/console/react/src/topology/svgUtils.js @@ -35,7 +35,8 @@ export function updateState(circle) { }) .classed("multiple", function(d) { return d.normals; - }); + }) + .classed("dropped", d => d.dropped); } export function appendCircle(g) { diff --git a/console/react/src/topology/topoUtils.js b/console/react/src/topology/topoUtils.js index 9acaa4b..8aed78a 100644 --- a/console/react/src/topology/topoUtils.js +++ b/console/react/src/topology/topoUtils.js @@ -292,6 +292,26 @@ export function reconcileArrays(existing, newArray) { // to the two nodes that it is linking. // So we need to fix the new links' source and target export function reconcileLinks(existingLinks, newLinks, existingNodes) { + // find links that are mirror images + newLinks.forEach(n => { + existingLinks.forEach(e => { + if ( + e.suid === n.tuid && + e.tuid === n.suid && + e.left === n.right && + e.right === n.left + ) { + e.suid = n.suid; + e.tuid = n.tuid; + e.left = n.left; + e.right = n.right; + e.uuid = n.uuid; + const tmp = e.source; + e.source = e.target; + e.target = tmp; + } + }); + }); reconcileArrays(existingLinks, newLinks); existingLinks.forEach(e => { // in new links, the source and target will be a number diff --git a/console/react/src/topology/topologyViewer.js b/console/react/src/topology/topologyViewer.js index 13d0e23..f7e400f 100644 --- a/console/react/src/topology/topologyViewer.js +++ b/console/react/src/topology/topologyViewer.js @@ -158,16 +158,14 @@ class TopologyViewer extends Component { if (!this.mounted) return; // create the svg - this.props.service.management.topology - .startUpdating(null, this.getEdgeNodes()) - .then(() => { - this.init().then(() => { - if (!this.mounted) return; - // get notified when a router is added/dropped and when - // the number of connections for a router changes - this.topology.addChangedAction("topology", this.topologyChanged); - }); + this.topology.startUpdating(null, this.getEdgeNodes()).then(() => { + this.init().then(() => { + if (!this.mounted) return; + // get notified when a router is added/dropped and when + // the number of connections for a router changes + this.topology.addChangedAction("topology", this.topologyChanged); }); + }); }); }); }; @@ -176,6 +174,21 @@ class TopologyViewer extends Component { let message; let silent = true; if (changed.connections.length > 0) { + // see if any routers have stale info + const nodeInfo = this.topology.nodeInfo(); + for (let id in nodeInfo) { + const ds = this.forceData.nodes.nodes.filter(n => n.key === id); + ds.forEach(d => { + d.dropped = nodeInfo[id].connection.stale; + }); + const links = this.forceData.links.links.filter( + l => l.source.key === id || l.target.key === id + ); + links.forEach(l => { + l.dropped = nodeInfo[id].connection.stale; + }); + this.restart(); + } message = `${ changed.connections[0].from > changed.connections[0].to ? "Lost" : "New" } connection for ${utils.nameFromId(changed.connections[0].router)}`; @@ -581,6 +594,7 @@ class TopologyViewer extends Component { .classed("selected", d => d.selected) .classed("highlighted", d => d.highlighted) .classed("unknown", d => !d.right && !d.left) + .classed("dropped", d => d.dropped) // reset the markers based on current highlighted/selected .attr("marker-end", d => { if (!this.showMarker(d)) return null; @@ -838,38 +852,36 @@ class TopologyViewer extends Component { reInit = () => { return new Promise(resolve => { - this.props.service.management.topology - .startUpdating(null, this.getEdgeNodes()) - .then(() => { - const nodeInfo = this.topology.nodeInfo(); - const newNodes = new Nodes(this.QDRLog); - const newLinks = new Links(this.QDRLog); - newNodes.initialize(nodeInfo, this.width, this.height, localStorage); - newLinks.initialize( - nodeInfo, - newNodes, - this.separateContainers, - [], - this.height, - localStorage - ); - reconcileArrays(this.forceData.nodes.nodes, newNodes.nodes); - reconcileLinks( - this.forceData.links.links, - newLinks.links, - this.forceData.nodes.nodes - ); + this.topology.startUpdating(null, this.getEdgeNodes()).then(() => { + const nodeInfo = this.topology.nodeInfo(); + const newNodes = new Nodes(this.QDRLog); + const newLinks = new Links(this.QDRLog); + newNodes.initialize(nodeInfo, this.width, this.height, localStorage); + newLinks.initialize( + nodeInfo, + newNodes, + this.separateContainers, + [], + this.height, + localStorage + ); + reconcileArrays(this.forceData.nodes.nodes, newNodes.nodes); + reconcileLinks( + this.forceData.links.links, + newLinks.links, + this.forceData.nodes.nodes + ); - this.force.nodes(this.forceData.nodes.nodes).links(this.forceData.links.links); - this.force.stop(); - this.force.start(); - this.restart(); - this.circle.call(this.force.drag); - delete this.legend; - this.legend = new Legend(this.forceData.nodes, this.QDRLog); - this.updateLegend(); - resolve(); - }); + this.force.nodes(this.forceData.nodes.nodes).links(this.forceData.links.links); + this.force.stop(); + this.force.start(); + this.restart(); + this.circle.call(this.force.drag); + delete this.legend; + this.legend = new Legend(this.forceData.nodes, this.QDRLog); + this.updateLegend(); + resolve(); + }); }); }; @@ -1102,14 +1114,14 @@ class TopologyViewer extends Component { {this.state.showRouterInfo && ( <RouterInfoComponent d={this.d} - topology={this.props.service.management.topology} + topology={this.topology} handleCloseRouterInfo={this.handleCloseRouterInfo} /> )} {this.state.showClientInfo && ( <ClientInfoComponent d={this.d} - topology={this.props.service.management.topology} + topology={this.topology} handleCloseClientInfo={this.handleCloseClientInfo} handleSeparate={this.handleSeparate} /> --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org