mirror of
https://github.com/prowler-cloud/prowler.git
synced 2025-12-19 05:17:47 +00:00
168 lines
5.2 KiB
TypeScript
168 lines
5.2 KiB
TypeScript
import {
|
|
adaptToSankeyData,
|
|
getFindingsBySeverity,
|
|
SeverityByProviderType,
|
|
} from "@/actions/overview";
|
|
import { getProviders } from "@/actions/providers";
|
|
import { SankeyChart } from "@/components/graphs/sankey-chart";
|
|
import { SearchParamsProps } from "@/types";
|
|
|
|
import { pickFilterParams } from "../../_lib/filter-params";
|
|
|
|
export async function RiskPipelineViewSSR({
|
|
searchParams,
|
|
}: {
|
|
searchParams: SearchParamsProps;
|
|
}) {
|
|
const filters = pickFilterParams(searchParams);
|
|
|
|
const providerTypeFilter = filters["filter[provider_type__in]"];
|
|
const providerIdFilter = filters["filter[provider_id__in]"];
|
|
|
|
// Fetch providers list to know account types
|
|
const providersListResponse = await getProviders({ pageSize: 200 });
|
|
const allProviders = providersListResponse?.data || [];
|
|
|
|
// Build severityByProviderType based on filters
|
|
const severityByProviderType: SeverityByProviderType = {};
|
|
let selectedProviderTypes: string[] | undefined;
|
|
|
|
if (providerIdFilter) {
|
|
// Case: Accounts are selected - group by provider type and make parallel calls
|
|
const selectedAccountIds = String(providerIdFilter)
|
|
.split(",")
|
|
.map((id) => id.trim());
|
|
|
|
// Group selected accounts by provider type
|
|
const accountsByType: Record<string, string[]> = {};
|
|
for (const accountId of selectedAccountIds) {
|
|
const provider = allProviders.find((p) => p.id === accountId);
|
|
if (provider) {
|
|
const type = provider.attributes.provider.toLowerCase();
|
|
if (!accountsByType[type]) {
|
|
accountsByType[type] = [];
|
|
}
|
|
accountsByType[type].push(accountId);
|
|
}
|
|
}
|
|
|
|
selectedProviderTypes = Object.keys(accountsByType);
|
|
|
|
// Make parallel calls for each provider type
|
|
const severityPromises = Object.entries(accountsByType).map(
|
|
async ([providerType, accountIds]) => {
|
|
const response = await getFindingsBySeverity({
|
|
filters: {
|
|
"filter[provider_id__in]": accountIds.join(","),
|
|
"filter[status]": "FAIL", // Only count failed findings
|
|
},
|
|
});
|
|
return { providerType, data: response?.data?.attributes };
|
|
},
|
|
);
|
|
|
|
const severityResults = await Promise.all(severityPromises);
|
|
|
|
for (const result of severityResults) {
|
|
if (result.data) {
|
|
severityByProviderType[result.providerType] = result.data;
|
|
}
|
|
}
|
|
} else if (providerTypeFilter) {
|
|
// Case: Provider types are selected - make parallel calls for each type
|
|
selectedProviderTypes = String(providerTypeFilter)
|
|
.split(",")
|
|
.map((t) => t.trim().toLowerCase());
|
|
|
|
const severityPromises = selectedProviderTypes.map(async (providerType) => {
|
|
const response = await getFindingsBySeverity({
|
|
filters: {
|
|
...filters,
|
|
"filter[provider_type__in]": providerType,
|
|
"filter[status]": "FAIL", // Only count failed findings
|
|
},
|
|
});
|
|
return { providerType, data: response?.data?.attributes };
|
|
});
|
|
|
|
const severityResults = await Promise.all(severityPromises);
|
|
|
|
for (const result of severityResults) {
|
|
if (result.data) {
|
|
severityByProviderType[result.providerType] = result.data;
|
|
}
|
|
}
|
|
} else {
|
|
// Case: No filters - get all provider types and make parallel calls
|
|
const allProviderTypes = Array.from(
|
|
new Set(allProviders.map((p) => p.attributes.provider.toLowerCase())),
|
|
);
|
|
|
|
const severityPromises = allProviderTypes.map(async (providerType) => {
|
|
const response = await getFindingsBySeverity({
|
|
filters: {
|
|
...filters,
|
|
"filter[provider_type__in]": providerType,
|
|
"filter[status]": "FAIL", // Only count failed findings
|
|
},
|
|
});
|
|
return { providerType, data: response?.data?.attributes };
|
|
});
|
|
|
|
const severityResults = await Promise.all(severityPromises);
|
|
|
|
for (const result of severityResults) {
|
|
if (result.data) {
|
|
severityByProviderType[result.providerType] = result.data;
|
|
}
|
|
}
|
|
}
|
|
|
|
const sankeyData = adaptToSankeyData(
|
|
severityByProviderType,
|
|
selectedProviderTypes,
|
|
);
|
|
|
|
// If no chart data and no zero-data providers, show empty state message
|
|
if (
|
|
sankeyData.nodes.length === 0 &&
|
|
sankeyData.zeroDataProviders.length === 0
|
|
) {
|
|
return (
|
|
<div className="flex h-[460px] w-full items-center justify-center">
|
|
<p className="text-text-neutral-tertiary text-sm">
|
|
No findings data available for the selected filters
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// If no chart data but there are zero-data providers, show only the legend
|
|
if (sankeyData.nodes.length === 0) {
|
|
return (
|
|
<div className="flex h-[460px] w-full items-center justify-center">
|
|
<div className="text-center">
|
|
<p className="text-text-neutral-tertiary mb-4 text-sm">
|
|
No failed findings for the selected accounts
|
|
</p>
|
|
<SankeyChart
|
|
data={sankeyData}
|
|
zeroDataProviders={sankeyData.zeroDataProviders}
|
|
height={100}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="w-full flex-1 overflow-visible">
|
|
<SankeyChart
|
|
data={sankeyData}
|
|
zeroDataProviders={sankeyData.zeroDataProviders}
|
|
height={460}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|