feat: add the `layers` filed and associate layers dashboards for the service topology nodes (#370)

This commit is contained in:
Fine0830 2024-01-30 15:59:16 +08:00 committed by GitHub
parent 860af150f7
commit ccb4d78f6b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 89 additions and 24 deletions

View File

@ -23,6 +23,7 @@ export const ServicesTopology = {
name name
type type
isReal isReal
layers
} }
calls { calls {
id id

View File

@ -16,7 +16,6 @@
*/ */
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { store } from "@/store"; import { store } from "@/store";
import type { Service } from "@/types/selector";
import type { Node, Call, HierarchyNode, ServiceHierarchy, InstanceHierarchy } from "@/types/topology"; import type { Node, Call, HierarchyNode, ServiceHierarchy, InstanceHierarchy } from "@/types/topology";
import graphql from "@/graphql"; import graphql from "@/graphql";
import { useSelectorStore } from "@/store/modules/selectors"; import { useSelectorStore } from "@/store/modules/selectors";
@ -88,12 +87,9 @@ export const topologyStore = defineStore({
}, },
setTopology(data: { nodes: Node[]; calls: Call[] }) { setTopology(data: { nodes: Node[]; calls: Call[] }) {
const obj = {} as Recordable; const obj = {} as Recordable;
const services = useSelectorStore().services;
const nodes = (data.nodes || []).reduce((prev: Node[], next: Node) => { const nodes = (data.nodes || []).reduce((prev: Node[], next: Node) => {
if (!obj[next.id]) { if (!obj[next.id]) {
obj[next.id] = true; obj[next.id] = true;
const s = services.filter((d: Service) => d.id === next.id)[0] || {};
next.layer = s.layers ? s.layers[0] : null;
prev.push(next); prev.push(next);
} }
return prev; return prev;
@ -603,7 +599,12 @@ export const topologyStore = defineStore({
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const { currentService } = useSelectorStore(); const { currentService } = useSelectorStore();
const id = this.node ? this.node.id : (currentService || {}).id; const id = this.node ? this.node.id : (currentService || {}).id;
const layer = this.node ? this.node.layer : dashboardStore.layerId; let layer = dashboardStore.layerId;
if (this.node) {
layer = this.node.layers.includes(dashboardStore.layerId)
? dashboardStore.layerId
: this.node.layers.filter((d: string) => d !== dashboardStore.layerId)[0];
}
if (!(id && layer)) { if (!(id && layer)) {
return new Promise((resolve) => resolve({})); return new Promise((resolve) => resolve({}));
} }
@ -659,7 +660,7 @@ export const topologyStore = defineStore({
return metrics; return metrics;
}, },
async queryHierarchyNodeExpressions(expressions: string[], layer: string) { async queryHierarchyNodeExpressions(expressions: string[], layer: string) {
const nodes = this.hierarchyServiceNodes.filter((n: Node) => n.layer === layer); const nodes = this.hierarchyServiceNodes.filter((n: HierarchyNode) => n.layer === layer);
if (!nodes.length) { if (!nodes.length) {
this.setHierarchyNodeMetricValue({}, layer); this.setHierarchyNodeMetricValue({}, layer);
return; return;
@ -672,7 +673,7 @@ export const topologyStore = defineStore({
this.setHierarchyNodeMetricValue(metrics, layer); this.setHierarchyNodeMetricValue(metrics, layer);
}, },
async queryHierarchyInstanceNodeExpressions(expressions: string[], layer: string) { async queryHierarchyInstanceNodeExpressions(expressions: string[], layer: string) {
const nodes = this.hierarchyInstanceNodes.filter((n: Node) => n.layer === layer); const nodes = this.hierarchyInstanceNodes.filter((n: HierarchyNode) => n.layer === layer);
if (!expressions.length) { if (!expressions.length) {
this.setHierarchyInstanceNodeMetricValue({}, layer); this.setHierarchyInstanceNodeMetricValue({}, layer);

View File

@ -43,7 +43,7 @@ export interface Node {
name: string; name: string;
type: string; type: string;
isReal: boolean; isReal: boolean;
layer?: string; layers: string[];
serviceName?: string; serviceName?: string;
height?: number; height?: number;
width?: number; width?: number;
@ -52,6 +52,7 @@ export interface Node {
level?: number; level?: number;
l?: number; l?: number;
key?: string; key?: string;
layer?: string;
} }
export interface ServiceHierarchy { export interface ServiceHierarchy {

View File

@ -124,11 +124,17 @@ limitations under the License. -->
left: operationsPos.x + 5 + 'px', left: operationsPos.x + 5 + 'px',
}" }"
> >
<span v-for="(item, index) of items" :key="index" @click="item.func(item.dashboard)"> <span v-for="(item, index) of items" :key="index" @click="item.func(item)">
{{ item.title }} {{ item.title }}
</span> </span>
</div> </div>
<el-dialog v-model="hierarchyRelated" :destroy-on-close="true" @closed="hierarchyRelated = false" width="640px"> <el-dialog
v-model="hierarchyRelated"
:title="getHierarchyTitle()"
:destroy-on-close="true"
@closed="hierarchyRelated = false"
width="640px"
>
<div class="hierarchy-related"> <div class="hierarchy-related">
<hierarchy-map :config="config" /> <hierarchy-map :config="config" />
</div> </div>
@ -160,6 +166,7 @@ limitations under the License. -->
import { layout, computeLevels, changeNode } from "../components/utils/layout"; import { layout, computeLevels, changeNode } from "../components/utils/layout";
import zoom from "@/views/dashboard/related/components/utils/zoom"; import zoom from "@/views/dashboard/related/components/utils/zoom";
import { useQueryTopologyExpressionsProcessor } from "@/hooks/useExpressionsProcessor"; import { useQueryTopologyExpressionsProcessor } from "@/hooks/useExpressionsProcessor";
import { ConfigFieldTypes } from "@/views/dashboard/data";
/*global Nullable, defineProps */ /*global Nullable, defineProps */
const props = defineProps({ const props = defineProps({
config: { config: {
@ -268,6 +275,20 @@ limitations under the License. -->
currentNode.value = null; currentNode.value = null;
} }
function getHierarchyTitle() {
if (!currentNode.value) {
return;
}
if (currentNode.value.layers.includes(dashboardStore.layerId)) {
return `${dashboardStore.layerId} --> ${currentNode.value.name}`;
}
const layer = currentNode.value.layers.filter((d: string) => d !== dashboardStore.layerId);
if (layer.length) {
return `${layer[0]} --> ${currentNode.value.name}`;
}
return "";
}
async function initLegendMetrics() { async function initLegendMetrics() {
if (!topologyStore.nodes.length) { if (!topologyStore.nodes.length) {
return; return;
@ -410,19 +431,15 @@ limitations under the License. -->
topologyStore.setLink(null); topologyStore.setLink(null);
operationsPos.x = event.offsetX; operationsPos.x = event.offsetX;
operationsPos.y = event.offsetY; operationsPos.y = event.offsetY;
if (d.layer === String(dashboardStore.layerId)) { if (d.layers.includes(dashboardStore.layerId)) {
setNodeTools(settings.value.nodeDashboard); setNodeTools(settings.value.nodeDashboard);
return; return;
} }
items.value = [ initNodeMenus();
{ id: "hierarchyServices", title: "Hierarchy Services", func: handleHierarchyRelatedServices },
{ id: "inspect", title: "Inspect", func: handleInspect },
{ id: "alerting", title: "Alerting", func: handleGoAlerting },
];
} }
function handleLinkClick(event: MouseEvent, d: Call) { function handleLinkClick(event: MouseEvent, d: Call) {
event.stopPropagation(); event.stopPropagation();
if (d.sourceObj.layer !== dashboardStore.layerId || d.targetObj.layer !== dashboardStore.layerId) { if (!d.sourceObj.layers.includes(dashboardStore.layerId) || !d.targetObj.layers.includes(dashboardStore.layerId)) {
return; return;
} }
topologyStore.setNode(null); topologyStore.setNode(null);
@ -462,25 +479,34 @@ limitations under the License. -->
topologyStore.setNode(null); topologyStore.setNode(null);
topologyStore.setLink(null); topologyStore.setLink(null);
} }
function handleGoEndpoint(name: string) { function handleGoEndpoint(params: { dashboard: string }) {
if (!params.dashboard) {
return;
}
const origin = dashboardStore.entity; const origin = dashboardStore.entity;
const path = `/dashboard/${dashboardStore.layerId}/${EntityType[2].value}/${topologyStore.node.id}/${name}`; const path = `/dashboard/${dashboardStore.layerId}/${EntityType[2].value}/${topologyStore.node.id}/${params.dashboard}`;
const routeUrl = router.resolve({ path }); const routeUrl = router.resolve({ path });
window.open(routeUrl.href, "_blank"); window.open(routeUrl.href, "_blank");
dashboardStore.setEntity(origin); dashboardStore.setEntity(origin);
} }
function handleGoInstance(name: string) { function handleGoInstance(params: { dashboard: string }) {
if (!params.dashboard) {
return;
}
const origin = dashboardStore.entity; const origin = dashboardStore.entity;
const path = `/dashboard/${dashboardStore.layerId}/${EntityType[3].value}/${topologyStore.node.id}/${name}`; const path = `/dashboard/${dashboardStore.layerId}/${EntityType[3].value}/${topologyStore.node.id}/${params.dashboard}`;
const routeUrl = router.resolve({ path }); const routeUrl = router.resolve({ path });
window.open(routeUrl.href, "_blank"); window.open(routeUrl.href, "_blank");
dashboardStore.setEntity(origin); dashboardStore.setEntity(origin);
} }
function handleGoDashboard(name: string) { function handleGoDashboard(params: { dashboard: string }) {
if (!params.dashboard) {
return;
}
const origin = dashboardStore.entity; const origin = dashboardStore.entity;
const path = `/dashboard/${dashboardStore.layerId}/${EntityType[0].value}/${topologyStore.node.id}/${name}`; const path = `/dashboard/${dashboardStore.layerId}/${EntityType[0].value}/${topologyStore.node.id}/${params.dashboard}`;
const routeUrl = router.resolve({ path }); const routeUrl = router.resolve({ path });
window.open(routeUrl.href, "_blank"); window.open(routeUrl.href, "_blank");
@ -492,6 +518,28 @@ limitations under the License. -->
window.open(routeUrl.href, "_blank"); window.open(routeUrl.href, "_blank");
} }
function handleGoLayerDashboard(param: { id: string }) {
if (!(param.id && currentNode.value)) {
return;
}
const origin = dashboardStore.entity;
const { dashboard } = getDashboard(
{
layer: param.id,
entity: EntityType[0].value,
},
ConfigFieldTypes.ISDEFAULT,
);
if (!dashboard) {
return ElMessage.info("No Dashboard");
}
const path = `/dashboard/${param.id}/${EntityType[0].value}/${currentNode.value.id}/${dashboard.name}`;
const routeUrl = router.resolve({ path });
window.open(routeUrl.href, "_blank");
dashboardStore.setEntity(origin);
}
async function backToTopology() { async function backToTopology() {
loading.value = true; loading.value = true;
await freshNodes(); await freshNodes();
@ -520,12 +568,26 @@ limitations under the License. -->
settings.value = config; settings.value = config;
setNodeTools(config.nodeDashboard); setNodeTools(config.nodeDashboard);
} }
function setNodeTools(nodeDashboard: any) { function initNodeMenus() {
items.value = [ items.value = [
{ id: "hierarchyServices", title: "Hierarchy Services", func: handleHierarchyRelatedServices }, { id: "hierarchyServices", title: "Hierarchy Services", func: handleHierarchyRelatedServices },
{ id: "inspect", title: "Inspect", func: handleInspect }, { id: "inspect", title: "Inspect", func: handleInspect },
{ id: "alerting", title: "Alerting", func: handleGoAlerting }, { id: "alerting", title: "Alerting", func: handleGoAlerting },
]; ];
if (!currentNode.value) {
return;
}
const diffLayers = currentNode.value.layers.filter((l: string) => l !== dashboardStore.layerId);
for (const l of diffLayers) {
items.value.push({
id: l,
title: `${l} Dashboard`,
func: handleGoLayerDashboard,
});
}
}
function setNodeTools(nodeDashboard: any) {
initNodeMenus();
if (!(nodeDashboard && nodeDashboard.length)) { if (!(nodeDashboard && nodeDashboard.length)) {
return; return;
} }