forked from cerc-io/snowballtools-base
		
	Add GQL mutation for redeploying deployment to production (#42)
* Add graphql mutation to redeploy deployment to production * Implement frontend to redeploy deployment to production --------- Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
		
							parent
							
								
									5310d7c7d0
								
							
						
					
					
						commit
						0feeb9408d
					
				| @ -24,6 +24,7 @@ | ||||
|       { | ||||
|         "allowArgumentsExplicitlyTypedAsAny": true | ||||
|       } | ||||
|     ] | ||||
|     ], | ||||
|     "@typescript-eslint/no-unused-vars": ["error", { "ignoreRestSiblings": true }] | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -8,7 +8,7 @@ import { User } from './entity/User'; | ||||
| import { Organization } from './entity/Organization'; | ||||
| import { UserOrganization } from './entity/UserOrganization'; | ||||
| import { Project } from './entity/Project'; | ||||
| import { Deployment } from './entity/Deployment'; | ||||
| import { Deployment, Environment } from './entity/Deployment'; | ||||
| import { ProjectMember } from './entity/ProjectMember'; | ||||
| import { EnvironmentVariable } from './entity/EnvironmentVariable'; | ||||
| 
 | ||||
| @ -243,4 +243,36 @@ export class Database { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   async redeployToProdById (deploymentId: string): Promise<boolean> { | ||||
|     const deploymentRepository = this.dataSource.getRepository(Deployment); | ||||
|     const deployment = await deploymentRepository.findOne({ | ||||
|       relations: { | ||||
|         project: true, | ||||
|         domain: true | ||||
|       }, | ||||
|       where: { | ||||
|         id: Number(deploymentId) | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     if (deployment === null) { | ||||
|       throw new Error('Deployment not found'); | ||||
|     } | ||||
|     const { id, createdAt, updatedAt, ...updatedDeployment } = deployment; | ||||
| 
 | ||||
|     if (updatedDeployment.environment === Environment.Production) { | ||||
|       // TODO: Put isCurrent field in project
 | ||||
|       updatedDeployment.isCurrent = true; | ||||
|     } | ||||
| 
 | ||||
|     await deploymentRepository.update({ id: Number(deploymentId) }, { domain: null, isCurrent: false }); | ||||
|     const savedUpdatedDeployment = await deploymentRepository.save(updatedDeployment); | ||||
| 
 | ||||
|     if (savedUpdatedDeployment) { | ||||
|       return true; | ||||
|     } else { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -35,7 +35,7 @@ export class Deployment { | ||||
| 
 | ||||
|   @OneToOne(() => Domain) | ||||
|   @JoinColumn({ name: 'domainId' }) | ||||
|     domain!: Domain; | ||||
|     domain!: Domain | null; | ||||
| 
 | ||||
|   @Column('varchar') | ||||
|     branch!: string; | ||||
|  | ||||
| @ -146,6 +146,15 @@ export const createResolvers = async (db: Database): Promise<any> => { | ||||
|           log(err); | ||||
|           return false; | ||||
|         } | ||||
|       }, | ||||
| 
 | ||||
|       redeployToProd: async (_: any, { deploymentId }: {deploymentId: string }) => { | ||||
|         try { | ||||
|           return db.redeployToProdById(deploymentId); | ||||
|         } catch (err) { | ||||
|           log(err); | ||||
|           return false; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
|  | ||||
| @ -127,6 +127,7 @@ type Mutation { | ||||
|   addEnvironmentVariables(projectId: String!, environmentVariables: [AddEnvironmentVariableInput!]): Boolean! | ||||
|   updateDeploymentToProd(deploymentId: String!): Boolean! | ||||
|   updateProject(projectId: String!, updateProject: UpdateProjectInput): Boolean! | ||||
|   redeployToProd(deploymentId: String!): Boolean! | ||||
| } | ||||
| 
 | ||||
| input AddEnvironmentVariableInput { | ||||
|  | ||||
							
								
								
									
										12
									
								
								packages/backend/test/fixtures/deployments.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								packages/backend/test/fixtures/deployments.json
									
									
									
									
										vendored
									
									
								
							| @ -5,7 +5,7 @@ | ||||
|     "title": "nextjs-boilerplate-1", | ||||
|     "status": "Building", | ||||
|     "environment": "Production", | ||||
|     "isCurrent": false, | ||||
|     "isCurrent": true, | ||||
|     "branch": "prod", | ||||
|     "commitHash": "testXyz" | ||||
|   }, | ||||
| @ -15,7 +15,7 @@ | ||||
|     "title": "nextjs-boilerplate-2", | ||||
|     "status": "Ready", | ||||
|     "environment": "Preview", | ||||
|     "isCurrent": true, | ||||
|     "isCurrent": false, | ||||
|     "branch": "prod", | ||||
|     "commitHash": "testXyz" | ||||
|   }, | ||||
| @ -35,7 +35,7 @@ | ||||
|     "title": "nextjs-boilerplate-1", | ||||
|     "status": "Building", | ||||
|     "environment": "Production", | ||||
|     "isCurrent": false, | ||||
|     "isCurrent": true, | ||||
|     "branch": "prod", | ||||
|     "commitHash": "testXyz" | ||||
|   }, | ||||
| @ -45,7 +45,7 @@ | ||||
|     "title": "nextjs-boilerplate-2", | ||||
|     "status": "Ready", | ||||
|     "environment": "Preview", | ||||
|     "isCurrent": true, | ||||
|     "isCurrent": false, | ||||
|     "branch": "prod", | ||||
|     "commitHash": "testXyz" | ||||
|   }, | ||||
| @ -65,7 +65,7 @@ | ||||
|     "title": "nextjs-boilerplate-1", | ||||
|     "status": "Building", | ||||
|     "environment": "Production", | ||||
|     "isCurrent": false, | ||||
|     "isCurrent": true, | ||||
|     "branch": "prod", | ||||
|     "commitHash": "testXyz" | ||||
|   }, | ||||
| @ -75,7 +75,7 @@ | ||||
|     "title": "nextjs-boilerplate-2", | ||||
|     "status": "Ready", | ||||
|     "environment": "Preview", | ||||
|     "isCurrent": true, | ||||
|     "isCurrent": false, | ||||
|     "branch": "prod", | ||||
|     "commitHash": "testXyz" | ||||
|   }, | ||||
|  | ||||
| @ -117,14 +117,12 @@ const main = async () => { | ||||
|     await dataSource.initialize(); | ||||
| 
 | ||||
|     await generateTestData(dataSource); | ||||
|     log('Data loaded successfully'); | ||||
|   } else { | ||||
|     throw new Error('Database already exists'); | ||||
|     log('WARNING: Database already exists'); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| main().then(() => { | ||||
|   log('Data loaded successfully'); | ||||
| }) | ||||
|   .catch((err) => { | ||||
|     log(err); | ||||
|   }); | ||||
| main().catch((err) => { | ||||
|   log(err); | ||||
| }); | ||||
|  | ||||
| @ -50,6 +50,16 @@ const DeploymentDetailsCard = ({ | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   const redeployToProd = async () => { | ||||
|     const isRedeployed = await client.redeployToProd(deployment.id); | ||||
|     if (isRedeployed) { | ||||
|       await onUpdate(); | ||||
|       toast.success('Redeployed to production'); | ||||
|     } else { | ||||
|       toast.error('Unable to redeploy to production'); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   return ( | ||||
|     <div className="grid grid-cols-4 gap-2 border-b border-gray-300 p-3 my-2"> | ||||
|       <div className="col-span-2"> | ||||
| @ -63,7 +73,9 @@ const DeploymentDetailsCard = ({ | ||||
|           /> | ||||
|         </div> | ||||
|         <Typography color="gray"> | ||||
|           {deployment.isProduction ? 'Production (Current)' : 'Preview'} | ||||
|           {deployment.isProduction | ||||
|             ? `Production ${deployment.isCurrent ? '(Current)' : ''}` | ||||
|             : 'Preview'} | ||||
|         </Typography> | ||||
|       </div> | ||||
|       <div className="col-span-1"> | ||||
| @ -94,6 +106,7 @@ const DeploymentDetailsCard = ({ | ||||
|             <hr className="my-3" /> | ||||
|             <MenuItem | ||||
|               onClick={() => setRedeployToProduction(!redeployToProduction)} | ||||
|               disabled={!(deployment.isProduction && deployment.isCurrent)} | ||||
|             > | ||||
|               ^ Redeploy to production | ||||
|             </MenuItem> | ||||
| @ -113,8 +126,8 @@ const DeploymentDetailsCard = ({ | ||||
|         open={changeToProduction} | ||||
|         confirmButtonTitle="Change" | ||||
|         color="blue" | ||||
|         handleConfirm={() => { | ||||
|           updateDeployment(); | ||||
|         handleConfirm={async () => { | ||||
|           await updateDeployment(); | ||||
|           setChangeToProduction((preVal) => !preVal); | ||||
|         }} | ||||
|       > | ||||
| @ -140,7 +153,10 @@ const DeploymentDetailsCard = ({ | ||||
|         open={redeployToProduction} | ||||
|         confirmButtonTitle="Redeploy" | ||||
|         color="blue" | ||||
|         handleConfirm={() => setRedeployToProduction((preVal) => !preVal)} | ||||
|         handleConfirm={async () => { | ||||
|           await redeployToProd(); | ||||
|           setRedeployToProduction((preVal) => !preVal); | ||||
|         }} | ||||
|       > | ||||
|         <div className="flex flex-col gap-2"> | ||||
|           <Typography variant="small"> | ||||
|  | ||||
| @ -1,8 +1,8 @@ | ||||
| import { ApolloClient, DefaultOptions, InMemoryCache, NormalizedCacheObject } from '@apollo/client'; | ||||
| 
 | ||||
| import { getUser, getOrganizations, getDeployments, getProjectMembers, searchProjects, getEnvironmentVariables, getProject } from './queries'; | ||||
| import { AddEnvironmentVariableInput, AddEnvironmentVariablesResponse, GetDeploymentsResponse, GetEnvironmentVariablesResponse, GetOrganizationsResponse, GetProjectMembersResponse, SearchProjectsResponse, GetUserResponse, RemoveMemberResponse, UpdateDeploymentToProdResponse, GetProjectResponse, UpdateProjectResponse, UpdateProjectInput } from './types'; | ||||
| import { removeMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation } from './mutations'; | ||||
| import { AddEnvironmentVariableInput, AddEnvironmentVariablesResponse, GetDeploymentsResponse, GetEnvironmentVariablesResponse, GetOrganizationsResponse, GetProjectMembersResponse, SearchProjectsResponse, GetUserResponse, RemoveMemberResponse, UpdateDeploymentToProdResponse, GetProjectResponse, UpdateProjectResponse, UpdateProjectInput, RedeployToProdResponse } from './types'; | ||||
| import { removeMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd } from './mutations'; | ||||
| 
 | ||||
| export interface GraphQLConfig { | ||||
|   gqlEndpoint: string; | ||||
| @ -147,4 +147,15 @@ export class GQLClient { | ||||
| 
 | ||||
|     return data; | ||||
|   } | ||||
| 
 | ||||
|   async redeployToProd (deploymentId: string): Promise<RedeployToProdResponse> { | ||||
|     const { data } = await this.client.mutate({ | ||||
|       mutation: redeployToProd, | ||||
|       variables: { | ||||
|         deploymentId | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -21,5 +21,10 @@ mutation ($deploymentId: String!) { | ||||
| export const updateProjectMutation = gql` | ||||
| mutation ($projectId: String!, $updateProject: UpdateProjectInput) { | ||||
|   updateProject(projectId: $projectId, updateProject: $updateProject) | ||||
| }`;
 | ||||
| 
 | ||||
| export const redeployToProd = gql` | ||||
| mutation ($deploymentId: String!) { | ||||
|   redeployToProd(deploymentId: $deploymentId) | ||||
| } | ||||
| `;
 | ||||
|  | ||||
| @ -182,3 +182,7 @@ export type UpdateProjectInput = { | ||||
|   name: string | ||||
|   description: string | ||||
| } | ||||
| 
 | ||||
| export type RedeployToProdResponse = { | ||||
|   redeployToProd: boolean | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user