import React, { useEffect, useRef, useState } from "react"; import { connect } from "react-redux"; import { RouteComponentProps } from "react-router-dom"; import { GlobalTime, serviceMapStore, getServiceMapItems, getDetailedServiceMapItems, } from "Src/store/actions"; import { Spin } from "antd"; import { StoreState } from "../../store/reducers"; import { getGraphData } from "./utils"; import SelectService from "./SelectService"; import { ForceGraph2D } from "react-force-graph"; interface ServiceMapProps extends RouteComponentProps { serviceMap: serviceMapStore; globalTime: GlobalTime; getServiceMapItems: Function; getDetailedServiceMapItems: Function; } interface graphNode { id: string; group: number; } interface graphLink { source: string; target: string; value: number; } export interface graphDataType { nodes: graphNode[]; links: graphLink[]; } const ServiceMap = (props: ServiceMapProps) => { const fgRef = useRef(); const { getDetailedServiceMapItems, getServiceMapItems, globalTime, serviceMap, } = props; useEffect(() => { getServiceMapItems(globalTime); getDetailedServiceMapItems(globalTime); }, []); if (!serviceMap.items.length || !serviceMap.services.length) { return ; } const zoomToService = (value: string) => { fgRef && fgRef.current.zoomToFit(700, 480, (e) => e.id === value); }; const { nodes, links } = getGraphData(serviceMap); const graphData = { nodes, links }; return (
fgRef.current.zoomToFit(100, 200)} graphData={graphData} nodeLabel="id" linkAutoColorBy={(d) => d.target} linkDirectionalParticles="value" linkDirectionalParticleSpeed={(d) => d.value} nodeCanvasObject={(node, ctx, globalScale) => { const label = node.id; const fontSize = node.fontSize; ctx.font = `${fontSize}px Sans-Serif`; const textWidth = ctx.measureText(label).width; const width = textWidth > node.width ? textWidth : node.width; const bckgDimensions = [width, node.height].map((n) => n + fontSize); // some padding ctx.fillStyle = node.color; ctx.fillRect( node.x - bckgDimensions[0] / 2, node.y - bckgDimensions[1] / 2, ...bckgDimensions, ); ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillStyle = "black"; ctx.fillText(label, node.x, node.y); node.__bckgDimensions = bckgDimensions; // to re-use in nodePointerAreaPaint }} onNodeClick={(node) => { const tooltip = document.querySelector(".graph-tooltip"); if (tooltip && node) { tooltip.innerHTML = `
${node.id}
P99 latency: ${node.p99 / 1000000}
Error Rate: ${node.errorRate}%
Request Per Sec: ${node.callRate}
`; } }} nodePointerAreaPaint={(node, color, ctx) => { ctx.fillStyle = color; const bckgDimensions = node.__bckgDimensions; bckgDimensions && ctx.fillRect( node.x - bckgDimensions[0] / 2, node.y - bckgDimensions[1] / 2, ...bckgDimensions, ); }} />
); }; const mapStateToProps = ( state: StoreState, ): { serviceMap: serviceMapStore; globalTime: GlobalTime; } => { return { serviceMap: state.serviceMap, globalTime: state.globalTime, }; }; export default connect(mapStateToProps, { getServiceMapItems: getServiceMapItems, getDetailedServiceMapItems: getDetailedServiceMapItems, })(ServiceMap);