Skip to content

Commit a52ad0a

Browse files
author
Laurie T. Malau
committed
Remove team when sole owner and remove projects
Fixes #6655
1 parent f3ecc7f commit a52ad0a

File tree

3 files changed

+40
-2
lines changed

3 files changed

+40
-2
lines changed

components/gitpod-db/src/team-db.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface TeamDB {
1111
findTeamById(teamId: string): Promise<Team | undefined>;
1212
findMembersByTeam(teamId: string): Promise<TeamMemberInfo[]>;
1313
findTeamsByUser(userId: string): Promise<Team[]>;
14+
findTeamsByUserAsSoleOwner(userId: string): Promise<Team[]>;
1415
createTeam(userId: string, name: string): Promise<Team>;
1516
addMemberToTeam(userId: string, teamId: string): Promise<void>;
1617
setTeamMemberRole(userId: string, teamId: string, role: TeamMemberRole): Promise<void>;

components/gitpod-db/src/typeorm/team-db-impl.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,22 @@ export class TeamDBImpl implements TeamDB {
103103
const membershipRepo = await this.getMembershipRepo();
104104
const memberships = await membershipRepo.find({ userId, deleted: false });
105105
const teams = await teamRepo.findByIds(memberships.map(m => m.teamId));
106-
return teams.filter(t => !t.deleted);
106+
return teams.filter(t => !t.markedDeleted);
107+
}
108+
109+
public async findTeamsByUserAsSoleOwner(userId: string): Promise<Team[]> {
110+
const teamRepo = await this.getTeamRepo();
111+
const membershipRepo = await this.getMembershipRepo();
112+
113+
// Find the memberships of this user,
114+
// and among the memberships, get the teams where the user is the sole owner
115+
const soleOwnedTeamIds = await membershipRepo.query(`SELECT tm_2.teamId FROM d_b_team_membership tm_1
116+
JOIN d_b_team_membership tm_2 ON tm_1.userId = ? AND tm_1.teamId=tm_2.teamId AND tm_1.role = 'owner' GROUP BY tm_2.teamId HAVING COUNT(tm_2.teamId) = 1;`, [userId]);
117+
118+
// @ts-ignore
119+
const teams = await teamRepo.findByIds(soleOwnedTeamIds.map(m => m.teamId));
120+
121+
return teams.filter(t => !t.markedDeleted);
107122
}
108123

109124
public async createTeam(userId: string, name: string): Promise<Team> {

components/server/src/user/user-deletion-service.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import { injectable, inject } from "inversify";
8-
import { UserDB, WorkspaceDB, UserStorageResourcesDB, TeamDB } from '@gitpod/gitpod-db/lib';
8+
import { UserDB, WorkspaceDB, UserStorageResourcesDB, TeamDB, ProjectDB } from '@gitpod/gitpod-db/lib';
99
import { User, Workspace } from "@gitpod/gitpod-protocol";
1010
import { StorageClient } from "../storage/storage-client";
1111
import { log } from '@gitpod/gitpod-protocol/lib/util/logging';
@@ -23,6 +23,7 @@ export class UserDeletionService {
2323
@inject(WorkspaceDB) protected readonly workspaceDb: WorkspaceDB;
2424
@inject(UserStorageResourcesDB) protected readonly userStorageResourcesDb: UserStorageResourcesDB;
2525
@inject(TeamDB) protected readonly teamDb: TeamDB;
26+
@inject(ProjectDB) protected readonly projectDb: ProjectDB;
2627
@inject(StorageClient) protected readonly storageClient: StorageClient;
2728
@inject(WorkspaceManagerClientProvider) protected readonly workspaceManagerClientProvider: WorkspaceManagerClientProvider;
2829
@inject(WorkspaceDeletionService) protected readonly workspaceDeletionService: WorkspaceDeletionService;
@@ -74,8 +75,12 @@ export class UserDeletionService {
7475
this.userStorageResourcesDb.deleteAllForUser(user.id),
7576
// Bucket
7677
this.deleteUserBucket(id),
78+
// Owned teams
79+
this.deleteTeams(id),
7780
// Team memberships
7881
this.deleteTeamMemberships(id),
82+
// User projects
83+
this.deleteUserProjects(id),
7984
]);
8085

8186
// Track the deletion Event for Analytics Purposes
@@ -140,6 +145,23 @@ export class UserDeletionService {
140145
await Promise.all(teams.map(t => this.teamDb.removeMemberFromTeam(userId, t.id)));
141146
}
142147

148+
protected async deleteTeams(userId: string) {
149+
const ownedTeams = await this.teamDb.findTeamsByUserAsSoleOwner(userId);
150+
151+
ownedTeams.forEach(async team => {
152+
const teamProjects = this.projectDb.findTeamProjects(team.id);
153+
(await teamProjects).forEach(project => this.projectDb.markDeleted(project.id));
154+
})
155+
156+
await Promise.all(ownedTeams.map(t => this.teamDb.deleteTeam(t.id)));
157+
}
158+
159+
protected async deleteUserProjects(id: string) {
160+
const userProjects = await this.projectDb.findUserProjects(id);
161+
162+
await Promise.all(userProjects.map(project => this.projectDb.markDeleted(project.id)));
163+
}
164+
143165
anonymizeWorkspace(ws: Workspace) {
144166
ws.context.title = 'deleted-title';
145167
ws.context.normalizedContextURL = 'deleted-normalizedContextURL';

0 commit comments

Comments
 (0)