Skip to content

Commit

Permalink
Merge pull request #382 from internxt/feat/delete-workspace-members
Browse files Browse the repository at this point in the history
[PB-2446]: feat/remove workspace member from workspace
  • Loading branch information
apsantiso authored Aug 6, 2024
2 parents 4cd6d95 + 3831c0b commit a75acfd
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 0 deletions.
19 changes: 19 additions & 0 deletions src/modules/workspaces/repositories/workspaces.repository.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,25 @@ describe('SequelizeWorkspaceRepository', () => {
const result = await repository.findWorkspaceUser({ memberId: '1' });
expect(result).toBeNull();
});

it('When a workspace user is searched with a user include, then it should return the user', async () => {
const user = newUser();
const mockWorworkspaceUserkspaceUser = newWorkspaceUser({
memberId: user.uuid,
member: user,
});

jest
.spyOn(workspaceUserModel, 'findOne')
.mockResolvedValueOnce(mockWorworkspaceUserkspaceUser as any);

const result = await repository.findWorkspaceUser(
{ memberId: '1' },
true,
);
expect(result).toBeInstanceOf(WorkspaceUser);
expect(result.member).toBeInstanceOf(User);
});
});

describe('getSpaceLimitInInvitations', () => {
Expand Down
2 changes: 2 additions & 0 deletions src/modules/workspaces/repositories/workspaces.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,11 @@ export class SequelizeWorkspaceRepository {

async findWorkspaceUser(
where: Partial<WorkspaceUserAttributes>,
includeUser = false,
): Promise<WorkspaceUser> {
const workspaceUser = await this.modelWorkspaceUser.findOne({
where,
include: includeUser ? [{ model: UserModel, as: 'member' }] : [],
});

return workspaceUser ? this.workspaceUserToDomain(workspaceUser) : null;
Expand Down
20 changes: 20 additions & 0 deletions src/modules/workspaces/workspaces.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -631,4 +631,24 @@ describe('Workspace Controller', () => {
).resolves.toEqual(workspace.toJSON());
});
});

describe('DELETE /:workspaceId/members/:memberId', () => {
it('When a member is removed from the workspace, then it should call the service with the respective arguments', async () => {
const workspaceId = v4();
const memberId = v4();

jest
.spyOn(workspacesUsecases, 'removeWorkspaceMember')
.mockResolvedValue(Promise.resolve());

await expect(
workspacesController.removeWorkspaceMember(workspaceId, memberId),
).resolves.toBeUndefined();

expect(workspacesUsecases.removeWorkspaceMember).toHaveBeenCalledWith(
workspaceId,
memberId,
);
});
});
});
21 changes: 21 additions & 0 deletions src/modules/workspaces/workspaces.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -995,4 +995,25 @@ export class WorkspacesController {
) {
return this.workspaceUseCases.getWorkspaceDetails(workspaceId);
}

@Delete(':workspaceId/members/:memberId')
@ApiBearerAuth()
@ApiOperation({
summary: 'Remove member from workspace',
})
@ApiParam({ name: 'workspaceId', type: String, required: true })
@ApiParam({ name: 'memberId', type: String, required: true })
@ApiOkResponse({
description: 'Member removed from workspace',
})
@UseGuards(WorkspaceGuard)
@WorkspaceRequiredAccess(AccessContext.WORKSPACE, WorkspaceRole.OWNER)
async removeWorkspaceMember(
@Param('workspaceId', ValidateUUIDPipe)
workspaceId: WorkspaceAttributes['id'],
@Param('memberId', ValidateUUIDPipe)
memberId: WorkspaceTeamAttributes['id'],
) {
return this.workspaceUseCases.removeWorkspaceMember(workspaceId, memberId);
}
}
62 changes: 62 additions & 0 deletions src/modules/workspaces/workspaces.usecase.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4191,6 +4191,68 @@ describe('WorkspacesUsecases', () => {
});
});

describe('removeMemberFromWorkspace', () => {
it('When member is not found, then it should throw', async () => {
const workspaceId = v4();
const memberId = v4();
jest
.spyOn(workspaceRepository, 'findWorkspaceUser')
.mockResolvedValue(null);

await expect(
service.removeWorkspaceMember(workspaceId, memberId),
).rejects.toThrow(NotFoundException);
});

it('When member is the owner of the workspace, then it should throw', async () => {
const owner = newUser();
const workspace = newWorkspace({ owner });
const workspaceUser = newWorkspaceUser({
workspaceId: workspace.id,
memberId: owner.uuid,
member: owner,
});

jest
.spyOn(workspaceRepository, 'findWorkspaceUser')
.mockResolvedValue(workspaceUser);
jest
.spyOn(workspaceRepository, 'findById')
.mockResolvedValue(workspace);

await expect(
service.removeWorkspaceMember(workspace.id, owner.uuid),
).rejects.toThrow(BadRequestException);
});

it('When member is not the owner of the workspace, then it should remove the member', async () => {
const owner = newUser();
const member = newUser();
const workspace = newWorkspace({ owner });
const workspaceUser = newWorkspaceUser({
workspaceId: workspace.id,
memberId: member.uuid,
member,
});

jest
.spyOn(workspaceRepository, 'findWorkspaceUser')
.mockResolvedValue(workspaceUser);
jest
.spyOn(workspaceRepository, 'findById')
.mockResolvedValue(workspace);
jest.spyOn(workspaceRepository, 'deleteUserFromWorkspace');

expect(
await service.removeWorkspaceMember(workspace.id, member.uuid),
).toBeUndefined();

expect(
workspaceRepository.deleteUserFromWorkspace,
).toHaveBeenCalledWith(member.uuid, workspace.id);
});
});

describe('leaveWorkspace', () => {
it('When workspace is not found, then it should throw', async () => {
const workspaceId = v4();
Expand Down
21 changes: 21 additions & 0 deletions src/modules/workspaces/workspaces.usecase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2213,6 +2213,26 @@ export class WorkspacesUsecases {
await this.folderUseCases.renameFolder(movedFolder, user.username);
}

async removeWorkspaceMember(
workspaceId: Workspace['id'],
memberId: User['uuid'],
): Promise<void> {
const workspaceUserToRemove =
await this.workspaceRepository.findWorkspaceUser(
{
workspaceId,
memberId,
},
true,
);

if (!workspaceUserToRemove) {
throw new NotFoundException('User not found in workspace');
}

await this.leaveWorkspace(workspaceId, workspaceUserToRemove.member);
}

async leaveWorkspace(
workspaceId: Workspace['id'],
user: User,
Expand Down Expand Up @@ -2246,6 +2266,7 @@ export class WorkspacesUsecases {
workspaceId,
);
}

async validateWorkspaceInvite(
inviteId: WorkspaceInvite['id'],
): Promise<string> {
Expand Down

0 comments on commit a75acfd

Please sign in to comment.