Implement Space membership by group

* Add all users to default group
* Fixes and updates
This commit is contained in:
Philipinho
2024-03-20 01:26:03 +00:00
parent a821e37028
commit 51b9808382
22 changed files with 425 additions and 82 deletions

View File

@ -10,3 +10,7 @@ export class CreateGroupDto {
@IsString()
description?: string;
}
export enum DefaultGroup {
EVERYONE = 'users',
}

View File

@ -20,7 +20,7 @@ export class GroupUser {
@Column()
userId: string;
@ManyToOne(() => User, (user) => user.groups, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn({ name: 'userId' })

View File

@ -11,8 +11,11 @@ import {
import { GroupUser } from './group-user.entity';
import { Workspace } from '../../workspace/entities/workspace.entity';
import { User } from '../../user/entities/user.entity';
import { Unique } from 'typeorm';
import { SpaceGroup } from '../../space/entities/space-group.entity';
@Entity('groups')
@Unique(['name', 'workspaceId'])
export class Group {
@PrimaryGeneratedColumn('uuid')
id: string;
@ -23,6 +26,9 @@ export class Group {
@Column({ type: 'text', nullable: true })
description: string;
@Column({ type: 'boolean', default: false })
isDefault: boolean;
@Column()
workspaceId: string;
@ -32,7 +38,7 @@ export class Group {
@JoinColumn({ name: 'workspaceId' })
workspace: Workspace;
@Column()
@Column({ nullable: true })
creatorId: string;
@ManyToOne(() => User)
@ -48,5 +54,8 @@ export class Group {
@OneToMany(() => GroupUser, (groupUser) => groupUser.group)
groupUsers: GroupUser[];
@OneToMany(() => SpaceGroup, (spaceGroup) => spaceGroup.group)
spaces: SpaceGroup[];
userCount?: number;
}

View File

@ -119,11 +119,12 @@ export class GroupController {
removeGroupMember(
@Body() removeGroupUserDto: RemoveGroupUserDto,
//@AuthUser() user: User,
//@CurrentWorkspace() workspace: Workspace,
@AuthWorkspace() workspace: Workspace,
) {
return this.groupUserService.removeUserFromGroup(
removeGroupUserDto.userId,
removeGroupUserDto.groupId,
workspace.id,
);
}

View File

@ -17,5 +17,6 @@ import { GroupUserService } from './services/group-user.service';
GroupRepository,
GroupUserRepository,
],
exports: [GroupService, GroupUserService],
})
export class GroupModule {}

View File

@ -27,7 +27,7 @@ export class GroupUserService {
workspaceId: string,
paginationOptions: PaginationOptions,
): Promise<PaginatedResult<User>> {
await this.groupService.validateGroup(groupId, workspaceId);
await this.groupService.findAndValidateGroup(groupId, workspaceId);
const [groupUsers, count] = await this.groupUserRepository.findAndCount({
relations: ['user'],
@ -49,16 +49,36 @@ export class GroupUserService {
return new PaginatedResult(users, paginationMeta);
}
async addUserToDefaultGroup(
userId: string,
workspaceId: string,
manager?: EntityManager,
): Promise<void> {
return await transactionWrapper(
async (manager) => {
const defaultGroup = await this.groupService.getDefaultGroup(
workspaceId,
manager,
);
await this.addUserToGroup(
userId,
defaultGroup.id,
workspaceId,
manager,
);
},
this.dataSource,
manager,
);
}
async addUserToGroup(
userId: string,
groupId: string,
workspaceId: string,
manager?: EntityManager,
): Promise<any> {
let addedUser;
/*
await transactionWrapper(
): Promise<GroupUser> {
return await transactionWrapper(
async (manager) => {
const group = await manager.findOneBy(Group, {
id: groupId,
@ -69,21 +89,18 @@ export class GroupUserService {
throw new NotFoundException('Group not found');
}
const userExists = await manager.exists(User, {
const find = await manager.findOne(User, {
where: { id: userId },
});
if (!userExists) {
throw new NotFoundException('User not found');
}
// only workspace users can be added to workspace groups
const workspaceUser = await manager.findOneBy(WorkspaceUser, {
userId: userId,
workspaceId: workspaceId,
console.log(find);
const userExists = await manager.exists(User, {
where: { id: userId, workspaceId },
});
if (!workspaceUser) {
throw new NotFoundException('User is not a member of this workspace');
if (!userExists) {
throw new NotFoundException('User not found');
}
const existingGroupUser = await manager.findOneBy(GroupUser, {
@ -101,16 +118,29 @@ export class GroupUserService {
groupUser.userId = userId;
groupUser.groupId = groupId;
addedUser = await manager.save(groupUser);
return manager.save(groupUser);
},
this.dataSource,
manager,
);
*/
return addedUser;
}
async removeUserFromGroup(userId: string, groupId: string): Promise<void> {
async removeUserFromGroup(
userId: string,
groupId: string,
workspaceId: string,
): Promise<void> {
const group = await this.groupService.findAndValidateGroup(
groupId,
workspaceId,
);
if (group.isDefault) {
throw new BadRequestException(
'You cannot remove users from a default group',
);
}
const groupUser = await this.getGroupUser(userId, groupId);
if (!groupUser) {
@ -129,12 +159,4 @@ export class GroupUserService {
groupId,
});
}
async getGroupUserCount(groupId: string): Promise<number> {
return await this.groupUserRepository.count({
where: {
groupId: groupId,
},
});
}
}

View File

@ -1,5 +1,9 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateGroupDto } from '../dto/create-group.dto';
import {
BadRequestException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { CreateGroupDto, DefaultGroup } from '../dto/create-group.dto';
import { GroupRepository } from '../respositories/group.repository';
import { Group } from '../entities/group.entity';
import { plainToInstance } from 'class-transformer';
@ -8,10 +12,15 @@ import { PaginationMetaDto } from '../../../helpers/pagination/pagination-meta-d
import { PaginatedResult } from '../../../helpers/pagination/paginated-result';
import { PaginationOptions } from '../../../helpers/pagination/pagination-options';
import { UpdateGroupDto } from '../dto/update-group.dto';
import { DataSource, EntityManager } from 'typeorm';
import { transactionWrapper } from '../../../helpers/db.helper';
@Injectable()
export class GroupService {
constructor(private groupRepository: GroupRepository) {}
constructor(
private groupRepository: GroupRepository,
private dataSource: DataSource,
) {}
async createGroup(
authUser: User,
@ -22,9 +31,52 @@ export class GroupService {
group.creatorId = authUser.id;
group.workspaceId = workspaceId;
const groupExists = await this.findGroupByName(
createGroupDto.name,
workspaceId,
);
if (groupExists) {
throw new BadRequestException('Group name already exists');
}
return await this.groupRepository.save(group);
}
async createDefaultGroup(
workspaceId: string,
userId?: string,
manager?: EntityManager,
): Promise<Group> {
return await transactionWrapper(
async (manager: EntityManager) => {
const group = new Group();
group.name = DefaultGroup.EVERYONE;
group.isDefault = true;
group.creatorId = userId ?? null;
group.workspaceId = workspaceId;
return await manager.save(group);
},
this.dataSource,
manager,
);
}
async getDefaultGroup(
workspaceId: string,
manager: EntityManager,
): Promise<Group> {
return await transactionWrapper(
async (manager: EntityManager) => {
return await manager.findOneBy(Group, {
isDefault: true,
workspaceId,
});
},
this.dataSource,
manager,
);
}
async updateGroup(
workspaceId: string,
updateGroupDto: UpdateGroupDto,
@ -38,6 +90,18 @@ export class GroupService {
throw new NotFoundException('Group not found');
}
if (group.isDefault) {
throw new BadRequestException('You cannot update a default group');
}
const groupExists = await this.findGroupByName(
updateGroupDto.name,
workspaceId,
);
if (groupExists) {
throw new BadRequestException('Group name already exists');
}
if (updateGroupDto.name) {
group.name = updateGroupDto.name;
}
@ -90,19 +154,38 @@ export class GroupService {
}
async deleteGroup(groupId: string, workspaceId: string): Promise<void> {
await this.validateGroup(groupId, workspaceId);
const group = await this.findAndValidateGroup(groupId, workspaceId);
if (group.isDefault) {
throw new BadRequestException('You cannot delete a default group');
}
await this.groupRepository.delete(groupId);
}
async validateGroup(groupId: string, workspaceId: string): Promise<void> {
const groupExists = await this.groupRepository.exists({
async findAndValidateGroup(
groupId: string,
workspaceId: string,
): Promise<Group> {
const group = await this.groupRepository.findOne({
where: {
id: groupId,
workspaceId: workspaceId,
},
});
if (!groupExists) {
if (!group) {
throw new NotFoundException('Group not found');
}
return group;
}
async findGroupByName(
groupName: string,
workspaceId: string,
): Promise<Group> {
return this.groupRepository
.createQueryBuilder('group')
.where('LOWER(group.name) = LOWER(:groupName)', { groupName })
.andWhere('group.workspaceId = :workspaceId', { workspaceId })
.getOne();
}
}