feat: get services topology with depth

This commit is contained in:
Qiuxia Fan 2022-02-18 15:10:10 +08:00
parent f8d4b60f2b
commit 85f70e4142
6 changed files with 176 additions and 121 deletions

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 GlobalTopology = {
variable: "$duration: Duration!",
query: `
topology: getGlobalTopology(duration: $duration) {
nodes {
id
name
type
isReal
}
calls {
id
source
detectPoints
target
sourceComponents
targetComponents
}
}`,
};
export const ServicesTopology = { export const ServicesTopology = {
variable: "$duration: Duration!, $serviceIds: [ID!]!", variable: "$duration: Duration!, $serviceIds: [ID!]!",
query: ` query: `
@ -49,8 +29,6 @@ export const ServicesTopology = {
source source
detectPoints detectPoints
target target
sourceComponents
targetComponents
} }
}`, }`,
}; };

View File

@ -17,11 +17,9 @@
import { import {
InstanceTopology, InstanceTopology,
EndpointTopology, EndpointTopology,
GlobalTopology,
ServicesTopology, ServicesTopology,
} from "../fragments/topology"; } from "../fragments/topology";
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 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

@ -55,27 +55,6 @@ export const topologyStore = defineStore({
setLink(link: Call) { setLink(link: Call) {
this.call = link; 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[] }) { setInstanceTopology(data: { nodes: Node[]; calls: Call[] }) {
for (const call of data.calls) { for (const call of data.calls) {
for (const node of data.nodes) { for (const node of data.nodes) {
@ -91,36 +70,38 @@ export const topologyStore = defineStore({
this.calls = data.calls; this.calls = data.calls;
this.nodes = data.nodes; this.nodes = data.nodes;
}, },
setEndpointTopology(data: { nodes: Node[]; calls: Call[] }) { setTopology(data: { nodes: Node[]; calls: Call[] }) {
const obj = {} as any; const obj = {} as any;
let nodes = []; const nodes = data.nodes.reduce((prev: Node[], next: Node) => {
let calls = [];
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 service =
useSelectorStore().services.filter(
(d: Service) => d.id === next.id
)[0] || {};
next.layer = service.layers ? service.layers[0] : null;
prev.push(next); prev.push(next);
} }
return prev; return prev;
}, []); }, []);
calls = data.calls.reduce((prev: Call[], next: Call) => { const calls = data.calls.reduce((prev: Call[], next: Call) => {
if (!obj[next.id]) { if (!obj[next.id]) {
obj[next.id] = true; obj[next.id] = true;
next.value = next.value || 1; 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); prev.push(next);
} }
return prev; 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.calls = calls;
this.nodes = nodes; this.nodes = nodes;
}, },
@ -133,8 +114,111 @@ export const topologyStore = defineStore({
setLinkClientMetrics(m: { id: string; value: unknown }[]) { setLinkClientMetrics(m: { id: string; value: unknown }[]) {
this.linkClientMetrics = m; this.linkClientMetrics = m;
}, },
async getServiceTopology(id: string) { async getDepthServiceTopology(serviceIds: string[], depth: number) {
const serviceIds = [id]; 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 duration = useAppStoreWithOut().durationTime;
const res: AxiosResponse = await graphql const res: AxiosResponse = await graphql
.query("getServicesTopology") .query("getServicesTopology")
@ -142,36 +226,10 @@ export const topologyStore = defineStore({
serviceIds, serviceIds,
duration, duration,
}); });
if (!res.data.errors) { if (res.data.errors) {
this.setTopology(res.data.data.topology);
}
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; return res.data.data.topology;
},
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;
}, },
async getInstanceTopology() { async getInstanceTopology() {
const serverServiceId = useSelectorStore().currentService.id; const serverServiceId = useSelectorStore().currentService.id;
@ -196,7 +254,7 @@ export const topologyStore = defineStore({
.map((item: Node) => item.id) .map((item: Node) => item.id)
.filter((d: string) => !endpointIds.includes(d)); .filter((d: string) => !endpointIds.includes(d));
if (!ids.length) { if (!ids.length) {
this.setEndpointTopology(res); this.setTopology(res);
return; return;
} }
const json = await this.getEndpointTopology(ids); const json = await this.getEndpointTopology(ids);
@ -207,7 +265,7 @@ export const topologyStore = defineStore({
if (!pods.length) { if (!pods.length) {
const nodes = [...res.nodes, ...json.nodes]; const nodes = [...res.nodes, ...json.nodes];
const calls = [...res.calls, ...json.calls]; const calls = [...res.calls, ...json.calls];
this.setEndpointTopology({ nodes, calls }); this.setTopology({ nodes, calls });
return; return;
} }
const topo = await this.getEndpointTopology(pods); const topo = await this.getEndpointTopology(pods);
@ -220,7 +278,7 @@ export const topologyStore = defineStore({
if (!endpoints.length) { if (!endpoints.length) {
const nodes = [...res.nodes, ...json.nodes, ...topo.nodes]; const nodes = [...res.nodes, ...json.nodes, ...topo.nodes];
const calls = [...res.calls, ...json.calls, ...topo.calls]; const calls = [...res.calls, ...json.calls, ...topo.calls];
this.setEndpointTopology({ nodes, calls }); this.setTopology({ nodes, calls });
return; return;
} }
const data = await this.getEndpointTopology(endpoints); const data = await this.getEndpointTopology(endpoints);
@ -244,7 +302,7 @@ export const topologyStore = defineStore({
...topo.calls, ...topo.calls,
...data.calls, ...data.calls,
]; ];
this.setEndpointTopology({ nodes, calls }); this.setTopology({ nodes, calls });
return; return;
} }
const toposObj = await this.getEndpointTopology(nodeIds); const toposObj = await this.getEndpointTopology(nodeIds);
@ -262,7 +320,7 @@ export const topologyStore = defineStore({
...data.calls, ...data.calls,
...toposObj.calls, ...toposObj.calls,
]; ];
this.setEndpointTopology({ nodes, calls }); this.setTopology({ nodes, calls });
} else { } else {
const nodes = [ const nodes = [
...res.nodes, ...res.nodes,
@ -276,21 +334,21 @@ export const topologyStore = defineStore({
...topo.calls, ...topo.calls,
...data.calls, ...data.calls,
]; ];
this.setEndpointTopology({ nodes, calls }); this.setTopology({ nodes, calls });
} }
} else { } else {
const nodes = [...res.nodes, ...json.nodes, ...topo.nodes]; const nodes = [...res.nodes, ...json.nodes, ...topo.nodes];
const calls = [...res.calls, ...json.calls, ...topo.calls]; const calls = [...res.calls, ...json.calls, ...topo.calls];
this.setEndpointTopology({ nodes, calls }); this.setTopology({ nodes, calls });
} }
} else { } else {
this.setEndpointTopology({ this.setTopology({
nodes: [...res.nodes, ...json.nodes], nodes: [...res.nodes, ...json.nodes],
calls: [...res.calls, ...json.calls], calls: [...res.calls, ...json.calls],
}); });
} }
} else { } else {
this.setEndpointTopology(res); this.setTopology(res);
} }
}, },
async getEndpointTopology(endpointIds: string[]) { async getEndpointTopology(endpointIds: string[]) {
@ -328,7 +386,7 @@ export const topologyStore = defineStore({
calls.push(...topo[key].calls); calls.push(...topo[key].calls);
nodes.push(...topo[key].nodes); nodes.push(...topo[key].nodes);
} }
// this.setEndpointTopology({ calls, nodes }); // this.setTopology({ calls, nodes });
return { calls, nodes }; return { calls, nodes };
}, },

View File

@ -169,7 +169,7 @@ export const ToolIcons = [
// { name: "save_alt", content: "Export", id: "export" }, // { name: "save_alt", content: "Export", id: "export" },
// { name: "folder_open", content: "Import", id: "import" }, // { name: "folder_open", content: "Import", id: "import" },
// { name: "settings", content: "Settings", id: "settings" }, // { 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" }, // { name: "save", content: "Apply", id: "apply" },
]; ];
export const ScopeType = [ export const ScopeType = [
@ -190,3 +190,7 @@ export enum LegendOpt {
VALUE = "value", VALUE = "value",
CONDITION = "condition", CONDITION = "condition",
} }
export const DepthList = ["1", "2", "3", "4", "5"].map((item: string) => ({
value: item,
label: item,
}));

View File

@ -23,6 +23,14 @@ limitations under the License. -->
<Settings @update="updateSettings" @updateNodes="freshNodes" /> <Settings @update="updateSettings" @updateNodes="freshNodes" />
</div> </div>
<div class="tool"> <div class="tool">
<span class="label">{{ t("currentDepth") }}</span>
<Selector
class="inputs"
:value="depth"
:options="DepthList"
placeholder="Select a option"
@change="changeDepth"
/>
<span class="switch-icon ml-5" title="Settings" @click="setConfig"> <span class="switch-icon ml-5" title="Settings" @click="setConfig">
<Icon size="middle" iconName="settings" /> <Icon size="middle" iconName="settings" />
</span> </span>
@ -66,10 +74,12 @@ import { Node, Call } from "@/types/topology";
import { useSelectorStore } from "@/store/modules/selectors"; 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, DepthList } from "../../../data";
import router from "@/router"; import router from "@/router";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import Settings from "./Settings.vue"; import Settings from "./Settings.vue";
import { Option } from "@/types/app";
import { Service } from "@/types/selector";
/*global Nullable */ /*global Nullable */
const { t } = useI18n(); const { t } = useI18n();
@ -98,6 +108,7 @@ const items = ref<
{ id: "inspect", title: "Inspect", func: handleInspect }, { id: "inspect", title: "Inspect", func: handleInspect },
{ id: "alarm", title: "Alarm", func: handleGoAlarm }, { id: "alarm", title: "Alarm", func: handleGoAlarm },
]); ]);
const depth = ref<string>("2");
onMounted(async () => { onMounted(async () => {
loading.value = true; loading.value = true;
@ -383,17 +394,15 @@ async function backToTopology() {
topologyStore.setLink(null); topologyStore.setLink(null);
} }
async function getTopology() { async function getTopology() {
let resp; const ids = selectorStore.services.map((d: Service) => d.id);
switch (dashboardStore.entity) { const serviceIds =
case EntityType[0].value: dashboardStore.entity === EntityType[0].value
resp = await topologyStore.getServiceTopology( ? [selectorStore.currentService.id]
selectorStore.currentService.id : ids;
const resp = await topologyStore.getDepthServiceTopology(
serviceIds,
Number(depth.value)
); );
break;
case EntityType[1].value:
resp = await topologyStore.getServicesTopology();
break;
}
return resp; return resp;
} }
function setConfig() { function setConfig() {
@ -442,6 +451,12 @@ async function freshNodes() {
await init(); await init();
update(); update();
} }
async function changeDepth(opt: Option[]) {
depth.value = opt[0].value;
await getTopology();
freshNodes();
}
onBeforeUnmount(() => { onBeforeUnmount(() => {
window.removeEventListener("resize", resize); window.removeEventListener("resize", resize);
}); });
@ -464,6 +479,12 @@ onBeforeUnmount(() => {
transition: all 0.5ms linear; transition: all 0.5ms linear;
} }
.label {
color: #ccc;
display: inline-block;
margin-right: 5px;
}
.operations-list { .operations-list {
position: absolute; position: absolute;
padding: 10px; padding: 10px;

View File

@ -19,7 +19,7 @@ limitations under the License. -->
<Selector <Selector
class="inputs" class="inputs"
:value="depth" :value="depth"
:options="depthList" :options="DepthList"
placeholder="Select a option" placeholder="Select a option"
@change="changeDepth" @change="changeDepth"
/> />
@ -73,7 +73,7 @@ import { Option } from "@/types/app";
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 { useSelectorStore } from "@/store/modules/selectors"; import { useSelectorStore } from "@/store/modules/selectors";
import { EntityType } from "../../../data"; import { EntityType, DepthList } from "../../../data";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import Sankey from "./Sankey.vue"; import Sankey from "./Sankey.vue";
import Settings from "./Settings.vue"; import Settings from "./Settings.vue";
@ -88,10 +88,6 @@ const height = ref<number>(document.body.clientHeight - 150);
const width = ref<number>(document.body.clientWidth - 40); const width = ref<number>(document.body.clientWidth - 40);
const showSettings = ref<boolean>(false); const showSettings = ref<boolean>(false);
const depth = ref<string>("2"); const depth = ref<string>("2");
const depthList = ["1", "2", "3", "4", "5"].map((item: string) => ({
value: item,
label: item,
}));
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 = [ const items = [