From 6d1a48905ada74641bd7bf979f9e3c182493b294 Mon Sep 17 00:00:00 2001 From: Nabarun Gogoi Date: Tue, 6 Feb 2024 19:11:53 +0530 Subject: [PATCH] Show domain connected status in overview tab (#58) * Implement functionality to visit deployment * Check and display domain details in overview tab for production branch * Update fixtures to remove project name from deployment url * Refactor and add uuid to typeorm entities * Fix deployment url * Display live domain details in project overview * Use database query to fetch live production domain --- packages/backend/src/constants.ts | 2 +- packages/backend/src/database.ts | 28 ++++----- packages/backend/src/entity/Domain.ts | 8 +-- .../backend/src/entity/EnvironmentVariable.ts | 4 +- packages/backend/src/entity/Organization.ts | 4 +- packages/backend/src/entity/Project.ts | 4 +- packages/backend/src/entity/ProjectMember.ts | 4 +- packages/backend/src/entity/User.ts | 4 +- .../backend/src/entity/UserOrganization.ts | 4 +- packages/backend/src/schema.gql | 1 + packages/backend/src/service.ts | 19 +++--- .../backend/test/fixtures/deployments.json | 20 +++--- packages/backend/test/fixtures/users.json | 3 + .../create/ConnectAccountTabPanel.tsx | 7 +-- .../projects/project/OverviewTabPanel.tsx | 62 ++++++++++++------- .../deployments/DeploymentDetailsCard.tsx | 8 ++- packages/frontend/src/types/project.ts | 4 +- packages/gql-client/src/types.ts | 3 +- 18 files changed, 109 insertions(+), 80 deletions(-) diff --git a/packages/backend/src/constants.ts b/packages/backend/src/constants.ts index b91868d4..7803a7b1 100644 --- a/packages/backend/src/constants.ts +++ b/packages/backend/src/constants.ts @@ -3,6 +3,6 @@ export const DEFAULT_CONFIG_FILE_PATH = 'environments/local.toml'; export const DEFAULT_GQL_PATH = '/graphql'; // Note: temporary hardcoded user, later to be derived from auth token -export const USER_ID = 1; +export const USER_ID = '59f4355d-9549-4aac-9b54-eeefceeabef0'; export const PROJECT_DOMAIN = 'snowball.xyz'; diff --git a/packages/backend/src/database.ts b/packages/backend/src/database.ts index 9d5cd3ef..517818fc 100644 --- a/packages/backend/src/database.ts +++ b/packages/backend/src/database.ts @@ -48,15 +48,15 @@ export class Database { return user; } - async updateUser (userId: number, data: DeepPartial): Promise { + async updateUser (userId: string, data: DeepPartial): Promise { const userRepository = this.dataSource.getRepository(User); - const updateResult = await userRepository.update({ id: Number(userId) }, data); + const updateResult = await userRepository.update({ id: userId }, data); assert(updateResult.affected); return updateResult.affected > 0; } - async getOrganizationsByUserId (userId: number): Promise { + async getOrganizationsByUserId (userId: string): Promise { const organizationRepository = this.dataSource.getRepository(Organization); const userOrgs = await organizationRepository.find({ @@ -72,7 +72,7 @@ export class Database { return userOrgs; } - async getProjectsByOrganizationId (organizationId: number): Promise { + async getProjectsByOrganizationId (organizationId: string): Promise { const projectRepository = this.dataSource.getRepository(Project); const projects = await projectRepository.find({ @@ -203,7 +203,7 @@ export class Database { async removeProjectMemberById (projectMemberId: string): Promise { const projectMemberRepository = this.dataSource.getRepository(ProjectMember); - const deleteResult = await projectMemberRepository.delete({ id: Number(projectMemberId) }); + const deleteResult = await projectMemberRepository.delete({ id: projectMemberId }); if (deleteResult.affected) { return deleteResult.affected > 0; @@ -214,7 +214,7 @@ export class Database { async updateProjectMemberById (projectMemberId: string, data: DeepPartial): Promise { const projectMemberRepository = this.dataSource.getRepository(ProjectMember); - const updateResult = await projectMemberRepository.update({ id: Number(projectMemberId) }, data); + const updateResult = await projectMemberRepository.update({ id: projectMemberId }, data); return Boolean(updateResult.affected); } @@ -235,14 +235,14 @@ export class Database { async updateEnvironmentVariable (environmentVariableId: string, update: DeepPartial): Promise { const environmentVariableRepository = this.dataSource.getRepository(EnvironmentVariable); - const updateResult = await environmentVariableRepository.update({ id: Number(environmentVariableId) }, update); + const updateResult = await environmentVariableRepository.update({ id: environmentVariableId }, update); return Boolean(updateResult.affected); } async deleteEnvironmentVariable (environmentVariableId: string): Promise { const environmentVariableRepository = this.dataSource.getRepository(EnvironmentVariable); - const deleteResult = await environmentVariableRepository.delete({ id: Number(environmentVariableId) }); + const deleteResult = await environmentVariableRepository.delete({ id: environmentVariableId }); if (deleteResult.affected) { return deleteResult.affected > 0; @@ -262,7 +262,7 @@ export class Database { member: true }, where: { - id: Number(projectMemberId) + id: projectMemberId } } ); @@ -274,7 +274,7 @@ export class Database { return projectMemberWithProject[0]; } - async getProjectsBySearchText (userId: number, searchText: string): Promise { + async getProjectsBySearchText (userId: string, searchText: string): Promise { const projectRepository = this.dataSource.getRepository(Project); const projects = await projectRepository @@ -308,11 +308,11 @@ export class Database { newProject.icon = ''; newProject.owner = Object.assign(new User(), { - id: Number(userId) + id: userId }); newProject.organization = Object.assign(new Organization(), { - id: Number(projectDetails.organizationId) + id: projectDetails.organizationId }); newProject.subDomain = `${newProject.name}.${PROJECT_DOMAIN}`; @@ -346,7 +346,7 @@ export class Database { async deleteDomainById (domainId: string): Promise { const domainRepository = this.dataSource.getRepository(Domain); - const deleteResult = await domainRepository.softDelete({ id: Number(domainId) }); + const deleteResult = await domainRepository.softDelete({ id: domainId }); if (deleteResult.affected) { return deleteResult.affected > 0; @@ -371,7 +371,7 @@ export class Database { async updateDomainById (domainId: string, updates: DeepPartial): Promise { const domainRepository = this.dataSource.getRepository(Domain); - const updateResult = await domainRepository.update({ id: Number(domainId) }, updates); + const updateResult = await domainRepository.update({ id: domainId }, updates); return Boolean(updateResult.affected); } diff --git a/packages/backend/src/entity/Domain.ts b/packages/backend/src/entity/Domain.ts index f4a02026..29f189ff 100644 --- a/packages/backend/src/entity/Domain.ts +++ b/packages/backend/src/entity/Domain.ts @@ -18,8 +18,8 @@ export enum Status { @Entity() export class Domain { - @PrimaryGeneratedColumn() - id!: number; + @PrimaryGeneratedColumn('uuid') + id!: string; @Column('varchar') projectId!: string; @@ -34,8 +34,8 @@ export class Domain { @Column('varchar', { length: 255 }) name!: string; - @Column('int', { nullable: true }) - redirectToId!: number | null; + @Column('string', { nullable: true }) + redirectToId!: string | null; @ManyToOne(() => Domain) @JoinColumn({ name: 'redirectToId' }) diff --git a/packages/backend/src/entity/EnvironmentVariable.ts b/packages/backend/src/entity/EnvironmentVariable.ts index f172e1ba..ad8bd8a8 100644 --- a/packages/backend/src/entity/EnvironmentVariable.ts +++ b/packages/backend/src/entity/EnvironmentVariable.ts @@ -18,8 +18,8 @@ enum Environment { @Entity() export class EnvironmentVariable { - @PrimaryGeneratedColumn() - id!: number; + @PrimaryGeneratedColumn('uuid') + id!: string; @ManyToOne(() => Project, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'projectId' }) diff --git a/packages/backend/src/entity/Organization.ts b/packages/backend/src/entity/Organization.ts index 7aa92d21..e1087422 100644 --- a/packages/backend/src/entity/Organization.ts +++ b/packages/backend/src/entity/Organization.ts @@ -10,8 +10,8 @@ import { UserOrganization } from './UserOrganization'; @Entity() export class Organization { - @PrimaryGeneratedColumn() - id!: number; + @PrimaryGeneratedColumn('uuid') + id!: string; @Column('varchar', { length: 255 }) name!: string; diff --git a/packages/backend/src/entity/Project.ts b/packages/backend/src/entity/Project.ts index 74e19df0..bb4d06d3 100644 --- a/packages/backend/src/entity/Project.ts +++ b/packages/backend/src/entity/Project.ts @@ -28,8 +28,8 @@ export class Project { @JoinColumn({ name: 'organizationId' }) organization!: Organization | null; - @Column('integer') - organizationId!: number; + @Column('varchar') + organizationId!: string; @Column('varchar') name!: string; diff --git a/packages/backend/src/entity/ProjectMember.ts b/packages/backend/src/entity/ProjectMember.ts index 5a361752..d4e77edc 100644 --- a/packages/backend/src/entity/ProjectMember.ts +++ b/packages/backend/src/entity/ProjectMember.ts @@ -21,8 +21,8 @@ export enum Permission { @Entity() @Unique(['project', 'member']) export class ProjectMember { - @PrimaryGeneratedColumn() - id!: number; + @PrimaryGeneratedColumn('uuid') + id!: string; @ManyToOne(() => User, (user) => user.projectMembers) @JoinColumn({ name: 'userId' }) diff --git a/packages/backend/src/entity/User.ts b/packages/backend/src/entity/User.ts index 14bae32c..d280edc7 100644 --- a/packages/backend/src/entity/User.ts +++ b/packages/backend/src/entity/User.ts @@ -13,8 +13,8 @@ import { UserOrganization } from './UserOrganization'; @Entity() @Unique(['email']) export class User { - @PrimaryGeneratedColumn() - id!: number; + @PrimaryGeneratedColumn('uuid') + id!: string; @Column('varchar', { length: 255, nullable: true }) name!: string | null; diff --git a/packages/backend/src/entity/UserOrganization.ts b/packages/backend/src/entity/UserOrganization.ts index 4c9195c1..dc61f94f 100644 --- a/packages/backend/src/entity/UserOrganization.ts +++ b/packages/backend/src/entity/UserOrganization.ts @@ -20,8 +20,8 @@ enum Role { @Entity() export class UserOrganization { - @PrimaryGeneratedColumn() - id!: number; + @PrimaryGeneratedColumn('uuid') + id!: string; @ManyToOne(() => User) @JoinColumn({ name: 'userId' }) diff --git a/packages/backend/src/schema.gql b/packages/backend/src/schema.gql index 2caa0af3..93ff8df9 100644 --- a/packages/backend/src/schema.gql +++ b/packages/backend/src/schema.gql @@ -169,6 +169,7 @@ input UpdateProjectMemberInput { input FilterDomainsInput { branch: String + status: DomainStatus } type Query { diff --git a/packages/backend/src/service.ts b/packages/backend/src/service.ts index 251c197d..3c8f9615 100644 --- a/packages/backend/src/service.ts +++ b/packages/backend/src/service.ts @@ -11,6 +11,7 @@ import { Organization } from './entity/Organization'; import { Project } from './entity/Project'; import { Permission, ProjectMember } from './entity/ProjectMember'; import { User } from './entity/User'; +import { PROJECT_DOMAIN } from './constants'; const nanoid = customAlphabet(lowercase + numbers, 8); @@ -21,7 +22,7 @@ export class Service { this.db = db; } - async getUser (userId: number): Promise { + async getUser (userId: string): Promise { return this.db.getUser({ where: { id: userId @@ -29,7 +30,7 @@ export class Service { }); } - async getOrganizationsByUserId (userId: number): Promise { + async getOrganizationsByUserId (userId: string): Promise { const dbOrganizations = await this.db.getOrganizationsByUserId(userId); return dbOrganizations; } @@ -59,8 +60,8 @@ export class Service { return dbProjectMembers; } - async searchProjects (userId:string, searchText: string): Promise { - const dbProjects = await this.db.getProjectsBySearchText(Number(userId), searchText); + async searchProjects (userId: string, searchText: string): Promise { + const dbProjects = await this.db.getProjectsBySearchText(userId, searchText); return dbProjects; } @@ -196,7 +197,7 @@ export class Service { async deleteDomain (domainId: string): Promise { const domainsRedirectedFrom = await this.db.getDomains({ where: { - redirectToId: Number(domainId) + redirectToId: domainId } }); @@ -229,12 +230,12 @@ export class Service { // TODO: Put isCurrent field in project updatedDeployment.isCurrent = true; updatedDeployment.createdBy = Object.assign(new User(), { - id: Number(userId) + id: userId }); } updatedDeployment.id = nanoid(); - updatedDeployment.url = `${updatedDeployment.id}-${updatedDeployment.project.subDomain}`; + updatedDeployment.url = `${updatedDeployment.project.name}-${updatedDeployment.id}.${PROJECT_DOMAIN}`; const oldDeployment = await this.db.updateDeploymentById(deploymentId, { domain: null, isCurrent: false }); const newDeployement = await this.db.addDeployement(updatedDeployment); @@ -302,7 +303,7 @@ export class Service { async updateDomain (domainId: string, domainDetails: DeepPartial): Promise { const domain = await this.db.getDomain({ where: { - id: Number(domainId) + id: domainId } }); @@ -331,7 +332,7 @@ export class Service { if (domainDetails.redirectToId) { const redirectedDomain = await this.db.getDomain({ where: { - id: Number(domainDetails.redirectToId) + id: domainDetails.redirectToId } }); diff --git a/packages/backend/test/fixtures/deployments.json b/packages/backend/test/fixtures/deployments.json index 2909aa46..dabf64bd 100644 --- a/packages/backend/test/fixtures/deployments.json +++ b/packages/backend/test/fixtures/deployments.json @@ -10,7 +10,7 @@ "isCurrent": true, "branch": "main", "commitHash": "testXyz", - "url": "testProject-ffhae3zq.testProject.snowball.xyz" + "url": "testProject-ffhae3zq.snowball.xyz" }, { "projectIndex": 0, @@ -23,7 +23,7 @@ "isCurrent": false, "branch": "test", "commitHash": "testXyz", - "url": "testProject-vehagei8.testProject.snowball.xyz" + "url": "testProject-vehagei8.snowball.xyz" }, { "projectIndex": 0, @@ -36,7 +36,7 @@ "isCurrent": false, "branch": "test", "commitHash": "testXyz", - "url": "testProject-qmgekyte.testProject.snowball.xyz" + "url": "testProject-qmgekyte.snowball.xyz" }, { "projectIndex": 0, @@ -49,7 +49,7 @@ "isCurrent": false, "branch": "prod", "commitHash": "testXyz", - "url": "testProject-f8wsyim6.testProject.snowball.xyz" + "url": "testProject-f8wsyim6.snowball.xyz" }, { "projectIndex": 1, @@ -62,7 +62,7 @@ "isCurrent": true, "branch": "main", "commitHash": "testXyz", - "url": "testProject-2-eO8cckxk.testProject-2.snowball.xyz" + "url": "testProject-2-eO8cckxk.snowball.xyz" }, { "projectIndex": 1, @@ -75,7 +75,7 @@ "isCurrent": false, "branch": "test", "commitHash": "testXyz", - "url": "testProject-2-yaq0t5yw.testProject-2.snowball.xyz" + "url": "testProject-2-yaq0t5yw.snowball.xyz" }, { "projectIndex": 1, @@ -88,7 +88,7 @@ "isCurrent": false, "branch": "test", "commitHash": "testXyz", - "url": "testProject-2-hwwr6sbx.testProject-2.snowball.xyz" + "url": "testProject-2-hwwr6sbx.snowball.xyz" }, { "projectIndex": 2, @@ -101,7 +101,7 @@ "isCurrent": true, "branch": "main", "commitHash": "testXyz", - "url": "iglootools-ndxje48a.iglootools.snowball.xyz" + "url": "iglootools-ndxje48a.snowball.xyz" }, { "projectIndex": 2, @@ -114,7 +114,7 @@ "isCurrent": false, "branch": "test", "commitHash": "testXyz", - "url": "iglootools-gtgpgvei.iglootools.snowball.xyz" + "url": "iglootools-gtgpgvei.snowball.xyz" }, { "projectIndex": 2, @@ -127,6 +127,6 @@ "isCurrent": false, "branch": "test", "commitHash": "testXyz", - "url": "iglootools-b4bpthjr.iglootools.snowball.xyz" + "url": "iglootools-b4bpthjr.snowball.xyz" } ] diff --git a/packages/backend/test/fixtures/users.json b/packages/backend/test/fixtures/users.json index 5a8a5317..aacd7b6b 100644 --- a/packages/backend/test/fixtures/users.json +++ b/packages/backend/test/fixtures/users.json @@ -1,15 +1,18 @@ [ { + "id": "59f4355d-9549-4aac-9b54-eeefceeabef0", "name": "Saugat Yadav", "email": "saugaty@airfoil.studio", "isVerified": true }, { + "id": "e505b212-8da6-48b2-9614-098225dab34b", "name": "Gideon Low", "email": "gideonl@airfoil.studio", "isVerified": true }, { + "id": "cd892fad-9138-4aa2-a62c-414a32776ea7", "name": "Sushan Yadav", "email": "sushany@airfoil.studio", "isVerified": true diff --git a/packages/frontend/src/components/projects/create/ConnectAccountTabPanel.tsx b/packages/frontend/src/components/projects/create/ConnectAccountTabPanel.tsx index d73b8ceb..d46d495a 100644 --- a/packages/frontend/src/components/projects/create/ConnectAccountTabPanel.tsx +++ b/packages/frontend/src/components/projects/create/ConnectAccountTabPanel.tsx @@ -4,12 +4,9 @@ import { Tabs, TabsHeader, Tab } from '@material-tailwind/react'; const ConnectAccountTabPanel = () => { return ( - + - + Import a repository diff --git a/packages/frontend/src/components/projects/project/OverviewTabPanel.tsx b/packages/frontend/src/components/projects/project/OverviewTabPanel.tsx index db7b6447..a198864b 100644 --- a/packages/frontend/src/components/projects/project/OverviewTabPanel.tsx +++ b/packages/frontend/src/components/projects/project/OverviewTabPanel.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { Project } from 'gql-client'; +import { Domain, DomainStatus, Project } from 'gql-client'; import { Typography, Button, Chip } from '@material-tailwind/react'; @@ -7,6 +7,7 @@ import ActivityCard from './ActivityCard'; import { relativeTimeMs } from '../../../utils/time'; import { useOctokit } from '../../../context/OctokitContext'; import { GitCommitDetails } from '../../../types/project'; +import { useGQLClient } from '../../../context/GQLClientContext'; const COMMITS_PER_PAGE = 4; @@ -15,11 +16,13 @@ interface OverviewProps { } // TODO: Check if any live domain is set for production branch -const IS_DOMAIN_SETUP = false; const OverviewTabPanel = ({ project }: OverviewProps) => { const { octokit } = useOctokit(); const [activities, setActivities] = useState([]); + const [liveDomain, setLiveDomain] = useState(); + + const client = useGQLClient(); useEffect(() => { if (!octokit) { @@ -75,6 +78,23 @@ const OverviewTabPanel = ({ project }: OverviewProps) => { fetchRepoActivity(); }, [octokit, project]); + useEffect(() => { + const fetchLiveProdDomain = async () => { + const { domains } = await client.getDomains(project.id, { + branch: project.prodBranch, + status: DomainStatus.Live, + }); + + if (domains.length === 0) { + return; + } + + setLiveDomain(domains[0]); + }; + + fetchLiveProdDomain(); + }, [project]); + return (
@@ -88,19 +108,8 @@ const OverviewTabPanel = ({ project }: OverviewProps) => {
-
- ^ Domain - {!IS_DOMAIN_SETUP && ( - - )} -
- {IS_DOMAIN_SETUP ? ( +
^ Domain
+ {liveDomain ? ( { color="green" /> ) : ( - +
+ + +
)}
{project.deployments.length !== 0 ? ( @@ -122,9 +144,7 @@ const OverviewTabPanel = ({ project }: OverviewProps) => {

^ Deployment

-

- {project.deployments[0]?.domain?.name} -

+

{liveDomain?.name}

^ Created

diff --git a/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx b/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx index f6360943..b6f47a88 100644 --- a/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx +++ b/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx @@ -113,7 +113,13 @@ const DeploymentDetailsCard = ({ - ^ Visit + + ^ Visit + setAssignDomainDialog(!assignDomainDialog)} > diff --git a/packages/frontend/src/types/project.ts b/packages/frontend/src/types/project.ts index 3be00599..deff8b75 100644 --- a/packages/frontend/src/types/project.ts +++ b/packages/frontend/src/types/project.ts @@ -71,8 +71,8 @@ export enum DomainStatus { } export interface DomainDetails { - id: number; - projectid: number; + id: string; + projectid: string; name: string; status: DomainStatus; record: { diff --git a/packages/gql-client/src/types.ts b/packages/gql-client/src/types.ts index 05b6ec02..4e4f4ea4 100644 --- a/packages/gql-client/src/types.ts +++ b/packages/gql-client/src/types.ts @@ -273,7 +273,8 @@ export type AddDomainInput = { } export type FilterDomainInput = { - branch: string + branch?: string + status?: DomainStatus } export type AddDomainResponse = {