mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 04:51:51 +00:00
feat: add PATCH method for scans
This commit is contained in:
+40
-1
@@ -42,7 +42,8 @@ export const getScans = async ({
|
||||
revalidatePath("/scans");
|
||||
return parsedData;
|
||||
} catch (error) {
|
||||
console.error("Error fetching providers:", error);
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error fetching scans:", error);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
@@ -94,3 +95,41 @@ export const scanOnDemand = async (formData: FormData) => {
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const updateScan = async (formData: FormData) => {
|
||||
const session = await auth();
|
||||
const keyServer = process.env.API_BASE_URL;
|
||||
|
||||
const scanId = formData.get("scanId");
|
||||
const scanName = formData.get("scanName");
|
||||
|
||||
const url = new URL(`${keyServer}/scans/${scanId}`);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/vnd.api+json",
|
||||
Accept: "application/vnd.api+json",
|
||||
Authorization: `Bearer ${session?.accessToken}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
data: {
|
||||
type: "Scan",
|
||||
id: scanId,
|
||||
attributes: {
|
||||
name: scanName,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
const data = await response.json();
|
||||
revalidatePath("/scans");
|
||||
return parseStringify(data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return {
|
||||
error: getErrorMessage(error),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -9,11 +9,11 @@ import {
|
||||
filterScans,
|
||||
} from "@/components/filters";
|
||||
import {
|
||||
ColumnGetScans,
|
||||
ColumnGetScansSchedule,
|
||||
SkeletonTableScans,
|
||||
} from "@/components/scans/table";
|
||||
import { ColumnProviderScans } from "@/components/scans/table/provider-scans";
|
||||
import { ColumnGetScans } from "@/components/scans/table/scans";
|
||||
import { Header } from "@/components/ui";
|
||||
import { DataTable } from "@/components/ui/table";
|
||||
import { SearchParamsProps } from "@/types";
|
||||
|
||||
@@ -77,7 +77,7 @@ export const EditForm = ({
|
||||
name="alias"
|
||||
type="text"
|
||||
label="Alias"
|
||||
labelPlacement="inside"
|
||||
labelPlacement="outside"
|
||||
placeholder={providerAlias}
|
||||
variant="bordered"
|
||||
isRequired={false}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import * as z from "zod";
|
||||
|
||||
import { updateScan } from "@/actions/scans";
|
||||
import { SaveIcon } from "@/components/icons";
|
||||
import { useToast } from "@/components/ui";
|
||||
import { CustomButton, CustomInput } from "@/components/ui/custom";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import { editScanFormSchema } from "@/types";
|
||||
|
||||
export const EditScanForm = ({
|
||||
scanId,
|
||||
scanName,
|
||||
setIsOpen,
|
||||
}: {
|
||||
scanId: string;
|
||||
scanName: string;
|
||||
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||
}) => {
|
||||
const formSchema = editScanFormSchema(scanName);
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
scanId: scanId,
|
||||
scanName: scanName,
|
||||
},
|
||||
});
|
||||
|
||||
const { toast } = useToast();
|
||||
|
||||
const isLoading = form.formState.isSubmitting;
|
||||
|
||||
const onSubmitClient = async (values: z.infer<typeof formSchema>) => {
|
||||
const formData = new FormData();
|
||||
|
||||
Object.entries(values).forEach(
|
||||
([key, value]) => value !== undefined && formData.append(key, value),
|
||||
);
|
||||
|
||||
const data = await updateScan(formData);
|
||||
|
||||
if (data?.errors && data.errors.length > 0) {
|
||||
const error = data.errors[0];
|
||||
const errorMessage = `${error.detail}`;
|
||||
// show error
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Oops! Something went wrong",
|
||||
description: errorMessage,
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
title: "Success!",
|
||||
description: "The scan was updated successfully.",
|
||||
});
|
||||
setIsOpen(false); // Close the modal on success
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmitClient)}
|
||||
className="flex flex-col space-y-4"
|
||||
>
|
||||
<div className="text-md">
|
||||
Current name: <span className="font-bold">{scanName}</span>
|
||||
</div>
|
||||
<div>
|
||||
<CustomInput
|
||||
control={form.control}
|
||||
name="scanName"
|
||||
type="text"
|
||||
label="Name"
|
||||
labelPlacement="outside"
|
||||
placeholder={scanName}
|
||||
variant="bordered"
|
||||
isRequired={false}
|
||||
isInvalid={!!form.formState.errors.scanName}
|
||||
/>
|
||||
</div>
|
||||
<input type="hidden" name="scanId" value={scanId} />
|
||||
|
||||
<div className="flex w-full justify-center sm:space-x-6">
|
||||
<CustomButton
|
||||
type="button"
|
||||
ariaLabel="Cancel"
|
||||
className="w-full bg-transparent"
|
||||
variant="faded"
|
||||
size="lg"
|
||||
radius="lg"
|
||||
onPress={() => setIsOpen(false)}
|
||||
disabled={isLoading}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
</CustomButton>
|
||||
|
||||
<CustomButton
|
||||
type="submit"
|
||||
ariaLabel="Save"
|
||||
className="w-full"
|
||||
variant="solid"
|
||||
color="action"
|
||||
size="lg"
|
||||
isLoading={isLoading}
|
||||
startContent={!isLoading && <SaveIcon size={24} />}
|
||||
>
|
||||
{isLoading ? <>Loading</> : <span>Save</span>}
|
||||
</CustomButton>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./edit-scan-form";
|
||||
export * from "./scan-on-demand-form";
|
||||
export * from "./schedule-form";
|
||||
|
||||
@@ -6,7 +6,7 @@ import { DateWithTime, EntityInfoShort } from "@/components/ui/entities";
|
||||
import { DataTableColumnHeader, StatusBadge } from "@/components/ui/table";
|
||||
import { ScanProps } from "@/types";
|
||||
|
||||
import { DataTableRowActions } from "./data-table-row-actions";
|
||||
import { DataTableRowActions } from "./scans/data-table-row-actions";
|
||||
|
||||
const getScanData = (row: { original: ScanProps }) => {
|
||||
return row.original;
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
export * from "./column-get-scans";
|
||||
export * from "./column-get-scans-schedule";
|
||||
export * from "./data-table-row-actions";
|
||||
export * from "./skeleton-table-scans";
|
||||
|
||||
+17
-27
@@ -9,7 +9,6 @@ import {
|
||||
DropdownTrigger,
|
||||
} from "@nextui-org/react";
|
||||
import {
|
||||
AddNoteBulkIcon,
|
||||
DeleteDocumentBulkIcon,
|
||||
EditDocumentBulkIcon,
|
||||
} from "@nextui-org/shared-icons";
|
||||
@@ -20,7 +19,8 @@ import { useState } from "react";
|
||||
import { VerticalDotsIcon } from "@/components/icons";
|
||||
import { CustomAlertModal } from "@/components/ui/custom";
|
||||
|
||||
// import { EditForm } from "../forms";
|
||||
import { EditScanForm } from "../../forms";
|
||||
|
||||
// import { DeleteForm } from "../forms/delete-form";
|
||||
|
||||
interface DataTableRowActionsProps<ProviderProps> {
|
||||
@@ -34,28 +34,27 @@ export function DataTableRowActions<ProviderProps>({
|
||||
}: DataTableRowActionsProps<ProviderProps>) {
|
||||
const [isEditOpen, setIsEditOpen] = useState(false);
|
||||
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
|
||||
const providerId = (row.original as { id: string }).id;
|
||||
// const providerAlias = (row.original as any).attributes?.alias;
|
||||
const scanId = (row.original as { id: string }).id;
|
||||
const scanName = (row.original as any).attributes?.name;
|
||||
return (
|
||||
<>
|
||||
<CustomAlertModal
|
||||
isOpen={isEditOpen}
|
||||
onOpenChange={setIsEditOpen}
|
||||
title="Edit Provider"
|
||||
description={"Edit the provider details"}
|
||||
title="Edit Scan"
|
||||
description={"Edit the scan details"}
|
||||
>
|
||||
<p>Hello</p>
|
||||
{/* <EditForm
|
||||
providerId={providerId}
|
||||
providerAlias={providerAlias}
|
||||
<EditScanForm
|
||||
scanId={scanId}
|
||||
scanName={scanName}
|
||||
setIsOpen={setIsEditOpen}
|
||||
/> */}
|
||||
/>
|
||||
</CustomAlertModal>
|
||||
<CustomAlertModal
|
||||
isOpen={isDeleteOpen}
|
||||
onOpenChange={setIsDeleteOpen}
|
||||
title="Are you absolutely sure?"
|
||||
description="This action cannot be undone. This will permanently delete your provider account and remove your data from the server."
|
||||
description="This action cannot be undone. This will permanently delete your scan."
|
||||
>
|
||||
<p>Hello</p>
|
||||
{/* <DeleteForm providerId={providerId} setIsOpen={setIsDeleteOpen} /> */}
|
||||
@@ -75,23 +74,14 @@ export function DataTableRowActions<ProviderProps>({
|
||||
variant="flat"
|
||||
>
|
||||
<DropdownSection title="Actions">
|
||||
<DropdownItem
|
||||
key="new"
|
||||
description="Check the connection to the provider"
|
||||
textValue="Check Connection"
|
||||
startContent={<AddNoteBulkIcon className={iconClasses} />}
|
||||
>
|
||||
<p>action here + {providerId}</p>
|
||||
{/* <CheckConnectionProvider id={providerId} /> */}
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
key="edit"
|
||||
description="Allows you to edit the provider"
|
||||
textValue="Edit Provider"
|
||||
description="Allows you to edit the scan"
|
||||
textValue="Edit Scan"
|
||||
startContent={<EditDocumentBulkIcon className={iconClasses} />}
|
||||
onClick={() => setIsEditOpen(true)}
|
||||
>
|
||||
Edit Provider
|
||||
Edit Scan
|
||||
</DropdownItem>
|
||||
</DropdownSection>
|
||||
<DropdownSection title="Danger zone">
|
||||
@@ -99,8 +89,8 @@ export function DataTableRowActions<ProviderProps>({
|
||||
key="delete"
|
||||
className="text-danger"
|
||||
color="danger"
|
||||
description="Delete the provider permanently"
|
||||
textValue="Delete Provider"
|
||||
description="Delete the scan permanently"
|
||||
textValue="Delete Scan"
|
||||
startContent={
|
||||
<DeleteDocumentBulkIcon
|
||||
className={clsx(iconClasses, "!text-danger")}
|
||||
@@ -108,7 +98,7 @@ export function DataTableRowActions<ProviderProps>({
|
||||
}
|
||||
onClick={() => setIsDeleteOpen(true)}
|
||||
>
|
||||
Delete Provider
|
||||
Delete Scan
|
||||
</DropdownItem>
|
||||
</DropdownSection>
|
||||
</DropdownMenu>
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./column-get-scans";
|
||||
export * from "./data-table-row-actions";
|
||||
@@ -1,5 +1,19 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const editScanFormSchema = (currentName: string) =>
|
||||
z.object({
|
||||
scanName: z
|
||||
.string()
|
||||
.refine((val) => val === "" || val.length >= 3, {
|
||||
message: "The alias must be empty or have at least 3 characters.",
|
||||
})
|
||||
.refine((val) => val !== currentName, {
|
||||
message: "The new name must be different from the current one.",
|
||||
})
|
||||
.optional(),
|
||||
scanId: z.string(),
|
||||
});
|
||||
|
||||
export const onDemandScanFormSchema = () =>
|
||||
z.object({
|
||||
providerId: z.string(),
|
||||
|
||||
Reference in New Issue
Block a user