blob: a8600171e6256828438046b1099a2563d38359cd [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { Component } from 'react';
import cytoscape from 'cytoscape';
import coseBilkent from 'cytoscape-cose-bilkent';
import dagre from 'cytoscape-dagre';
import cyCanvas from 'cytoscape-canvas';
cytoscape.use(coseBilkent);
cytoscape.use(dagre);
cytoscape.use(cyCanvas);
const config = {
layout: {
name: 'cose-bilkent',
animate: true,
idealEdgeLength: 200,
edgeElasticity: 0.1,
},
};
export default class Base extends Component {
static defaultProps = {
height: '600px',
display: 'block',
}
componentDidMount() {
const { elements, layout = config.layout, metrics } = this.props;
this.layout = layout;
this.metrics = metrics;
this.elements = elements;
let nextElements = this.transform(elements);
if (this.setUp) {
nextElements = this.setUp(nextElements);
}
this.cy = cytoscape({
container: this.container,
zoom: 1,
maxZoom: 1,
boxSelectionEnabled: true,
wheelSensitivity: 0.2,
layout,
elements: nextElements,
style: this.getStyle(),
});
if (this.bindEvent) {
this.bindEvent(this.cy);
}
}
componentWillReceiveProps(nextProps) {
this.updateTopology(nextProps);
this.updateMetric(nextProps);
}
shouldComponentUpdate() {
return false;
}
componentWillUnmount() {
this.cy.destroy();
}
getCy() {
return this.cy;
}
isSame = (nodes, nextNodes) => {
if (nodes.length !== nextNodes.length) {
return false;
}
const diff = nextNodes.diff(nodes);
return diff.left.length < 1 && diff.right.length < 1;
}
loadMetrics = (elementes) => {
const { onLoadMetircs } = this.props;
if (onLoadMetircs) {
onLoadMetircs(
elementes.nodes.filter(_ => _.data.id.indexOf('USER') < 0).map(_ => _.data.id),
elementes.edges.filter(_ => _.data.detectPoint === 'SERVER').map(_ => _.data.dataId),
elementes.edges.filter(_ => _.data.detectPoint === 'CLIENT').map(_ => _.data.dataId),
);
}
}
transform = (elements) => {
if (!elements) {
return [];
}
const { nodes, calls } = elements;
return {
nodes: nodes.map(node => ({ data: node })),
edges: calls.filter(call => (nodes.findIndex(node => node.id === call.source) > -1
&& nodes.findIndex(node => node.id === call.target) > -1))
.map(call => ({ data: { ...call } })),
};
}
updateTopology(nextProps) {
const { elements, layout: nextLayout, appRegExps } = nextProps;
let thisElements = this.elements;
let nextElements = elements;
const filteredElements = this.filter(elements, appRegExps);
if (filteredElements) {
thisElements = this.filteredElements;
nextElements = filteredElements;
this.filteredElements = filteredElements;
}
if (thisElements === nextElements && nextLayout === this.layout) {
return;
}
this.elements = elements;
nextElements = this.transform(nextElements);
if (this.setUp) {
nextElements = this.setUp(nextElements);
}
const nodes = this.cy.nodes();
this.cy.json({ elements: nextElements });
if (this.bindEvent) {
this.bindEvent(this.cy);
}
this.loadMetrics(nextElements);
if (nextLayout === this.layout && this.isSame(nodes, this.cy.nodes())) {
return;
}
this.layout = nextLayout;
const layout = this.cy.layout(nextLayout);
layout.pon('layoutstop').then(() => {
this.cy.minZoom(this.cy.zoom() - 0.3);
});
layout.run();
}
updateMetric(nextProps) {
if (nextProps.metrics === this.metrics && nextProps.latencyRange === this.latencyRange) {
return;
}
this.metrics = nextProps.metrics;
this.latencyRange = nextProps.latencyRange;
if (this.updateMetrics) {
this.updateMetrics(this.cy, this.metrics);
}
}
filter(elements, appRegExps) {
if (!appRegExps) {
this.appRegExps = appRegExps;
return elements;
}
if (this.elements === elements && this.appRegExps === appRegExps) {
return this.filteredElements;
}
this.appRegExps = appRegExps;
const nn = elements.nodes.filter(_ => appRegExps
.findIndex(r => _.name.match(r)) > -1);
const cc = elements.calls.filter(_ => nn
.findIndex(n => n.id === _.source || n.id === _.target) > -1);
return {
nodes: elements.nodes.filter(_ => cc
.findIndex(c => c.source === _.id || c.target === _.id) > -1),
calls: cc,
};
}
render() {
return (<div style={{ ...this.props }} ref={(el) => { this.container = el; }} />);
}
}