From 1a6ab74be829e375b7135f5a895195e1b0f75eb7 Mon Sep 17 00:00:00 2001 From: Qiuxia Fan Date: Tue, 8 Feb 2022 14:10:24 +0800 Subject: [PATCH] feat: add graph for topology --- src/store/modules/topology.ts | 52 ++++ src/types/topology.d.ts | 41 +++ src/views/dashboard/Edit.vue | 3 +- .../dashboard/related/topology/Graph.vue | 245 +++++++++++++++++- .../topology/utils/{icons.js => icons.ts} | 21 +- .../topology/utils/{legend.js => legend.ts} | 6 +- .../utils/{linkElement.js => linkElement.ts} | 14 +- .../utils/{nodeElement.js => nodeElement.ts} | 28 +- .../utils/{simulation.js => simulation.ts} | 11 +- .../topology/utils/{tool.js => tool.ts} | 33 +-- .../topology/utils/{zoom.js => zoom.ts} | 2 +- 11 files changed, 405 insertions(+), 51 deletions(-) create mode 100644 src/store/modules/topology.ts create mode 100644 src/types/topology.d.ts rename src/views/dashboard/related/topology/utils/{icons.js => icons.ts} (72%) rename src/views/dashboard/related/topology/utils/{legend.js => legend.ts} (93%) rename src/views/dashboard/related/topology/utils/{linkElement.js => linkElement.ts} (76%) rename src/views/dashboard/related/topology/utils/{nodeElement.js => nodeElement.ts} (76%) rename src/views/dashboard/related/topology/utils/{simulation.js => simulation.ts} (86%) rename src/views/dashboard/related/topology/utils/{tool.js => tool.ts} (69%) rename src/views/dashboard/related/topology/utils/{zoom.js => zoom.ts} (96%) diff --git a/src/store/modules/topology.ts b/src/store/modules/topology.ts new file mode 100644 index 00000000..201311f4 --- /dev/null +++ b/src/store/modules/topology.ts @@ -0,0 +1,52 @@ +/** + * 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 { defineStore } from "pinia"; +import { store } from "@/store"; +import { Node, Call } from "@/types/topology"; + +interface TopologyState { + node: Node | null; + call: Call | null; + calls: Call[]; + nodes: Node[]; +} + +export const topologyStore = defineStore({ + id: "topology", + state: (): TopologyState => ({ + calls: [], + nodes: [], + node: null, + call: null, + }), + actions: { + setNode(node: Node) { + this.node = node; + }, + setLink(link: Call) { + this.call = link; + }, + setTopology(nodes: Node[], links: Call[]) { + this.nodes = nodes; + this.calls = links; + }, + }, +}); + +export function useTopologyStore(): any { + return topologyStore(store); +} diff --git a/src/types/topology.d.ts b/src/types/topology.d.ts new file mode 100644 index 00000000..eb419746 --- /dev/null +++ b/src/types/topology.d.ts @@ -0,0 +1,41 @@ +/** + * 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. + */ +export interface Call { + avgResponseTime: number; + cpm: number; + isAlert: boolean; + source: string | any; + target: string | any; + id: string; + detectPoints: string[]; + type?: string; + sourceObj?: any; +} +export interface Node { + apdex: number; + avgResponseTime: number; + cpm: number; + id: string; + isAlarm: boolean; + name: string; + numOfServer: number; + numOfServerAlarm: number; + numOfServiceAlarm: number; + sla: number; + type: string; + isReal: boolean; +} diff --git a/src/views/dashboard/Edit.vue b/src/views/dashboard/Edit.vue index 10cb8759..a3c23d9a 100644 --- a/src/views/dashboard/Edit.vue +++ b/src/views/dashboard/Edit.vue @@ -31,7 +31,7 @@ limitations under the License. --> :destroy-on-close="true" @closed="dashboardStore.setTopology(false)" > - topology + @@ -41,6 +41,7 @@ import GridLayout from "./panel/Layout.vue"; // import { LayoutConfig } from "@/types/dashboard"; import Tool from "./panel/Tool.vue"; import ConfigEdit from "./configuration/ConfigEdit.vue"; +import Graph from "./related/topology/Graph.vue"; import { useDashboardStore } from "@/store/modules/dashboard"; import { useAppStoreWithOut } from "@/store/modules/app"; diff --git a/src/views/dashboard/related/topology/Graph.vue b/src/views/dashboard/related/topology/Graph.vue index 380b6a36..20b896c5 100644 --- a/src/views/dashboard/related/topology/Graph.vue +++ b/src/views/dashboard/related/topology/Graph.vue @@ -13,9 +13,252 @@ 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. --> + diff --git a/src/views/dashboard/related/topology/utils/icons.js b/src/views/dashboard/related/topology/utils/icons.ts similarity index 72% rename from src/views/dashboard/related/topology/utils/icons.js rename to src/views/dashboard/related/topology/utils/icons.ts index 8c669f60..bfd40353 100755 --- a/src/views/dashboard/related/topology/utils/icons.js +++ b/src/views/dashboard/related/topology/utils/icons.ts @@ -16,19 +16,22 @@ */ const requireComponent = require.context("../../assets", false, /\.png$/); -const result = {}; -function capitalizeFirstLetter(str) { +const result: { [key: string]: string } = {}; +function capitalizeFirstLetter(str: string) { return str.toUpperCase(); } -function validateFileName(str) { - return ( - /^\S+\.png$/.test(str) && - str.replace(/^\S+\/(\w+)\.png$/, (rs, $1) => capitalizeFirstLetter($1)) - ); +function validateFileName(str: string): string | undefined { + if (/^\S+\.png$/.test(str)) { + return str.replace(/^\S+\/(\w+)\.png$/, (rs, $1) => + capitalizeFirstLetter($1) + ); + } } -requireComponent.keys().forEach((filePath) => { +requireComponent.keys().forEach((filePath: string) => { const componentConfig = requireComponent(filePath); const fileName = validateFileName(filePath); - result[fileName] = componentConfig; + if (fileName) { + result[fileName] = componentConfig; + } }); export default result; diff --git a/src/views/dashboard/related/topology/utils/legend.js b/src/views/dashboard/related/topology/utils/legend.ts similarity index 93% rename from src/views/dashboard/related/topology/utils/legend.js rename to src/views/dashboard/related/topology/utils/legend.ts index 7c075674..be34a06b 100644 --- a/src/views/dashboard/related/topology/utils/legend.js +++ b/src/views/dashboard/related/topology/utils/legend.ts @@ -16,7 +16,11 @@ */ import icons from "./icons"; -export default function topoLegend(graph, clientHeight, clientWidth) { +export default function topoLegend( + graph: any, + clientHeight: number, + clientWidth: number +) { for (const item of ["CUBE", "CUBEERROR"]) { graph .append("image") diff --git a/src/views/dashboard/related/topology/utils/linkElement.js b/src/views/dashboard/related/topology/utils/linkElement.ts similarity index 76% rename from src/views/dashboard/related/topology/utils/linkElement.js rename to src/views/dashboard/related/topology/utils/linkElement.ts index c56c14d7..13bd3514 100644 --- a/src/views/dashboard/related/topology/utils/linkElement.js +++ b/src/views/dashboard/related/topology/utils/linkElement.ts @@ -14,26 +14,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export const linkElement = (graph) => { +export const linkElement = (graph: any) => { const linkEnter = graph .append("path") .attr("class", "topo-line") - .attr("stroke", (d) => (d.cpm ? "#217EF25f" : "#6a6d7777")); + .attr("stroke", (d: { cpm: number }) => + d.cpm ? "#217EF25f" : "#6a6d7777" + ); return linkEnter; }; -export const anchorElement = (graph, funcs, tip) => { +export const anchorElement = (graph: any, funcs: any, tip: any) => { const linkEnter = graph .append("circle") .attr("class", "topo-line-anchor") .attr("r", 5) - .attr("fill", (d) => (d.cpm ? "#217EF25f" : "#6a6d7777")) - .on("mouseover", function (d) { + .attr("fill", (d: { cpm: number }) => (d.cpm ? "#217EF25f" : "#6a6d7777")) + .on("mouseover", function (d: unknown) { tip.html(funcs.$tip).show(d, this); }) .on("mouseout", function () { tip.hide(this); }) - .on("click", (d) => { + .on("click", (d: unknown) => { funcs.handleLinkClick(d); }); return linkEnter; diff --git a/src/views/dashboard/related/topology/utils/nodeElement.js b/src/views/dashboard/related/topology/utils/nodeElement.ts similarity index 76% rename from src/views/dashboard/related/topology/utils/nodeElement.js rename to src/views/dashboard/related/topology/utils/nodeElement.ts index c81136f8..30412892 100644 --- a/src/views/dashboard/related/topology/utils/nodeElement.js +++ b/src/views/dashboard/related/topology/utils/nodeElement.ts @@ -17,7 +17,7 @@ import icons from "./icons"; icons["KAFKA-CONSUMER"] = icons.KAFKA; -export default (d3, graph, tool, funcs, tip) => { +export default (d3: any, graph: any, tool: any, funcs: any, tip: any) => { const nodeEnter = graph .append("g") .call( @@ -27,22 +27,22 @@ export default (d3, graph, tool, funcs, tip) => { .on("drag", funcs.dragged) .on("end", funcs.dragended) ) - .on("mouseover", function (d) { - tip.html((data) => `
${data.name}
`).show(d, this); + .on("mouseover", function (d: any) { + tip.html((data: any) => `
${data.name}
`).show(d, this); }) .on("mouseout", function () { tip.hide(this); }) - .on("click", (d) => { + .on("click", (event: any, d: any) => { event.stopPropagation(); - event.preventDefault(); - tool.attr("style", "display: none"); + // event.preventDefault(); + // tool.attr("style", "display: none"); funcs.handleNodeClick(d); - if (d.isReal) { - tool - .attr("transform", `translate(${d.x},${d.y - 20})`) - .attr("style", "display: block"); - } + // if (d.isReal) { + // tool + // .attr("transform", `translate(${d.x},${d.y - 20})`) + // .attr("style", "display: block"); + // } }); nodeEnter .append("image") @@ -51,7 +51,7 @@ export default (d3, graph, tool, funcs, tip) => { .attr("x", 2) .attr("y", 10) .attr("style", "cursor: move;") - .attr("xlink:href", (d) => + .attr("xlink:href", (d: { isReal: number; sla: number; cpm: number }) => d.sla < 95 && d.isReal && d.cpm > 1 ? icons.CUBEERROR : icons.CUBE ); nodeEnter @@ -68,7 +68,7 @@ export default (d3, graph, tool, funcs, tip) => { .attr("height", 18) .attr("x", 13) .attr("y", -7) - .attr("xlink:href", (d) => + .attr("xlink:href", (d: { type: string }) => !d.type || d.type === "N/A" ? icons.UNDEFINED : icons[d.type.toUpperCase().replace("-", "")] @@ -79,7 +79,7 @@ export default (d3, graph, tool, funcs, tip) => { .attr("text-anchor", "middle") .attr("x", 22) .attr("y", 70) - .text((d) => + .text((d: { name: string }) => d.name.length > 20 ? `${d.name.substring(0, 20)}...` : d.name ); return nodeEnter; diff --git a/src/views/dashboard/related/topology/utils/simulation.js b/src/views/dashboard/related/topology/utils/simulation.ts similarity index 86% rename from src/views/dashboard/related/topology/utils/simulation.js rename to src/views/dashboard/related/topology/utils/simulation.ts index 58f37b4d..e05fbad1 100644 --- a/src/views/dashboard/related/topology/utils/simulation.js +++ b/src/views/dashboard/related/topology/utils/simulation.ts @@ -14,7 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export const simulationInit = (d3, data_nodes, data_links, ticked) => { +export const simulationInit = ( + d3: any, + data_nodes: any, + dataLinks: any, + ticked: any +) => { const simulation = d3 .forceSimulation(data_nodes) .force( @@ -26,7 +31,7 @@ export const simulationInit = (d3, data_nodes, data_links, ticked) => { .force("charge", d3.forceManyBody().strength(-520)) .force( "link", - d3.forceLink(data_links).id((d) => d.id) + d3.forceLink(dataLinks).id((d: { id: string }) => d.id) ) .force( "center", @@ -38,7 +43,7 @@ export const simulationInit = (d3, data_nodes, data_links, ticked) => { return simulation; }; -export const simulationSkip = (d3, simulation, ticked) => { +export const simulationSkip = (d3: any, simulation: any, ticked: any) => { d3.timeout(() => { const n = Math.ceil( Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay()) diff --git a/src/views/dashboard/related/topology/utils/tool.js b/src/views/dashboard/related/topology/utils/tool.ts similarity index 69% rename from src/views/dashboard/related/topology/utils/tool.js rename to src/views/dashboard/related/topology/utils/tool.ts index 98a9f81c..89ddd0a8 100644 --- a/src/views/dashboard/related/topology/utils/tool.js +++ b/src/views/dashboard/related/topology/utils/tool.ts @@ -15,40 +15,43 @@ * limitations under the License. */ const requireComponent = require.context("./tool", false, /\.png$/); +const icons: { [key: string]: string } = {}; -const icons = {}; -function capitalizeFirstLetter(str) { +function capitalizeFirstLetter(str: string) { return str.toUpperCase(); } -function validateFileName(str) { - return ( - /^\S+\.png$/.test(str) && - str.replace(/^\S+\/(\w+)\.png$/, (rs, $1) => capitalizeFirstLetter($1)) - ); +function validateFileName(str: string): string | undefined { + if (/^\S+\.png$/.test(str)) { + return str.replace(/^\S+\/(\w+)\.png$/, (rs, $1) => + capitalizeFirstLetter($1) + ); + } } -requireComponent.keys().forEach((filePath) => { +requireComponent.keys().forEach((filePath: string) => { const componentConfig = requireComponent(filePath); const fileName = validateFileName(filePath); - icons[fileName] = componentConfig; + if (fileName) { + icons[fileName] = componentConfig; + } }); -const Hexagon = (side, r, cx, cy) => { +const Hexagon = (side: number, r: number, cx: number, cy: number) => { let path = ""; for (let i = 0; i < side; i += 1) { - let x = Math.cos(((2 / side) * i + 1 / side) * Math.PI) * r + cx; - let y = -Math.sin(((2 / side) * i + 1 / side) * Math.PI) * r + cy; + const x = Math.cos(((2 / side) * i + 1 / side) * Math.PI) * r + cx; + const y = -Math.sin(((2 / side) * i + 1 / side) * Math.PI) * r + cy; path += !i ? `M${x},${y} ` : `L${x},${y} `; if (i == side - 1) path += "Z"; } return path; }; -export default (graph, data) => { +export default (graph: any, data: any) => { const tool = graph.append("g").attr("class", "topo-tool"); const side = 6; for (let i = 0; i < data.length; i += 1) { - let x = Math.cos((2 / side) * i * Math.PI) * 34; - let y = -Math.sin((2 / side) * i * Math.PI) * 34; + const x = Math.cos((2 / side) * i * Math.PI) * 34; + const y = -Math.sin((2 / side) * i * Math.PI) * 34; const tool_g = tool .append("g") .attr("class", "topo-tool-i") diff --git a/src/views/dashboard/related/topology/utils/zoom.js b/src/views/dashboard/related/topology/utils/zoom.ts similarity index 96% rename from src/views/dashboard/related/topology/utils/zoom.js rename to src/views/dashboard/related/topology/utils/zoom.ts index f7046881..8a41d294 100644 --- a/src/views/dashboard/related/topology/utils/zoom.js +++ b/src/views/dashboard/related/topology/utils/zoom.ts @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export default (d3, graph) => +export default (d3: any, graph: any) => d3 .zoom() .scaleExtent([0.3, 10])