From d1b9c316d8d30e593d55bade01f6abf54965aada Mon Sep 17 00:00:00 2001 From: Scott Wilson Date: Thu, 28 Nov 2024 15:23:57 -0800 Subject: [PATCH 1/3] improvement: use multi-select for environment selection on create secret --- .../SecretOverviewPage/SecretOverviewPage.tsx | 1 - .../CreateSecretForm/CreateSecretForm.tsx | 139 +++++++----------- 2 files changed, 51 insertions(+), 89 deletions(-) diff --git a/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx b/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx index cb49f8509..f4c83e2d0 100644 --- a/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx +++ b/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx @@ -1128,7 +1128,6 @@ export const SecretOverviewPage = () => { > handlePopUpClose("addSecretsInAllEnvs")} /> diff --git a/frontend/src/views/SecretOverviewPage/components/CreateSecretForm/CreateSecretForm.tsx b/frontend/src/views/SecretOverviewPage/components/CreateSecretForm/CreateSecretForm.tsx index 0716309d3..146d2035d 100644 --- a/frontend/src/views/SecretOverviewPage/components/CreateSecretForm/CreateSecretForm.tsx +++ b/frontend/src/views/SecretOverviewPage/components/CreateSecretForm/CreateSecretForm.tsx @@ -1,13 +1,13 @@ import { ClipboardEvent } from "react"; import { Controller, useForm } from "react-hook-form"; import { subject } from "@casl/ability"; -import { faTriangleExclamation, faWarning } from "@fortawesome/free-solid-svg-icons"; +import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { createNotification } from "@app/components/notifications"; -import { Button, Checkbox, FormControl, FormLabel, Input, Tooltip } from "@app/components/v2"; +import { Button, FilterableSelect, FormControl, Input } from "@app/components/v2"; import { CreatableSelect } from "@app/components/v2/CreatableSelect"; import { InfisicalSecretInput } from "@app/components/v2/InfisicalSecretInput"; import { @@ -17,20 +17,14 @@ import { useWorkspace } from "@app/context"; import { getKeyValue } from "@app/helpers/parseEnvVar"; -import { - useCreateFolder, - useCreateSecretV3, - useCreateWsTag, - useGetWsTags, - useUpdateSecretV3 -} from "@app/hooks/api"; -import { SecretType, SecretV3RawSanitized } from "@app/hooks/api/types"; +import { useCreateFolder, useCreateSecretV3, useCreateWsTag, useGetWsTags } from "@app/hooks/api"; +import { SecretType } from "@app/hooks/api/types"; const typeSchema = z .object({ key: z.string().trim().min(1, "Key is required"), value: z.string().optional(), - environments: z.record(z.boolean().optional()), + environments: z.object({ name: z.string(), slug: z.string() }).array(), tags: z.array(z.object({ label: z.string().trim(), value: z.string().trim() })).optional() }) .refine((data) => data.key !== undefined, { @@ -41,22 +35,19 @@ type TFormSchema = z.infer; type Props = { secretPath?: string; - getSecretByKey: (slug: string, key: string) => SecretV3RawSanitized | undefined; // modal props onClose: () => void; }; -export const CreateSecretForm = ({ secretPath = "/", getSecretByKey, onClose }: Props) => { +export const CreateSecretForm = ({ secretPath = "/", onClose }: Props) => { const { register, handleSubmit, control, reset, - watch, setValue, formState: { isSubmitting, errors } } = useForm({ resolver: zodResolver(typeSchema) }); - const newSecretKey = watch("key"); const { currentWorkspace } = useWorkspace(); const { permission } = useProjectPermission(); @@ -65,22 +56,14 @@ export const CreateSecretForm = ({ secretPath = "/", getSecretByKey, onClose }: const environments = currentWorkspace?.environments || []; const { mutateAsync: createSecretV3 } = useCreateSecretV3(); - const { mutateAsync: updateSecretV3 } = useUpdateSecretV3(); + // const { mutateAsync: updateSecretV3 } = useUpdateSecretV3(); const { mutateAsync: createFolder } = useCreateFolder(); const { data: projectTags, isLoading: isTagsLoading } = useGetWsTags( canReadTags ? workspaceId : "" ); const handleFormSubmit = async ({ key, value, environments: selectedEnv, tags }: TFormSchema) => { - const environmentsSelected = environments.filter(({ slug }) => selectedEnv[slug]); - const isEnvironmentsSelected = environmentsSelected.length; - - if (!isEnvironmentsSelected) { - createNotification({ type: "error", text: "Select at least one environment" }); - return; - } - - const promises = environmentsSelected.map(async (env) => { + const promises = selectedEnv.map(async (env) => { const environment = env.slug; // create folder if not existing if (secretPath !== "/") { @@ -106,21 +89,22 @@ export const CreateSecretForm = ({ secretPath = "/", getSecretByKey, onClose }: } } - const isEdit = getSecretByKey(environment, key) !== undefined; - if (isEdit) { - return { - ...(await updateSecretV3({ - environment, - workspaceId, - secretPath, - secretKey: key, - secretValue: value || "", - type: SecretType.Shared, - tagIds: tags?.map((el) => el.value) - })), - environment - }; - } + // TODO: add back - need to fetch secrets by key to check for conflicts as this method broke with pagination + // const isEdit = getSecretByKey(environment, key) !== undefined; + // if (isEdit) { + // return { + // ...(await updateSecretV3({ + // environment, + // workspaceId, + // secretPath, + // secretKey: key, + // secretValue: value || "", + // type: SecretType.Shared, + // tagIds: tags?.map((el) => el.value) + // })), + // environment + // }; + // } return { ...(await createSecretV3({ @@ -278,54 +262,33 @@ export const CreateSecretForm = ({ secretPath = "/", getSecretByKey, onClose }: )} /> - -
- {environments - .filter((environmentSlug) => - permission.can( - ProjectPermissionActions.Create, - subject(ProjectPermissionSub.Secrets, { - environment: environmentSlug.slug, - secretPath, - secretName: "*", - secretTags: ["*"] - }) - ) - ) - .map((env) => { - return ( - ( - - - - {env.name} - - - {getSecretByKey(env.slug, newSecretKey) && ( - - - - )} - - - - )} - /> - ); - })} -
+ ( + + + permission.can( + ProjectPermissionActions.Create, + subject(ProjectPermissionSub.Secrets, { + environment: environment.slug, + secretPath, + secretName: "*", + secretTags: ["*"] + }) + ) + )} + value={value} + onChange={onChange} + placeholder="Select environments to create secret in..." + getOptionLabel={(option) => option.name} + getOptionValue={(option) => option.slug} + /> + + )} + name="environments" + />