diff --git a/src/graphql/fetch.ts b/src/graphql/fetch.ts new file mode 100644 index 00000000..4521b43d --- /dev/null +++ b/src/graphql/fetch.ts @@ -0,0 +1,37 @@ +/** + * 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 axios, { AxiosResponse } from "axios"; +import { cancelToken } from "@/utils/cancelToken"; + +async function query(param: { + queryStr: string; + conditions: { [key: string]: unknown }; +}) { + const res: AxiosResponse = await axios.post( + "/graphql", + { query: param.queryStr, variables: { ...param.conditions } }, + { cancelToken: cancelToken() } + ); + if (res.data.errors) { + res.data.errors = res.data.errors + .map((e: { message: string }) => e.message) + .join(" "); + } + return res; +} + +export default query; diff --git a/src/graphql/index.ts b/src/graphql/index.ts index aa112b08..b1224520 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -27,7 +27,7 @@ const query: { [key: string]: string } = { ...dashboard, ...topology, }; -class Graph { +class Graphql { private queryData = ""; public query(queryData: string) { this.queryData = queryData; @@ -57,4 +57,4 @@ class Graph { } } -export default new Graph(); +export default new Graphql(); diff --git a/src/hooks/useProcessor.ts b/src/hooks/useProcessor.ts index 94b8c449..2b2ce80b 100644 --- a/src/hooks/useProcessor.ts +++ b/src/hooks/useProcessor.ts @@ -258,3 +258,28 @@ export function usePodsSource( }); return data; } +export function useQueryNodesMetrics(metrics: string[], ids: string[]) { + const appStore = useAppStoreWithOut(); + const conditions: { [key: string]: unknown } = { + duration: appStore.durationTime, + ids, + }; + const variables: string[] = [`$duration: Duration!`, `$ids: [ID!]!`]; + const fragmentList = metrics.map((d: string, index: number) => { + conditions[`m${index}`] = d; + variables.push(`$m${index}: String!`); + + return `${d}: getValues(metric: { + name: $m${index} + ids: $ids + }, duration: $duration) { + values { + id + value + } + }`; + }); + const queryStr = `query queryData(${variables}) {${fragmentList.join(" ")}}`; + + return { queryStr, conditions }; +} diff --git a/src/store/modules/dashboard.ts b/src/store/modules/dashboard.ts index 4a903c24..3500c87d 100644 --- a/src/store/modules/dashboard.ts +++ b/src/store/modules/dashboard.ts @@ -18,6 +18,7 @@ import { defineStore } from "pinia"; import { store } from "@/store"; import { LayoutConfig } from "@/types/dashboard"; import graphql from "@/graphql"; +import query from "@/graphql/fetch"; import { ConfigData, ConfigData1, @@ -29,8 +30,7 @@ import { useAppStoreWithOut } from "@/store/modules/app"; import { useSelectorStore } from "@/store/modules/selectors"; import { NewControl } from "../data"; import { Duration } from "@/types/app"; -import axios, { AxiosResponse } from "axios"; -import { cancelToken } from "@/utils/cancelToken"; +import { AxiosResponse } from "axios"; interface DashboardState { showConfig: boolean; layout: LayoutConfig[]; @@ -212,11 +212,7 @@ export const dashboardStore = defineStore({ queryStr: string; conditions: { [key: string]: unknown }; }) { - const res: AxiosResponse = await axios.post( - "/graphql", - { query: param.queryStr, variables: { ...param.conditions } }, - { cancelToken: cancelToken() } - ); + const res: AxiosResponse = await query(param); return res.data; }, }, diff --git a/src/store/modules/topology.ts b/src/store/modules/topology.ts index 40a950ae..04af4b3f 100644 --- a/src/store/modules/topology.ts +++ b/src/store/modules/topology.ts @@ -18,15 +18,17 @@ import { defineStore } from "pinia"; import { store } from "@/store"; import { Node, Call } from "@/types/topology"; import graphql from "@/graphql"; -import { AxiosResponse } from "axios"; import { useSelectorStore } from "@/store/modules/selectors"; import { useAppStoreWithOut } from "@/store/modules/app"; +import { AxiosResponse } from "axios"; +import query from "@/graphql/fetch"; interface TopologyState { node: Node | null; call: Call | null; calls: Call[]; nodes: Node[]; + nodeMetrics: { id: string; value: unknown }[]; } export const topologyStore = defineStore({ @@ -36,6 +38,7 @@ export const topologyStore = defineStore({ nodes: [], node: null, call: null, + nodeMetrics: [], }), actions: { setNode(node: Node) { @@ -48,75 +51,6 @@ export const topologyStore = defineStore({ this.nodes = data.nodes; this.calls = data.calls; }, - async setMetrics(data: { nodes: Node[]; calls: Call[] }, params: any) { - const ids = data.nodes.map((i: Node) => i.id); - const idsC = data.calls - .filter((i: Call) => i.detectPoints.includes("CLIENT")) - .map((b: any) => b.id); - const idsS = data.calls - .filter((i: Call) => i.detectPoints.includes("SERVER")) - .map((b: any) => b.id); - const res: AxiosResponse = await graphql - .query("queryTopoInfo") - .params({ ...params, ids, idsC, idsS }); - const resInfo = res.data.data; - if (!resInfo.sla) { - return this.setTopology(data); - } - for (let i = 0; i < resInfo.sla.values.length; i += 1) { - for (let j = 0; j < data.nodes.length; j += 1) { - if (data.nodes[j].id === resInfo.sla.values[i].id) { - data.nodes[j] = { - ...data.nodes[j], - isGroupActive: true, - sla: resInfo.sla.values[i].value - ? resInfo.sla.values[i].value / 100 - : -1, - cpm: resInfo.nodeCpm.values[i] - ? resInfo.nodeCpm.values[i].value - : -1, - latency: resInfo.nodeLatency.values[i] - ? resInfo.nodeLatency.values[i].value - : -1, - }; - } - } - } - if (!resInfo.cpmC) { - return this.setTopology(data); - } - for (let i = 0; i < resInfo.cpmC.values.length; i += 1) { - for (let j = 0; j < data.calls.length; j += 1) { - if (data.calls[j].id === resInfo.cpmC.values[i].id) { - data.calls[j] = { - ...data.calls[j], - isGroupActive: true, - cpm: resInfo.cpmC.values[i] ? resInfo.cpmC.values[i].value : "", - latency: resInfo.latencyC.values[i] - ? resInfo.latencyC.values[i].value - : "", - }; - } - } - } - if (!resInfo.cpmS) { - return this.setTopology(data); - } - for (let i = 0; i < resInfo.cpmS.values.length; i += 1) { - for (let j = 0; j < data.calls.length; j += 1) { - if (data.calls[j].id === resInfo.cpmS.values[i].id) { - data.calls[j] = { - ...data.calls[j], - cpm: resInfo.cpmS.values[i] ? resInfo.cpmS.values[i].value : "", - latency: resInfo.latencyS.values[i] - ? resInfo.latencyS.values[i].value - : "", - }; - } - } - } - this.setTopology(data); - }, async getServiceTopology() { const serviceId = useSelectorStore().currentService.id; const duration = useAppStoreWithOut().durationTime; @@ -127,7 +61,7 @@ export const topologyStore = defineStore({ duration, }); if (!res.data.errors) { - this.setMetrics(res.data.data.topology, { duration }); + this.setTopology(res.data.data.topology); } return res.data; }, @@ -139,7 +73,7 @@ export const topologyStore = defineStore({ duration, }); if (!res.data.errors) { - this.setMetrics(res.data.data.topology, { duration }); + this.setTopology(res.data.data.topology); } return res.data; }, @@ -173,6 +107,18 @@ export const topologyStore = defineStore({ } return res.data; }, + async getNodeMetrics(param: { + queryStr: string; + conditions: { [key: string]: unknown }; + }) { + const res: AxiosResponse = await query(param); + + if (res.data.errors) { + return res.data; + } + this.nodeMetrics = res.data.data; + return res.data; + }, }, }); diff --git a/src/views/dashboard/related/topology/Graph.vue b/src/views/dashboard/related/topology/Graph.vue index e4140980..93c20506 100644 --- a/src/views/dashboard/related/topology/Graph.vue +++ b/src/views/dashboard/related/topology/Graph.vue @@ -122,8 +122,9 @@ onMounted(async () => { event.stopPropagation(); event.preventDefault(); topologyStore.setNode(null); - showSetting.value = false; + // showSetting.value = false; }); + update(); }); function ticked() { link.value.attr( @@ -182,6 +183,9 @@ function handleLinkClick(event: any, d: Call) { } function update() { // node element + if (!node.value || !link.value) { + return; + } node.value = node.value.data(topologyStore.nodes, (d: Node) => d.id); node.value.exit().remove(); node.value = nodeElement( @@ -192,9 +196,25 @@ function update() { dragged: dragged, dragended: dragended, handleNodeClick: handleNodeClick, + tipHtml: (data: Node) => { + const nodeMetrics: string[] = settings.value.nodeMetrics; + if (!nodeMetrics) { + return; + } + const html = nodeMetrics.map((m) => { + const metric = + topologyStore.nodeMetrics[m].values.filter( + (val: { id: string; value: unknown }) => val.id === data.id + )[0] || {}; + return `