diff --git a/src/views/dashboard/related/Topology.vue b/src/views/dashboard/related/topology/Graph.vue
similarity index 100%
rename from src/views/dashboard/related/Topology.vue
rename to src/views/dashboard/related/topology/Graph.vue
diff --git a/src/views/dashboard/related/topology/utils/icons.js b/src/views/dashboard/related/topology/utils/icons.js
new file mode 100755
index 00000000..8c669f60
--- /dev/null
+++ b/src/views/dashboard/related/topology/utils/icons.js
@@ -0,0 +1,34 @@
+/**
+ * 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.
+ */
+const requireComponent = require.context("../../assets", false, /\.png$/);
+
+const result = {};
+function capitalizeFirstLetter(str) {
+ return str.toUpperCase();
+}
+function validateFileName(str) {
+ return (
+ /^\S+\.png$/.test(str) &&
+ str.replace(/^\S+\/(\w+)\.png$/, (rs, $1) => capitalizeFirstLetter($1))
+ );
+}
+requireComponent.keys().forEach((filePath) => {
+ const componentConfig = requireComponent(filePath);
+ const fileName = validateFileName(filePath);
+ 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.js
new file mode 100644
index 00000000..7c075674
--- /dev/null
+++ b/src/views/dashboard/related/topology/utils/legend.js
@@ -0,0 +1,42 @@
+/**
+ * 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 icons from "./icons";
+
+export default function topoLegend(graph, clientHeight, clientWidth) {
+ for (const item of ["CUBE", "CUBEERROR"]) {
+ graph
+ .append("image")
+ .attr("width", 30)
+ .attr("height", 30)
+ .attr("x", clientWidth - (item === "CUBEERROR" ? 340 : 440))
+ .attr("y", clientHeight - 50)
+ .attr("xlink:href", () =>
+ item === "CUBEERROR" ? icons.CUBEERROR : icons.CUBE
+ );
+ graph
+ .append("text")
+ .attr("x", clientWidth - (item === "CUBEERROR" ? 310 : 410))
+ .attr("y", clientHeight - 30)
+ .text(() => {
+ return item === "CUBEERROR"
+ ? "Unhealthy (Successful Rate < 95% and Traffic > 1 call/min)"
+ : "Healthy";
+ })
+ .style("fill", "#efeff1")
+ .style("font-size", "11px");
+ }
+}
diff --git a/src/views/dashboard/related/topology/utils/linkElement.js b/src/views/dashboard/related/topology/utils/linkElement.js
new file mode 100644
index 00000000..c56c14d7
--- /dev/null
+++ b/src/views/dashboard/related/topology/utils/linkElement.js
@@ -0,0 +1,40 @@
+/**
+ * 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 const linkElement = (graph) => {
+ const linkEnter = graph
+ .append("path")
+ .attr("class", "topo-line")
+ .attr("stroke", (d) => (d.cpm ? "#217EF25f" : "#6a6d7777"));
+ return linkEnter;
+};
+export const anchorElement = (graph, funcs, tip) => {
+ const linkEnter = graph
+ .append("circle")
+ .attr("class", "topo-line-anchor")
+ .attr("r", 5)
+ .attr("fill", (d) => (d.cpm ? "#217EF25f" : "#6a6d7777"))
+ .on("mouseover", function (d) {
+ tip.html(funcs.$tip).show(d, this);
+ })
+ .on("mouseout", function () {
+ tip.hide(this);
+ })
+ .on("click", (d) => {
+ funcs.handleLinkClick(d);
+ });
+ return linkEnter;
+};
diff --git a/src/views/dashboard/related/topology/utils/nodeElement.js b/src/views/dashboard/related/topology/utils/nodeElement.js
new file mode 100644
index 00000000..c81136f8
--- /dev/null
+++ b/src/views/dashboard/related/topology/utils/nodeElement.js
@@ -0,0 +1,86 @@
+/**
+ * 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 icons from "./icons";
+
+icons["KAFKA-CONSUMER"] = icons.KAFKA;
+export default (d3, graph, tool, funcs, tip) => {
+ const nodeEnter = graph
+ .append("g")
+ .call(
+ d3
+ .drag()
+ .on("start", funcs.dragstart)
+ .on("drag", funcs.dragged)
+ .on("end", funcs.dragended)
+ )
+ .on("mouseover", function (d) {
+ tip.html((data) => `
${data.name}
`).show(d, this);
+ })
+ .on("mouseout", function () {
+ tip.hide(this);
+ })
+ .on("click", (d) => {
+ event.stopPropagation();
+ 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");
+ }
+ });
+ nodeEnter
+ .append("image")
+ .attr("width", 49)
+ .attr("height", 49)
+ .attr("x", 2)
+ .attr("y", 10)
+ .attr("style", "cursor: move;")
+ .attr("xlink:href", (d) =>
+ d.sla < 95 && d.isReal && d.cpm > 1 ? icons.CUBEERROR : icons.CUBE
+ );
+ nodeEnter
+ .append("image")
+ .attr("width", 32)
+ .attr("height", 32)
+ .attr("x", 6)
+ .attr("y", -10)
+ .attr("style", "opacity: 0.5;")
+ .attr("xlink:href", icons.LOCAL);
+ nodeEnter
+ .append("image")
+ .attr("width", 18)
+ .attr("height", 18)
+ .attr("x", 13)
+ .attr("y", -7)
+ .attr("xlink:href", (d) =>
+ !d.type || d.type === "N/A"
+ ? icons.UNDEFINED
+ : icons[d.type.toUpperCase().replace("-", "")]
+ );
+ nodeEnter
+ .append("text")
+ .attr("class", "topo-text")
+ .attr("text-anchor", "middle")
+ .attr("x", 22)
+ .attr("y", 70)
+ .text((d) =>
+ 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.js
new file mode 100644
index 00000000..58f37b4d
--- /dev/null
+++ b/src/views/dashboard/related/topology/utils/simulation.js
@@ -0,0 +1,51 @@
+/**
+ * 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 const simulationInit = (d3, data_nodes, data_links, ticked) => {
+ const simulation = d3
+ .forceSimulation(data_nodes)
+ .force(
+ "collide",
+ d3.forceCollide().radius(() => 60)
+ )
+ .force("yPos", d3.forceY().strength(1))
+ .force("xPos", d3.forceX().strength(1))
+ .force("charge", d3.forceManyBody().strength(-520))
+ .force(
+ "link",
+ d3.forceLink(data_links).id((d) => d.id)
+ )
+ .force(
+ "center",
+ d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2 - 20)
+ )
+ .on("tick", ticked)
+ .stop();
+ simulationSkip(d3, simulation, ticked);
+ return simulation;
+};
+
+export const simulationSkip = (d3, simulation, ticked) => {
+ d3.timeout(() => {
+ const n = Math.ceil(
+ Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay())
+ );
+ for (let i = 0; i < n; i += 1) {
+ simulation.tick();
+ ticked();
+ }
+ });
+};
diff --git a/src/views/dashboard/related/topology/utils/tool.js b/src/views/dashboard/related/topology/utils/tool.js
new file mode 100644
index 00000000..98a9f81c
--- /dev/null
+++ b/src/views/dashboard/related/topology/utils/tool.js
@@ -0,0 +1,70 @@
+/**
+ * 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.
+ */
+const requireComponent = require.context("./tool", false, /\.png$/);
+
+const icons = {};
+function capitalizeFirstLetter(str) {
+ return str.toUpperCase();
+}
+function validateFileName(str) {
+ return (
+ /^\S+\.png$/.test(str) &&
+ str.replace(/^\S+\/(\w+)\.png$/, (rs, $1) => capitalizeFirstLetter($1))
+ );
+}
+requireComponent.keys().forEach((filePath) => {
+ const componentConfig = requireComponent(filePath);
+ const fileName = validateFileName(filePath);
+ icons[fileName] = componentConfig;
+});
+
+const Hexagon = (side, r, cx, cy) => {
+ 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;
+ path += !i ? `M${x},${y} ` : `L${x},${y} `;
+ if (i == side - 1) path += "Z";
+ }
+ return path;
+};
+
+export default (graph, data) => {
+ 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 tool_g = tool
+ .append("g")
+ .attr("class", "topo-tool-i")
+ .on("click", data[i].click);
+ tool_g
+ .append("path")
+ .attr("class", "tool-hexagon")
+ .attr("d", Hexagon(6, 17, x, y));
+ tool_g
+ .append("svg:image")
+ .attr("width", 14)
+ .attr("height", 14)
+ .attr("x", x - 7)
+ .attr("y", y - 7)
+ .attr("style", "opacity: 0.8")
+ .attr("xlink:href", icons[data[i].icon]);
+ }
+ return tool;
+};
diff --git a/src/views/dashboard/related/topology/utils/tool/ALARM.png b/src/views/dashboard/related/topology/utils/tool/ALARM.png
new file mode 100644
index 00000000..85009cb2
Binary files /dev/null and b/src/views/dashboard/related/topology/utils/tool/ALARM.png differ
diff --git a/src/views/dashboard/related/topology/utils/tool/API.png b/src/views/dashboard/related/topology/utils/tool/API.png
new file mode 100644
index 00000000..385dcaf6
Binary files /dev/null and b/src/views/dashboard/related/topology/utils/tool/API.png differ
diff --git a/src/views/dashboard/related/topology/utils/tool/ENDPOINT.png b/src/views/dashboard/related/topology/utils/tool/ENDPOINT.png
new file mode 100644
index 00000000..de26135f
Binary files /dev/null and b/src/views/dashboard/related/topology/utils/tool/ENDPOINT.png differ
diff --git a/src/views/dashboard/related/topology/utils/tool/INSTANCE.png b/src/views/dashboard/related/topology/utils/tool/INSTANCE.png
new file mode 100644
index 00000000..f0f1dbff
Binary files /dev/null and b/src/views/dashboard/related/topology/utils/tool/INSTANCE.png differ
diff --git a/src/views/dashboard/related/topology/utils/tool/TRACE.png b/src/views/dashboard/related/topology/utils/tool/TRACE.png
new file mode 100644
index 00000000..2fe870f0
Binary files /dev/null and b/src/views/dashboard/related/topology/utils/tool/TRACE.png differ
diff --git a/src/views/dashboard/related/topology/utils/zoom.js b/src/views/dashboard/related/topology/utils/zoom.js
new file mode 100644
index 00000000..f7046881
--- /dev/null
+++ b/src/views/dashboard/related/topology/utils/zoom.js
@@ -0,0 +1,26 @@
+/**
+ * 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 default (d3, graph) =>
+ d3
+ .zoom()
+ .scaleExtent([0.3, 10])
+ .on("zoom", () => {
+ graph.attr(
+ "transform",
+ `translate(${d3.event.transform.x},${d3.event.transform.y})scale(${d3.event.transform.k})`
+ );
+ });