diff --git a/packages/backend/src/database.ts b/packages/backend/src/database.ts index 7ccd1c1d..d9fec249 100644 --- a/packages/backend/src/database.ts +++ b/packages/backend/src/database.ts @@ -347,6 +347,28 @@ export class Database { } } + async deleteDomainById (domainId: string): Promise { + 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 { const deploymentRepository = this.dataSource.getRepository(Deployment); diff --git a/packages/backend/src/entity/Domain.ts b/packages/backend/src/entity/Domain.ts index 5f6fde28..d59dd782 100644 --- a/packages/backend/src/entity/Domain.ts +++ b/packages/backend/src/entity/Domain.ts @@ -5,7 +5,8 @@ import { CreateDateColumn, UpdateDateColumn, ManyToOne, - JoinColumn + JoinColumn, + DeleteDateColumn } from 'typeorm'; import { Project } from './Project'; @@ -52,4 +53,7 @@ export class Domain { @UpdateDateColumn() updatedAt!: Date; + + @DeleteDateColumn() + deletedAt?: Date; } diff --git a/packages/backend/src/resolvers.ts b/packages/backend/src/resolvers.ts index 3565a84b..0a21e04e 100644 --- a/packages/backend/src/resolvers.ts +++ b/packages/backend/src/resolvers.ts @@ -216,6 +216,16 @@ export const createResolvers = async (db: Database, app: OAuthApp): Promise } }, + 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 }) => { try { return db.rollbackDeploymentById(projectId, deploymentId); diff --git a/packages/backend/src/schema.gql b/packages/backend/src/schema.gql index 298e9d7e..f2bf0db7 100644 --- a/packages/backend/src/schema.gql +++ b/packages/backend/src/schema.gql @@ -141,6 +141,7 @@ type Mutation { updateProject(projectId: String!, projectDetails: UpdateProjectInput): Boolean! redeployToProd(deploymentId: String!): Boolean! deleteProject(projectId: String!): Boolean! + deleteDomain(domainId: String!): Boolean! rollbackDeployment(projectId: String!, deploymentId: String!): Boolean! addDomain(projectId: String!, domainDetails: AddDomainInput!): Boolean! updateDomain(domainId: String!, domainDetails: UpdateDomainInput!): Boolean! diff --git a/packages/frontend/src/assets/repositories.json b/packages/frontend/src/assets/repositories.json index 765b5420..0f6652e8 100644 --- a/packages/frontend/src/assets/repositories.json +++ b/packages/frontend/src/assets/repositories.json @@ -5,7 +5,7 @@ "updatedAt": "2023-12-21T08:30:00", "user": "bob", "private": false, - "branch": ["main", "prod", "dev"] + "branch": ["main", "prod", "test"] }, { "id": 2, @@ -13,7 +13,7 @@ "updatedAt": "2023-12-21T08:30:00", "user": "alice", "private": true, - "branch": ["main", "prod", "dev"] + "branch": ["main", "prod", "test"] }, { "id": 3, @@ -21,7 +21,7 @@ "updatedAt": "2023-12-21T04:20:00", "user": "charlie", "private": false, - "branch": ["main", "prod", "dev"] + "branch": ["main", "prod", "test"] }, { "id": 4, @@ -29,7 +29,7 @@ "updatedAt": "2023-12-21T04:27:00", "user": "alice", "private": false, - "branch": ["main", "prod", "dev"] + "branch": ["main", "prod", "test"] }, { "id": 5, @@ -37,6 +37,6 @@ "updatedAt": "2023-12-21T04:41:00", "user": "ivan", "private": false, - "branch": ["main", "prod", "dev"] + "branch": ["main", "prod", "test"] } ] diff --git a/packages/frontend/src/components/projects/project/settings/DomainCard.tsx b/packages/frontend/src/components/projects/project/settings/DomainCard.tsx index a3a61f8c..21ce9a33 100644 --- a/packages/frontend/src/components/projects/project/settings/DomainCard.tsx +++ b/packages/frontend/src/components/projects/project/settings/DomainCard.tsx @@ -15,6 +15,7 @@ import { import { ProjectDetails, RepositoryDetails } from '../../../../types/project'; import ConfirmDialog from '../../../shared/ConfirmDialog'; import EditDomainDialog from './EditDomainDialog'; +import { useGQLClient } from '../../../../context/GQLClientContext'; enum RefreshStatus { IDLE, @@ -51,6 +52,19 @@ const DomainCard = ({ const [deleteDialogOpen, setDeleteDialogOpen] = 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 ( <>
@@ -109,8 +123,8 @@ const DomainCard = ({ open={deleteDialogOpen} confirmButtonTitle="Yes, Delete domain" handleConfirm={() => { + deleteDomain(); setDeleteDialogOpen((preVal) => !preVal); - toast.success(`Domain "${domain.name}" has been deleted`); }} color="red" > diff --git a/packages/frontend/src/components/projects/project/settings/Domains.tsx b/packages/frontend/src/components/projects/project/settings/Domains.tsx index 0286f545..7869a868 100644 --- a/packages/frontend/src/components/projects/project/settings/Domains.tsx +++ b/packages/frontend/src/components/projects/project/settings/Domains.tsx @@ -1,12 +1,13 @@ import React, { useEffect, useMemo, useState } from 'react'; import { useParams, Link, useOutletContext } from 'react-router-dom'; +import { Domain } from 'gql-client'; import { Button, Typography } from '@material-tailwind/react'; import DomainCard from './DomainCard'; import { ProjectSearchOutletContext } from '../../../../types/project'; import { useGQLClient } from '../../../../context/GQLClientContext'; -import { Domain } from 'gql-client'; +import repositories from '../../../../assets/repositories.json'; const Domains = () => { const { id } = useParams(); @@ -22,16 +23,6 @@ const Domains = () => { }); }, [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 () => { if (currentProject === undefined) { return; @@ -62,7 +53,8 @@ const Domains = () => { domains={domains} domain={domain} key={domain.id} - repo={linkedRepo!} + // TODO: Use github API for getting linked repository + repo={repositories[0]!} project={currentProject!} onUpdate={fetchDomains} /> diff --git a/packages/frontend/src/components/projects/project/settings/EditDomainDialog.tsx b/packages/frontend/src/components/projects/project/settings/EditDomainDialog.tsx index f9dd6cc4..9137148e 100644 --- a/packages/frontend/src/components/projects/project/settings/EditDomainDialog.tsx +++ b/packages/frontend/src/components/projects/project/settings/EditDomainDialog.tsx @@ -63,7 +63,7 @@ const EditDomainDialog = ({ }, [domains, domain]); const isDisableDropdown = useMemo(() => { - return domainRedirectedFrom?.redirectTo?.id !== undefined; + return domainRedirectedFrom !== undefined; }, [domain, domains]); const { diff --git a/packages/gql-client/src/client.ts b/packages/gql-client/src/client.ts index 37e08e04..55e4a6f2 100644 --- a/packages/gql-client/src/client.ts +++ b/packages/gql-client/src/client.ts @@ -1,8 +1,8 @@ import { ApolloClient, DefaultOptions, InMemoryCache, NormalizedCacheObject } from '@apollo/client'; 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 { removeProjectMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd, deleteProject, addDomain, rollbackDeployment, updateDomainMutation, authenticateGitHub, unauthenticateGitHub, updateEnvironmentVariable, removeEnvironmentVariable, updateProjectMember } from './mutations'; +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, deleteDomain } from './mutations'; export interface GraphQLConfig { gqlEndpoint: string; @@ -228,6 +228,17 @@ export class GQLClient { return data; } + async deleteDomain (domainId: string): Promise { + const { data } = await this.client.mutate({ + mutation: deleteDomain, + variables: { + domainId + } + }); + + return data; + } + async rollbackDeployment (projectId: string, deploymentId: string): Promise { const { data } = await this.client.mutate({ mutation: rollbackDeployment, diff --git a/packages/gql-client/src/mutations.ts b/packages/gql-client/src/mutations.ts index ea3e571f..17949959 100644 --- a/packages/gql-client/src/mutations.ts +++ b/packages/gql-client/src/mutations.ts @@ -58,6 +58,11 @@ mutation ($projectId: String!) { } `; +export const deleteDomain = gql` +mutation ($domainId: String!) { + deleteDomain(domainId: $domainId) +}`; + export const rollbackDeployment = gql` mutation ($projectId: String! ,$deploymentId: String!) { rollbackDeployment(projectId: $projectId, deploymentId: $deploymentId) diff --git a/packages/gql-client/src/types.ts b/packages/gql-client/src/types.ts index 96469a18..454dfd1d 100644 --- a/packages/gql-client/src/types.ts +++ b/packages/gql-client/src/types.ts @@ -218,6 +218,10 @@ export type DeleteProjectResponse = { deleteProject: boolean; } +export type DeleteDomainResponse = { + deleteDomain: boolean; +} + export type UpdateProjectInput = { name: string description: string