refactor: remove getScansByFields and use getScans with flexible filters

This commit is contained in:
Pablo Lara
2025-05-16 13:33:18 +02:00
parent 9365eaf3da
commit 72137590fe
7 changed files with 175 additions and 201 deletions
+10 -48
View File
@@ -16,6 +16,12 @@ export const getScans = async ({
sort = "",
filters = {},
pageSize = 10,
}: {
page?: number;
query?: string;
sort?: string;
filters?: Record<string, string | number | boolean>;
pageSize?: number;
}) => {
const headers = await getAuthHeaders({ contentType: false });
@@ -28,23 +34,18 @@ export const getScans = async ({
if (query) url.searchParams.append("filter[search]", query);
if (sort) url.searchParams.append("sort", sort);
// Handle multiple filters
// Add dynamic filters (e.g., "filter[state]", "fields[scans]")
Object.entries(filters).forEach(([key, value]) => {
if (key !== "filter[search]") {
url.searchParams.append(key, String(value));
}
url.searchParams.append(key, String(value));
});
try {
const scans = await fetch(url.toString(), {
headers,
});
const data = await scans.json();
const response = await fetch(url.toString(), { headers });
const data = await response.json();
const parsedData = parseStringify(data);
revalidatePath("/scans");
return parsedData;
} catch (error) {
// eslint-disable-next-line no-console
console.error("Error fetching scans:", error);
return undefined;
}
@@ -260,45 +261,6 @@ export const getExportsZip = async (scanId: string) => {
}
};
export const getScansByFields = async (
fields: string = "state",
filters = {},
) => {
const headers = await getAuthHeaders({ contentType: false });
const url = new URL(`${apiBaseUrl}/scans`);
// Request only the necessary fields to optimize the response
url.searchParams.append("fields[scans]", fields);
Object.entries(filters).forEach(([key, value]) => {
url.searchParams.append(key, String(value));
});
try {
const response = await fetch(url.toString(), {
headers,
});
if (!response.ok) {
try {
const errorData = await response.json();
throw new Error(errorData?.message || "Failed to fetch scans by state");
} catch {
throw new Error("Failed to fetch scans by state");
}
}
const data = await response.json();
return parseStringify(data);
} catch (error) {
// eslint-disable-next-line no-console
console.error("Error fetching scans by state:", error);
return undefined;
}
};
export const getComplianceCsv = async (
scanId: string,
complianceId: string,
+11 -6
View File
@@ -3,7 +3,7 @@ import { format, parseISO } from "date-fns";
import { Suspense } from "react";
import { getResourceFields, getResources } from "@/actions/resources";
import { getScansByFields } from "@/actions/scans";
import { getScans } from "@/actions/scans";
import { FilterControls } from "@/components/filters";
import { SkeletonTableResources } from "@/components/resources/skeleton/skeleton-table-resources";
import { ColumnResources } from "@/components/resources/table/column-resources";
@@ -41,9 +41,11 @@ export default async function Resources({
),
};
// Fetch scans data latest date not fully done
const scansData = await getScansByFields("inserted_at", {
"filter[state]": "completed",
const scansData = await getScans({
filters: {
"filter[state]": "completed",
"fields[scans]": "inserted_at",
},
});
if (scansData.data?.length !== 0) {
@@ -161,8 +163,11 @@ const SSRDataTable = async ({
};
// Fetch scans data latest date
const scansData = await getScansByFields("inserted_at", {
"filter[state]": "completed",
const scansData = await getScans({
filters: {
"filter[state]": "completed",
"fields[scans]": "inserted_at",
},
});
if (scansData.data?.length !== 0) {
+7 -1
View File
@@ -123,7 +123,13 @@ const SSRDataTableScans = async ({
const query = (filters["filter[search]"] as string) || "";
// Fetch scans data
const scansData = await getScans({ query, page, sort, filters, pageSize });
const scansData = await getScans({
query,
page,
sort,
filters: filters as Record<string, string | number | boolean>,
pageSize,
});
// Handle expanded scans data
const expandedScansData = await Promise.all(
@@ -131,10 +131,10 @@ export const ResourceDetail = ({
</div>
</Section>
{/* Finding Details section */}
{/* Finding associated with this resource section */}
<div>
<h2 className="line-clamp-2 text-lg font-medium leading-tight text-gray-800 dark:text-prowler-theme-pale/90">
Findings Details
<h2 className="text-md line-clamp-2 font-medium leading-tight text-gray-800 dark:text-prowler-theme-pale/90">
Findings associated with this resource
</h2>
</div>
{isLoading ? (
+1 -143
View File
@@ -1,7 +1,7 @@
import { LucideIcon } from "lucide-react";
import { SVGProps } from "react";
import { ProviderType } from "./providers";
import { ProviderProps, ProviderType } from "./providers";
export type IconSvgProps = SVGProps<SVGSVGElement> & {
size?: number;
@@ -704,145 +704,3 @@ export interface UserProps {
dateAdded: string;
status: "active" | "inactive";
}
export interface ResourceProps {
type: "resources";
id: string;
attributes: {
inserted_at: string;
updated_at: string;
uid: string;
name: string;
region: string;
service: string;
tags: Record<string, string>;
type: string;
};
relationships: {
provider: {
data: {
type: "providers";
id: string;
attributes: {
inserted_at: string;
updated_at: string;
provider: string;
uid: string;
alias: string | null;
connection: {
connected: boolean;
last_checked_at: string;
};
};
relationships: {
secret: {
data: {
type: "provider-secrets";
id: string;
};
};
};
links: {
self: string;
};
};
};
findings: {
meta: {
count: number;
};
data: {
type: "findings";
id: string;
attributes: { status: string };
}[];
};
};
links: {
self: string;
};
}
interface Provider {
type: "providers" | "findings";
id: string;
attributes: {
uid: string;
delta: string;
status: "PASS" | "FAIL" | "MANUAL";
status_extended: string;
severity: "informational" | "low" | "medium" | "high" | "critical";
check_id: string;
check_metadata: CheckMetadata;
raw_result: Record<string, any>;
inserted_at: string;
updated_at: string;
first_seen_at: string;
muted: boolean;
};
relationships: {
secret: {
data: {
type: string;
id: string;
};
};
scan: {
data: {
type: string;
id: string;
};
};
provider_groups: {
meta: {
count: number;
};
data: [];
};
};
links: {
self: string;
};
}
interface CheckMetadata {
risk: string;
notes: string;
checkid: string;
provider: string;
severity: string;
checktype: string[];
dependson: string[];
relatedto: string[];
categories: string[];
checktitle: string;
compliance: any;
relatedurl: string;
description: string;
remediation: {
code: {
cli: string;
other: string;
nativeiac: string;
terraform: string;
};
recommendation: {
url: string;
text: string;
};
};
servicename: string;
checkaliases: string[];
resourcetype: string;
subservicename: string;
resourceidtemplate: string;
}
interface Meta {
version: string;
}
export interface ResourceApiResponse {
data: ResourceProps;
included: Provider[];
meta: Meta;
}
+1
View File
@@ -3,3 +3,4 @@ export * from "./components";
export * from "./filters";
export * from "./formSchemas";
export * from "./providers";
export * from "./resources";
+142
View File
@@ -0,0 +1,142 @@
export interface ResourceProps {
type: "resources";
id: string;
attributes: {
inserted_at: string;
updated_at: string;
uid: string;
name: string;
region: string;
service: string;
tags: Record<string, string>;
type: string;
};
relationships: {
provider: {
data: {
type: "providers";
id: string;
attributes: {
inserted_at: string;
updated_at: string;
provider: string;
uid: string;
alias: string | null;
connection: {
connected: boolean;
last_checked_at: string;
};
};
relationships: {
secret: {
data: {
type: "provider-secrets";
id: string;
};
};
};
links: {
self: string;
};
};
};
findings: {
meta: {
count: number;
};
data: {
type: "findings";
id: string;
attributes: { status: string };
}[];
};
};
links: {
self: string;
};
}
interface ResourceItemProps {
type: "providers" | "findings";
id: string;
attributes: {
uid: string;
delta: string;
status: "PASS" | "FAIL" | "MANUAL";
status_extended: string;
severity: "informational" | "low" | "medium" | "high" | "critical";
check_id: string;
check_metadata: CheckMetadataProps;
raw_result: Record<string, any>;
inserted_at: string;
updated_at: string;
first_seen_at: string;
muted: boolean;
};
relationships: {
secret: {
data: {
type: string;
id: string;
};
};
scan: {
data: {
type: string;
id: string;
};
};
provider_groups: {
meta: {
count: number;
};
data: [];
};
};
links: {
self: string;
};
}
interface CheckMetadataProps {
risk: string;
notes: string;
checkid: string;
provider: string;
severity: string;
checktype: string[];
dependson: string[];
relatedto: string[];
categories: string[];
checktitle: string;
compliance: any;
relatedurl: string;
description: string;
remediation: {
code: {
cli: string;
other: string;
nativeiac: string;
terraform: string;
};
recommendation: {
url: string;
text: string;
};
};
servicename: string;
checkaliases: string[];
resourcetype: string;
subservicename: string;
resourceidtemplate: string;
}
interface Meta {
version: string;
}
export interface ResourceApiResponse {
data: ResourceProps;
included: ResourceItemProps[];
meta: Meta;
}