1+ import { getProviderDisplayName } from "@/types/providers" ;
2+
13import {
24 FindingsSeverityOverviewResponse ,
35 ProviderOverview ,
46 ProvidersOverviewResponse ,
57} from "./types" ;
68
7- /**
8- * Sankey chart node structure
9- */
109export interface SankeyNode {
1110 name : string ;
1211}
1312
14- /**
15- * Sankey chart link structure
16- */
1713export interface SankeyLink {
1814 source : number ;
1915 target : number ;
2016 value : number ;
2117}
2218
23- /**
24- * Sankey chart data structure
25- */
2619export interface SankeyData {
2720 nodes : SankeyNode [ ] ;
2821 links : SankeyLink [ ] ;
2922}
3023
31- /**
32- * Provider display name mapping
33- * Maps provider IDs to user-friendly display names
34- * These names must match the COLOR_MAP keys in sankey-chart.tsx
35- */
36- const PROVIDER_DISPLAY_NAMES : Record < string , string > = {
37- aws : "AWS" ,
38- azure : "Azure" ,
39- gcp : "Google Cloud" ,
40- kubernetes : "Kubernetes" ,
41- github : "GitHub" ,
42- m365 : "Microsoft 365" ,
43- iac : "Infrastructure as Code" ,
44- oraclecloud : "Oracle Cloud Infrastructure" ,
45- } ;
46-
47- /**
48- * Aggregated provider data after grouping by provider type
49- */
5024interface AggregatedProvider {
5125 id : string ;
5226 displayName : string ;
5327 pass : number ;
5428 fail : number ;
5529}
5630
57- /**
58- * Provider types to exclude from the Sankey chart
59- */
6031const EXCLUDED_PROVIDERS = new Set ( [ "mongo" , "mongodb" , "mongodbatlas" ] ) ;
6132
62- /**
63- * Aggregates multiple provider entries by provider type (id)
64- * Since the API can return multiple entries for the same provider type,
65- * we need to sum up their findings
66- *
67- * @param providers - Raw provider overview data from API
68- * @returns Aggregated providers with summed findings
69- */
33+ // API can return multiple entries for the same provider type, so we sum their findings
7034function aggregateProvidersByType (
7135 providers : ProviderOverview [ ] ,
7236) : AggregatedProvider [ ] {
@@ -75,10 +39,7 @@ function aggregateProvidersByType(
7539 for ( const provider of providers ) {
7640 const { id, attributes } = provider ;
7741
78- // Skip excluded providers
79- if ( EXCLUDED_PROVIDERS . has ( id ) ) {
80- continue ;
81- }
42+ if ( EXCLUDED_PROVIDERS . has ( id ) ) continue ;
8243
8344 const existing = aggregated . get ( id ) ;
8445
@@ -88,7 +49,7 @@ function aggregateProvidersByType(
8849 } else {
8950 aggregated . set ( id , {
9051 id,
91- displayName : PROVIDER_DISPLAY_NAMES [ id ] || id ,
52+ displayName : getProviderDisplayName ( id ) ,
9253 pass : attributes . findings . pass ,
9354 fail : attributes . findings . fail ,
9455 } ) ;
@@ -98,9 +59,6 @@ function aggregateProvidersByType(
9859 return Array . from ( aggregated . values ( ) ) ;
9960}
10061
101- /**
102- * Severity display names in order
103- */
10462const SEVERITY_ORDER = [
10563 "Critical" ,
10664 "High" ,
@@ -110,18 +68,8 @@ const SEVERITY_ORDER = [
11068] as const ;
11169
11270/**
113- * Adapts providers overview and findings severity API responses to Sankey chart format
114- *
115- * Creates a 2-level flow visualization:
116- * - Level 1: Cloud providers (AWS, Azure, GCP, etc.)
117- * - Level 2: Severity breakdown (Critical, High, Medium, Low, Informational)
118- *
119- * The severity distribution is calculated proportionally based on each provider's
120- * fail count relative to the total fails across all providers.
121- *
122- * @param providersResponse - Raw API response from /overviews/providers
123- * @param severityResponse - Raw API response from /overviews/findings_severity
124- * @returns Sankey chart data with nodes and links
71+ * Adapts providers overview and findings severity API responses to Sankey chart format.
72+ * Severity distribution is calculated proportionally based on each provider's fail count.
12573 */
12674export function adaptProvidersOverviewToSankey (
12775 providersResponse : ProvidersOverviewResponse | undefined ,
@@ -131,34 +79,23 @@ export function adaptProvidersOverviewToSankey(
13179 return { nodes : [ ] , links : [ ] } ;
13280 }
13381
134- // Aggregate providers by type
13582 const aggregatedProviders = aggregateProvidersByType ( providersResponse . data ) ;
136-
137- // Filter out providers with no findings (only need fail > 0 for severity view)
13883 const providersWithFailures = aggregatedProviders . filter ( ( p ) => p . fail > 0 ) ;
13984
14085 if ( providersWithFailures . length === 0 ) {
14186 return { nodes : [ ] , links : [ ] } ;
14287 }
14388
144- // Build nodes array: providers first, then severities
14589 const providerNodes : SankeyNode [ ] = providersWithFailures . map ( ( p ) => ( {
14690 name : p . displayName ,
14791 } ) ) ;
148-
14992 const severityNodes : SankeyNode [ ] = SEVERITY_ORDER . map ( ( severity ) => ( {
15093 name : severity ,
15194 } ) ) ;
152-
15395 const nodes = [ ...providerNodes , ...severityNodes ] ;
154-
155- // Calculate severity start index (after provider nodes)
15696 const severityStartIndex = providerNodes . length ;
157-
158- // Build links from each provider to severities
15997 const links : SankeyLink [ ] = [ ] ;
16098
161- // If we have severity data, distribute proportionally
16299 if ( severityResponse ?. data ?. attributes ) {
163100 const { critical, high, medium, low, informational } =
164101 severityResponse . data . attributes ;
@@ -167,18 +104,15 @@ export function adaptProvidersOverviewToSankey(
167104 const totalSeverity = severityValues . reduce ( ( sum , v ) => sum + v , 0 ) ;
168105
169106 if ( totalSeverity > 0 ) {
170- // Calculate total fails across all providers
171107 const totalFails = providersWithFailures . reduce (
172108 ( sum , p ) => sum + p . fail ,
173109 0 ,
174110 ) ;
175111
176112 providersWithFailures . forEach ( ( provider , sourceIndex ) => {
177- // Calculate this provider's proportion of total fails
178113 const providerRatio = provider . fail / totalFails ;
179114
180115 severityValues . forEach ( ( severityValue , severityIndex ) => {
181- // Distribute severity proportionally to this provider
182116 const value = Math . round ( severityValue * providerRatio ) ;
183117
184118 if ( value > 0 ) {
@@ -192,7 +126,7 @@ export function adaptProvidersOverviewToSankey(
192126 } ) ;
193127 }
194128 } else {
195- // Fallback: if no severity data, just show fail counts to a generic "Fail" node
129+ // Fallback when no severity data available
196130 const failNode : SankeyNode = { name : "Fail" } ;
197131 nodes . push ( failNode ) ;
198132 const failIndex = nodes . length - 1 ;
0 commit comments