mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 13:01:56 +00:00
feat: add watchlist component (#9199)
Co-authored-by: Alan Buscaglia <gentlemanprogramming@gmail.com>
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
import { StaticImageData } from "next/image";
|
||||
|
||||
import { getComplianceIcon } from "@/components/icons/compliance/IconCompliance";
|
||||
import { MetaDataProps } from "@/types";
|
||||
import { ComplianceOverviewData } from "@/types/compliance";
|
||||
|
||||
/**
|
||||
* Raw API response from /compliance-overviews endpoint
|
||||
*/
|
||||
export interface ComplianceOverviewsResponse {
|
||||
data: ComplianceOverviewData[];
|
||||
meta?: {
|
||||
pagination?: {
|
||||
page: number;
|
||||
pages: number;
|
||||
count: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Enriched compliance overview with computed fields
|
||||
*/
|
||||
export interface EnrichedComplianceOverview {
|
||||
id: string;
|
||||
framework: string;
|
||||
version: string;
|
||||
requirements_passed: number;
|
||||
requirements_failed: number;
|
||||
requirements_manual: number;
|
||||
total_requirements: number;
|
||||
score: number;
|
||||
label: string;
|
||||
icon: string | StaticImageData | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats framework name for display by replacing hyphens with spaces
|
||||
* e.g., "FedRAMP-20x-KSI-Low" -> "FedRAMP 20x KSI Low"
|
||||
*/
|
||||
function formatFrameworkName(framework: string): string {
|
||||
return framework.replace(/-/g, " ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts the raw API response to enriched compliance data
|
||||
* - Computes score percentage (rounded)
|
||||
* - Formats label (framework + version)
|
||||
* - Resolves framework icon
|
||||
* - Preserves pagination metadata
|
||||
*
|
||||
* @param response - Raw API response with data and optional pagination
|
||||
* @returns Object with enriched compliance data and metadata
|
||||
*/
|
||||
export function adaptComplianceOverviewsResponse(
|
||||
response: ComplianceOverviewsResponse | undefined,
|
||||
): {
|
||||
data: EnrichedComplianceOverview[];
|
||||
metadata?: MetaDataProps;
|
||||
} {
|
||||
if (!response?.data) {
|
||||
return { data: [] };
|
||||
}
|
||||
|
||||
const enrichedData = response.data.map((compliance) => {
|
||||
const { id, attributes } = compliance;
|
||||
const {
|
||||
framework,
|
||||
version,
|
||||
requirements_passed,
|
||||
requirements_failed,
|
||||
requirements_manual,
|
||||
total_requirements,
|
||||
} = attributes;
|
||||
|
||||
const totalRequirements = Number(total_requirements) || 0;
|
||||
const passedRequirements = Number(requirements_passed) || 0;
|
||||
|
||||
const score =
|
||||
totalRequirements > 0
|
||||
? Math.round((passedRequirements / totalRequirements) * 100)
|
||||
: 0;
|
||||
|
||||
const formattedFramework = formatFrameworkName(framework);
|
||||
const label = version
|
||||
? `${formattedFramework} - ${version}`
|
||||
: formattedFramework;
|
||||
const icon = getComplianceIcon(framework);
|
||||
|
||||
return {
|
||||
id,
|
||||
framework,
|
||||
version,
|
||||
requirements_passed,
|
||||
requirements_failed,
|
||||
requirements_manual,
|
||||
total_requirements,
|
||||
score,
|
||||
label,
|
||||
icon,
|
||||
};
|
||||
});
|
||||
|
||||
const metadata: MetaDataProps | undefined = response.meta?.pagination
|
||||
? {
|
||||
pagination: {
|
||||
page: response.meta.pagination.page,
|
||||
pages: response.meta.pagination.pages,
|
||||
count: response.meta.pagination.count,
|
||||
itemsPerPage: [10, 25, 50, 100],
|
||||
},
|
||||
version: "1.0",
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return { data: enrichedData, metadata };
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts compliances for watchlist display:
|
||||
* - Excludes ProwlerThreatScore
|
||||
* - Sorted by score ascending (worst/lowest scores first)
|
||||
* - Limited to specified count
|
||||
*
|
||||
* @param data - Enriched compliance data
|
||||
* @param limit - Maximum number of items to return (default: 9)
|
||||
* @returns Sorted and limited compliance data
|
||||
*/
|
||||
export function sortCompliancesForWatchlist(
|
||||
data: EnrichedComplianceOverview[],
|
||||
limit: number = 9,
|
||||
): EnrichedComplianceOverview[] {
|
||||
return [...data]
|
||||
.filter((item) => item.framework !== "ProwlerThreatScore")
|
||||
.sort((a, b) => a.score - b.score)
|
||||
.slice(0, limit);
|
||||
}
|
||||
@@ -7,22 +7,31 @@ export const getCompliancesOverview = async ({
|
||||
scanId,
|
||||
region,
|
||||
query,
|
||||
filters = {},
|
||||
}: {
|
||||
scanId: string;
|
||||
scanId?: string;
|
||||
region?: string | string[];
|
||||
query?: string;
|
||||
}) => {
|
||||
filters?: Record<string, string | string[] | undefined>;
|
||||
} = {}) => {
|
||||
const headers = await getAuthHeaders({ contentType: false });
|
||||
|
||||
const url = new URL(`${apiBaseUrl}/compliance-overviews`);
|
||||
|
||||
if (scanId) url.searchParams.append("filter[scan_id]", scanId);
|
||||
if (query) url.searchParams.append("filter[search]", query);
|
||||
const setParam = (key: string, value?: string | string[]) => {
|
||||
if (!value) return;
|
||||
|
||||
if (region) {
|
||||
const regionValue = Array.isArray(region) ? region.join(",") : region;
|
||||
url.searchParams.append("filter[region__in]", regionValue);
|
||||
}
|
||||
const serializedValue = Array.isArray(value) ? value.join(",") : value;
|
||||
if (serializedValue.trim().length > 0) {
|
||||
url.searchParams.set(key, serializedValue);
|
||||
}
|
||||
};
|
||||
|
||||
Object.entries(filters).forEach(([key, value]) => setParam(key, value));
|
||||
|
||||
setParam("filter[scan_id]", scanId);
|
||||
setParam("filter[region__in]", region);
|
||||
if (query) url.searchParams.set("filter[search]", query);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
@@ -31,7 +40,7 @@ export const getCompliancesOverview = async ({
|
||||
|
||||
return handleApiResponse(response);
|
||||
} catch (error) {
|
||||
console.error("Error fetching providers:", error);
|
||||
console.error("Error fetching compliances overview:", error);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from "./compliances";
|
||||
export * from "./compliances.adapter";
|
||||
|
||||
Reference in New Issue
Block a user