Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(v5): tweaks UI for v5 release #5971

Merged
merged 18 commits into from
Nov 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion ui/actions/findings/findings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { parseStringify } from "@/lib";

export const getFindings = async ({
page = 1,
pageSize = 10,
query = "",
sort = "",
filters = {},
Expand All @@ -21,10 +22,11 @@ export const getFindings = async ({
const url = new URL(`${keyServer}/findings?include=resources.provider,scan`);

if (page) url.searchParams.append("page[number]", page.toString());
if (pageSize) url.searchParams.append("page[size]", pageSize.toString());

if (query) url.searchParams.append("filter[search]", query);
if (sort) url.searchParams.append("sort", sort);

// Handle multiple filters
Object.entries(filters).forEach(([key, value]) => {
if (key !== "filter[search]") {
url.searchParams.append(key, String(value));
Expand All @@ -47,3 +49,46 @@ export const getFindings = async ({
return undefined;
}
};

export const getServicesRegions = async ({
query = "",
sort = "",
filters = {},
}) => {
const session = await auth();

const keyServer = process.env.API_BASE_URL;
const url = new URL(`${keyServer}/findings/findings_services_regions`);

if (query) url.searchParams.append("filter[search]", query);
if (sort) url.searchParams.append("sort", sort);

Object.entries(filters).forEach(([key, value]) => {
if (key !== "filter[search]") {
url.searchParams.append(key, String(value));
}
});

try {
const response = await fetch(url.toString(), {
headers: {
Accept: "application/vnd.api+json",
Authorization: `Bearer ${session?.accessToken}`,
},
});

if (!response.ok) {
throw new Error(
`Failed to fetch services regions: ${response.statusText}`,
);
}

const data = await response.json();
const parsedData = parseStringify(data);
return parsedData;
} catch (error) {
// eslint-disable-next-line no-console
console.error("Error fetching services regions:", error);
return undefined;
}
};
32 changes: 19 additions & 13 deletions ui/actions/providers/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export const updateProvider = async (formData: FormData) => {
revalidatePath("/providers");
return parseStringify(data);
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
return {
error: getErrorMessage(error),
Expand All @@ -113,35 +114,39 @@ export const addProvider = async (formData: FormData) => {
const session = await auth();
const keyServer = process.env.API_BASE_URL;

const providerType = formData.get("providerType");
const providerUid = formData.get("providerUid");
const providerAlias = formData.get("providerAlias");
const providerType = formData.get("providerType") as string;
const providerUid = formData.get("providerUid") as string;
const providerAlias = formData.get("providerAlias") as string;

const url = new URL(`${keyServer}/providers`);

try {
const bodyData = {
data: {
type: "providers",
attributes: {
provider: providerType,
uid: providerUid,
...(providerAlias?.trim() && { alias: providerAlias.trim() }),
},
},
};

const response = await fetch(url.toString(), {
method: "POST",
headers: {
"Content-Type": "application/vnd.api+json",
Accept: "application/vnd.api+json",
Authorization: `Bearer ${session?.accessToken}`,
},
body: JSON.stringify({
data: {
type: "providers",
attributes: {
provider: providerType,
uid: providerUid,
alias: providerAlias,
},
},
}),
body: JSON.stringify(bodyData),
});

const data = await response.json();
revalidatePath("/providers");
return parseStringify(data);
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
return {
error: getErrorMessage(error),
Expand Down Expand Up @@ -240,6 +245,7 @@ export const addCredentialsProvider = async (formData: FormData) => {
revalidatePath("/providers");
return parseStringify(data);
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
return {
error: getErrorMessage(error),
Expand Down
100 changes: 57 additions & 43 deletions ui/app/(prowler)/findings/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Spacer } from "@nextui-org/react";
import React, { Suspense } from "react";

import { getFindings } from "@/actions/findings";
import { getFindings, getServicesRegions } from "@/actions/findings";
import { getProviders } from "@/actions/providers";
import { getScans } from "@/actions/scans";
import { filterFindings } from "@/components/filters/data-filters";
Expand All @@ -21,9 +21,37 @@ export default async function Findings({
searchParams: SearchParamsProps;
}) {
const searchParamsKey = JSON.stringify(searchParams || {});
const defaultSort = "severity,status";
const sort = searchParams.sort?.toString() || defaultSort;

// Make sure the sort is correctly encoded
const encodedSort = sort.replace(/^\+/, "");

// Extract all filter parameters and combine with default filters
const defaultFilters = {
"filter[status__in]": "FAIL, PASS",
"filter[delta__in]": "new",
};

const filters: Record<string, string> = {
...defaultFilters,
...Object.fromEntries(
Object.entries(searchParams).filter(([key]) => key.startsWith("filter[")),
),
};

const query = filters["filter[search]"] || "";

const servicesRegionsData = await getServicesRegions({
query,
sort: encodedSort,
filters,
});

// Extract unique regions and services from the new endpoint
const uniqueRegions = servicesRegionsData?.data?.attributes?.regions || [];
const uniqueServices = servicesRegionsData?.data?.attributes?.services || [];
// Get findings data
const findingsData = await getFindings({});
const providersData = await getProviders({});
const scansData = await getScans({});

Expand All @@ -47,40 +75,9 @@ export default async function Findings({

const completedScanIds = completedScans?.map((scan: any) => scan.id) || [];

// Create resource dictionary
const resourceDict = createDict("resources", findingsData);

// Get unique regions and services
const allRegionsAndServices =
findingsData?.data
?.flatMap((finding: FindingProps) => {
const resource =
resourceDict[finding.relationships?.resources?.data?.[0]?.id];
return {
region: resource?.attributes?.region,
service: resource?.attributes?.service,
};
})
.filter(Boolean) || [];

const uniqueRegions = Array.from(
new Set<string>(
allRegionsAndServices
.map((item: { region: string }) => item.region)
.filter(Boolean) || [],
),
);
const uniqueServices = Array.from(
new Set<string>(
allRegionsAndServices
.map((item: { service: string }) => item.service)
.filter(Boolean) || [],
),
);

return (
<>
<Header title="Findings" icon="ph:list-checks-duotone" />
<Header title="Findings" icon="carbon:data-view-alt" />
<Spacer />
<Spacer y={4} />
<FilterControls search date />
Expand All @@ -106,7 +103,7 @@ export default async function Findings({
{
key: "scan__in",
labelCheckboxGroup: "Scans",
values: completedScanIds, // Use UUIDs in the filter
values: completedScanIds,
},
]}
defaultOpen={true}
Expand All @@ -125,17 +122,34 @@ const SSRDataTable = async ({
searchParams: SearchParamsProps;
}) => {
const page = parseInt(searchParams.page?.toString() || "1", 10);
const sort = searchParams.sort?.toString();
const defaultSort = "severity,status";
const sort = searchParams.sort?.toString() || defaultSort;

// Extract all filter parameters
const filters = Object.fromEntries(
Object.entries(searchParams).filter(([key]) => key.startsWith("filter[")),
);
// Make sure the sort is correctly encoded
const encodedSort = sort.replace(/^\+/, "");

// Extract all filter parameters and combine with default filters
const defaultFilters = {
"filter[status__in]": "FAIL, PASS",
"filter[delta__in]": "new",
};

const filters: Record<string, string> = {
...defaultFilters,
...Object.fromEntries(
Object.entries(searchParams).filter(([key]) => key.startsWith("filter[")),
),
};

// Extract query from filters
const query = (filters["filter[search]"] as string) || "";
const query = filters["filter[search]"] || "";

const findingsData = await getFindings({ query, page, sort, filters });
const findingsData = await getFindings({
query,
page,
sort: encodedSort,
filters,
pageSize: 10,
});

// Create dictionaries for resources, scans, and providers
const resourceDict = createDict("resources", findingsData);
Expand Down
Loading
Loading