NodeGraph: Add node graph visualization (#29706)

* Add GraphView component

* Add service map panel

* Add more metadata visuals

* Add context menu on click

* Add context menu for services

* Fix service map in dashboard

* Add field proxy in explore linkSupplier

* Refactor the link creation

* Remove test file

* Fix scale change when view is panned

* Fix node centering

* Don't show context menu if no links

* Fix service map containers

* Add collapsible around the service map

* Fix stats computation

* Remove debug log

* Fix time stats

* Allow string timestamp

* Make panning bounded

* Add zooming by mouse wheel

* Clean up the colors

* Fix stats for single trace graph

* Don't show debug config

* Add more complex layout

* Update layout with better fixing of the root nodes

* Code cleanup

* Change how we pass in link creation function and some more cleanup

* Refactor the panel section into separate render methods

* Make the edge hover more readable

* Move stats computation to data source

* Put edge labels to front

* Simplify layout for better multi graph layout

* Update for dark theme

* Move function to utils

* Visual improvements

* Improve context menu detail

* Allow custom details

* Rename to NodeGraph

* Remove unused dependencies

* Use named color palette and add some fallbacks for missing data

* Add test data scenario

* Rename plugin

* Switch scroll zoom direction to align with google maps

* Do some perf optimisations and rise the node limit

* Update alert styling

* Rename function

* Add tests

* Add more tests

* Change data frame column mapping to use column names

* Fix test

* Fix type errors

* Don't show context menu without links

* Add beta status to panel

* Fix tests

* Changed function to standard methods

* Fix typing

* Clean up yarn.lock

* Add some UI improvements

- better styling of the zoom buttons
- disable buttons when max reached

* Fix panel references after rename

* Add panel icon
This commit is contained in:
Andrej Ocenas
2021-01-19 16:34:43 +01:00
committed by GitHub
parent 6f0bfa78ec
commit 218a8de220
45 changed files with 2838 additions and 63 deletions

View File

@@ -0,0 +1,155 @@
import { ArrayVector, FieldType, MutableDataFrame } from '@grafana/data';
import { nodes, edges } from './testData/serviceMapResponse';
import { NodeGraphDataFrameFieldNames } from '@grafana/ui';
export function generateRandomNodes(count = 10) {
const nodes = [];
const root = {
id: '0',
title: 'root',
subTitle: 'client',
success: 1,
error: 0,
stat1: Math.random(),
stat2: Math.random(),
edges: [] as any[],
};
nodes.push(root);
const nodesWithoutMaxEdges = [root];
const maxEdges = 3;
for (let i = 1; i < count; i++) {
const node = makeRandomNode(i);
nodes.push(node);
const sourceIndex = Math.floor(Math.random() * Math.floor(nodesWithoutMaxEdges.length - 1));
const source = nodesWithoutMaxEdges[sourceIndex];
source.edges.push(node.id);
if (source.edges.length >= maxEdges) {
nodesWithoutMaxEdges.splice(sourceIndex, 1);
}
nodesWithoutMaxEdges.push(node);
}
// Add some random edges to create possible cycle
const additionalEdges = Math.floor(count / 2);
for (let i = 0; i <= additionalEdges; i++) {
const sourceIndex = Math.floor(Math.random() * Math.floor(nodes.length - 1));
const targetIndex = Math.floor(Math.random() * Math.floor(nodes.length - 1));
if (sourceIndex === targetIndex || nodes[sourceIndex].id === '0' || nodes[sourceIndex].id === '0') {
continue;
}
nodes[sourceIndex].edges.push(nodes[sourceIndex].id);
}
const nodeFields: any = {
[NodeGraphDataFrameFieldNames.id]: {
values: new ArrayVector(),
type: FieldType.string,
},
[NodeGraphDataFrameFieldNames.title]: {
values: new ArrayVector(),
type: FieldType.string,
},
[NodeGraphDataFrameFieldNames.subTitle]: {
values: new ArrayVector(),
type: FieldType.string,
},
[NodeGraphDataFrameFieldNames.mainStat]: {
values: new ArrayVector(),
type: FieldType.number,
},
[NodeGraphDataFrameFieldNames.secondaryStat]: {
values: new ArrayVector(),
type: FieldType.number,
},
[NodeGraphDataFrameFieldNames.arc + 'success']: {
values: new ArrayVector(),
type: FieldType.number,
config: { color: { fixedColor: 'green' } },
},
[NodeGraphDataFrameFieldNames.arc + 'errors']: {
values: new ArrayVector(),
type: FieldType.number,
config: { color: { fixedColor: 'red' } },
},
};
const nodeFrame = new MutableDataFrame({
name: 'nodes',
fields: Object.keys(nodeFields).map(key => ({
...nodeFields[key],
name: key,
})),
meta: { preferredVisualisationType: 'nodeGraph' },
});
const edgeFields: any = {
[NodeGraphDataFrameFieldNames.id]: {
values: new ArrayVector(),
type: FieldType.string,
},
[NodeGraphDataFrameFieldNames.source]: {
values: new ArrayVector(),
type: FieldType.string,
},
[NodeGraphDataFrameFieldNames.target]: {
values: new ArrayVector(),
type: FieldType.string,
},
};
const edgesFrame = new MutableDataFrame({
name: 'edges',
fields: Object.keys(edgeFields).map(key => ({
...edgeFields[key],
name: key,
})),
meta: { preferredVisualisationType: 'nodeGraph' },
});
const edgesSet = new Set();
for (const node of nodes) {
nodeFields.id.values.add(node.id);
nodeFields.title.values.add(node.title);
nodeFields.subTitle.values.add(node.subTitle);
nodeFields.mainStat.values.add(node.stat1);
nodeFields.secondaryStat.values.add(node.stat2);
nodeFields.arc__success.values.add(node.success);
nodeFields.arc__errors.values.add(node.error);
for (const edge of node.edges) {
const id = `${node.id}--${edge}`;
// We can have duplicate edges when we added some more by random
if (edgesSet.has(id)) {
continue;
}
edgesSet.add(id);
edgeFields.id.values.add(`${node.id}--${edge}`);
edgeFields.source.values.add(node.id);
edgeFields.target.values.add(edge);
}
}
return [nodeFrame, edgesFrame];
}
function makeRandomNode(index: number) {
const success = Math.random();
const error = 1 - success;
return {
id: index.toString(),
title: `service:${index}`,
subTitle: 'service',
success,
error,
stat1: Math.random(),
stat2: Math.random(),
edges: [],
};
}
export function savedNodesResponse(): any {
return [new MutableDataFrame(nodes), new MutableDataFrame(edges)];
}