From 85f70e4142baf7afce2b498a42e7eeee8e709cc6 Mon Sep 17 00:00:00 2001 From: Qiuxia Fan Date: Fri, 18 Feb 2022 15:10:10 +0800 Subject: [PATCH] feat: get services topology with depth --- src/graphql/fragments/topology.ts | 22 -- src/graphql/query/topology.ts | 2 - src/store/modules/topology.ts | 214 +++++++++++------- src/views/dashboard/data.ts | 6 +- .../related/topology/components/Graph.vue | 45 +++- .../topology/components/PodTopology.vue | 8 +- 6 files changed, 176 insertions(+), 121 deletions(-) diff --git a/src/graphql/fragments/topology.ts b/src/graphql/fragments/topology.ts index 6c57ef8b..7b84951c 100644 --- a/src/graphql/fragments/topology.ts +++ b/src/graphql/fragments/topology.ts @@ -14,26 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export const GlobalTopology = { - variable: "$duration: Duration!", - query: ` - topology: getGlobalTopology(duration: $duration) { - nodes { - id - name - type - isReal - } - calls { - id - source - detectPoints - target - sourceComponents - targetComponents - } - }`, -}; export const ServicesTopology = { variable: "$duration: Duration!, $serviceIds: [ID!]!", query: ` @@ -49,8 +29,6 @@ export const ServicesTopology = { source detectPoints target - sourceComponents - targetComponents } }`, }; diff --git a/src/graphql/query/topology.ts b/src/graphql/query/topology.ts index e07767da..705d94bc 100644 --- a/src/graphql/query/topology.ts +++ b/src/graphql/query/topology.ts @@ -17,11 +17,9 @@ import { InstanceTopology, EndpointTopology, - GlobalTopology, ServicesTopology, } from "../fragments/topology"; -export const getGlobalTopology = `query queryData(${GlobalTopology.variable}) {${GlobalTopology.query}}`; export const getInstanceTopology = `query queryData(${InstanceTopology.variable}) {${InstanceTopology.query}}`; export const getEndpointTopology = `query queryData(${EndpointTopology.variable}) {${EndpointTopology.query}}`; export const getServicesTopology = `query queryData(${ServicesTopology.variable}) {${ServicesTopology.query}}`; diff --git a/src/store/modules/topology.ts b/src/store/modules/topology.ts index 69b01e2c..0bc4f943 100644 --- a/src/store/modules/topology.ts +++ b/src/store/modules/topology.ts @@ -55,27 +55,6 @@ export const topologyStore = defineStore({ setLink(link: Call) { this.call = link; }, - setTopology(data: { nodes: Node[]; calls: Call[] }) { - this.nodes = data.nodes.map((n: Node) => { - const service = - useSelectorStore().services.filter( - (d: Service) => d.id === n.id - )[0] || {}; - n.layer = service.layers ? service.layers[0] : null; - return n; - }); - this.calls = data.calls.map((c: Call) => { - for (const s of useSelectorStore().services) { - if (c.source.id === s.id) { - c.source.layer = s.layers[0]; - } - if (c.target.id === s.id) { - c.target.layer = s.layers[0]; - } - } - return c; - }); - }, setInstanceTopology(data: { nodes: Node[]; calls: Call[] }) { for (const call of data.calls) { for (const node of data.nodes) { @@ -91,36 +70,38 @@ export const topologyStore = defineStore({ this.calls = data.calls; this.nodes = data.nodes; }, - setEndpointTopology(data: { nodes: Node[]; calls: Call[] }) { + setTopology(data: { nodes: Node[]; calls: Call[] }) { const obj = {} as any; - let nodes = []; - let calls = []; - nodes = data.nodes.reduce((prev: Node[], next: Node) => { + const nodes = data.nodes.reduce((prev: Node[], next: Node) => { if (!obj[next.id]) { obj[next.id] = true; + const service = + useSelectorStore().services.filter( + (d: Service) => d.id === next.id + )[0] || {}; + next.layer = service.layers ? service.layers[0] : null; prev.push(next); } return prev; }, []); - calls = data.calls.reduce((prev: Call[], next: Call) => { + const calls = data.calls.reduce((prev: Call[], next: Call) => { if (!obj[next.id]) { obj[next.id] = true; next.value = next.value || 1; + for (const node of data.nodes) { + if (next.source === node.id) { + next.sourceObj = node; + } + if (next.target === node.id) { + next.targetObj = node; + } + } + next.value = next.value || 1; prev.push(next); } return prev; }, []); - for (const call of calls) { - for (const node of nodes) { - if (call.source === node.id) { - call.sourceObj = node; - } - if (call.target === node.id) { - call.targetObj = node; - } - } - call.value = call.value || 1; - } + this.calls = calls; this.nodes = nodes; }, @@ -133,8 +114,111 @@ export const topologyStore = defineStore({ setLinkClientMetrics(m: { id: string; value: unknown }[]) { this.linkClientMetrics = m; }, - async getServiceTopology(id: string) { - const serviceIds = [id]; + async getDepthServiceTopology(serviceIds: string[], depth: number) { + const res = await this.getServicesTopology(serviceIds); + if (depth > 1) { + const ids = res.nodes + .map((item: Node) => item.id) + .filter((d: string) => !serviceIds.includes(d)); + if (!ids.length) { + this.setTopology(res); + return; + } + const json = await this.getServicesTopology(ids); + if (depth > 2) { + const pods = json.nodes + .map((item: Node) => item.id) + .filter((d: string) => ![...ids, ...serviceIds].includes(d)); + if (!pods.length) { + const nodes = [...res.nodes, ...json.nodes]; + const calls = [...res.calls, ...json.calls]; + this.setTopology({ nodes, calls }); + return; + } + const topo = await this.getServicesTopology(pods); + if (depth > 3) { + const services = topo.nodes + .map((item: Node) => item.id) + .filter( + (d: string) => ![...ids, ...pods, ...serviceIds].includes(d) + ); + if (!services.length) { + const nodes = [...res.nodes, ...json.nodes, ...topo.nodes]; + const calls = [...res.calls, ...json.calls, ...topo.calls]; + this.setTopology({ nodes, calls }); + return; + } + const data = await this.getServicesTopology(services); + if (depth > 4) { + const nodeIds = data.nodes + .map((item: Node) => item.id) + .filter( + (d: string) => + ![...services, ...ids, ...pods, ...serviceIds].includes(d) + ); + if (!nodeIds.length) { + const nodes = [ + ...res.nodes, + ...json.nodes, + ...topo.nodes, + ...data.nodes, + ]; + const calls = [ + ...res.calls, + ...json.calls, + ...topo.calls, + ...data.calls, + ]; + this.setTopology({ nodes, calls }); + return; + } + const toposObj = await this.getServicesTopology(nodeIds); + const nodes = [ + ...res.nodes, + ...json.nodes, + ...topo.nodes, + ...data.nodes, + ...toposObj.nodes, + ]; + const calls = [ + ...res.calls, + ...json.calls, + ...topo.calls, + ...data.calls, + ...toposObj.calls, + ]; + this.setTopology({ nodes, calls }); + } else { + const nodes = [ + ...res.nodes, + ...json.nodes, + ...topo.nodes, + ...data.nodes, + ]; + const calls = [ + ...res.calls, + ...json.calls, + ...topo.calls, + ...data.calls, + ]; + this.setTopology({ nodes, calls }); + } + } else { + const nodes = [...res.nodes, ...json.nodes, ...topo.nodes]; + const calls = [...res.calls, ...json.calls, ...topo.calls]; + this.setTopology({ nodes, calls }); + } + } else { + this.setTopology({ + nodes: [...res.nodes, ...json.nodes], + calls: [...res.calls, ...json.calls], + }); + } + } else { + this.setTopology(res); + } + }, + async getServicesTopology(serviceIds: string[]) { const duration = useAppStoreWithOut().durationTime; const res: AxiosResponse = await graphql .query("getServicesTopology") @@ -142,36 +226,10 @@ export const topologyStore = defineStore({ serviceIds, duration, }); - if (!res.data.errors) { - this.setTopology(res.data.data.topology); + if (res.data.errors) { + return res.data; } - return res.data; - }, - async getServicesTopology() { - const serviceIds = useSelectorStore().services.map((d: Service) => d.id); - const duration = useAppStoreWithOut().durationTime; - const res: AxiosResponse = await graphql - .query("getServicesTopology") - .params({ - serviceIds, - duration, - }); - if (!res.data.errors) { - this.setTopology(res.data.data.topology); - } - return res.data; - }, - async getGlobalTopology() { - const duration = useAppStoreWithOut().durationTime; - const res: AxiosResponse = await graphql - .query("getGlobalTopology") - .params({ - duration, - }); - if (!res.data.errors) { - this.setTopology(res.data.data.topology); - } - return res.data; + return res.data.data.topology; }, async getInstanceTopology() { const serverServiceId = useSelectorStore().currentService.id; @@ -196,7 +254,7 @@ export const topologyStore = defineStore({ .map((item: Node) => item.id) .filter((d: string) => !endpointIds.includes(d)); if (!ids.length) { - this.setEndpointTopology(res); + this.setTopology(res); return; } const json = await this.getEndpointTopology(ids); @@ -207,7 +265,7 @@ export const topologyStore = defineStore({ if (!pods.length) { const nodes = [...res.nodes, ...json.nodes]; const calls = [...res.calls, ...json.calls]; - this.setEndpointTopology({ nodes, calls }); + this.setTopology({ nodes, calls }); return; } const topo = await this.getEndpointTopology(pods); @@ -220,7 +278,7 @@ export const topologyStore = defineStore({ if (!endpoints.length) { const nodes = [...res.nodes, ...json.nodes, ...topo.nodes]; const calls = [...res.calls, ...json.calls, ...topo.calls]; - this.setEndpointTopology({ nodes, calls }); + this.setTopology({ nodes, calls }); return; } const data = await this.getEndpointTopology(endpoints); @@ -244,7 +302,7 @@ export const topologyStore = defineStore({ ...topo.calls, ...data.calls, ]; - this.setEndpointTopology({ nodes, calls }); + this.setTopology({ nodes, calls }); return; } const toposObj = await this.getEndpointTopology(nodeIds); @@ -262,7 +320,7 @@ export const topologyStore = defineStore({ ...data.calls, ...toposObj.calls, ]; - this.setEndpointTopology({ nodes, calls }); + this.setTopology({ nodes, calls }); } else { const nodes = [ ...res.nodes, @@ -276,21 +334,21 @@ export const topologyStore = defineStore({ ...topo.calls, ...data.calls, ]; - this.setEndpointTopology({ nodes, calls }); + this.setTopology({ nodes, calls }); } } else { const nodes = [...res.nodes, ...json.nodes, ...topo.nodes]; const calls = [...res.calls, ...json.calls, ...topo.calls]; - this.setEndpointTopology({ nodes, calls }); + this.setTopology({ nodes, calls }); } } else { - this.setEndpointTopology({ + this.setTopology({ nodes: [...res.nodes, ...json.nodes], calls: [...res.calls, ...json.calls], }); } } else { - this.setEndpointTopology(res); + this.setTopology(res); } }, async getEndpointTopology(endpointIds: string[]) { @@ -328,7 +386,7 @@ export const topologyStore = defineStore({ calls.push(...topo[key].calls); nodes.push(...topo[key].nodes); } - // this.setEndpointTopology({ calls, nodes }); + // this.setTopology({ calls, nodes }); return { calls, nodes }; }, diff --git a/src/views/dashboard/data.ts b/src/views/dashboard/data.ts index 5d8b9279..2f9cc42f 100644 --- a/src/views/dashboard/data.ts +++ b/src/views/dashboard/data.ts @@ -169,7 +169,7 @@ export const ToolIcons = [ // { name: "save_alt", content: "Export", id: "export" }, // { name: "folder_open", content: "Import", id: "import" }, // { name: "settings", content: "Settings", id: "settings" }, - { name: "device_hub", content: "Topology", id: "topology" }, + { name: "device_hub", content: "Add Topology", id: "topology" }, // { name: "save", content: "Apply", id: "apply" }, ]; export const ScopeType = [ @@ -190,3 +190,7 @@ export enum LegendOpt { VALUE = "value", CONDITION = "condition", } +export const DepthList = ["1", "2", "3", "4", "5"].map((item: string) => ({ + value: item, + label: item, +})); diff --git a/src/views/dashboard/related/topology/components/Graph.vue b/src/views/dashboard/related/topology/components/Graph.vue index f394f279..7761f224 100644 --- a/src/views/dashboard/related/topology/components/Graph.vue +++ b/src/views/dashboard/related/topology/components/Graph.vue @@ -23,6 +23,14 @@ limitations under the License. -->
+ {{ t("currentDepth") }} + @@ -66,10 +74,12 @@ import { Node, Call } from "@/types/topology"; import { useSelectorStore } from "@/store/modules/selectors"; import { useTopologyStore } from "@/store/modules/topology"; import { useDashboardStore } from "@/store/modules/dashboard"; -import { EntityType } from "../../../data"; +import { EntityType, DepthList } from "../../../data"; import router from "@/router"; import { ElMessage } from "element-plus"; import Settings from "./Settings.vue"; +import { Option } from "@/types/app"; +import { Service } from "@/types/selector"; /*global Nullable */ const { t } = useI18n(); @@ -98,6 +108,7 @@ const items = ref< { id: "inspect", title: "Inspect", func: handleInspect }, { id: "alarm", title: "Alarm", func: handleGoAlarm }, ]); +const depth = ref("2"); onMounted(async () => { loading.value = true; @@ -383,17 +394,15 @@ async function backToTopology() { topologyStore.setLink(null); } async function getTopology() { - let resp; - switch (dashboardStore.entity) { - case EntityType[0].value: - resp = await topologyStore.getServiceTopology( - selectorStore.currentService.id - ); - break; - case EntityType[1].value: - resp = await topologyStore.getServicesTopology(); - break; - } + const ids = selectorStore.services.map((d: Service) => d.id); + const serviceIds = + dashboardStore.entity === EntityType[0].value + ? [selectorStore.currentService.id] + : ids; + const resp = await topologyStore.getDepthServiceTopology( + serviceIds, + Number(depth.value) + ); return resp; } function setConfig() { @@ -442,6 +451,12 @@ async function freshNodes() { await init(); update(); } + +async function changeDepth(opt: Option[]) { + depth.value = opt[0].value; + await getTopology(); + freshNodes(); +} onBeforeUnmount(() => { window.removeEventListener("resize", resize); }); @@ -464,6 +479,12 @@ onBeforeUnmount(() => { transition: all 0.5ms linear; } + .label { + color: #ccc; + display: inline-block; + margin-right: 5px; + } + .operations-list { position: absolute; padding: 10px; diff --git a/src/views/dashboard/related/topology/components/PodTopology.vue b/src/views/dashboard/related/topology/components/PodTopology.vue index 3a58c22c..ff1579e4 100644 --- a/src/views/dashboard/related/topology/components/PodTopology.vue +++ b/src/views/dashboard/related/topology/components/PodTopology.vue @@ -19,7 +19,7 @@ limitations under the License. --> @@ -73,7 +73,7 @@ import { Option } from "@/types/app"; import { useTopologyStore } from "@/store/modules/topology"; import { useDashboardStore } from "@/store/modules/dashboard"; import { useSelectorStore } from "@/store/modules/selectors"; -import { EntityType } from "../../../data"; +import { EntityType, DepthList } from "../../../data"; import { ElMessage } from "element-plus"; import Sankey from "./Sankey.vue"; import Settings from "./Settings.vue"; @@ -88,10 +88,6 @@ const height = ref(document.body.clientHeight - 150); const width = ref(document.body.clientWidth - 40); const showSettings = ref(false); const depth = ref("2"); -const depthList = ["1", "2", "3", "4", "5"].map((item: string) => ({ - value: item, - label: item, -})); const settings = ref({}); const operationsPos = reactive<{ x: number; y: number }>({ x: NaN, y: NaN }); const items = [