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
* 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
}
}`,
};

View File

@ -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}}`;

View File

@ -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 };
},

View File

@ -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,
}));

View File

@ -23,6 +23,14 @@ limitations under the License. -->
<Settings @update="updateSettings" @updateNodes="freshNodes" />
</div>
<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">
<Icon size="middle" iconName="settings" />
</span>
@ -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<string>("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;

View File

@ -19,7 +19,7 @@ limitations under the License. -->
<Selector
class="inputs"
:value="depth"
:options="depthList"
:options="DepthList"
placeholder="Select a option"
@change="changeDepth"
/>
@ -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<number>(document.body.clientHeight - 150);
const width = ref<number>(document.body.clientWidth - 40);
const showSettings = ref<boolean>(false);
const depth = ref<string>("2");
const depthList = ["1", "2", "3", "4", "5"].map((item: string) => ({
value: item,
label: item,
}));
const settings = ref<any>({});
const operationsPos = reactive<{ x: number; y: number }>({ x: NaN, y: NaN });
const items = [