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 <neeraj.rtly@gmail.com>
This commit is contained in:
parent
fdf06f9bd0
commit
d97794f1bf
@ -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;
|
||||
|
@ -56,8 +56,8 @@ export const createResolvers = async (db: Database): Promise<any> => {
|
||||
},
|
||||
|
||||
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 }) => {
|
||||
|
@ -25,9 +25,8 @@ const ProjectCard: React.FC<ProjectCardProps> = ({ project }) => {
|
||||
<Link to={`projects/${project.id}`}>
|
||||
<Typography>{project.name}</Typography>
|
||||
<Typography color="gray" variant="small">
|
||||
{project.deployments[0]?.domain.name
|
||||
? project.deployments[0]?.domain.name
|
||||
: ''}
|
||||
{project.deployments[0]?.domain?.name ??
|
||||
'No Production Deployment'}
|
||||
</Typography>
|
||||
</Link>
|
||||
</div>
|
||||
|
@ -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<DeploymentDetails[]>(() => {
|
||||
return deployments.filter((deployment) => {
|
||||
@ -102,8 +89,9 @@ const DeploymentsTabPanel = ({ projectId }: { projectId: string }) => {
|
||||
<DeploymentDetailsCard
|
||||
deployment={deployment}
|
||||
key={key}
|
||||
productionDeployment={productionDeployment}
|
||||
currentDeployment={currentDeployment!}
|
||||
onUpdate={onUpdateDeploymenToProd}
|
||||
projectId={projectId}
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
@ -31,9 +31,8 @@ const OverviewTabPanel = ({ project, organizationProject }: OverviewProps) => {
|
||||
<div className="grow">
|
||||
<Typography>{project.name}</Typography>
|
||||
<Typography variant="small" color="gray">
|
||||
{project.deployments[0]?.domain.name
|
||||
? project.deployments[0]?.domain.name
|
||||
: ''}
|
||||
{project.deployments[0]?.domain?.name ??
|
||||
'No Production Deployment'}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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<void>;
|
||||
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 (
|
||||
<div className="grid grid-cols-4 gap-2 border-b border-gray-300 p-3 my-2">
|
||||
<div className="col-span-2">
|
||||
@ -73,7 +89,7 @@ const DeploymentDetailsCard = ({
|
||||
/>
|
||||
</div>
|
||||
<Typography color="gray">
|
||||
{deployment.isProduction
|
||||
{deployment.environment === Environment.Production
|
||||
? `Production ${deployment.isCurrent ? '(Current)' : ''}`
|
||||
: 'Preview'}
|
||||
</Typography>
|
||||
@ -81,7 +97,7 @@ const DeploymentDetailsCard = ({
|
||||
<div className="col-span-1">
|
||||
<Typography color="gray">^ {deployment.branch}</Typography>
|
||||
<Typography color="gray">
|
||||
^ {deployment.commit.hash} {deployment.commit.message}
|
||||
^ {deployment.commitHash} {deployment.commit.message}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="col-span-1 flex items-center">
|
||||
@ -95,7 +111,7 @@ const DeploymentDetailsCard = ({
|
||||
<MenuList>
|
||||
<MenuItem>^ Visit</MenuItem>
|
||||
<MenuItem>^ Assign domain</MenuItem>
|
||||
{!deployment.isProduction && (
|
||||
{!(deployment.environment === Environment.Production) && (
|
||||
<MenuItem
|
||||
onClick={() => setChangeToProduction(!changeToProduction)}
|
||||
>
|
||||
@ -106,13 +122,19 @@ const DeploymentDetailsCard = ({
|
||||
<hr className="my-3" />
|
||||
<MenuItem
|
||||
onClick={() => setRedeployToProduction(!redeployToProduction)}
|
||||
disabled={!(deployment.isProduction && deployment.isCurrent)}
|
||||
disabled={
|
||||
!(
|
||||
deployment.environment === Environment.Production &&
|
||||
deployment.isCurrent
|
||||
)
|
||||
}
|
||||
>
|
||||
^ Redeploy to production
|
||||
</MenuItem>
|
||||
{!deployment.isProduction && (
|
||||
{deployment.environment === Environment.Production && (
|
||||
<MenuItem
|
||||
onClick={() => setRollbackDeployment(!rollbackDeployment)}
|
||||
disabled={deployment.isCurrent}
|
||||
>
|
||||
^ Rollback to this version
|
||||
</MenuItem>
|
||||
@ -181,7 +203,10 @@ const DeploymentDetailsCard = ({
|
||||
open={rollbackDeployment}
|
||||
confirmButtonTitle="Rollback"
|
||||
color="blue"
|
||||
handleConfirm={() => setRollbackDeployment((preVal) => !preVal)}
|
||||
handleConfirm={async () => {
|
||||
await rollbackDeploymentHandler();
|
||||
setRollbackDeployment((preVal) => !preVal);
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Typography variant="small">
|
||||
@ -189,7 +214,7 @@ const DeploymentDetailsCard = ({
|
||||
deployment
|
||||
</Typography>
|
||||
<DeploymentDialogBodyCard
|
||||
deployment={productionDeployment}
|
||||
deployment={currentDeployment}
|
||||
chip={{
|
||||
value: 'Live Deployment',
|
||||
color: 'green',
|
||||
@ -206,10 +231,7 @@ const DeploymentDetailsCard = ({
|
||||
These domains will point to your new deployment:
|
||||
</Typography>
|
||||
<Typography variant="small" color="blue">
|
||||
^ saugatt.com
|
||||
</Typography>
|
||||
<Typography variant="small" color="blue">
|
||||
^ www.saugatt.com
|
||||
^ {currentDeployment.domain?.name}
|
||||
</Typography>
|
||||
</div>
|
||||
</ConfirmDialog>
|
||||
|
@ -31,7 +31,7 @@ const DeploymentDialogBodyCard = ({
|
||||
{deployment.title}
|
||||
</Typography>
|
||||
<Typography variant="small">
|
||||
^ {deployment.branch} ^ {deployment.commit.hash}{' '}
|
||||
^ {deployment.branch} ^ {deployment.commitHash}{' '}
|
||||
{deployment.commit.message}
|
||||
</Typography>
|
||||
<Typography variant="small">
|
||||
|
5
packages/frontend/src/constants.ts
Normal file
5
packages/frontend/src/constants.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export const COMMIT_DETAILS = {
|
||||
message: 'subscription added',
|
||||
createdAt: '2023-12-11T04:20:00',
|
||||
branch: 'main',
|
||||
};
|
@ -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,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
`;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user