feat(ui-toolkit, cp-frontend): Allow topology nodes are displayed at top when not connected

This commit is contained in:
JUDIT GRESKOVITS 2017-07-12 12:54:55 +01:00 committed by Sérgio Ramos
parent be7bb5f871
commit abdd9f9f6a
8 changed files with 108 additions and 69 deletions

View File

@ -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] }
})
})
});

View File

@ -126,6 +126,7 @@ const UiConnect = connect(mapStateToProps, mapDispatchToProps);
const ServicesGql = graphql(ServicesQuery, {
options(props) {
return {
pollInterval: 1000,
variables: {
deploymentGroupSlug: props.match.params.deploymentGroup
}

View File

@ -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,

View File

@ -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;

View File

@ -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)
});

View File

@ -50,13 +50,8 @@ const GraphNodeContent = ({ child = false, data, index = 0 }) => {
const nodeInfo =
<GraphNodeInfo
datacenter={data.datacenter}
instances={data.instances}
instanceStatuses={data.instanceStatuses}
healthy
data={data}
pos={nodeInfoPos}
isConsul={data.isConsul}
instancesActive={data.instancesActive}
/>;
return (

View File

@ -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) =>
<GraphText key={index} consul={isConsul} active={instancesActive}>
{`${instanceStatus.count}
${instanceStatus.status.toLowerCase()}`}
</GraphText>
);
const statuses = transitionalStatus
? <GraphText consul={isConsul} active={instancesActive}>
{ status.toLowerCase() }
</GraphText>
: instanceStatuses.map((instanceStatus, index) =>
<GraphText key={index} consul={isConsul} active={instancesActive}>
{`${instanceStatus.count}
${instanceStatus.status.toLowerCase()}`}
</GraphText>
);
return (
<g transform={`translate(${x}, ${y})`}>
@ -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);

View File

@ -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,