mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 20:42:02 +00:00
202 lines
5.6 KiB
TypeScript
202 lines
5.6 KiB
TypeScript
import type { FindingGroupRow } from "@/types";
|
|
|
|
type FindingGroupMutedState = Pick<
|
|
FindingGroupRow,
|
|
"muted" | "mutedCount" | "resourcesFail" | "resourcesTotal"
|
|
>;
|
|
|
|
type FindingGroupDeltaState = Pick<
|
|
FindingGroupRow,
|
|
| "newCount"
|
|
| "changedCount"
|
|
| "newFailCount"
|
|
| "newFailMutedCount"
|
|
| "newPassCount"
|
|
| "newPassMutedCount"
|
|
| "newManualCount"
|
|
| "newManualMutedCount"
|
|
| "changedFailCount"
|
|
| "changedFailMutedCount"
|
|
| "changedPassCount"
|
|
| "changedPassMutedCount"
|
|
| "changedManualCount"
|
|
| "changedManualMutedCount"
|
|
>;
|
|
|
|
export function isFindingGroupMuted(group: FindingGroupMutedState): boolean {
|
|
if (typeof group.muted === "boolean") {
|
|
return group.muted;
|
|
}
|
|
|
|
const mutedCount = group.mutedCount ?? 0;
|
|
if (mutedCount === 0) {
|
|
return false;
|
|
}
|
|
|
|
return (
|
|
mutedCount === group.resourcesFail || mutedCount === group.resourcesTotal
|
|
);
|
|
}
|
|
|
|
function getNewDeltaTotal(group: FindingGroupDeltaState): number {
|
|
const breakdownTotal =
|
|
(group.newFailCount ?? 0) +
|
|
(group.newFailMutedCount ?? 0) +
|
|
(group.newPassCount ?? 0) +
|
|
(group.newPassMutedCount ?? 0) +
|
|
(group.newManualCount ?? 0) +
|
|
(group.newManualMutedCount ?? 0);
|
|
|
|
return breakdownTotal > 0 ? breakdownTotal : group.newCount;
|
|
}
|
|
|
|
function getChangedDeltaTotal(group: FindingGroupDeltaState): number {
|
|
const breakdownTotal =
|
|
(group.changedFailCount ?? 0) +
|
|
(group.changedFailMutedCount ?? 0) +
|
|
(group.changedPassCount ?? 0) +
|
|
(group.changedPassMutedCount ?? 0) +
|
|
(group.changedManualCount ?? 0) +
|
|
(group.changedManualMutedCount ?? 0);
|
|
|
|
return breakdownTotal > 0 ? breakdownTotal : group.changedCount;
|
|
}
|
|
|
|
export function getFindingGroupDelta(
|
|
group: FindingGroupDeltaState,
|
|
): "new" | "changed" | "none" {
|
|
if (getNewDeltaTotal(group) > 0) {
|
|
return "new";
|
|
}
|
|
|
|
if (getChangedDeltaTotal(group) > 0) {
|
|
return "changed";
|
|
}
|
|
|
|
return "none";
|
|
}
|
|
|
|
const FINDING_GROUP_STATUSES = ["FAIL", "PASS", "MANUAL"] as const;
|
|
type FindingGroupStatus = (typeof FINDING_GROUP_STATUSES)[number];
|
|
|
|
type FindingGroupFiltersRecord = Record<string, string | string[] | undefined>;
|
|
|
|
function parseStatusFilterValue(
|
|
rawValue: string | string[] | undefined,
|
|
): FindingGroupStatus[] {
|
|
if (!rawValue) {
|
|
return [];
|
|
}
|
|
|
|
const joined = Array.isArray(rawValue) ? rawValue.join(",") : rawValue;
|
|
|
|
return joined
|
|
.split(",")
|
|
.map((status) => status.trim().toUpperCase())
|
|
.filter((status): status is FindingGroupStatus =>
|
|
(FINDING_GROUP_STATUSES as readonly string[]).includes(status),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns the set of statuses the user has explicitly narrowed the findings
|
|
* view to, or null when no status filter is active (→ all statuses should be
|
|
* considered). Supports both `filter[status]` (single value) and
|
|
* `filter[status__in]` (comma-separated values).
|
|
*/
|
|
export function getActiveStatusFilter(
|
|
filters: FindingGroupFiltersRecord,
|
|
): Set<FindingGroupStatus> | null {
|
|
const direct = parseStatusFilterValue(filters["filter[status]"]);
|
|
if (direct.length > 0) {
|
|
return new Set(direct);
|
|
}
|
|
|
|
const multi = parseStatusFilterValue(filters["filter[status__in]"]);
|
|
if (multi.length > 0) {
|
|
return new Set(multi);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function hasAnyDeltaBreakdown(group: FindingGroupDeltaState): boolean {
|
|
return (
|
|
(group.newFailCount ?? 0) > 0 ||
|
|
(group.newFailMutedCount ?? 0) > 0 ||
|
|
(group.newPassCount ?? 0) > 0 ||
|
|
(group.newPassMutedCount ?? 0) > 0 ||
|
|
(group.newManualCount ?? 0) > 0 ||
|
|
(group.newManualMutedCount ?? 0) > 0 ||
|
|
(group.changedFailCount ?? 0) > 0 ||
|
|
(group.changedFailMutedCount ?? 0) > 0 ||
|
|
(group.changedPassCount ?? 0) > 0 ||
|
|
(group.changedPassMutedCount ?? 0) > 0 ||
|
|
(group.changedManualCount ?? 0) > 0 ||
|
|
(group.changedManualMutedCount ?? 0) > 0
|
|
);
|
|
}
|
|
|
|
function getNewDeltaForStatuses(
|
|
group: FindingGroupDeltaState,
|
|
statuses: Set<FindingGroupStatus>,
|
|
): number {
|
|
let total = 0;
|
|
if (statuses.has("FAIL")) {
|
|
total += (group.newFailCount ?? 0) + (group.newFailMutedCount ?? 0);
|
|
}
|
|
if (statuses.has("PASS")) {
|
|
total += (group.newPassCount ?? 0) + (group.newPassMutedCount ?? 0);
|
|
}
|
|
if (statuses.has("MANUAL")) {
|
|
total += (group.newManualCount ?? 0) + (group.newManualMutedCount ?? 0);
|
|
}
|
|
return total;
|
|
}
|
|
|
|
function getChangedDeltaForStatuses(
|
|
group: FindingGroupDeltaState,
|
|
statuses: Set<FindingGroupStatus>,
|
|
): number {
|
|
let total = 0;
|
|
if (statuses.has("FAIL")) {
|
|
total += (group.changedFailCount ?? 0) + (group.changedFailMutedCount ?? 0);
|
|
}
|
|
if (statuses.has("PASS")) {
|
|
total += (group.changedPassCount ?? 0) + (group.changedPassMutedCount ?? 0);
|
|
}
|
|
if (statuses.has("MANUAL")) {
|
|
total +=
|
|
(group.changedManualCount ?? 0) + (group.changedManualMutedCount ?? 0);
|
|
}
|
|
return total;
|
|
}
|
|
|
|
/**
|
|
* Filter-aware variant of {@link getFindingGroupDelta}. When a status filter
|
|
* is active, only delta counters belonging to the filtered statuses contribute
|
|
* to the indicator. When no status filter is active, or when the API response
|
|
* lacks breakdown counters (legacy shape), this falls back to the aggregate
|
|
* delta so rows still surface deltas correctly.
|
|
*/
|
|
export function getFilteredFindingGroupDelta(
|
|
group: FindingGroupDeltaState,
|
|
filters: FindingGroupFiltersRecord,
|
|
): "new" | "changed" | "none" {
|
|
const activeStatuses = getActiveStatusFilter(filters);
|
|
|
|
if (!activeStatuses || !hasAnyDeltaBreakdown(group)) {
|
|
return getFindingGroupDelta(group);
|
|
}
|
|
|
|
if (getNewDeltaForStatuses(group, activeStatuses) > 0) {
|
|
return "new";
|
|
}
|
|
|
|
if (getChangedDeltaForStatuses(group, activeStatuses) > 0) {
|
|
return "changed";
|
|
}
|
|
|
|
return "none";
|
|
}
|