From abdd9f9f6ac2a515426fff592d64f38876314293 Mon Sep 17 00:00:00 2001 From: JUDIT GRESKOVITS Date: Wed, 12 Jul 2017 12:54:55 +0100 Subject: [PATCH] feat(ui-toolkit, cp-frontend): Allow topology nodes are displayed at top when not connected --- .../src/containers/service/delete.js | 19 +----- .../src/containers/services/topology.js | 1 + packages/cp-frontend/src/state/selectors.js | 23 ++++++-- packages/ui-toolkit/src/topology/functions.js | 12 +++- packages/ui-toolkit/src/topology/index.js | 59 ++++++++++++------- .../ui-toolkit/src/topology/node/content.js | 7 +-- packages/ui-toolkit/src/topology/node/info.js | 34 ++++++++--- .../ui-toolkit/src/topology/simulation.js | 22 ++++--- 8 files changed, 108 insertions(+), 69 deletions(-) diff --git a/packages/cp-frontend/src/containers/service/delete.js b/packages/cp-frontend/src/containers/service/delete.js index 6b500576..37b75314 100644 --- a/packages/cp-frontend/src/containers/service/delete.js +++ b/packages/cp-frontend/src/containers/service/delete.js @@ -51,24 +51,7 @@ const DeleteServicesGql = graphql(ServicesDeleteMutation, { props: ({ mutate }) => ({ deleteServices: serviceId => mutate({ - variables: { ids: [serviceId] }, - updateQueries: { - Services: (prev, { mutationResult }) => { - const deletedService = mutationResult.data.deleteServices[0]; - const prevServices = prev.deploymentGroup.services; - const services = prevServices.filter( - service => - service.id !== deletedService.id && - service.parent !== deletedService.id - ); - return { - deploymentGroup: { - ...prev.deploymentGroup, - services - } - }; - } - } + variables: { ids: [serviceId] } }) }) }); diff --git a/packages/cp-frontend/src/containers/services/topology.js b/packages/cp-frontend/src/containers/services/topology.js index dea52ccf..fac0a61b 100644 --- a/packages/cp-frontend/src/containers/services/topology.js +++ b/packages/cp-frontend/src/containers/services/topology.js @@ -126,6 +126,7 @@ const UiConnect = connect(mapStateToProps, mapDispatchToProps); const ServicesGql = graphql(ServicesQuery, { options(props) { return { + pollInterval: 1000, variables: { deploymentGroupSlug: props.match.params.deploymentGroup } diff --git a/packages/cp-frontend/src/state/selectors.js b/packages/cp-frontend/src/state/selectors.js index 8c5c9b98..0bdfd9c7 100644 --- a/packages/cp-frontend/src/state/selectors.js +++ b/packages/cp-frontend/src/state/selectors.js @@ -158,12 +158,23 @@ const processServicesForTopology = services => { const processedServices = processServices(services); const connectedServices = processedServices.reduce( - (connections, service) => - service.connections && service.connections.length - ? connections.concat(service.connections).concat(service.id) - : connections, - [] - ); + (connections, service) => { + if(!service.connections || !service.connections.length) { + return connections; + } + + const existingConnections = service.connections.reduce((connections, connection) => { + const connectionExists = processedServices.filter(ps => ps.id === connection).length; + if(connectionExists) { + connections.push(connection); + } + return connections; + }, []); + + return existingConnections.length + ? connections.concat(existingConnections).concat(service.id) + : connections + }, []); return processedServices.map(service => ({ ...service, diff --git a/packages/ui-toolkit/src/topology/functions.js b/packages/ui-toolkit/src/topology/functions.js index bba76d63..1b3cd195 100644 --- a/packages/ui-toolkit/src/topology/functions.js +++ b/packages/ui-toolkit/src/topology/functions.js @@ -99,15 +99,21 @@ const calculateLineLayout = ({ source, target }) => { }; }; +const getStatusesLength = (data) => + data.transitionalStatus + ? 1 + : data.instanceStatuses.length; + const getNodeRect = (data) => { const nodeSize = data.children ? Constants.nodeSizeWithChildren : Constants.nodeSize; const statuses = data.children - ? data.children.reduce((statuses, child) => - statuses + child.instanceStatuses.length, 0) - : data.instanceStatuses.length; + ? data.children.reduce((statuses, child) => { + return statuses + getStatusesLength(child), 0 + }) + : getStatusesLength(data); const { width, height } = nodeSize; diff --git a/packages/ui-toolkit/src/topology/index.js b/packages/ui-toolkit/src/topology/index.js index 10bf0175..32cec7a0 100644 --- a/packages/ui-toolkit/src/topology/index.js +++ b/packages/ui-toolkit/src/topology/index.js @@ -79,6 +79,7 @@ class Topology extends React.Component { getNextNodes(nextServices) { const nodes = this.state.nodes; + let notConnectedX = 0; return nodes.reduce((nextNodes, node) => { const keep = nextServices.filter(nextService => nextService.id === node.id).length; if(keep) { @@ -94,6 +95,9 @@ class Topology extends React.Component { // on other updates, we should update the services on the state and that's it // we should forceUpdate once the state has been updated const nextServices = nextProps.services.sort(); + const connectedNextServices = nextServices.filter(service => service.connected); + const notConnectedNextServices = nextServices.filter(service => !service.connected); + const { services, nodes } = this.state; if(nextServices.length > services.length) { // new service added, we need to redraw @@ -117,14 +121,16 @@ class Topology extends React.Component { this.create(nextProps); } else if(servicesRemoved.length || changedConnections.removed) { - const nextNodes = servicesRemoved.length - ? this.getNextNodes(nextServices) - : nodes; + + const nextNodes = this.getNextNodes(connectedNextServices); + const notConnectedNodes = this.getNotConnectedNodes(notConnectedNextServices); const nextLinks = this.getNextLinks(nextServices); + this.setState({ services: nextServices, links: nextLinks, nodes: nextNodes, + notConnectedNodes }, () => this.forceUpdate()); } else { @@ -135,6 +141,21 @@ class Topology extends React.Component { } } + getNotConnectedNodes(notConnectedServices) { + return notConnectedServices.map((notConnectedService, index) => { + const svgSize = this.getSvgSize(); + const x = notConnectedService.isConsul + ? svgSize.width - Constants.nodeSize.width + : (Constants.nodeSize.width + 10)*index; + + return ({ + id: notConnectedService.id, + x, + y: 0 + }) + }); + } + handleResize(evt) { this.create(this.props); // resize should just rejig the positions @@ -144,12 +165,14 @@ class Topology extends React.Component { // other updates should also just update the services rather than recreate the simulation const services = props.services.sort(); const connectedServices = services.filter(service => service.connected); - const notConnectedServices = services.filter(service => !service.connected && !service.isConsul); + const notConnectedServices = services.filter(service => !service.connected); const svgSize = this.getSvgSize(); - const { nodes, links, simulation } = createSimulation(services, svgSize); + const { nodes, links, simulation } = createSimulation(connectedServices, svgSize); + const notConnectedNodes = this.getNotConnectedNodes(notConnectedServices); this.setState({ + notConnectedNodes, nodes, links, simulation, @@ -207,21 +230,16 @@ class Topology extends React.Component { ); } - getConsulNodePosition() { - const svgSize = this.getSvgSize(); - const x = svgSize.width - Constants.nodeSize.width; - - return { - x, - y: 0 - }; - } - getConstrainedNodePosition(nodeId, children = false) { const node = this.findNode(nodeId); return this.constrainNodePosition(node.x, node.y, children); } + getNotConnectedNodePosition(nodeId) { + return this.state.notConnectedNodes.filter(ncn => + ncn.id === nodeId).shift(); + } + findNodeData(nodesData, nodeId) { return nodesData.filter(nodeData => nodeData.id === nodeId).shift(); } @@ -240,9 +258,9 @@ class Topology extends React.Component { const { nodes, links, services } = this.state; const nodesData = services.map((service, index) => { - const nodePosition = service.isConsul - ? this.getConsulNodePosition() - : this.getConstrainedNodePosition(service.id, service.children); + const nodePosition = service.connected + ? this.getConstrainedNodePosition(service.id, service.children) + : this.getNotConnectedNodePosition(service.id); const nodeRect = getNodeRect(service); @@ -255,12 +273,11 @@ class Topology extends React.Component { // TODO links will need to know whether a service has children // if it does, the height of it will be different - const t = links + const linksData = links .map((link, index) => ({ source: this.findNodeData(nodesData, link.source.id), target: this.findNodeData(nodesData, link.target.id) - })); - const linksData = t + })) .map((linkData, index) => { return calculateLineLayout(linkData, index) }); diff --git a/packages/ui-toolkit/src/topology/node/content.js b/packages/ui-toolkit/src/topology/node/content.js index 7d317769..219c67ea 100644 --- a/packages/ui-toolkit/src/topology/node/content.js +++ b/packages/ui-toolkit/src/topology/node/content.js @@ -50,13 +50,8 @@ const GraphNodeContent = ({ child = false, data, index = 0 }) => { const nodeInfo = ; return ( diff --git a/packages/ui-toolkit/src/topology/node/info.js b/packages/ui-toolkit/src/topology/node/info.js index ef4061b8..69b7f0db 100644 --- a/packages/ui-toolkit/src/topology/node/info.js +++ b/packages/ui-toolkit/src/topology/node/info.js @@ -33,15 +33,31 @@ const StyledDataCentresIcon = styled(DataCentresIcon)` `}; `; -const GraphNodeInfo = ({ datacenter, instances, instanceStatuses, healthy, pos, isConsul, instancesActive }) => { +const GraphNodeInfo = ({ data, pos }) => { + + const { + datacenter, + instances, + instanceStatuses, + healthy, + isConsul, + instancesActive, + transitionalStatus, + status + } = data; + const { x, y } = pos; - const statuses = instanceStatuses.map((instanceStatus, index) => - - {`${instanceStatus.count} - ${instanceStatus.status.toLowerCase()}`} - - ); + const statuses = transitionalStatus + ? + { status.toLowerCase() } + + : instanceStatuses.map((instanceStatus, index) => + + {`${instanceStatus.count} + ${instanceStatus.status.toLowerCase()}`} + + ); return ( @@ -69,13 +85,15 @@ const GraphNodeInfo = ({ datacenter, instances, instanceStatuses, healthy, pos, }; GraphNodeInfo.propTypes = { + data: PropTypes.object.isRequired, + pos: Point.isRequired/*, datacenter: PropTypes.string, healthy: PropTypes.bool, instances: PropTypes.array, instanceStatuses: PropTypes.array, pos: Point.isRequired, isConsul: PropTypes.bool, - instancesActive: PropTypes.bool + instancesActive: PropTypes.bool*/ }; export default Baseline(GraphNodeInfo); diff --git a/packages/ui-toolkit/src/topology/simulation.js b/packages/ui-toolkit/src/topology/simulation.js index 192393fe..040e54e4 100644 --- a/packages/ui-toolkit/src/topology/simulation.js +++ b/packages/ui-toolkit/src/topology/simulation.js @@ -10,7 +10,7 @@ const forcePlayAnimation = (simulation, animationTicks) => { const n = Math.ceil( Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay()) - ) - animationTicks; + ) + 100; // - animationTicks; for (let i = 0; i < n; ++i) { simulation.tick(); @@ -41,13 +41,20 @@ const createLinks = services => const createSimulation = (services, svgSize, animationTicks = 0) => { // This is not going to work given that as well as the d3 layout stuff, other things might be at play too // We should pass two objects to the components - one for positioning and one for data - const nodes = services.map((service, index) => ({ - id: service.id, - index - })); - + console.log('createSimulation services = ', services); + console.log('createSimulation services.length = ', services.length); + const nodes = services.map((service, index) => { + console.log('createSimulation services.map service.id = ', service.id); + console.log('createSimulation services.map service.connected = ', service.connected); + return ({ + id: service.id, + index + }) + }); + console.log('createSimulation nodes = ', nodes); + console.log('createSimulation nodes.length = ', nodes.length); const links = createLinks(services); - + console.log('createSimulation links = ', links); const { width, height } = svgSize; const nodeRadius = rectRadius(Constants.nodeSizeWithChildren); @@ -58,6 +65,7 @@ const createSimulation = (services, svgSize, animationTicks = 0) => { .force('center', forceCenter(width / 2, height / 2)); forcePlayAnimation(simulation, animationTicks); + console.log('createSimulation nodes = ', nodes); return { nodes,