From 829b399cda9ce9a449f70177738a506468cb0ba4 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Sat, 9 Nov 2024 03:02:22 +0800 Subject: [PATCH] misc: moved to react hook form --- .../aws-secret-manager/create.tsx | 638 ++++++++++-------- 1 file changed, 368 insertions(+), 270 deletions(-) diff --git a/frontend/src/pages/integrations/aws-secret-manager/create.tsx b/frontend/src/pages/integrations/aws-secret-manager/create.tsx index ceddb5ade..136dffa4b 100644 --- a/frontend/src/pages/integrations/aws-secret-manager/create.tsx +++ b/frontend/src/pages/integrations/aws-secret-manager/create.tsx @@ -1,4 +1,5 @@ -import { useEffect, useState } from "react"; +import { useEffect } from "react"; +import { Controller, useForm } from "react-hook-form"; import Head from "next/head"; import Image from "next/image"; import Link from "next/link"; @@ -10,9 +11,12 @@ import { faCircleInfo } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { zodResolver } from "@hookform/resolvers/zod"; import { motion } from "framer-motion"; import queryString from "query-string"; +import z from "zod"; +import { SecretPathInput } from "@app/components/v2/SecretPathInput"; import { useCreateIntegration } from "@app/hooks/api"; import { useGetIntegrationAuthAwsKmsKeys } from "@app/hooks/api/integrationAuth/queries"; import { IntegrationMappingBehavior } from "@app/hooks/api/integrations/types"; @@ -83,10 +87,61 @@ const mappingBehaviors = [ } ]; +const schema = z + .object({ + awsRegion: z.string().trim().min(1, { message: "AWS region is required" }), + secretPath: z.string().trim().min(1, { message: "Secret path is required" }), + sourceEnvironment: z.string().trim().min(1, { message: "Source environment is required" }), + secretPrefix: z.string().default(""), + secretName: z.string().trim().min(1).optional(), + mappingBehavior: z.nativeEnum(IntegrationMappingBehavior), + kmsKeyId: z.string().optional(), + shouldTag: z.boolean().optional(), + tags: z + .object({ + key: z.string(), + value: z.string() + }) + .array() + }) + .refine( + (val) => + val.mappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE || + (val.mappingBehavior === IntegrationMappingBehavior.MANY_TO_ONE && + val.secretName && + val.secretName !== ""), + { + message: "Secret name must be defined for many-to-one integrations", + path: ["secretName"] + } + ); + +type TFormSchema = z.infer; + export default function AWSSecretManagerCreateIntegrationPage() { const router = useRouter(); const { mutateAsync } = useCreateIntegration(); + const { + control, + setValue, + handleSubmit, + watch, + formState: { isSubmitting } + } = useForm({ + resolver: zodResolver(schema), + defaultValues: { + shouldTag: false, + secretPath: "/", + secretPrefix: "", + mappingBehavior: IntegrationMappingBehavior.MANY_TO_ONE, + tags: [] + } + }); + const shouldTagState = watch("shouldTag"); + const selectedSourceEnvironment = watch("sourceEnvironment"); + const selectedAWSRegion = watch("awsRegion"); + const selectedMappingBehavior = watch("mappingBehavior"); const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]); const { data: workspace } = useGetWorkspaceById(localStorage.getItem("projectData.id") ?? ""); @@ -94,25 +149,6 @@ export default function AWSSecretManagerCreateIntegrationPage() { (integrationAuthId as string) ?? "" ); - const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(""); - const [secretPath, setSecretPath] = useState("/"); - const [selectedAWSRegion, setSelectedAWSRegion] = useState(""); - const [selectedMappingBehavior, setSelectedMappingBehavior] = useState( - IntegrationMappingBehavior.MANY_TO_ONE - ); - const [targetSecretName, setTargetSecretName] = useState(""); - const [targetSecretNameErrorText, setTargetSecretNameErrorText] = useState(""); - const [tagKey, setTagKey] = useState(""); - const [tagValue, setTagValue] = useState(""); - const [kmsKeyId, setKmsKeyId] = useState(""); - const [secretPrefix, setSecretPrefix] = useState(""); - - // const [path, setPath] = useState(''); - // const [pathErrorText, setPathErrorText] = useState(''); - - const [isLoading, setIsLoading] = useState(false); - const [shouldTag, setShouldTag] = useState(false); - const { data: integrationAuthAwsKmsKeys, isLoading: isIntegrationAuthAwsKmsKeysLoading } = useGetIntegrationAuthAwsKmsKeys({ integrationAuthId: String(integrationAuthId), @@ -121,63 +157,46 @@ export default function AWSSecretManagerCreateIntegrationPage() { useEffect(() => { if (workspace) { - setSelectedSourceEnvironment(workspace.environments[0].slug); - setSelectedAWSRegion(awsRegions[0].slug); + setValue("sourceEnvironment", workspace.environments[0].slug); + setValue("awsRegion", awsRegions[0].slug); } }, [workspace]); - // const isValidAWSPath = (path: string) => { - // const pattern = /^\/[\w./]+\/$/; - // return pattern.test(path) && path.length <= 2048; - // } - - const handleButtonClick = async () => { + const handleButtonClick = async ({ + secretName, + sourceEnvironment, + awsRegion, + secretPath, + shouldTag, + tags, + secretPrefix, + kmsKeyId, + mappingBehavior + }: TFormSchema) => { try { - if (!selectedMappingBehavior) { - return; - } - - if ( - selectedMappingBehavior === IntegrationMappingBehavior.MANY_TO_ONE && - targetSecretName.trim() === "" - ) { - setTargetSecretName("Secret name cannot be blank"); - return; - } - if (!integrationAuth?.id) return; - setIsLoading(true); - await mutateAsync({ integrationAuthId: integrationAuth?.id, isActive: true, - app: targetSecretName.trim(), - sourceEnvironment: selectedSourceEnvironment, - region: selectedAWSRegion, + app: secretName, + sourceEnvironment, + region: awsRegion, secretPath, metadata: { ...(shouldTag ? { - secretAWSTag: [ - { - key: tagKey, - value: tagValue - } - ] + secretAWSTag: tags } : {}), ...(secretPrefix && { secretPrefix }), ...(kmsKeyId && { kmsKeyId }), - mappingBehavior: selectedMappingBehavior + mappingBehavior } }); - setIsLoading(false); - setTargetSecretNameErrorText(""); router.push(`/integrations/${localStorage.getItem("projectData.id")}`); } catch (err) { - setIsLoading(false); console.error(err); } }; @@ -191,226 +210,305 @@ export default function AWSSecretManagerCreateIntegrationPage() { Set Up AWS Secrets Manager Integration - - -
-
- AWS logo -
- AWS Secrets Manager Integration - - -
- - Docs - -
-
- -
-
- - -
- Connection - Options -
-
- - - - - - - setSecretPath(evt.target.value)} - placeholder="Provide a path, default is /" +
+ + +
+
+ AWS logo - - - - - - - - {selectedMappingBehavior === IntegrationMappingBehavior.MANY_TO_ONE && ( - - setTargetSecretName(e.target.value)} - /> - - )} - - - - -
- setShouldTag(!shouldTag)} - isChecked={shouldTag} - > - Tag in AWS Secrets Manager -
- {shouldTag && ( -
- - setTagKey(e.target.value)} + AWS Secrets Manager Integration + + +
+ + Docs + - - - setTagValue(e.target.value)} - /> - -
- )} - - - setSecretPrefix(e.target.value)} - placeholder="INFISICAL_" - /> - - - - { + field.onChange(val); + }} + > + {workspace?.environments.map((sourceEnvironment) => ( + + {sourceEnvironment.name} + + ))} + + )} - -
- - - - - -
-
-
- {" "} - Pro Tip + /> + ( + + + + )} + /> + ( + + + + )} + /> + ( + + + + )} + /> + {selectedMappingBehavior === IntegrationMappingBehavior.MANY_TO_ONE && ( + ( + + + + )} + /> + )} + + + + +
+ ( + onChange(isChecked)} + isChecked={value} + > + Tag in AWS Secrets Manager + + )} + /> +
+ {shouldTagState && ( +
+ ( + + + + )} + /> + ( + + + + )} + /> +
+ )} + + ( + + + + )} + /> + + ( + + + + )} + /> +
+
+ + + +
+
+
+ {" "} + Pro Tip +
+ + After creating an integration, your secrets will start syncing immediately. This might + cause an unexpected override of current secrets in AWS Secrets Manager with secrets from + Infisical. +
- - After creating an integration, your secrets will start syncing immediately. This might - cause an unexpected override of current secrets in AWS Secrets Manager with secrets from - Infisical. - -
+
) : (