feat: inspect nodes

This commit is contained in:
Qiuxia Fan 2022-02-14 21:06:05 +08:00
parent 413646261d
commit 4badfffac8
6 changed files with 109 additions and 55 deletions

View File

@ -0,0 +1,17 @@
<!-- 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. -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M21 11.016v1.969h-14.156l3.563 3.609-1.406 1.406-6-6 6-6 1.406 1.406-3.563 3.609h14.156z"></path>
</svg>

After

Width:  |  Height:  |  Size: 977 B

View File

@ -14,26 +14,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
export const ServiceTopology = {
variable: "$duration: Duration!, $serviceId: ID!",
query: `
topology: getServiceTopology(duration: $duration, serviceId: $serviceId) {
nodes {
id
name
type
isReal
}
calls {
id
source
detectPoints
target
sourceComponents
targetComponents
}
}`,
};
export const GlobalTopology = { export const GlobalTopology = {
variable: "$duration: Duration!", variable: "$duration: Duration!",
query: ` query: `

View File

@ -17,13 +17,11 @@
import { import {
InstanceTopology, InstanceTopology,
EndpointTopology, EndpointTopology,
ServiceTopology,
GlobalTopology, GlobalTopology,
ServicesTopology, ServicesTopology,
} from "../fragments/topology"; } from "../fragments/topology";
export const getGlobalTopology = `query queryData(${GlobalTopology.variable}) {${GlobalTopology.query}}`; export const getGlobalTopology = `query queryData(${GlobalTopology.variable}) {${GlobalTopology.query}}`;
export const getInstanceTopology = `query queryData(${InstanceTopology.variable}) {${InstanceTopology.query}}`; export const getInstanceTopology = `query queryData(${InstanceTopology.variable}) {${InstanceTopology.query}}`;
export const getServiceTopology = `query queryData(${ServiceTopology.variable}) {${ServiceTopology.query}}`;
export const getEndpointTopology = `query queryData(${EndpointTopology.variable}) {${EndpointTopology.query}}`; export const getEndpointTopology = `query queryData(${EndpointTopology.variable}) {${EndpointTopology.query}}`;
export const getServicesTopology = `query queryData(${ServicesTopology.variable}) {${ServicesTopology.query}}`; export const getServicesTopology = `query queryData(${ServicesTopology.variable}) {${ServicesTopology.query}}`;

View File

@ -68,13 +68,13 @@ export const topologyStore = defineStore({
setLinkClientMetrics(m: { id: string; value: unknown }[]) { setLinkClientMetrics(m: { id: string; value: unknown }[]) {
this.linkClientMetrics = m; this.linkClientMetrics = m;
}, },
async getServiceTopology() { async getServiceTopology(id: string) {
const serviceId = useSelectorStore().currentService.id; const serviceIds = [id];
const duration = useAppStoreWithOut().durationTime; const duration = useAppStoreWithOut().durationTime;
const res: AxiosResponse = await graphql const res: AxiosResponse = await graphql
.query("getServiceTopology") .query("getServicesTopology")
.params({ .params({
serviceId, serviceIds,
duration, duration,
}); });
if (!res.data.errors) { if (!res.data.errors) {

View File

@ -25,13 +25,11 @@ limitations under the License. -->
v-show="data.widget?.tips" v-show="data.widget?.tips"
/> />
</el-tooltip> </el-tooltip>
<el-popover <el-popover placement="bottom" trigger="click" :width="100">
placement="bottom"
trigger="click"
:style="{ width: '100px' }"
>
<template #reference> <template #reference>
<Icon iconName="ellipsis_v" size="middle" class="operation" /> <span>
<Icon iconName="ellipsis_v" size="middle" class="operation" />
</span>
</template> </template>
<div class="tools" @click="editConfig"> <div class="tools" @click="editConfig">
<span>{{ t("edit") }}</span> <span>{{ t("edit") }}</span>

View File

@ -22,12 +22,18 @@ limitations under the License. -->
<div class="setting" v-show="showSetting"> <div class="setting" v-show="showSetting">
<Settings @update="updateSettings" /> <Settings @update="updateSettings" />
</div> </div>
<Icon <div class="tool">
@click="setConfig" <span class="switch-icon ml-5" title="Settings">
class="switch-icon" <Icon @click="setConfig" size="middle" iconName="settings" />
size="middle" </span>
iconName="settings" <span class="switch-icon ml-5" title="Back to overview topology">
/> <Icon
@click="backToTopology"
size="middle"
iconName="keyboard_backspace"
/>
</span>
</div>
<div <div
class="operations-list" class="operations-list"
v-if="topologyStore.node && topologyStore.node.isReal" v-if="topologyStore.node && topologyStore.node.isReal"
@ -43,8 +49,8 @@ limitations under the License. -->
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount, reactive } from "vue"; import { ref, onMounted, onBeforeUnmount, reactive, watch } from "vue";
// import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import * as d3 from "d3"; import * as d3 from "d3";
import d3tip from "d3-tip"; import d3tip from "d3-tip";
import zoom from "./utils/zoom"; import zoom from "./utils/zoom";
@ -53,6 +59,7 @@ import nodeElement from "./utils/nodeElement";
import { linkElement, anchorElement, arrowMarker } from "./utils/linkElement"; import { linkElement, anchorElement, arrowMarker } from "./utils/linkElement";
import topoLegend from "./utils/legend"; import topoLegend from "./utils/legend";
import { Node, Call } from "@/types/topology"; import { Node, Call } from "@/types/topology";
import { useSelectorStore } from "@/store/modules/selectors";
import { useTopologyStore } from "@/store/modules/topology"; import { useTopologyStore } from "@/store/modules/topology";
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import { EntityType } from "../../data"; import { EntityType } from "../../data";
@ -61,7 +68,8 @@ import { ElMessage } from "element-plus";
import Settings from "./Settings.vue"; import Settings from "./Settings.vue";
/*global Nullable */ /*global Nullable */
// const { t } = useI18n(); const { t } = useI18n();
const selectorStore = useSelectorStore();
const topologyStore = useTopologyStore(); const topologyStore = useTopologyStore();
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const height = ref<number>(document.body.clientHeight - 90); const height = ref<number>(document.body.clientHeight - 90);
@ -80,7 +88,10 @@ const legend = ref<any>(null);
const showSetting = ref<boolean>(false); const showSetting = ref<boolean>(false);
const settings = ref<any>({}); const settings = ref<any>({});
const operationsPos = reactive<{ x: number; y: number }>({ x: NaN, y: NaN }); const operationsPos = reactive<{ x: number; y: number }>({ x: NaN, y: NaN });
const items = ref([{ id: "alarm", title: "Alarm", func: handleGoAlarm }]); const items = ref([
{ id: "inspect", title: "Inspect", func: handleInspect },
{ id: "alarm", title: "Alarm", func: handleGoAlarm },
]);
onMounted(async () => { onMounted(async () => {
loading.value = true; loading.value = true;
@ -96,6 +107,10 @@ onMounted(async () => {
.attr("class", "topo-svg") .attr("class", "topo-svg")
.attr("height", height.value) .attr("height", height.value)
.attr("width", width.value); .attr("width", width.value);
await init();
update();
});
async function init() {
tip.value = (d3tip as any)().attr("class", "d3-tip").offset([-8, 0]); tip.value = (d3tip as any)().attr("class", "d3-tip").offset([-8, 0]);
graph.value = svg.value.append("g").attr("class", "topo-svg-graph"); graph.value = svg.value.append("g").attr("class", "topo-svg-graph");
graph.value.call(tip.value); graph.value.call(tip.value);
@ -117,10 +132,10 @@ onMounted(async () => {
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
topologyStore.setNode(null); topologyStore.setNode(null);
topologyStore.setLink(null);
// showSetting.value = false; // showSetting.value = false;
}); });
update(); }
});
function ticked() { function ticked() {
link.value.attr( link.value.attr(
"d", "d",
@ -196,10 +211,7 @@ function update() {
dragended: dragended, dragended: dragended,
handleNodeClick: handleNodeClick, handleNodeClick: handleNodeClick,
tipHtml: (data: Node) => { tipHtml: (data: Node) => {
const nodeMetrics: string[] = settings.value.nodeMetrics; const nodeMetrics: string[] = settings.value.nodeMetrics || [];
if (!nodeMetrics) {
return;
}
const html = nodeMetrics.map((m) => { const html = nodeMetrics.map((m) => {
const metric = const metric =
topologyStore.nodeMetrics[m].values.filter( topologyStore.nodeMetrics[m].values.filter(
@ -247,7 +259,13 @@ function update() {
return ` <div class="mb-5"><span class="grey">${m}: </span>${metric.value}</div>`; return ` <div class="mb-5"><span class="grey">${m}: </span>${metric.value}</div>`;
} }
}); });
const html = [...htmlServer, ...htmlClient].join(" "); const html = [
...htmlServer,
...htmlClient,
`<div><span class="grey">${t(
"detectPoint"
)}:</span>${data.detectPoints.join(" | ")}</div>`,
].join(" ");
return html; return html;
}, },
@ -285,6 +303,18 @@ function update() {
} }
} }
} }
async function handleInspect() {
svg.value.selectAll(".topo-svg-graph").remove();
const resp = await topologyStore.getServiceTopology(topologyStore.node.id);
if (resp.errors) {
ElMessage.error(resp.errors);
}
topologyStore.setNode(null);
topologyStore.setLink(null);
await init();
update();
}
function handleGoEndpoint() { function handleGoEndpoint() {
const path = `/dashboard/${dashboardStore.layerId}/Endpoint/${topologyStore.node.id}/${settings.value.endpointDashboard}`; const path = `/dashboard/${dashboardStore.layerId}/Endpoint/${topologyStore.node.id}/${settings.value.endpointDashboard}`;
const routeUrl = router.resolve({ path }); const routeUrl = router.resolve({ path });
@ -309,11 +339,25 @@ function handleGoAlarm() {
window.open(routeUrl.href, "_blank"); window.open(routeUrl.href, "_blank");
} }
async function backToTopology() {
svg.value.selectAll(".topo-svg-graph").remove();
const resp = await topologyStore.getServicesTopology();
if (resp.errors) {
ElMessage.error(resp.errors);
}
await init();
update();
topologyStore.setNode(null);
topologyStore.setLink(null);
}
async function getTopology() { async function getTopology() {
let resp; let resp;
switch (dashboardStore.entity) { switch (dashboardStore.entity) {
case EntityType[0].value: case EntityType[0].value:
resp = await topologyStore.getServiceTopology(); resp = await topologyStore.getServiceTopology(
selectorStore.currentService.id
);
break; break;
case EntityType[1].value: case EntityType[1].value:
resp = await topologyStore.getServicesTopology(); resp = await topologyStore.getServicesTopology();
@ -336,7 +380,10 @@ function resize() {
svg.value.attr("height", height.value).attr("width", width.value); svg.value.attr("height", height.value).attr("width", width.value);
} }
function updateSettings(config: any) { function updateSettings(config: any) {
items.value = [{ id: "alarm", title: "Alarm", func: handleGoAlarm }]; items.value = [
{ id: "inspect", title: "Inspect", func: handleInspect },
{ id: "alarm", title: "Alarm", func: handleGoAlarm },
];
settings.value = config; settings.value = config;
if (config.nodeDashboard) { if (config.nodeDashboard) {
items.value.push({ items.value.push({
@ -363,6 +410,12 @@ function updateSettings(config: any) {
onBeforeUnmount(() => { onBeforeUnmount(() => {
window.removeEventListener("resize", resize); window.removeEventListener("resize", resize);
}); });
// watch(
// () => [topologyStore.nodes, topologyStore.calls],
// () => {
// update();
// }
// );
</script> </script>
<style lang="scss"> <style lang="scss">
.micro-topo-chart { .micro-topo-chart {
@ -372,7 +425,7 @@ onBeforeUnmount(() => {
position: absolute; position: absolute;
top: 20px; top: 20px;
right: 20px; right: 20px;
width: 350px; width: 360px;
height: 700px; height: 700px;
background-color: #2b3037; background-color: #2b3037;
overflow: auto; overflow: auto;
@ -399,17 +452,25 @@ onBeforeUnmount(() => {
} }
span:hover { span:hover {
color: #217ef2; color: #409eff;
background-color: #eee;
} }
} }
.switch-icon { .tool {
position: absolute; position: absolute;
top: 22px; top: 22px;
right: 0; right: 0;
color: #ccc; }
.switch-icon {
cursor: pointer; cursor: pointer;
transition: all 0.5ms linear; transition: all 0.5ms linear;
background-color: #252a2f99;
color: #ddd;
display: inline-block;
padding: 5px 8px 8px;
border-radius: 3px;
} }
.topo-svg { .topo-svg {