Implement functionality to delete domain (#59)

* Implement functionality to delete domain

* Remove redirectToId from frontend

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
Nabarun Gogoi 2024-02-01 09:04:24 +05:30 committed by Ashwin Phatak
parent 3a7f16467e
commit 1d58beb2ec
11 changed files with 85 additions and 22 deletions

View File

@ -347,6 +347,28 @@ export class Database {
} }
} }
async deleteDomainById (domainId: string): Promise<boolean> {
const domainRepository = this.dataSource.getRepository(Domain);
const domainsRedirectedFrom = await domainRepository.find({
where: {
redirectToId: Number(domainId)
}
});
if (domainsRedirectedFrom.length > 0) {
throw new Error('Cannot delete domain since it has redirects from other domains');
}
const deleteResult = await domainRepository.softDelete({ id: Number(domainId) });
if (deleteResult.affected) {
return deleteResult.affected > 0;
} else {
return false;
}
}
async rollbackDeploymentById (projectId: string, deploymentId: string): Promise<boolean> { async rollbackDeploymentById (projectId: string, deploymentId: string): Promise<boolean> {
const deploymentRepository = this.dataSource.getRepository(Deployment); const deploymentRepository = this.dataSource.getRepository(Deployment);

View File

@ -5,7 +5,8 @@ import {
CreateDateColumn, CreateDateColumn,
UpdateDateColumn, UpdateDateColumn,
ManyToOne, ManyToOne,
JoinColumn JoinColumn,
DeleteDateColumn
} from 'typeorm'; } from 'typeorm';
import { Project } from './Project'; import { Project } from './Project';
@ -52,4 +53,7 @@ export class Domain {
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Date;
@DeleteDateColumn()
deletedAt?: Date;
} }

View File

@ -216,6 +216,16 @@ export const createResolvers = async (db: Database, app: OAuthApp): Promise<any>
} }
}, },
deleteDomain: async (_: any, { domainId }: { domainId: string }) => {
try {
await db.deleteDomainById(domainId);
return true;
} catch (err) {
log(err);
return false;
}
},
rollbackDeployment: async (_: any, { projectId, deploymentId }: {deploymentId: string, projectId: string }) => { rollbackDeployment: async (_: any, { projectId, deploymentId }: {deploymentId: string, projectId: string }) => {
try { try {
return db.rollbackDeploymentById(projectId, deploymentId); return db.rollbackDeploymentById(projectId, deploymentId);

View File

@ -141,6 +141,7 @@ type Mutation {
updateProject(projectId: String!, projectDetails: UpdateProjectInput): Boolean! updateProject(projectId: String!, projectDetails: UpdateProjectInput): Boolean!
redeployToProd(deploymentId: String!): Boolean! redeployToProd(deploymentId: String!): Boolean!
deleteProject(projectId: String!): Boolean! deleteProject(projectId: String!): Boolean!
deleteDomain(domainId: String!): Boolean!
rollbackDeployment(projectId: String!, deploymentId: String!): Boolean! rollbackDeployment(projectId: String!, deploymentId: String!): Boolean!
addDomain(projectId: String!, domainDetails: AddDomainInput!): Boolean! addDomain(projectId: String!, domainDetails: AddDomainInput!): Boolean!
updateDomain(domainId: String!, domainDetails: UpdateDomainInput!): Boolean! updateDomain(domainId: String!, domainDetails: UpdateDomainInput!): Boolean!

View File

@ -5,7 +5,7 @@
"updatedAt": "2023-12-21T08:30:00", "updatedAt": "2023-12-21T08:30:00",
"user": "bob", "user": "bob",
"private": false, "private": false,
"branch": ["main", "prod", "dev"] "branch": ["main", "prod", "test"]
}, },
{ {
"id": 2, "id": 2,
@ -13,7 +13,7 @@
"updatedAt": "2023-12-21T08:30:00", "updatedAt": "2023-12-21T08:30:00",
"user": "alice", "user": "alice",
"private": true, "private": true,
"branch": ["main", "prod", "dev"] "branch": ["main", "prod", "test"]
}, },
{ {
"id": 3, "id": 3,
@ -21,7 +21,7 @@
"updatedAt": "2023-12-21T04:20:00", "updatedAt": "2023-12-21T04:20:00",
"user": "charlie", "user": "charlie",
"private": false, "private": false,
"branch": ["main", "prod", "dev"] "branch": ["main", "prod", "test"]
}, },
{ {
"id": 4, "id": 4,
@ -29,7 +29,7 @@
"updatedAt": "2023-12-21T04:27:00", "updatedAt": "2023-12-21T04:27:00",
"user": "alice", "user": "alice",
"private": false, "private": false,
"branch": ["main", "prod", "dev"] "branch": ["main", "prod", "test"]
}, },
{ {
"id": 5, "id": 5,
@ -37,6 +37,6 @@
"updatedAt": "2023-12-21T04:41:00", "updatedAt": "2023-12-21T04:41:00",
"user": "ivan", "user": "ivan",
"private": false, "private": false,
"branch": ["main", "prod", "dev"] "branch": ["main", "prod", "test"]
} }
] ]

View File

@ -15,6 +15,7 @@ import {
import { ProjectDetails, RepositoryDetails } from '../../../../types/project'; import { ProjectDetails, RepositoryDetails } from '../../../../types/project';
import ConfirmDialog from '../../../shared/ConfirmDialog'; import ConfirmDialog from '../../../shared/ConfirmDialog';
import EditDomainDialog from './EditDomainDialog'; import EditDomainDialog from './EditDomainDialog';
import { useGQLClient } from '../../../../context/GQLClientContext';
enum RefreshStatus { enum RefreshStatus {
IDLE, IDLE,
@ -51,6 +52,19 @@ const DomainCard = ({
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [editDialogOpen, setEditDialogOpen] = useState(false); const [editDialogOpen, setEditDialogOpen] = useState(false);
const client = useGQLClient();
const deleteDomain = async () => {
const { deleteDomain } = await client.deleteDomain(domain.id);
if (deleteDomain) {
onUpdate();
toast.success(`Domain ${domain.name} deleted successfully`);
} else {
toast.error(`Error deleting domain ${domain.name}`);
}
};
return ( return (
<> <>
<div className="flex justify-between py-3"> <div className="flex justify-between py-3">
@ -109,8 +123,8 @@ const DomainCard = ({
open={deleteDialogOpen} open={deleteDialogOpen}
confirmButtonTitle="Yes, Delete domain" confirmButtonTitle="Yes, Delete domain"
handleConfirm={() => { handleConfirm={() => {
deleteDomain();
setDeleteDialogOpen((preVal) => !preVal); setDeleteDialogOpen((preVal) => !preVal);
toast.success(`Domain "${domain.name}" has been deleted`);
}} }}
color="red" color="red"
> >

View File

@ -1,12 +1,13 @@
import React, { useEffect, useMemo, useState } from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import { useParams, Link, useOutletContext } from 'react-router-dom'; import { useParams, Link, useOutletContext } from 'react-router-dom';
import { Domain } from 'gql-client';
import { Button, Typography } from '@material-tailwind/react'; import { Button, Typography } from '@material-tailwind/react';
import DomainCard from './DomainCard'; import DomainCard from './DomainCard';
import { ProjectSearchOutletContext } from '../../../../types/project'; import { ProjectSearchOutletContext } from '../../../../types/project';
import { useGQLClient } from '../../../../context/GQLClientContext'; import { useGQLClient } from '../../../../context/GQLClientContext';
import { Domain } from 'gql-client'; import repositories from '../../../../assets/repositories.json';
const Domains = () => { const Domains = () => {
const { id } = useParams(); const { id } = useParams();
@ -22,16 +23,6 @@ const Domains = () => {
}); });
}, [id, projects]); }, [id, projects]);
// TODO: Use github API for getting linked repository
const linkedRepo = {
id: 3,
title: 'project-103',
updatedAt: '2023-12-21T04:20:00',
user: 'charlie',
private: false,
branch: ['main', 'prod', 'test'],
};
const fetchDomains = async () => { const fetchDomains = async () => {
if (currentProject === undefined) { if (currentProject === undefined) {
return; return;
@ -62,7 +53,8 @@ const Domains = () => {
domains={domains} domains={domains}
domain={domain} domain={domain}
key={domain.id} key={domain.id}
repo={linkedRepo!} // TODO: Use github API for getting linked repository
repo={repositories[0]!}
project={currentProject!} project={currentProject!}
onUpdate={fetchDomains} onUpdate={fetchDomains}
/> />

View File

@ -63,7 +63,7 @@ const EditDomainDialog = ({
}, [domains, domain]); }, [domains, domain]);
const isDisableDropdown = useMemo(() => { const isDisableDropdown = useMemo(() => {
return domainRedirectedFrom?.redirectTo?.id !== undefined; return domainRedirectedFrom !== undefined;
}, [domain, domains]); }, [domain, domains]);
const { const {

View File

@ -1,8 +1,8 @@
import { ApolloClient, DefaultOptions, InMemoryCache, NormalizedCacheObject } from '@apollo/client'; import { ApolloClient, DefaultOptions, InMemoryCache, NormalizedCacheObject } from '@apollo/client';
import { getUser, getOrganizations, getDeployments, getProjectMembers, searchProjects, getEnvironmentVariables, getProject, getDomains, getProjectsInOrganization } from './queries'; import { getUser, getOrganizations, getDeployments, getProjectMembers, searchProjects, getEnvironmentVariables, getProject, getDomains, getProjectsInOrganization } from './queries';
import { AddEnvironmentVariableInput, AddEnvironmentVariablesResponse, GetDeploymentsResponse, GetEnvironmentVariablesResponse, GetOrganizationsResponse, GetProjectMembersResponse, SearchProjectsResponse, GetUserResponse, UpdateDeploymentToProdResponse, GetProjectResponse, UpdateProjectResponse, UpdateProjectInput, RedeployToProdResponse, DeleteProjectResponse, GetProjectsInOrganizationResponse, RollbackDeploymentResponse, AddDomainInput, AddDomainResponse, GetDomainsResponse, UpdateDomainInput, UpdateDomainResponse, AuthenticateGitHubResponse, UnauthenticateGitHubResponse, UpdateEnvironmentVariableResponse, UpdateEnvironmentVariableInput, RemoveEnvironmentVariableResponse, UpdateProjectMemberInput, RemoveProjectMemberResponse, UpdateProjectMemberResponse } from './types'; import { AddEnvironmentVariableInput, AddEnvironmentVariablesResponse, GetDeploymentsResponse, GetEnvironmentVariablesResponse, GetOrganizationsResponse, GetProjectMembersResponse, SearchProjectsResponse, GetUserResponse, UpdateDeploymentToProdResponse, GetProjectResponse, UpdateProjectResponse, UpdateProjectInput, RedeployToProdResponse, DeleteProjectResponse, GetProjectsInOrganizationResponse, RollbackDeploymentResponse, AddDomainInput, AddDomainResponse, GetDomainsResponse, UpdateDomainInput, UpdateDomainResponse, AuthenticateGitHubResponse, UnauthenticateGitHubResponse, UpdateEnvironmentVariableResponse, UpdateEnvironmentVariableInput, RemoveEnvironmentVariableResponse, UpdateProjectMemberInput, RemoveProjectMemberResponse, UpdateProjectMemberResponse, DeleteDomainResponse } from './types';
import { removeProjectMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd, deleteProject, addDomain, rollbackDeployment, updateDomainMutation, authenticateGitHub, unauthenticateGitHub, updateEnvironmentVariable, removeEnvironmentVariable, updateProjectMember } from './mutations'; import { removeProjectMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd, deleteProject, addDomain, rollbackDeployment, updateDomainMutation, authenticateGitHub, unauthenticateGitHub, updateEnvironmentVariable, removeEnvironmentVariable, updateProjectMember, deleteDomain } from './mutations';
export interface GraphQLConfig { export interface GraphQLConfig {
gqlEndpoint: string; gqlEndpoint: string;
@ -228,6 +228,17 @@ export class GQLClient {
return data; return data;
} }
async deleteDomain (domainId: string): Promise<DeleteDomainResponse> {
const { data } = await this.client.mutate({
mutation: deleteDomain,
variables: {
domainId
}
});
return data;
}
async rollbackDeployment (projectId: string, deploymentId: string): Promise<RollbackDeploymentResponse> { async rollbackDeployment (projectId: string, deploymentId: string): Promise<RollbackDeploymentResponse> {
const { data } = await this.client.mutate({ const { data } = await this.client.mutate({
mutation: rollbackDeployment, mutation: rollbackDeployment,

View File

@ -58,6 +58,11 @@ mutation ($projectId: String!) {
} }
`; `;
export const deleteDomain = gql`
mutation ($domainId: String!) {
deleteDomain(domainId: $domainId)
}`;
export const rollbackDeployment = gql` export const rollbackDeployment = gql`
mutation ($projectId: String! ,$deploymentId: String!) { mutation ($projectId: String! ,$deploymentId: String!) {
rollbackDeployment(projectId: $projectId, deploymentId: $deploymentId) rollbackDeployment(projectId: $projectId, deploymentId: $deploymentId)

View File

@ -218,6 +218,10 @@ export type DeleteProjectResponse = {
deleteProject: boolean; deleteProject: boolean;
} }
export type DeleteDomainResponse = {
deleteDomain: boolean;
}
export type UpdateProjectInput = { export type UpdateProjectInput = {
name: string name: string
description: string description: string