Skip to content

Commit

Permalink
Simplify service map layout (#60949)
Browse files Browse the repository at this point in the history
Clean up the cytoscape component and event handlers to simplify the layout logic.

Make all centering animations animated.

Add logging of cytoscape events when we're in debug mode.

Add Elasticsearch icon.
  • Loading branch information
smith authored Mar 23, 2020
1 parent d5c13c0 commit a0a85db
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ storiesOf('app/ServiceMap/Cytoscape', module)
{ data: { id: 'default' } },
{ data: { id: 'cache', label: 'cache', 'span.type': 'cache' } },
{ data: { id: 'database', label: 'database', 'span.type': 'db' } },
{
data: {
id: 'elasticsearch',
label: 'elasticsearch',
'span.type': 'db',
'span.subtype': 'elasticsearch'
}
},
{
data: { id: 'external', label: 'external', 'span.type': 'external' }
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import React, {
createContext,
CSSProperties,
ReactNode,
useCallback,
useEffect,
useRef,
useState
Expand Down Expand Up @@ -109,36 +108,36 @@ export function Cytoscape({
serviceName,
style
}: CytoscapeProps) {
const initialElements = elements.map(element => ({
...element,
// prevents flash of unstyled elements
classes: [element.classes, 'invisible'].join(' ').trim()
}));

const [ref, cy] = useCytoscape({
...cytoscapeOptions,
elements: initialElements
elements
});

// Add the height to the div style. The height is a separate prop because it
// is required and can trigger rendering when changed.
const divStyle = { ...style, height };

const resetConnectedEdgeStyle = useCallback(
(node?: cytoscape.NodeSingular) => {
// Trigger a custom "data" event when data changes
useEffect(() => {
if (cy && elements.length > 0) {
cy.add(elements);
cy.trigger('data');
}
}, [cy, elements]);

// Set up cytoscape event handlers
useEffect(() => {
const resetConnectedEdgeStyle = (node?: cytoscape.NodeSingular) => {
if (cy) {
cy.edges().removeClass('highlight');

if (node) {
node.connectedEdges().addClass('highlight');
}
}
},
[cy]
);
};

const dataHandler = useCallback<cytoscape.EventHandler>(
event => {
const dataHandler: cytoscape.EventHandler = event => {
if (cy) {
if (serviceName) {
resetConnectedEdgeStyle(cy.getElementById(serviceName));
Expand All @@ -150,36 +149,25 @@ export function Cytoscape({
} else {
resetConnectedEdgeStyle();
}
if (event.cy.elements().length > 0) {
const selectedRoots = selectRoots(event.cy);
const layout = cy.layout(
getLayoutOptions(selectedRoots, height, width)
);
layout.one('layoutstop', () => {
if (serviceName) {
const focusedNode = cy.getElementById(serviceName);
cy.center(focusedNode);
}
// show elements after layout is applied
cy.elements().removeClass('invisible');
});
layout.run();
}
}
},
[cy, resetConnectedEdgeStyle, serviceName, height, width]
);

// Trigger a custom "data" event when data changes
useEffect(() => {
if (cy) {
cy.add(elements);
cy.trigger('data');
}
}, [cy, elements]);
const selectedRoots = selectRoots(event.cy);
const layout = cy.layout(
getLayoutOptions(selectedRoots, height, width)
);

// Set up cytoscape event handlers
useEffect(() => {
layout.run();
}
};
const layoutstopHandler: cytoscape.EventHandler = event => {
event.cy.animate({
...animationOptions,
center: {
eles: serviceName
? event.cy.getElementById(serviceName)
: event.cy.collection()
}
});
};
const mouseoverHandler: cytoscape.EventHandler = event => {
event.target.addClass('hover');
event.target.connectedEdges().addClass('nodeHover');
Expand All @@ -194,10 +182,18 @@ export function Cytoscape({
const unselectHandler: cytoscape.EventHandler = event => {
resetConnectedEdgeStyle();
};
const debugHandler: cytoscape.EventHandler = event => {
const debugEnabled = sessionStorage.getItem('apm_debug') === 'true';
if (debugEnabled) {
// eslint-disable-next-line no-console
console.debug('cytoscape:', event);
}
};

if (cy) {
cy.on('data layoutstop select unselect', debugHandler);
cy.on('data', dataHandler);
cy.ready(dataHandler);
cy.on('layoutstop', layoutstopHandler);
cy.on('mouseover', 'edge, node', mouseoverHandler);
cy.on('mouseout', 'edge, node', mouseoutHandler);
cy.on('select', 'node', selectHandler);
Expand All @@ -207,15 +203,19 @@ export function Cytoscape({
return () => {
if (cy) {
cy.removeListener(
'data',
'data layoutstop select unselect',
undefined,
dataHandler as cytoscape.EventHandler
debugHandler
);
cy.removeListener('data', undefined, dataHandler);
cy.removeListener('layoutstop', undefined, layoutstopHandler);
cy.removeListener('mouseover', 'edge, node', mouseoverHandler);
cy.removeListener('mouseout', 'edge, node', mouseoutHandler);
cy.removeListener('select', 'node', selectHandler);
cy.removeListener('unselect', 'node', unselectHandler);
}
};
}, [cy, dataHandler, resetConnectedEdgeStyle, serviceName]);
}, [cy, height, serviceName, width]);

return (
<CytoscapeContext.Provider value={cy}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import React, {
import { SERVICE_NAME } from '../../../../../../../../plugins/apm/common/elasticsearch_fieldnames';
import { CytoscapeContext } from '../Cytoscape';
import { Contents } from './Contents';
import { animationOptions } from '../cytoscapeOptions';

interface PopoverProps {
focusedServiceName?: string;
Expand Down Expand Up @@ -88,7 +89,10 @@ export function Popover({ focusedServiceName }: PopoverProps) {

const centerSelectedNode = useCallback(() => {
if (cy) {
cy.center(cy.getElementById(selectedNodeServiceName));
cy.animate({
...animationOptions,
center: { eles: cy.getElementById(selectedNodeServiceName) }
});
}
}, [cy, selectedNodeServiceName]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,6 @@ const style: cytoscape.Stylesheet[] = [
selector: 'edge[isInverseEdge]',
style: { visibility: 'hidden' }
},
// @ts-ignore
{
selector: '.invisible',
style: { visibility: 'hidden' }
},
{
selector: 'edge.nodeHover',
style: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import cytoscape from 'cytoscape';
import {
AGENT_NAME,
SERVICE_NAME,
SPAN_TYPE
SPAN_TYPE,
SPAN_SUBTYPE
} from '../../../../../../../plugins/apm/common/elasticsearch_fieldnames';
import databaseIcon from './icons/database.svg';
import defaultIconImport from './icons/default.svg';
import documentsIcon from './icons/documents.svg';
import dotNetIcon from './icons/dot-net.svg';
import elasticsearchIcon from './icons/elasticsearch.svg';
import globeIcon from './icons/globe.svg';
import goIcon from './icons/go.svg';
import javaIcon from './icons/java.svg';
Expand Down Expand Up @@ -63,6 +65,11 @@ export function iconForNode(node: cytoscape.NodeSingular) {
return serviceIcons[node.data(AGENT_NAME) as string];
} else if (isIE11) {
return defaultIcon;
} else if (
node.data(SPAN_TYPE) === 'db' &&
node.data(SPAN_SUBTYPE) === 'elasticsearch'
) {
return elasticsearchIcon;
} else if (icons[type]) {
return icons[type];
} else {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit a0a85db

Please sign in to comment.