From d97794f1bfa98788c525ee7356a8515fb601a801 Mon Sep 17 00:00:00 2001 From: Nabarun Gogoi Date: Tue, 30 Jan 2024 14:40:04 +0530 Subject: [PATCH] Integrate rollback deployment GQL mutation in frontend (#49) * Use rollback deployment client in UI * Check if deployements domain is undefined * Fix typo * Rename variable to current deployment * Handle deployment domain relation on rollback --------- Co-authored-by: neeraj --- packages/backend/src/database.ts | 17 ++++++- packages/backend/src/resolvers.ts | 4 +- .../src/components/projects/ProjectCard.tsx | 5 +- .../projects/project/DeploymentsTabPanel.tsx | 30 ++++-------- .../projects/project/OverviewTabPanel.tsx | 5 +- .../deployments/DeploymentDetailsCard.tsx | 48 ++++++++++++++----- .../deployments/DeploymentDialogBodyCard.tsx | 2 +- packages/frontend/src/constants.ts | 5 ++ packages/frontend/src/pages/index.tsx | 7 +-- packages/frontend/src/types/project.ts | 31 ++++-------- packages/gql-client/src/mutations.ts | 2 +- 11 files changed, 84 insertions(+), 72 deletions(-) create mode 100644 packages/frontend/src/constants.ts diff --git a/packages/backend/src/database.ts b/packages/backend/src/database.ts index 2da2f6d4..88e7174d 100644 --- a/packages/backend/src/database.ts +++ b/packages/backend/src/database.ts @@ -304,8 +304,21 @@ export class Database { const deploymentRepository = this.dataSource.getRepository(Deployment); // TODO: Implement transactions - const oldCurrentDeploymentUpdate = await deploymentRepository.update({ project: { id: projectId }, isCurrent: true }, { isCurrent: false }); - const newCurrentDeploymentUpdate = await deploymentRepository.update({ id: Number(deploymentId) }, { isCurrent: true }); + const oldCurrentDeployment = await deploymentRepository.findOne({ + relations: { + domain: true + }, + where: { + project: { + id: projectId + }, + isCurrent: true + } + }); + + const oldCurrentDeploymentUpdate = await deploymentRepository.update({ project: { id: projectId }, isCurrent: true }, { isCurrent: false, domain: null }); + + const newCurrentDeploymentUpdate = await deploymentRepository.update({ id: Number(deploymentId) }, { isCurrent: true, domain: oldCurrentDeployment?.domain }); if (oldCurrentDeploymentUpdate.affected && newCurrentDeploymentUpdate.affected) { return oldCurrentDeploymentUpdate.affected > 0 && newCurrentDeploymentUpdate.affected > 0; diff --git a/packages/backend/src/resolvers.ts b/packages/backend/src/resolvers.ts index da186f25..59e7be0e 100644 --- a/packages/backend/src/resolvers.ts +++ b/packages/backend/src/resolvers.ts @@ -56,8 +56,8 @@ export const createResolvers = async (db: Database): Promise => { }, projectsInOrganization: async (_: any, { organizationId }: {organizationId: string }, context: any) => { - const dbProject = await db.getProjectsInOrganization(context.userId, organizationId); - return dbProject; + const dbProjects = await db.getProjectsInOrganization(context.userId, organizationId); + return dbProjects; }, deployments: async (_: any, { projectId }: { projectId: string }) => { diff --git a/packages/frontend/src/components/projects/ProjectCard.tsx b/packages/frontend/src/components/projects/ProjectCard.tsx index f9ec6970..91399452 100644 --- a/packages/frontend/src/components/projects/ProjectCard.tsx +++ b/packages/frontend/src/components/projects/ProjectCard.tsx @@ -25,9 +25,8 @@ const ProjectCard: React.FC = ({ project }) => { {project.name} - {project.deployments[0]?.domain.name - ? project.deployments[0]?.domain.name - : ''} + {project.deployments[0]?.domain?.name ?? + 'No Production Deployment'} diff --git a/packages/frontend/src/components/projects/project/DeploymentsTabPanel.tsx b/packages/frontend/src/components/projects/project/DeploymentsTabPanel.tsx index d5c1fd87..9b385ec3 100644 --- a/packages/frontend/src/components/projects/project/DeploymentsTabPanel.tsx +++ b/packages/frontend/src/components/projects/project/DeploymentsTabPanel.tsx @@ -9,6 +9,7 @@ import FilterForm, { } from './deployments/FilterForm'; import { DeploymentDetails } from '../../../types/project'; import { useGQLClient } from '../../../context/GQLClientContext'; +import { COMMIT_DETAILS } from '../../../constants'; const DEFAULT_FILTER_VALUE: FilterValue = { searchedBranch: '', @@ -23,25 +24,11 @@ const DeploymentsTabPanel = ({ projectId }: { projectId: string }) => { const fetchDeployments = async () => { const { deployments } = await client.getDeployments(projectId); - const updatedDeployments = deployments.map((deployment: any) => { + const updatedDeployments = deployments.map((deployment) => { return { ...deployment, - isProduction: deployment.environment === 'Production', author: '', - commit: { - hash: '', - message: '', - }, - domain: deployment.domain - ? { - ...deployment.domain, - record: { - type: '', - name: '', - value: '', - }, - } - : null, + commit: COMMIT_DETAILS, }; }); setDeployments(updatedDeployments); @@ -51,11 +38,11 @@ const DeploymentsTabPanel = ({ projectId }: { projectId: string }) => { fetchDeployments(); }, []); - const productionDeployment = useMemo(() => { + const currentDeployment = useMemo(() => { return deployments.find((deployment) => { - return deployment.isProduction === true; - }) as DeploymentDetails; - }, []); + return deployment.isCurrent === true; + }); + }, [deployments]); const filteredDeployments = useMemo(() => { return deployments.filter((deployment) => { @@ -102,8 +89,9 @@ const DeploymentsTabPanel = ({ projectId }: { projectId: string }) => { ); }) diff --git a/packages/frontend/src/components/projects/project/OverviewTabPanel.tsx b/packages/frontend/src/components/projects/project/OverviewTabPanel.tsx index a638d6e4..497b6afc 100644 --- a/packages/frontend/src/components/projects/project/OverviewTabPanel.tsx +++ b/packages/frontend/src/components/projects/project/OverviewTabPanel.tsx @@ -31,9 +31,8 @@ const OverviewTabPanel = ({ project, organizationProject }: OverviewProps) => {
{project.name} - {project.deployments[0]?.domain.name - ? project.deployments[0]?.domain.name - : ''} + {project.deployments[0]?.domain?.name ?? + 'No Production Deployment'}
diff --git a/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx b/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx index 6d8846d5..09d8f0b4 100644 --- a/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx +++ b/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx @@ -10,6 +10,7 @@ import { ChipProps, } from '@material-tailwind/react'; import toast from 'react-hot-toast'; +import { Environment } from 'gql-client'; import { relativeTimeMs } from '../../../../utils/time'; import ConfirmDialog from '../../../shared/ConfirmDialog'; @@ -19,8 +20,9 @@ import { useGQLClient } from '../../../../context/GQLClientContext'; interface DeployDetailsCardProps { deployment: DeploymentDetails; - productionDeployment: DeploymentDetails; + currentDeployment: DeploymentDetails; onUpdate: () => Promise; + projectId: string; } const STATUS_COLORS: { [key in Status]: ChipProps['color'] } = { @@ -31,8 +33,9 @@ const STATUS_COLORS: { [key in Status]: ChipProps['color'] } = { const DeploymentDetailsCard = ({ deployment, - productionDeployment, + currentDeployment, onUpdate, + projectId, }: DeployDetailsCardProps) => { const client = useGQLClient(); @@ -60,6 +63,19 @@ const DeploymentDetailsCard = ({ } }; + const rollbackDeploymentHandler = async () => { + const isRollbacked = await client.rollbackDeployment( + projectId, + deployment.id, + ); + if (isRollbacked) { + await onUpdate(); + toast.success('Deployment rolled back'); + } else { + toast.error('Unable to rollback deployment'); + } + }; + return (
@@ -73,7 +89,7 @@ const DeploymentDetailsCard = ({ />
- {deployment.isProduction + {deployment.environment === Environment.Production ? `Production ${deployment.isCurrent ? '(Current)' : ''}` : 'Preview'} @@ -81,7 +97,7 @@ const DeploymentDetailsCard = ({
^ {deployment.branch} - ^ {deployment.commit.hash} {deployment.commit.message} + ^ {deployment.commitHash} {deployment.commit.message}
@@ -95,7 +111,7 @@ const DeploymentDetailsCard = ({ ^ Visit ^ Assign domain - {!deployment.isProduction && ( + {!(deployment.environment === Environment.Production) && ( setChangeToProduction(!changeToProduction)} > @@ -106,13 +122,19 @@ const DeploymentDetailsCard = ({
setRedeployToProduction(!redeployToProduction)} - disabled={!(deployment.isProduction && deployment.isCurrent)} + disabled={ + !( + deployment.environment === Environment.Production && + deployment.isCurrent + ) + } > ^ Redeploy to production - {!deployment.isProduction && ( + {deployment.environment === Environment.Production && ( setRollbackDeployment(!rollbackDeployment)} + disabled={deployment.isCurrent} > ^ Rollback to this version @@ -181,7 +203,10 @@ const DeploymentDetailsCard = ({ open={rollbackDeployment} confirmButtonTitle="Rollback" color="blue" - handleConfirm={() => setRollbackDeployment((preVal) => !preVal)} + handleConfirm={async () => { + await rollbackDeploymentHandler(); + setRollbackDeployment((preVal) => !preVal); + }} >
@@ -189,7 +214,7 @@ const DeploymentDetailsCard = ({ deployment - ^ saugatt.com - - - ^ www.saugatt.com + ^ {currentDeployment.domain?.name}
diff --git a/packages/frontend/src/components/projects/project/deployments/DeploymentDialogBodyCard.tsx b/packages/frontend/src/components/projects/project/deployments/DeploymentDialogBodyCard.tsx index 46f48409..c2aba303 100644 --- a/packages/frontend/src/components/projects/project/deployments/DeploymentDialogBodyCard.tsx +++ b/packages/frontend/src/components/projects/project/deployments/DeploymentDialogBodyCard.tsx @@ -31,7 +31,7 @@ const DeploymentDialogBodyCard = ({ {deployment.title} - ^ {deployment.branch} ^ {deployment.commit.hash}{' '} + ^ {deployment.branch} ^ {deployment.commitHash}{' '} {deployment.commit.message} diff --git a/packages/frontend/src/constants.ts b/packages/frontend/src/constants.ts new file mode 100644 index 00000000..c964d922 --- /dev/null +++ b/packages/frontend/src/constants.ts @@ -0,0 +1,5 @@ +export const COMMIT_DETAILS = { + message: 'subscription added', + createdAt: '2023-12-11T04:20:00', + branch: 'main', +}; diff --git a/packages/frontend/src/pages/index.tsx b/packages/frontend/src/pages/index.tsx index 09cc0e67..f27e56a2 100644 --- a/packages/frontend/src/pages/index.tsx +++ b/packages/frontend/src/pages/index.tsx @@ -6,6 +6,7 @@ import { Button, Typography, Chip } from '@material-tailwind/react'; import ProjectCard from '../components/projects/ProjectCard'; import { useGQLClient } from '../context/GQLClientContext'; import { ProjectDetails } from '../types/project'; +import { COMMIT_DETAILS } from '../constants'; // TODO: Implement organization switcher const USER_ORGANIZATION_ID = '1'; @@ -22,11 +23,7 @@ const Projects = () => { return { ...project, // TODO: Populate from github API - latestCommit: { - message: 'subscription added', - createdAt: '2023-12-11T04:20:00', - branch: 'main', - }, + latestCommit: COMMIT_DETAILS, }; }); diff --git a/packages/frontend/src/types/project.ts b/packages/frontend/src/types/project.ts index 2472f0d7..5407d18f 100644 --- a/packages/frontend/src/types/project.ts +++ b/packages/frontend/src/types/project.ts @@ -1,15 +1,11 @@ -import { Environment, Project } from 'gql-client'; +import { Project, Deployment } from 'gql-client'; export interface ProjectDetails extends Project { // TODO: isDomain flag domain?: string | null; // TODO: Use deployment branch source?: string; - latestCommit: { - message: string; - createdAt: string; - branch: string; - }; + latestCommit: Commit; // TODO: Move out of project repositories?: RepositoryDetails[]; @@ -22,22 +18,9 @@ export interface ProjectMember { permissions: string[]; } -export interface DeploymentDetails { - id: string; - title: string; - isProduction: boolean; - domain: DomainDetails; - status: Status; - branch: string; - environment: Environment; - isCurrent: boolean; - commit: { - hash: string; - message: string; - }; +export interface DeploymentDetails extends Deployment { + commit: Commit; author: string; - createdAt: string; - updatedAt: string; } export enum Status { @@ -92,3 +75,9 @@ export interface Member { export interface ProjectSearchOutletContext { projects: ProjectDetails[]; } + +export interface Commit { + message: string; + createdAt: string; + branch: string; +} diff --git a/packages/gql-client/src/mutations.ts b/packages/gql-client/src/mutations.ts index aec887d7..5a59dce0 100644 --- a/packages/gql-client/src/mutations.ts +++ b/packages/gql-client/src/mutations.ts @@ -42,7 +42,7 @@ mutation ($projectId: String!) { export const rollbackDeployment = gql` mutation ($projectId: String! ,$deploymentId: String!) { - rollbackDeployment(proejctId: $projectId, deploymentId: $deploymentId) + rollbackDeployment(projectId: $projectId, deploymentId: $deploymentId) } `;