Skip to content

Commit

Permalink
Remove team when sole owner and remove projects
Browse files Browse the repository at this point in the history
Fixes #6655
  • Loading branch information
laushinka committed Nov 16, 2021
1 parent f3ecc7f commit 0ba0bf9
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 4 deletions.
18 changes: 16 additions & 2 deletions components/gitpod-db/src/team-db.spec.db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ import { DBIdentity } from './typeorm/entity/db-identity';
db = testContainer.get<TeamDBImpl>(TeamDBImpl);
userDb = testContainer.get<TypeORMUserDBImpl>(TypeORMUserDBImpl);

async before() {
async beforeEach() {
await this.wipeRepo();
}

async after() {
async afterEach() {
await this.wipeRepo();
}

Expand Down Expand Up @@ -62,6 +62,20 @@ import { DBIdentity } from './typeorm/entity/db-identity';
expect(members[0].primaryEmail).to.eq('[email protected]');
}

@test(timeout(10000))
public async findTeamMembersByUserAsOwner() {
const user = await this.userDb.newUser();
user.identities.push({ authProviderId: 'GitHub', authId: '2345', authName: 'Nana', primaryEmail: '[email protected]' });
await this.userDb.storeUser(user);

const ownTeam = await this.db.createTeam(user.id, 'My Own Team');
this.db.setTeamMemberRole('3456', ownTeam.id, 'owner');

const teams = await this.db.findTeamsByUserAsSoleOwner(user.id);

expect(teams.length).to.eq(1);
expect(teams[0].id).to.eq(ownTeam.id);
}
}

module.exports = new TeamDBSpec()
1 change: 1 addition & 0 deletions components/gitpod-db/src/team-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface TeamDB {
findTeamById(teamId: string): Promise<Team | undefined>;
findMembersByTeam(teamId: string): Promise<TeamMemberInfo[]>;
findTeamsByUser(userId: string): Promise<Team[]>;
findTeamsByUserAsSoleOwner(userId: string): Promise<Team[]>;
createTeam(userId: string, name: string): Promise<Team>;
addMemberToTeam(userId: string, teamId: string): Promise<void>;
setTeamMemberRole(userId: string, teamId: string, role: TeamMemberRole): Promise<void>;
Expand Down
17 changes: 16 additions & 1 deletion components/gitpod-db/src/typeorm/team-db-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,22 @@ export class TeamDBImpl implements TeamDB {
const membershipRepo = await this.getMembershipRepo();
const memberships = await membershipRepo.find({ userId, deleted: false });
const teams = await teamRepo.findByIds(memberships.map(m => m.teamId));
return teams.filter(t => !t.deleted);
return teams.filter(t => !t.markedDeleted);
}

public async findTeamsByUserAsSoleOwner(userId: string): Promise<Team[]> {
const teamRepo = await this.getTeamRepo();
const membershipRepo = await this.getMembershipRepo();

// Find the memberships of this user,
// and among the memberships, get the teams where the user is the sole owner
const soleOwnedTeamIds = await membershipRepo.query(`SELECT tm_2.teamId FROM d_b_team_membership tm_1
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]);

// @ts-ignore
const teams = await teamRepo.findByIds(soleOwnedTeamIds.map(m => m.teamId));

return teams.filter(t => !t.markedDeleted);
}

public async createTeam(userId: string, name: string): Promise<Team> {
Expand Down
24 changes: 23 additions & 1 deletion components/server/src/user/user-deletion-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { injectable, inject } from "inversify";
import { UserDB, WorkspaceDB, UserStorageResourcesDB, TeamDB } from '@gitpod/gitpod-db/lib';
import { UserDB, WorkspaceDB, UserStorageResourcesDB, TeamDB, ProjectDB } from '@gitpod/gitpod-db/lib';
import { User, Workspace } from "@gitpod/gitpod-protocol";
import { StorageClient } from "../storage/storage-client";
import { log } from '@gitpod/gitpod-protocol/lib/util/logging';
Expand All @@ -23,6 +23,7 @@ export class UserDeletionService {
@inject(WorkspaceDB) protected readonly workspaceDb: WorkspaceDB;
@inject(UserStorageResourcesDB) protected readonly userStorageResourcesDb: UserStorageResourcesDB;
@inject(TeamDB) protected readonly teamDb: TeamDB;
@inject(ProjectDB) protected readonly projectDb: ProjectDB;
@inject(StorageClient) protected readonly storageClient: StorageClient;
@inject(WorkspaceManagerClientProvider) protected readonly workspaceManagerClientProvider: WorkspaceManagerClientProvider;
@inject(WorkspaceDeletionService) protected readonly workspaceDeletionService: WorkspaceDeletionService;
Expand Down Expand Up @@ -74,8 +75,12 @@ export class UserDeletionService {
this.userStorageResourcesDb.deleteAllForUser(user.id),
// Bucket
this.deleteUserBucket(id),
// Owned teams
this.deleteTeams(id),
// Team memberships
this.deleteTeamMemberships(id),
// User projects
this.deleteUserProjects(id),
]);

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

protected async deleteTeams(userId: string) {
const ownedTeams = await this.teamDb.findTeamsByUserAsSoleOwner(userId);

ownedTeams.forEach(async team => {
const teamProjects = this.projectDb.findTeamProjects(team.id);
(await teamProjects).forEach(project => this.projectDb.markDeleted(project.id));
})

await Promise.all(ownedTeams.map(t => this.teamDb.deleteTeam(t.id)));
}

protected async deleteUserProjects(id: string) {
const userProjects = await this.projectDb.findUserProjects(id);

await Promise.all(userProjects.map(project => this.projectDb.markDeleted(project.id)));
}

anonymizeWorkspace(ws: Workspace) {
ws.context.title = 'deleted-title';
ws.context.normalizedContextURL = 'deleted-normalizedContextURL';
Expand Down

0 comments on commit 0ba0bf9

Please sign in to comment.