From b6c4d7590aaeac5d46bf317d03fa9978e85946eb Mon Sep 17 00:00:00 2001 From: Isha Venikar Date: Wed, 16 Oct 2024 23:00:07 +0530 Subject: [PATCH 1/6] Create separate form for adding environment variables --- .../id/settings/EnvironmentVariables.tsx | 119 ++---------------- .../id/settings/EnvironmentVariablesForm.tsx | 98 +++++++++++++++ 2 files changed, 105 insertions(+), 112 deletions(-) create mode 100644 packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx diff --git a/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx index a710c243..b3c9314d 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx @@ -1,22 +1,19 @@ -import { useCallback, useEffect, useMemo, useState } from 'react'; -import { useFieldArray, useForm } from 'react-hook-form'; +import { useCallback, useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; -import { Collapse, Checkbox } from '@snowballtools/material-tailwind-react-fork'; +import { Collapse } from '@snowballtools/material-tailwind-react-fork'; -import AddEnvironmentVariableRow from 'components/projects/project/settings/AddEnvironmentVariableRow'; import DisplayEnvironmentVariables from 'components/projects/project/settings/DisplayEnvironmentVariables'; import { useGQLClient } from 'context/GQLClientContext'; import { EnvironmentVariablesFormValues } from '../../../../../types'; import HorizontalLine from 'components/HorizontalLine'; import { Heading } from 'components/shared/Heading'; -import { Button } from 'components/shared/Button'; // import { Checkbox } from 'components/shared/Checkbox'; import { PlusIcon } from 'components/shared/CustomIcon'; -import { InlineNotification } from 'components/shared/InlineNotification'; import { ProjectSettingContainer } from 'components/projects/project/settings/ProjectSettingContainer'; import { useToast } from 'components/shared/Toast'; import { Environment, EnvironmentVariable } from 'gql-client'; +import EnvironmentVariablesForm from './EnvironmentVariablesForm'; export const EnvironmentVariablesTabPanel = () => { const { id } = useParams(); @@ -27,38 +24,8 @@ export const EnvironmentVariablesTabPanel = () => { EnvironmentVariable[] >([]); - const { - handleSubmit, - register, - control, - reset, - formState: { isSubmitSuccessful, errors }, - } = useForm({ - defaultValues: { - variables: [{ key: '', value: '' }], - environment: { - development: false, - preview: false, - production: false, - }, - }, - }); const [createNewVariable, setCreateNewVariable] = useState(false); - const { fields, append, remove } = useFieldArray({ - name: 'variables', - control, - rules: { - required: 'Add at least 1 environment variables', - }, - }); - - useEffect(() => { - if (isSubmitSuccessful) { - reset(); - } - }, [isSubmitSuccessful, reset, id]); - const getEnvironmentVariables = useCallback( (environment: Environment) => { return environmentVariables.filter( @@ -68,21 +35,6 @@ export const EnvironmentVariablesTabPanel = () => { [environmentVariables, id], ); - const isFieldEmpty = useMemo(() => { - if (errors.variables) { - return fields.some((_, index) => { - if ( - errors.variables![index]?.value?.type === 'required' || - errors.variables![index]?.key?.type === 'required' - ) { - return true; - } - }); - } - - return false; - }, [fields, errors.variables, id]); - const fetchEnvironmentVariables = useCallback( async (id: string | undefined) => { if (id) { @@ -99,7 +51,7 @@ export const EnvironmentVariablesTabPanel = () => { }, [id]); const createEnvironmentVariablesHandler = useCallback( - async (createFormData: EnvironmentVariablesFormValues) => { + async (createFormData: EnvironmentVariablesFormValues, reset: () => void) => { const environmentVariables = createFormData.variables.map((variable) => { return { key: variable.key, @@ -160,66 +112,9 @@ export const EnvironmentVariablesTabPanel = () => {
-
- {fields.map((field, index) => { - return ( - remove(index)} - isDeleteDisabled={fields.length === 1} - /> - ); - })} -
- - {/* TODO: Implement import environment varible functionality */} - -
- {isFieldEmpty && ( - - )} -
- - - -
-
- -
- -
+ createEnvironmentVariablesHandler(data, reset)} /> +
diff --git a/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx new file mode 100644 index 00000000..5c70c1f1 --- /dev/null +++ b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx @@ -0,0 +1,98 @@ +import React, { useEffect, useMemo } from 'react'; +import { useForm, useFieldArray } from 'react-hook-form'; + +import { Checkbox } from '@snowballtools/material-tailwind-react-fork'; + +import { Button } from 'components/shared/Button'; +import { InlineNotification } from 'components/shared/InlineNotification'; +import AddEnvironmentVariableRow from 'components/projects/project/settings/AddEnvironmentVariableRow'; +import { EnvironmentVariablesFormValues } from 'types/types'; + +interface EnvironmentVariablesFormProps { + onSubmit: (data: EnvironmentVariablesFormValues, reset: () => void) => void; +} + +const EnvironmentVariablesForm: React.FC = ({ onSubmit }) => { + const { + handleSubmit, + register, + control, + reset, + formState: { isSubmitSuccessful, errors }, + } = useForm({ + defaultValues: { + variables: [{ key: '', value: '' }], + environment: { + development: false, + preview: false, + production: false, + }, + }, + }); + + const { fields, append, remove } = useFieldArray({ + name: 'variables', + control, + rules: { + required: 'Add at least 1 environment variables', + }, + }); + + useEffect(() => { + if (isSubmitSuccessful) { + reset(); + } + }, [isSubmitSuccessful, reset]); + + const isFieldEmpty = useMemo(() => { + if (errors.variables) { + return fields.some((_, index) => { + if ( + errors.variables![index]?.value?.type === 'required' || + errors.variables![index]?.key?.type === 'required' + ) { + return true; + } + }); + } + + return false; + }, [fields, errors.variables]); + + return ( +
onSubmit(data, reset))}> + {fields.map((field, index) => ( + remove(index)} + isDeleteDisabled={fields.length === 1} + /> + ))} +
+ +
+ {isFieldEmpty && ( + + )} +
+ + + +
+
+ +
+ + ); +}; + +export default EnvironmentVariablesForm; -- 2.45.2 From 05a24dcb776fadf18001fd9c2c8057094d526d15 Mon Sep 17 00:00:00 2001 From: IshaVenikar Date: Thu, 17 Oct 2024 12:57:48 +0530 Subject: [PATCH 2/6] Integrate env variables form in configure deployment step --- packages/backend/src/registry.ts | 6 +- packages/backend/src/service.ts | 7 +- .../components/projects/create/Configure.tsx | 335 +++++++++++------- .../id/settings/EnvironmentVariables.tsx | 35 +- .../id/settings/EnvironmentVariablesForm.tsx | 68 ++-- 5 files changed, 257 insertions(+), 194 deletions(-) diff --git a/packages/backend/src/registry.ts b/packages/backend/src/registry.ts index 45305c57..904376ed 100644 --- a/packages/backend/src/registry.ts +++ b/packages/backend/src/registry.ts @@ -416,11 +416,11 @@ export class Registry { }; } - async getCompletedAuctionIds(auctionIds: (string | null | undefined)[]): Promise { - const validAuctionIds = auctionIds.filter((id): id is string => id !== null && id !== undefined); + async getCompletedAuctionIds(auctionIds: string[]): Promise { + const validAuctionIds = auctionIds.filter((id): id is string => id !== ''); if (!validAuctionIds.length) { - return null; + return []; } const auctions = await this.registry.getAuctionsByIds(validAuctionIds); diff --git a/packages/backend/src/service.ts b/packages/backend/src/service.ts index 728a067b..e52595fa 100644 --- a/packages/backend/src/service.ts +++ b/packages/backend/src/service.ts @@ -313,10 +313,9 @@ export class Service { return project.deployments.length === 0; }); - const auctionIds = projects.map((project) => project.auctionId); - const completedAuctionIds = await this.laconicRegistry.getCompletedAuctionIds(auctionIds); - - if (completedAuctionIds) { + if (projects.length > 0) { + const auctionIds = projects.map((project) => project.auctionId!); + const completedAuctionIds = await this.laconicRegistry.getCompletedAuctionIds(auctionIds); const projectsToBedeployed = projects.filter((project) => completedAuctionIds.includes(project.auctionId!) ); diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index d1361788..4f622326 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -1,5 +1,6 @@ import { useCallback, useState } from 'react'; -import { useForm, Controller, SubmitHandler } from 'react-hook-form'; +import { useForm, Controller } from 'react-hook-form'; +import { FormProvider, FieldValues } from 'react-hook-form'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { useMediaQuery } from 'usehooks-ts'; import { AuctionParams } from 'gql-client'; @@ -14,15 +15,20 @@ import { Select, SelectOption } from 'components/shared/Select'; import { Input } from 'components/shared/Input'; import { useToast } from 'components/shared/Toast'; import { useGQLClient } from '../../../context/GQLClientContext'; +import EnvironmentVariablesForm from 'pages/org-slug/projects/id/settings/EnvironmentVariablesForm'; +import { EnvironmentVariablesFormValues } from 'types/types'; -type ConfigureFormValues = { +type ConfigureDeploymentFormValues = { option: string; lrn?: string; numProviders?: number; maxPrice?: string; }; +type ConfigureFormValues = ConfigureDeploymentFormValues & EnvironmentVariablesFormValues; + const Configure = () => { + const [isLoading, setIsLoading] = useState(false); const [searchParams] = useSearchParams(); const templateId = searchParams.get('templateId'); const queryParams = new URLSearchParams(location.search); @@ -40,19 +46,18 @@ const Configure = () => { const { toast, dismiss } = useToast(); const client = useGQLClient(); - const [isLoading, setIsLoading] = useState(false); - const { handleSubmit, control, watch } = useForm({ + const methods = useForm({ defaultValues: { option: 'LRN' }, }); - const selectedOption = watch('option'); + const selectedOption = methods.watch('option'); const isTabletView = useMediaQuery('(min-width: 720px)'); // md: const buttonSize = isTabletView ? { size: 'lg' as const } : {}; - const onSubmit: SubmitHandler = useCallback( - async (data) => { - setIsLoading(true); + const createProject = async (data: FieldValues): Promise => { + setIsLoading(true); + let projectId: string | null = null; try { let lrn: string | undefined; @@ -67,69 +72,118 @@ const Configure = () => { }; } - if (templateId) { - // Template-based project creation - const projectData: any = { - templateOwner, - templateRepo, - owner, - name, - isPrivate, - }; + if (templateId) { + const projectData: any = { + templateOwner, + templateRepo, + owner, + name, + isPrivate, + }; - const { addProjectFromTemplate } = await client.addProjectFromTemplate( - orgSlug!, - projectData, - lrn, - auctionParams - ); + const { addProjectFromTemplate } = await client.addProjectFromTemplate( + orgSlug!, + projectData, + lrn, + auctionParams + ); + projectId = addProjectFromTemplate.id; + } else { + const { addProject } = await client.addProject( + orgSlug!, + { + name: fullName!, + prodBranch: defaultBranch!, + repository: fullName!, + template: 'webapp', + }, + lrn, + auctionParams + ); - data.option === 'Auction' - ? navigate( - `/${orgSlug}/projects/create/success/${addProjectFromTemplate.id}?isAuction=true`, - ) - : navigate( - `/${orgSlug}/projects/create/template/deploy?projectId=${addProjectFromTemplate.id}&templateId=${templateId}` - ); - } else { - const { addProject } = await client.addProject( - orgSlug!, - { - name: fullName!, - prodBranch: defaultBranch!, - repository: fullName!, - template: 'webapp', - }, - lrn, - auctionParams - ); + projectId = addProject.id; + } + } catch (error) { + console.error('Error creating project:', error); + toast({ + id: 'error-creating-project', + title: 'Error creating project', + variant: 'error', + onDismiss: dismiss, + }); + } finally { + setIsLoading(false); + } - data.option === 'Auction' - ? navigate( - `/${orgSlug}/projects/create/success/${addProject.id}?isAuction=true` - ) - : navigate( - `/${orgSlug}/projects/create/deploy?projectId=${addProject.id}` - ); - } - } catch (error) { - console.error('Error creating project:', error); + if (projectId) { + return projectId; + } else { + throw new Error('Project creation failed'); + } + }; + + const createEnvironmentVariablesHandler = useCallback( + async (createFormData: FieldValues) => { + const environmentVariables = createFormData.variables.map((variable: any) => { + return { + key: variable.key, + value: variable.value, + environments: Object.entries(createFormData.environment) + .filter(([, value]) => value === true) + .map(([key]) => key.charAt(0).toUpperCase() + key.slice(1)), + }; + }); + + const projectId = await createProject(createFormData); + + const { addEnvironmentVariables: isEnvironmentVariablesAdded } = + await client.addEnvironmentVariables(projectId, environmentVariables); + + if (isEnvironmentVariablesAdded) { toast({ - id: 'error-creating-project', - title: 'Error creating project', + id: + createFormData.variables.length > 1 + ? 'env_variable_added' + : 'env_variables_added', + title: + createFormData.variables.length > 1 + ? `${createFormData.variables.length} variables added` + : `Variable added`, + variant: 'success', + onDismiss: dismiss, + }); + } else { + toast({ + id: 'env_variables_not_added', + title: 'Environment variables not added', variant: 'error', onDismiss: dismiss, }); - } finally { - setIsLoading(false); + } + if (templateId) { + createFormData.option === 'Auction' + ? navigate( + `/${orgSlug}/projects/create/success/${projectId}?isAuction=true`, + ) + : navigate( + `/${orgSlug}/projects/create/template/deploy?projectId=${projectId}&templateId=${templateId}` + ); + } else { + createFormData.option === 'Auction' + ? navigate( + `/${orgSlug}/projects/create/success/${projectId}?isAuction=true` + ) + : navigate( + `/${orgSlug}/projects/create/deploy?projectId=${projectId}` + ); } }, - [client, isPrivate, templateId, navigate, dismiss, toast] + [client, createProject, dismiss, toast] ); return ( -
-
+
+
Configure deployment @@ -142,99 +196,108 @@ const Configure = () => {
-
-
-
- ( - + + )} />
-
- - Maximum Price (alnt) - - ( - - )} - /> -
- - )} + )} -
- -
-
-
+ {selectedOption === 'Auction' && ( + <> +
+ + Set the number of deployers and maximum price for each deployment + + + Number of Deployers + + ( + + )} + /> +
+
+ + Maximum Price (alnt) + + ( + + )} + /> +
+ + )} + + + Environment Variables + +
+ +
+ +
+ +
+ + +
); }; diff --git a/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx index b3c9314d..459c4f15 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx @@ -14,6 +14,8 @@ import { ProjectSettingContainer } from 'components/projects/project/settings/Pr import { useToast } from 'components/shared/Toast'; import { Environment, EnvironmentVariable } from 'gql-client'; import EnvironmentVariablesForm from './EnvironmentVariablesForm'; +import { FieldValues, FormProvider, useForm } from 'react-hook-form'; +import { Button } from 'components/shared'; export const EnvironmentVariablesTabPanel = () => { const { id } = useParams(); @@ -26,6 +28,17 @@ export const EnvironmentVariablesTabPanel = () => { const [createNewVariable, setCreateNewVariable] = useState(false); + const methods = useForm({ + defaultValues: { + variables: [{ key: '', value: '' }], + environment: { + development: false, + preview: false, + production: false, + }, + }, + }); + const getEnvironmentVariables = useCallback( (environment: Environment) => { return environmentVariables.filter( @@ -51,8 +64,8 @@ export const EnvironmentVariablesTabPanel = () => { }, [id]); const createEnvironmentVariablesHandler = useCallback( - async (createFormData: EnvironmentVariablesFormValues, reset: () => void) => { - const environmentVariables = createFormData.variables.map((variable) => { + async (createFormData: FieldValues) => { + const environmentVariables = createFormData.variables.map((variable: any) => { return { key: variable.key, value: variable.value, @@ -66,7 +79,7 @@ export const EnvironmentVariablesTabPanel = () => { await client.addEnvironmentVariables(id!, environmentVariables); if (isEnvironmentVariablesAdded) { - reset(); + // reset(); setCreateNewVariable((cur) => !cur); fetchEnvironmentVariables(id); @@ -111,10 +124,18 @@ export const EnvironmentVariablesTabPanel = () => {
-
- createEnvironmentVariablesHandler(data, reset)} /> -
+ +
createEnvironmentVariablesHandler(data))}> +
+ +
+
+ +
+
+
diff --git a/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx index 5c70c1f1..c5860522 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx @@ -1,35 +1,20 @@ -import React, { useEffect, useMemo } from 'react'; -import { useForm, useFieldArray } from 'react-hook-form'; +import { useEffect } from 'react'; +import { useFieldArray, useFormContext } from 'react-hook-form'; import { Checkbox } from '@snowballtools/material-tailwind-react-fork'; import { Button } from 'components/shared/Button'; -import { InlineNotification } from 'components/shared/InlineNotification'; +// import { InlineNotification } from 'components/shared/InlineNotification'; import AddEnvironmentVariableRow from 'components/projects/project/settings/AddEnvironmentVariableRow'; import { EnvironmentVariablesFormValues } from 'types/types'; -interface EnvironmentVariablesFormProps { - onSubmit: (data: EnvironmentVariablesFormValues, reset: () => void) => void; -} - -const EnvironmentVariablesForm: React.FC = ({ onSubmit }) => { +const EnvironmentVariablesForm = () => { const { - handleSubmit, register, control, reset, - formState: { isSubmitSuccessful, errors }, - } = useForm({ - defaultValues: { - variables: [{ key: '', value: '' }], - environment: { - development: false, - preview: false, - production: false, - }, - }, - }); - + formState: { isSubmitSuccessful }, + } = useFormContext(); const { fields, append, remove } = useFieldArray({ name: 'variables', control, @@ -43,24 +28,24 @@ const EnvironmentVariablesForm: React.FC = ({ onS reset(); } }, [isSubmitSuccessful, reset]); - - const isFieldEmpty = useMemo(() => { - if (errors.variables) { - return fields.some((_, index) => { - if ( - errors.variables![index]?.value?.type === 'required' || - errors.variables![index]?.key?.type === 'required' - ) { - return true; - } - }); - } - return false; - }, [fields, errors.variables]); + // const isFieldEmpty = useMemo(() => { + // if (errors.variables) { + // return fields.some((_, index) => { + // if ( + // errors.variables![index]?.value?.type === 'required' || + // errors.variables![index]?.key?.type === 'required' + // ) { + // return true; + // } + // }); + // } + + // return false; + // }, [fields, errors.variables]); return ( -
onSubmit(data, reset))}> + <> {fields.map((field, index) => ( = ({ onS + Add variable
- {isFieldEmpty && ( + {/* {isFieldEmpty && ( - )} + )} */}
-
- -
- + ); }; -- 2.45.2 From 88f2def068f2654000981eff70c3635283185e8e Mon Sep 17 00:00:00 2001 From: IshaVenikar Date: Thu, 17 Oct 2024 15:45:15 +0530 Subject: [PATCH 3/6] Fix search in repository list component --- .../projects/create/RepositoryList/RepositoryList.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/projects/create/RepositoryList/RepositoryList.tsx b/packages/frontend/src/components/projects/create/RepositoryList/RepositoryList.tsx index 8f7c7811..aa51f0b2 100644 --- a/packages/frontend/src/components/projects/create/RepositoryList/RepositoryList.tsx +++ b/packages/frontend/src/components/projects/create/RepositoryList/RepositoryList.tsx @@ -64,9 +64,9 @@ export const RepositoryList = () => { // Check if selected account is an organization if (selectedAccount.value === gitUser.login) { - query = query + ` user:${selectedAccount}`; + query = query + ` user:${selectedAccount.value}`; } else { - query = query + ` org:${selectedAccount}`; + query = query + ` org:${selectedAccount.value}`; } const result = await octokit.rest.search.repos({ -- 2.45.2 From 7ceee9894199471299b75f457b4b9db97e1c7bba Mon Sep 17 00:00:00 2001 From: IshaVenikar Date: Thu, 17 Oct 2024 18:22:48 +0530 Subject: [PATCH 4/6] Add validation for form fields --- .../components/projects/create/Configure.tsx | 3 ++ .../projects/create/template/index.tsx | 3 +- .../id/settings/EnvironmentVariablesForm.tsx | 43 +++++++++---------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index 4f622326..75061055 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -233,6 +233,7 @@ const Configure = () => { ( )} @@ -252,6 +253,7 @@ const Configure = () => { ( )} @@ -264,6 +266,7 @@ const Configure = () => { ( )} diff --git a/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx b/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx index ebb9f3b9..4024e521 100644 --- a/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx +++ b/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx @@ -163,6 +163,7 @@ const CreateRepo = () => { ( )} @@ -172,7 +173,7 @@ const CreateRepo = () => { ( + render={({ }) => ( )} /> diff --git a/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx index c5860522..6b14b181 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx @@ -1,10 +1,10 @@ -import { useEffect } from 'react'; +import { useEffect, useMemo } from 'react'; import { useFieldArray, useFormContext } from 'react-hook-form'; import { Checkbox } from '@snowballtools/material-tailwind-react-fork'; import { Button } from 'components/shared/Button'; -// import { InlineNotification } from 'components/shared/InlineNotification'; +import { InlineNotification } from 'components/shared/InlineNotification'; import AddEnvironmentVariableRow from 'components/projects/project/settings/AddEnvironmentVariableRow'; import { EnvironmentVariablesFormValues } from 'types/types'; @@ -13,14 +13,11 @@ const EnvironmentVariablesForm = () => { register, control, reset, - formState: { isSubmitSuccessful }, + formState: { isSubmitSuccessful, errors }, } = useFormContext(); const { fields, append, remove } = useFieldArray({ name: 'variables', control, - rules: { - required: 'Add at least 1 environment variables', - }, }); useEffect(() => { @@ -29,20 +26,20 @@ const EnvironmentVariablesForm = () => { } }, [isSubmitSuccessful, reset]); - // const isFieldEmpty = useMemo(() => { - // if (errors.variables) { - // return fields.some((_, index) => { - // if ( - // errors.variables![index]?.value?.type === 'required' || - // errors.variables![index]?.key?.type === 'required' - // ) { - // return true; - // } - // }); - // } + const isFieldEmpty = useMemo(() => { + if (errors.variables) { + return fields.some((_, index) => { + if ( + errors.variables![index]?.value?.type === 'required' || + errors.variables![index]?.key?.type === 'required' + ) { + return true; + } + }); + } - // return false; - // }, [fields, errors.variables]); + return false; + }, [fields, errors.variables]); return ( <> @@ -52,7 +49,7 @@ const EnvironmentVariablesForm = () => { index={index} register={register} onDelete={() => remove(index)} - isDeleteDisabled={fields.length === 1} + isDeleteDisabled={fields.length === 0} /> ))}
@@ -60,18 +57,18 @@ const EnvironmentVariablesForm = () => { + Add variable
- {/* {isFieldEmpty && ( + {isFieldEmpty && ( - )} */} + )}
- + ); }; -- 2.45.2 From 9461bd04f1c1bb30bd8f29915ea76e1f092f707b Mon Sep 17 00:00:00 2001 From: IshaVenikar Date: Fri, 18 Oct 2024 10:06:27 +0530 Subject: [PATCH 5/6] Pass env variables when creating project --- packages/backend/src/resolvers.ts | 34 +++++++++++++++---- packages/backend/src/schema.gql | 2 ++ packages/backend/src/service.ts | 15 +++++--- .../components/projects/create/Configure.tsx | 31 +++++++++-------- packages/gql-client/src/client.ts | 8 +++-- packages/gql-client/src/mutations.ts | 8 ++--- 6 files changed, 67 insertions(+), 31 deletions(-) diff --git a/packages/backend/src/resolvers.ts b/packages/backend/src/resolvers.ts index 5e640749..dbe65f3b 100644 --- a/packages/backend/src/resolvers.ts +++ b/packages/backend/src/resolvers.ts @@ -211,8 +211,15 @@ export const createResolvers = async (service: Service): Promise => { organizationSlug, data, lrn, - auctionParams - }: { organizationSlug: string; data: AddProjectFromTemplateInput; lrn: string; auctionParams: AuctionParams }, + auctionParams, + environmentVariables + }: { + organizationSlug: string; + data: AddProjectFromTemplateInput; + lrn: string; + auctionParams: AuctionParams, + environmentVariables: { environments: string[]; key: string; value: string }[]; + }, context: any, ) => { try { @@ -221,7 +228,8 @@ export const createResolvers = async (service: Service): Promise => { organizationSlug, data, lrn, - auctionParams + auctionParams, + environmentVariables ); } catch (err) { log(err); @@ -235,12 +243,26 @@ export const createResolvers = async (service: Service): Promise => { organizationSlug, data, lrn, - auctionParams - }: { organizationSlug: string; data: DeepPartial; lrn: string; auctionParams: AuctionParams }, + auctionParams, + environmentVariables + }: { + organizationSlug: string; + data: DeepPartial; + lrn: string; + auctionParams: AuctionParams, + environmentVariables: { environments: string[]; key: string; value: string }[]; + }, context: any, ) => { try { - return await service.addProject(context.user, organizationSlug, data, lrn, auctionParams); + return await service.addProject( + context.user, + organizationSlug, + data, + lrn, + auctionParams, + environmentVariables + ); } catch (err) { log(err); throw err; diff --git a/packages/backend/src/schema.gql b/packages/backend/src/schema.gql index df8bd03a..b4f74d72 100644 --- a/packages/backend/src/schema.gql +++ b/packages/backend/src/schema.gql @@ -271,12 +271,14 @@ type Mutation { data: AddProjectFromTemplateInput lrn: String auctionParams: AuctionParams + environmentVariables: [AddEnvironmentVariableInput!] ): Project! addProject( organizationSlug: String! data: AddProjectInput! lrn: String auctionParams: AuctionParams + environmentVariables: [AddEnvironmentVariableInput!] ): Project! updateProject(projectId: String!, data: UpdateProjectInput): Boolean! redeployToProd(deploymentId: String!): Boolean! diff --git a/packages/backend/src/service.ts b/packages/backend/src/service.ts index e52595fa..5b127712 100644 --- a/packages/backend/src/service.ts +++ b/packages/backend/src/service.ts @@ -784,7 +784,8 @@ export class Service { organizationSlug: string, data: AddProjectFromTemplateInput, lrn?: string, - auctionParams?: AuctionParams + auctionParams?: AuctionParams, + environmentVariables?: { environments: string[]; key: string; value: string }[], ): Promise { try { const octokit = await this.getOctokit(user.id); @@ -815,7 +816,7 @@ export class Service { repository: gitRepo.data.full_name, // TODO: Set selected template template: 'webapp', - }, lrn, auctionParams); + }, lrn, auctionParams, environmentVariables); if (!project || !project.id) { throw new Error('Failed to create project from template'); @@ -833,7 +834,8 @@ export class Service { organizationSlug: string, data: DeepPartial, lrn?: string, - auctionParams?: AuctionParams + auctionParams?: AuctionParams, + environmentVariables?: { environments: string[]; key: string; value: string }[], ): Promise { const organization = await this.db.getOrganization({ where: { @@ -844,8 +846,11 @@ export class Service { throw new Error('Organization does not exist'); } - const project = await this.db.addProject(user, organization.id, data); - log(`Project created ${project.id}`); + const project = await this.db.addProject(user, organization.id, data);4 + + if(environmentVariables) { + await this.addEnvironmentVariables(project.id, environmentVariables); + } const octokit = await this.getOctokit(user.id); const [owner, repo] = project.repository.split('/'); diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index 75061055..7f920c0a 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -3,7 +3,7 @@ import { useForm, Controller } from 'react-hook-form'; import { FormProvider, FieldValues } from 'react-hook-form'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { useMediaQuery } from 'usehooks-ts'; -import { AuctionParams } from 'gql-client'; +import { AddEnvironmentVariableInput, AuctionParams } from 'gql-client'; import { ArrowRightCircleFilledIcon, @@ -55,7 +55,7 @@ const Configure = () => { const isTabletView = useMediaQuery('(min-width: 720px)'); // md: const buttonSize = isTabletView ? { size: 'lg' as const } : {}; - const createProject = async (data: FieldValues): Promise => { + const createProject = async (data: FieldValues, envVariables: AddEnvironmentVariableInput[]): Promise => { setIsLoading(true); let projectId: string | null = null; @@ -81,13 +81,15 @@ const Configure = () => { isPrivate, }; - const { addProjectFromTemplate } = await client.addProjectFromTemplate( - orgSlug!, - projectData, - lrn, - auctionParams - ); - projectId = addProjectFromTemplate.id; + const { addProjectFromTemplate } = await client.addProjectFromTemplate( + orgSlug!, + projectData, + lrn, + auctionParams, + envVariables + ); + + projectId = addProjectFromTemplate.id; } else { const { addProject } = await client.addProject( orgSlug!, @@ -98,7 +100,8 @@ const Configure = () => { template: 'webapp', }, lrn, - auctionParams + auctionParams, + envVariables ); projectId = addProject.id; @@ -134,12 +137,12 @@ const Configure = () => { }; }); - const projectId = await createProject(createFormData); + const projectId = await createProject(createFormData, environmentVariables); - const { addEnvironmentVariables: isEnvironmentVariablesAdded } = - await client.addEnvironmentVariables(projectId, environmentVariables); + const { environmentVariables: isEnvironmentVariablesAdded } = + await client.getEnvironmentVariables(projectId); - if (isEnvironmentVariablesAdded) { + if (isEnvironmentVariablesAdded.length > 0) { toast({ id: createFormData.variables.length > 1 diff --git a/packages/gql-client/src/client.ts b/packages/gql-client/src/client.ts index f7099de5..0e0eff9f 100644 --- a/packages/gql-client/src/client.ts +++ b/packages/gql-client/src/client.ts @@ -233,6 +233,7 @@ export class GQLClient { data: types.AddProjectFromTemplateInput, lrn?: string, auctionParams?: types.AuctionParams, + environmentVariables?: types.AddEnvironmentVariableInput[] ): Promise { const result = await this.client.mutate({ mutation: mutations.addProjectFromTemplate, @@ -240,7 +241,8 @@ export class GQLClient { organizationSlug, data, lrn, - auctionParams + auctionParams, + environmentVariables }, }); @@ -252,6 +254,7 @@ export class GQLClient { data: types.AddProjectInput, lrn?: string, auctionParams?: types.AuctionParams, + environmentVariables?: types.AddEnvironmentVariableInput[] ): Promise { const result = await this.client.mutate({ mutation: mutations.addProject, @@ -259,7 +262,8 @@ export class GQLClient { organizationSlug, data, lrn, - auctionParams + auctionParams, + environmentVariables }, }); diff --git a/packages/gql-client/src/mutations.ts b/packages/gql-client/src/mutations.ts index 1ad04e22..876e5611 100644 --- a/packages/gql-client/src/mutations.ts +++ b/packages/gql-client/src/mutations.ts @@ -49,16 +49,16 @@ export const updateDeploymentToProd = gql` `; export const addProjectFromTemplate = gql` - mutation ($organizationSlug: String!, $data: AddProjectFromTemplateInput, $lrn: String, $auctionParams: AuctionParams) { - addProjectFromTemplate(organizationSlug: $organizationSlug, data: $data, lrn: $lrn, auctionParams: $auctionParams) { + mutation ($organizationSlug: String!, $data: AddProjectFromTemplateInput, $lrn: String, $auctionParams: AuctionParams, $environmentVariables: [AddEnvironmentVariableInput!]) { + addProjectFromTemplate(organizationSlug: $organizationSlug, data: $data, lrn: $lrn, auctionParams: $auctionParams, environmentVariables: $environmentVariables) { id } } `; export const addProject = gql` - mutation ($organizationSlug: String!, $data: AddProjectInput!, $lrn: String, $auctionParams: AuctionParams) { - addProject(organizationSlug: $organizationSlug, data: $data, lrn: $lrn, auctionParams: $auctionParams) { + mutation ($organizationSlug: String!, $data: AddProjectInput!, $lrn: String, $auctionParams: AuctionParams, $environmentVariables: [AddEnvironmentVariableInput!]) { + addProject(organizationSlug: $organizationSlug, data: $data, lrn: $lrn, auctionParams: $auctionParams, environmentVariables: $environmentVariables) { id } } -- 2.45.2 From aac2221af0f6d3b4cc8e89d3c1b3fa24daa1b96f Mon Sep 17 00:00:00 2001 From: IshaVenikar Date: Mon, 21 Oct 2024 10:07:34 +0530 Subject: [PATCH 6/6] Use query builder to get projects without deployments --- packages/backend/src/database.ts | 91 +++++++++++-------- packages/backend/src/registry.ts | 8 +- packages/backend/src/resolvers.ts | 6 +- packages/backend/src/service.ts | 75 ++++++--------- packages/backend/src/types.ts | 6 ++ .../components/projects/create/Configure.tsx | 58 ++++++------ .../id/settings/EnvironmentVariables.tsx | 3 +- .../id/settings/EnvironmentVariablesForm.tsx | 1 + 8 files changed, 120 insertions(+), 128 deletions(-) diff --git a/packages/backend/src/database.ts b/packages/backend/src/database.ts index 1a48c35d..35c3aa65 100644 --- a/packages/backend/src/database.ts +++ b/packages/backend/src/database.ts @@ -33,7 +33,7 @@ export class Database { private dataSource: DataSource; private projectDomain: string; - constructor ({ dbPath } : DatabaseConfig, { projectDomain } : MiscConfig) { + constructor({ dbPath }: DatabaseConfig, { projectDomain }: MiscConfig) { this.dataSource = new DataSource({ type: 'better-sqlite3', database: dbPath, @@ -45,7 +45,7 @@ export class Database { this.projectDomain = projectDomain; } - async init (): Promise { + async init(): Promise { await this.dataSource.initialize(); log('database initialized'); @@ -58,21 +58,21 @@ export class Database { } } - async getUser (options: FindOneOptions): Promise { + async getUser(options: FindOneOptions): Promise { const userRepository = this.dataSource.getRepository(User); const user = await userRepository.findOne(options); return user; } - async addUser (data: DeepPartial): Promise { + async addUser(data: DeepPartial): Promise { const userRepository = this.dataSource.getRepository(User); const user = await userRepository.save(data); return user; } - async updateUser (user: User, data: DeepPartial): Promise { + async updateUser(user: User, data: DeepPartial): Promise { const userRepository = this.dataSource.getRepository(User); const updateResult = await userRepository.update({ id: user.id }, data); assert(updateResult.affected); @@ -80,7 +80,7 @@ export class Database { return updateResult.affected > 0; } - async getOrganizations ( + async getOrganizations( options: FindManyOptions ): Promise { const organizationRepository = this.dataSource.getRepository(Organization); @@ -89,7 +89,7 @@ export class Database { return organizations; } - async getOrganization ( + async getOrganization( options: FindOneOptions ): Promise { const organizationRepository = this.dataSource.getRepository(Organization); @@ -98,7 +98,7 @@ export class Database { return organization; } - async getOrganizationsByUserId (userId: string): Promise { + async getOrganizationsByUserId(userId: string): Promise { const organizationRepository = this.dataSource.getRepository(Organization); const userOrgs = await organizationRepository.find({ @@ -114,21 +114,21 @@ export class Database { return userOrgs; } - async addUserOrganization (data: DeepPartial): Promise { + async addUserOrganization(data: DeepPartial): Promise { const userOrganizationRepository = this.dataSource.getRepository(UserOrganization); const newUserOrganization = await userOrganizationRepository.save(data); return newUserOrganization; } - async getProjects (options: FindManyOptions): Promise { + async getProjects(options: FindManyOptions): Promise { const projectRepository = this.dataSource.getRepository(Project); const projects = await projectRepository.find(options); return projects; } - async getProjectById (projectId: string): Promise { + async getProjectById(projectId: string): Promise { const projectRepository = this.dataSource.getRepository(Project); const project = await projectRepository @@ -150,7 +150,20 @@ export class Database { return project; } - async getProjectsInOrganization ( + async allProjectsWithoutDeployments(): Promise { + const projectRepository = this.dataSource.getRepository(Project); + + const projects = await projectRepository + .createQueryBuilder('project') + .leftJoinAndSelect('project.deployments', 'deployment', 'deployment.deletedAt IS NULL') // Join only non-soft-deleted deployments + .where('deployment.id IS NULL') // Get projects where no deployments are present + .andWhere('project.auctionId IS NOT NULL') // Ensure auctionId is not null + .getMany(); + + return projects; + } + + async getProjectsInOrganization( userId: string, organizationSlug: string ): Promise { @@ -181,7 +194,7 @@ export class Database { /** * Get deployments with specified filter */ - async getDeployments ( + async getDeployments( options: FindManyOptions ): Promise { const deploymentRepository = this.dataSource.getRepository(Deployment); @@ -190,7 +203,7 @@ export class Database { return deployments; } - async getDeploymentsByProjectId (projectId: string): Promise { + async getDeploymentsByProjectId(projectId: string): Promise { return this.getDeployments({ relations: { project: true, @@ -208,7 +221,7 @@ export class Database { }); } - async getDeployment ( + async getDeployment( options: FindOneOptions ): Promise { const deploymentRepository = this.dataSource.getRepository(Deployment); @@ -217,14 +230,14 @@ export class Database { return deployment; } - async getDomains (options: FindManyOptions): Promise { + async getDomains(options: FindManyOptions): Promise { const domainRepository = this.dataSource.getRepository(Domain); const domains = await domainRepository.find(options); return domains; } - async addDeployment (data: DeepPartial): Promise { + async addDeployment(data: DeepPartial): Promise { const deploymentRepository = this.dataSource.getRepository(Deployment); const id = nanoid(); @@ -238,7 +251,7 @@ export class Database { return deployment; } - async getProjectMembersByProjectId ( + async getProjectMembersByProjectId( projectId: string ): Promise { const projectMemberRepository = @@ -259,7 +272,7 @@ export class Database { return projectMembers; } - async getEnvironmentVariablesByProjectId ( + async getEnvironmentVariablesByProjectId( projectId: string, filter?: FindOptionsWhere ): Promise { @@ -278,7 +291,7 @@ export class Database { return environmentVariables; } - async removeProjectMemberById (projectMemberId: string): Promise { + async removeProjectMemberById(projectMemberId: string): Promise { const projectMemberRepository = this.dataSource.getRepository(ProjectMember); @@ -293,7 +306,7 @@ export class Database { } } - async updateProjectMemberById ( + async updateProjectMemberById( projectMemberId: string, data: DeepPartial ): Promise { @@ -307,7 +320,7 @@ export class Database { return Boolean(updateResult.affected); } - async addProjectMember ( + async addProjectMember( data: DeepPartial ): Promise { const projectMemberRepository = @@ -317,7 +330,7 @@ export class Database { return newProjectMember; } - async addEnvironmentVariables ( + async addEnvironmentVariables( data: DeepPartial[] ): Promise { const environmentVariableRepository = @@ -328,7 +341,7 @@ export class Database { return savedEnvironmentVariables; } - async updateEnvironmentVariable ( + async updateEnvironmentVariable( environmentVariableId: string, data: DeepPartial ): Promise { @@ -342,7 +355,7 @@ export class Database { return Boolean(updateResult.affected); } - async deleteEnvironmentVariable ( + async deleteEnvironmentVariable( environmentVariableId: string ): Promise { const environmentVariableRepository = @@ -358,7 +371,7 @@ export class Database { } } - async getProjectMemberById (projectMemberId: string): Promise { + async getProjectMemberById(projectMemberId: string): Promise { const projectMemberRepository = this.dataSource.getRepository(ProjectMember); @@ -381,7 +394,7 @@ export class Database { return projectMemberWithProject[0]; } - async getProjectsBySearchText ( + async getProjectsBySearchText( userId: string, searchText: string ): Promise { @@ -403,14 +416,14 @@ export class Database { return projects; } - async updateDeploymentById ( + async updateDeploymentById( deploymentId: string, data: DeepPartial ): Promise { return this.updateDeployment({ id: deploymentId }, data); } - async updateDeployment ( + async updateDeployment( criteria: FindOptionsWhere, data: DeepPartial ): Promise { @@ -420,7 +433,7 @@ export class Database { return Boolean(updateResult.affected); } - async updateDeploymentsByProjectIds ( + async updateDeploymentsByProjectIds( projectIds: string[], data: DeepPartial ): Promise { @@ -436,7 +449,7 @@ export class Database { return Boolean(updateResult.affected); } - async deleteDeploymentById (deploymentId: string): Promise { + async deleteDeploymentById(deploymentId: string): Promise { const deploymentRepository = this.dataSource.getRepository(Deployment); const deployment = await deploymentRepository.findOneOrFail({ where: { @@ -449,7 +462,7 @@ export class Database { return Boolean(deleteResult); } - async addProject (user: User, organizationId: string, data: DeepPartial): Promise { + async addProject(user: User, organizationId: string, data: DeepPartial): Promise { const projectRepository = this.dataSource.getRepository(Project); // TODO: Check if organization exists @@ -468,7 +481,7 @@ export class Database { return projectRepository.save(newProject); } - async updateProjectById ( + async updateProjectById( projectId: string, data: DeepPartial ): Promise { @@ -481,7 +494,7 @@ export class Database { return Boolean(updateResult.affected); } - async deleteProjectById (projectId: string): Promise { + async deleteProjectById(projectId: string): Promise { const projectRepository = this.dataSource.getRepository(Project); const project = await projectRepository.findOneOrFail({ where: { @@ -497,7 +510,7 @@ export class Database { return Boolean(deleteResult); } - async deleteDomainById (domainId: string): Promise { + async deleteDomainById(domainId: string): Promise { const domainRepository = this.dataSource.getRepository(Domain); const deleteResult = await domainRepository.softDelete({ id: domainId }); @@ -509,21 +522,21 @@ export class Database { } } - async addDomain (data: DeepPartial): Promise { + async addDomain(data: DeepPartial): Promise { const domainRepository = this.dataSource.getRepository(Domain); const newDomain = await domainRepository.save(data); return newDomain; } - async getDomain (options: FindOneOptions): Promise { + async getDomain(options: FindOneOptions): Promise { const domainRepository = this.dataSource.getRepository(Domain); const domain = await domainRepository.findOne(options); return domain; } - async updateDomainById ( + async updateDomainById( domainId: string, data: DeepPartial ): Promise { @@ -533,7 +546,7 @@ export class Database { return Boolean(updateResult.affected); } - async getDomainsByProjectId ( + async getDomainsByProjectId( projectId: string, filter?: FindOptionsWhere ): Promise { diff --git a/packages/backend/src/registry.ts b/packages/backend/src/registry.ts index 904376ed..89f53ebc 100644 --- a/packages/backend/src/registry.ts +++ b/packages/backend/src/registry.ts @@ -417,13 +417,7 @@ export class Registry { } async getCompletedAuctionIds(auctionIds: string[]): Promise { - const validAuctionIds = auctionIds.filter((id): id is string => id !== ''); - - if (!validAuctionIds.length) { - return []; - } - - const auctions = await this.registry.getAuctionsByIds(validAuctionIds); + const auctions = await this.registry.getAuctionsByIds(auctionIds); const completedAuctions = auctions .filter((auction: { id: string, status: string }) => auction.status === 'completed') diff --git a/packages/backend/src/resolvers.ts b/packages/backend/src/resolvers.ts index dbe65f3b..067a9f72 100644 --- a/packages/backend/src/resolvers.ts +++ b/packages/backend/src/resolvers.ts @@ -6,7 +6,7 @@ import { Permission } from './entity/ProjectMember'; import { Domain } from './entity/Domain'; import { Project } from './entity/Project'; import { EnvironmentVariable } from './entity/EnvironmentVariable'; -import { AddProjectFromTemplateInput, AuctionParams } from './types'; +import { AddProjectFromTemplateInput, AuctionParams, EnvironmentVariables } from './types'; const log = debug('snowball:resolver'); @@ -218,7 +218,7 @@ export const createResolvers = async (service: Service): Promise => { data: AddProjectFromTemplateInput; lrn: string; auctionParams: AuctionParams, - environmentVariables: { environments: string[]; key: string; value: string }[]; + environmentVariables: EnvironmentVariables[]; }, context: any, ) => { @@ -250,7 +250,7 @@ export const createResolvers = async (service: Service): Promise => { data: DeepPartial; lrn: string; auctionParams: AuctionParams, - environmentVariables: { environments: string[]; key: string; value: string }[]; + environmentVariables: EnvironmentVariables[]; }, context: any, ) => { diff --git a/packages/backend/src/service.ts b/packages/backend/src/service.ts index 5b127712..57ad9ef5 100644 --- a/packages/backend/src/service.ts +++ b/packages/backend/src/service.ts @@ -20,6 +20,7 @@ import { AppDeploymentRecord, AppDeploymentRemovalRecord, AuctionParams, + EnvironmentVariables, GitPushEventPayload, } from './types'; import { Role } from './entity/UserOrganization'; @@ -167,7 +168,7 @@ export class Service { async updateDeploymentsWithRecordData( records: AppDeploymentRecord[], ): Promise { - // get and update deployments to be updated using request id + // Fetch the deployments to be updated using deployment requestId const deployments = await this.db.getDeployments({ where: records.map((record) => ({ applicationDeploymentRequestId: record.attributes.request, @@ -220,10 +221,10 @@ export class Service { await Promise.all(deploymentUpdatePromises); - // if iscurrent is true for this deployment then update the old ones + // Get deployments that are in production environment const prodDeployments = Object.values(recordToDeploymentsMap).filter(deployment => deployment.isCurrent); - // Get deployment IDs of deployments that are in production environment + // Set the isCurrent state to false for the old deployments for (const deployment of prodDeployments) { const projectDeployments = await this.db.getDeploymentsByProjectId(deployment.projectId); const oldDeployments = projectDeployments @@ -236,15 +237,6 @@ export class Service { } } - // Get old deployments for ApplicationDeploymentRecords - // flter out deps with is current false - - // loop over these deps - // get the project - // get all the deployemnts in that proj with the same deployer lrn (query filter not above updated dep) - // set is current to false - - await Promise.all(deploymentUpdatePromises); } @@ -298,43 +290,30 @@ export class Service { * Calls the createDeploymentFromAuction method for deployments with completed auctions */ async checkAuctionStatus(): Promise { - const allProjects = await this.db.getProjects({ - where: { - auctionId: Not(IsNull()), - }, - relations: ['deployments'], - withDeleted: true, - }); + const projects = await this.db.allProjectsWithoutDeployments(); - // Should only check on the first deployment - const projects = allProjects.filter(project => { - if (project.deletedAt !== null) return false; + const validAuctionIds = projects.map((project) => project.auctionId!) + .filter((id): id is string => Boolean(id)); + const completedAuctionIds = await this.laconicRegistry.getCompletedAuctionIds(validAuctionIds); - return project.deployments.length === 0; - }); + const projectsToBedeployed = projects.filter((project) => + completedAuctionIds.includes(project.auctionId!) + ); - if (projects.length > 0) { - const auctionIds = projects.map((project) => project.auctionId!); - const completedAuctionIds = await this.laconicRegistry.getCompletedAuctionIds(auctionIds); - const projectsToBedeployed = projects.filter((project) => - completedAuctionIds.includes(project.auctionId!) - ); + for (const project of projectsToBedeployed) { + const deployerLrns = await this.laconicRegistry.getAuctionWinningDeployers(project!.auctionId!); - for (const project of projectsToBedeployed) { - const deployerLrns = await this.laconicRegistry.getAuctionWinningDeployers(project!.auctionId!); + if (!deployerLrns) { + log(`No winning deployer for auction ${project!.auctionId}`); + } else { + // Update project with deployer LRNs + await this.db.updateProjectById(project.id!, { + deployerLrns + }); - if (!deployerLrns) { - log(`No winning deployer for auction ${project!.auctionId}`); - } else { - // Update project with deployer LRNs - await this.db.updateProjectById(project.id!, { - deployerLrns - }); - - for (const deployer of deployerLrns) { - log(`Creating deployment for deployer LRN ${deployer}`); - await this.createDeploymentFromAuction(project, deployer); - } + for (const deployer of deployerLrns) { + log(`Creating deployment for deployer LRN ${deployer}`); + await this.createDeploymentFromAuction(project, deployer); } } } @@ -785,7 +764,7 @@ export class Service { data: AddProjectFromTemplateInput, lrn?: string, auctionParams?: AuctionParams, - environmentVariables?: { environments: string[]; key: string; value: string }[], + environmentVariables?: EnvironmentVariables[], ): Promise { try { const octokit = await this.getOctokit(user.id); @@ -835,7 +814,7 @@ export class Service { data: DeepPartial, lrn?: string, auctionParams?: AuctionParams, - environmentVariables?: { environments: string[]; key: string; value: string }[], + environmentVariables?: EnvironmentVariables[], ): Promise { const organization = await this.db.getOrganization({ where: { @@ -846,9 +825,9 @@ export class Service { throw new Error('Organization does not exist'); } - const project = await this.db.addProject(user, organization.id, data);4 + const project = await this.db.addProject(user, organization.id, data); - if(environmentVariables) { + if (environmentVariables) { await this.addEnvironmentVariables(project.id, environmentVariables); } diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 33cf0108..0b6a3a1b 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -76,3 +76,9 @@ export interface AuctionParams { maxPrice: string, numProviders: number, } + +export interface EnvironmentVariables { + environments: string[], + key: string, + value: string, +} diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index 7f920c0a..7d81ee77 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -1,6 +1,6 @@ import { useCallback, useState } from 'react'; import { useForm, Controller } from 'react-hook-form'; -import { FormProvider, FieldValues } from 'react-hook-form'; +import { FormProvider, FieldValues } from 'react-hook-form'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { useMediaQuery } from 'usehooks-ts'; import { AddEnvironmentVariableInput, AuctionParams } from 'gql-client'; @@ -59,18 +59,18 @@ const Configure = () => { setIsLoading(true); let projectId: string | null = null; - try { - let lrn: string | undefined; - let auctionParams: AuctionParams | undefined; + try { + let lrn: string | undefined; + let auctionParams: AuctionParams | undefined; - if (data.option === 'LRN') { - lrn = data.lrn; - } else if (data.option === 'Auction') { - auctionParams = { - numProviders: Number(data.numProviders!), - maxPrice: (data.maxPrice!).toString(), - }; - } + if (data.option === 'LRN') { + lrn = data.lrn; + } else if (data.option === 'Auction') { + auctionParams = { + numProviders: Number(data.numProviders!), + maxPrice: (data.maxPrice!).toString(), + }; + } if (templateId) { const projectData: any = { @@ -125,7 +125,7 @@ const Configure = () => { } }; - const createEnvironmentVariablesHandler = useCallback( + const handleFormSubmit = useCallback( async (createFormData: FieldValues) => { const environmentVariables = createFormData.variables.map((variable: any) => { return { @@ -165,20 +165,20 @@ const Configure = () => { } if (templateId) { createFormData.option === 'Auction' - ? navigate( - `/${orgSlug}/projects/create/success/${projectId}?isAuction=true`, - ) - : navigate( - `/${orgSlug}/projects/create/template/deploy?projectId=${projectId}&templateId=${templateId}` - ); + ? navigate( + `/${orgSlug}/projects/create/success/${projectId}?isAuction=true`, + ) + : navigate( + `/${orgSlug}/projects/create/template/deploy?projectId=${projectId}&templateId=${templateId}` + ); } else { createFormData.option === 'Auction' - ? navigate( - `/${orgSlug}/projects/create/success/${projectId}?isAuction=true` - ) - : navigate( - `/${orgSlug}/projects/create/deploy?projectId=${projectId}` - ); + ? navigate( + `/${orgSlug}/projects/create/success/${projectId}?isAuction=true` + ) + : navigate( + `/${orgSlug}/projects/create/deploy?projectId=${projectId}` + ); } }, [client, createProject, dismiss, toast] @@ -201,7 +201,7 @@ const Configure = () => {
-
+
{ ( )} @@ -256,7 +256,7 @@ const Configure = () => { ( )} @@ -269,7 +269,7 @@ const Configure = () => { ( )} diff --git a/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx index 459c4f15..d284a4a6 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx @@ -8,7 +8,6 @@ import { useGQLClient } from 'context/GQLClientContext'; import { EnvironmentVariablesFormValues } from '../../../../../types'; import HorizontalLine from 'components/HorizontalLine'; import { Heading } from 'components/shared/Heading'; -// import { Checkbox } from 'components/shared/Checkbox'; import { PlusIcon } from 'components/shared/CustomIcon'; import { ProjectSettingContainer } from 'components/projects/project/settings/ProjectSettingContainer'; import { useToast } from 'components/shared/Toast'; @@ -79,7 +78,7 @@ export const EnvironmentVariablesTabPanel = () => { await client.addEnvironmentVariables(id!, environmentVariables); if (isEnvironmentVariablesAdded) { - // reset(); + methods.reset(); setCreateNewVariable((cur) => !cur); fetchEnvironmentVariables(id); diff --git a/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx index 6b14b181..ebe9bdc2 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariablesForm.tsx @@ -1,6 +1,7 @@ import { useEffect, useMemo } from 'react'; import { useFieldArray, useFormContext } from 'react-hook-form'; +// TODO: Use custom checkbox component import { Checkbox } from '@snowballtools/material-tailwind-react-fork'; import { Button } from 'components/shared/Button'; -- 2.45.2