diff --git a/frontend/dash/src/App.vue b/frontend/dash/src/App.vue index 877f871..e9e1da6 100644 --- a/frontend/dash/src/App.vue +++ b/frontend/dash/src/App.vue @@ -80,34 +80,35 @@

No network policies missing. You are good to go!

+
- Loading visualization data... -
+ {{ loadingMessage }} + - +
- -
Loading visualization data...
-
-
- - -
+
+ +
- -
-
- - -
+ +
+
+ + +
+
@@ -131,6 +132,7 @@ export default { menuVisible: false, currentPage: 1, pageSize: 10, + clusterMapGenerationCount: 0, expandedNamespaces: {}, selectedNamespace: '', allNamespaces: [], @@ -138,6 +140,7 @@ export default { namespaceVisualizationData: {}, isLoadingVisualization: false, isShowClusterMap: false, + isClusterMapLoading: false, clusterVisualizationData: [], }; }, @@ -204,6 +207,15 @@ export default { } return 0; }, + loadingMessage() { + if (this.isClusterMapLoading) { + return 'Loading cluster visualization data...'; + } + if (this.isLoadingVisualization) { + return 'Loading visualization data...'; + } + return ''; + } }, methods: { toggleMenu() { @@ -319,8 +331,13 @@ export default { return; } + this.isShowClusterMap = false; + this.namespaceVisualizationData = {}; + this.clusterVisualizationData = []; this.lastScanType = 'namespace'; this.scanInitiated = true; + this.isLoadingVisualization = true; + try { const scanResponse = await axios.get(`http://localhost:8080/scan?namespace=${namespace}`); this.scanResults = scanResponse.data; @@ -329,7 +346,7 @@ export default { this.netfetchScore = scanResponse.data.Score || null; } else { this.unprotectedPods = []; - this.netfetchScore = 42; // Default score for no missing policies + this.netfetchScore = 42; } this.updateExpandedNamespaces(); @@ -338,6 +355,8 @@ export default { } catch (error) { console.error('Error scanning namespace:', namespace, error); this.message = { type: 'error', text: `Failed to scan namespace: ${namespace}. Error: ${error.message}` }; + } finally { + this.isLoadingVisualization = false; } }, // Fetch and update visualization data for multiple namespaces @@ -362,6 +381,22 @@ export default { } this.isLoadingVisualization = false; }, + async generateClusterNetworkMap() { + this.isShowClusterMap = true; + this.isLoadingVisualization = true; + this.namespaceVisualizationData = {}; + this.clusterMapGenerationCount++; + + try { + const response = await axios.get('http://localhost:8080/visualization/cluster'); + this.clusterVisualizationData = response.data; + } catch (error) { + console.error('Error fetching cluster visualization data:', error); + } finally { + this.isLoadingVisualization = false; + } + }, + // Viz async fetchVisualizationData(namespace) { if (!namespace) return; @@ -376,17 +411,6 @@ export default { console.error('Error fetching visualization data:', error); } }, - async generateClusterNetworkMap() { - this.isShowClusterMap = true; - this.isLoadingVisualization = true; - try { - const response = await axios.get('http://localhost:8080/visualization/cluster'); - this.clusterVisualizationData = response.data; - } catch (error) { - console.error('Error fetching cluster visualization data:', error); - } - this.isLoadingVisualization = false; - } }, mounted() { this.updateExpandedNamespaces(); diff --git a/frontend/dash/src/Viz.vue b/frontend/dash/src/Viz.vue index d28bc29..f5b8e7d 100644 --- a/frontend/dash/src/Viz.vue +++ b/frontend/dash/src/Viz.vue @@ -9,22 +9,48 @@ name: 'NetworkPolicyVisualization', props: { policies: Array, + clusterData: { + type: Array, + default: () => [], + }, + visualizationType: { + type: String, + default: 'namespace' // Possible values: 'namespace', 'cluster' + }, }, mounted() { this.createNetworkMap(); }, methods: { createNetworkMap() { - console.log("Policies data:", this.policies) + let data; + if (this.visualizationType === 'cluster') { + // Flatten the policies array from the nested structure + data = this.clusterVisualizationData.reduce((acc, item) => { + if (item.policies && Array.isArray(item.policies)) { + return [...acc, ...item.policies]; + } + return acc; + }, []); + } else { + data = this.policies; + } + + console.log("Visualization data:", data); const container = d3.select(this.$refs.vizContainer); const width = 800; const height = 600; - // Transform policies into nodes and links const nodes = []; const links = []; - this.policies.forEach(policy => { + // Iterate over flattened policies + data.forEach(policy => { + if (!policy.targetPods || !Array.isArray(policy.targetPods)) { + console.warn(`Skipping policy ${policy.name} as it has no targetPods or targetPods is not an array`); + return; + } + const policyNode = { id: policy.name, type: 'policy' }; nodes.push(policyNode);