in studio/src/components/graph-visualization.tsx [104:269]
function GraphVisualization({
subgraphMetrics,
federatedGraphMetrics,
supportsFederation,
}: {
subgraphMetrics?: SubgraphMetrics[];
federatedGraphMetrics?: FederatedGraphMetrics;
supportsFederation: boolean;
}) {
const graphData = useContext(GraphContext);
const reactFlowInstance = useReactFlow();
const nodesInitialized = useNodesInitialized();
const dr = useDateRangeQueryState();
const [nodes, setNodes] = useState<Node[]>([]);
const [edges, setEdges] = useState<Edge[]>([]);
const [showAll, setShowAll] = useState(false);
const [topCategory, setTopCategory] = useState("latency");
const subgraphs = useMemo(() => {
let tempSubgraphs = [...(graphData?.subgraphs ?? [])];
tempSubgraphs.sort((a, b) => {
const metricA = subgraphMetrics?.find((x) => x.subgraphID === a.id);
const metricB = subgraphMetrics?.find((x) => x.subgraphID === b.id);
if (!metricA || !metricB) {
return 0;
}
if (topCategory === "latency") {
return metricB.latency - metricA.latency;
}
if (topCategory === "errorRate") {
return metricB.errorRate - metricA.errorRate;
}
return metricB.requestRate - metricA.requestRate;
});
if (!showAll) {
tempSubgraphs = tempSubgraphs.slice(0, 5);
}
return tempSubgraphs;
}, [showAll, topCategory, graphData?.subgraphs, subgraphMetrics]);
useEffect(() => {
if (!graphData?.graph) return;
const buildGraphs = (subgraphs: Subgraph[]): Graph[] => {
const rootName = supportsFederation ? graphData.graph?.name : "router";
const graphs: Graph[] = [
{
id: `root-${rootName}`,
kind: "graph",
name: rootName!,
parentId: "",
errorRate: federatedGraphMetrics?.errorRate,
requestRate: federatedGraphMetrics?.requestRate,
},
];
for (const subgraph of subgraphs) {
graphs.push({
id: `root-${graphData.graph?.name}-${subgraph.name}}`,
subgraphId: subgraph.id,
kind: "subgraph",
name: subgraph.name,
parentId: graphs[0].id,
});
}
return graphs;
};
let graphs = buildGraphs(subgraphs);
const buildNodes = (spans: Graph[]): Node[] => {
return spans.map((span, index) => {
if (span.kind === "graph") {
return {
id: span.id,
type: "span",
data: {
label: span.name,
kind: span.kind,
parentId: span.parentId,
errorRate: federatedGraphMetrics?.errorRate,
requestRate: federatedGraphMetrics?.requestRate,
},
connectable: false,
deletable: false,
position: {
x: 0,
y: 0,
},
};
}
const sm = subgraphMetrics?.find(
(x) => x.subgraphID === span.subgraphId,
);
return {
id: span.id,
type: "span",
data: {
label: span.name,
kind: span.kind,
parentId: span.parentId,
errorRate: sm?.errorRate,
requestRate: sm?.requestRate,
},
connectable: false,
deletable: false,
position: {
x: 0,
y: 0,
},
};
});
};
const buildEdges = (spans: Graph[]): Edge[] => {
return spans
.filter((s) => !!s.parentId)
.map((span, index) => {
const sm = subgraphMetrics?.find(
(x) => x.subgraphID === span.subgraphId,
);
return {
id: span.id,
source: span.parentId,
animated: true,
target: span.id,
type: "metricsEdge",
data: {
latency: sm?.latency,
},
};
});
};
if (!graphs.length) {
return;
}
const n = buildNodes(graphs);
const e = buildEdges(graphs);
// Create a new directed graph per graph
// otherwise a single graph will contain all nodes and edges from all graphs
// this will cause the layout to be incorrect when not all nodes and edges have unique ids
const dagreGraph = new dagre.graphlib.Graph();
dagreGraph.setDefaultEdgeLabel(function () {
return { minlen: 5, weight: 1 };
});
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(
dagreGraph,
n,
e,
);
setNodes(layoutedNodes);
setEdges(layoutedEdges);
}, [