mirror of
https://github.com/docmost/docmost.git
synced 2025-11-22 01:31:09 +10:00
feat: groups
This commit is contained in:
121
apps/server/src/core/group/services/group-user.service.ts
Normal file
121
apps/server/src/core/group/services/group-user.service.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { DataSource, EntityManager } from 'typeorm';
|
||||
import { GroupUserRepository } from '../respositories/group-user.repository';
|
||||
import { PaginationOptions } from '../../../helpers/pagination/pagination-options';
|
||||
import {
|
||||
WorkspaceUser,
|
||||
WorkspaceUserRole,
|
||||
} from '../../workspace/entities/workspace-user.entity';
|
||||
import { transactionWrapper } from '../../../helpers/db.helper';
|
||||
import { User } from '../../user/entities/user.entity';
|
||||
import { GroupUser } from '../entities/group-user.entity';
|
||||
import { PaginationMetaDto } from '../../../helpers/pagination/pagination-meta-dto';
|
||||
import { PaginatedResult } from '../../../helpers/pagination/paginated-result';
|
||||
|
||||
@Injectable()
|
||||
export class GroupUserService {
|
||||
constructor(
|
||||
private groupUserRepository: GroupUserRepository,
|
||||
private dataSource: DataSource,
|
||||
) {}
|
||||
|
||||
async getGroupUsers(
|
||||
groupId,
|
||||
workspaceId: string,
|
||||
paginationOptions: PaginationOptions,
|
||||
): Promise<PaginatedResult<User>> {
|
||||
const [groupUsers, count] = await this.groupUserRepository.findAndCount({
|
||||
relations: ['user'],
|
||||
where: {
|
||||
group: {
|
||||
workspaceId: workspaceId,
|
||||
},
|
||||
},
|
||||
|
||||
take: paginationOptions.limit,
|
||||
skip: paginationOptions.skip,
|
||||
});
|
||||
|
||||
const users = groupUsers.map((groupUser: GroupUser) => groupUser.user);
|
||||
|
||||
const paginationMeta = new PaginationMetaDto({ count, paginationOptions });
|
||||
|
||||
return new PaginatedResult(users, paginationMeta);
|
||||
}
|
||||
|
||||
async addUserToGroup(
|
||||
userId: string,
|
||||
groupId: string,
|
||||
workspaceId: string,
|
||||
manager?: EntityManager,
|
||||
): Promise<WorkspaceUser> {
|
||||
let addedUser;
|
||||
|
||||
await transactionWrapper(
|
||||
async (manager) => {
|
||||
// TODO: make duplicate code reusable
|
||||
const userExists = await manager.exists(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,
|
||||
});
|
||||
|
||||
if (!workspaceUser) {
|
||||
throw new NotFoundException('User is not a member of this workspace');
|
||||
}
|
||||
|
||||
const existingGroupUser = await manager.findOneBy(GroupUser, {
|
||||
userId: userId,
|
||||
groupId: groupId,
|
||||
});
|
||||
|
||||
if (existingGroupUser) {
|
||||
throw new BadRequestException(
|
||||
'User is already a member of this group',
|
||||
);
|
||||
}
|
||||
|
||||
const groupUser = new GroupUser();
|
||||
groupUser.userId = userId;
|
||||
groupUser.groupId = groupId;
|
||||
|
||||
addedUser = await manager.save(groupUser);
|
||||
},
|
||||
this.dataSource,
|
||||
manager,
|
||||
);
|
||||
|
||||
return addedUser;
|
||||
}
|
||||
|
||||
async removeUserFromGroup(userId: string, groupId: string): Promise<void> {
|
||||
const groupUser = await this.findGroupUser(userId, groupId);
|
||||
|
||||
if (!groupUser) {
|
||||
throw new BadRequestException('Group member not found');
|
||||
}
|
||||
|
||||
await this.groupUserRepository.delete({
|
||||
userId,
|
||||
groupId,
|
||||
});
|
||||
}
|
||||
|
||||
async findGroupUser(userId: string, groupId: string): Promise<GroupUser> {
|
||||
return await this.groupUserRepository.findOneBy({
|
||||
userId,
|
||||
groupId,
|
||||
});
|
||||
}
|
||||
}
|
||||
18
apps/server/src/core/group/services/group.service.spec.ts
Normal file
18
apps/server/src/core/group/services/group.service.spec.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { GroupService } from './group.service';
|
||||
|
||||
describe('GroupService', () => {
|
||||
let service: GroupService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [GroupService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<GroupService>(GroupService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
82
apps/server/src/core/group/services/group.service.ts
Normal file
82
apps/server/src/core/group/services/group.service.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { CreateGroupDto } from '../dto/create-group.dto';
|
||||
import { GroupRepository } from '../respositories/group.repository';
|
||||
import { Group } from '../entities/group.entity';
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
import { User } from '../../user/entities/user.entity';
|
||||
import { PaginationMetaDto } from '../../../helpers/pagination/pagination-meta-dto';
|
||||
import { PaginatedResult } from '../../../helpers/pagination/paginated-result';
|
||||
import { PaginationOptions } from '../../../helpers/pagination/pagination-options';
|
||||
import { UpdateGroupDto } from '../dto/update-group.dto';
|
||||
|
||||
@Injectable()
|
||||
export class GroupService {
|
||||
constructor(private groupRepository: GroupRepository) {}
|
||||
|
||||
async createGroup(
|
||||
authUser: User,
|
||||
workspaceId: string,
|
||||
createGroupDto: CreateGroupDto,
|
||||
): Promise<Group> {
|
||||
const group = plainToInstance(Group, createGroupDto);
|
||||
group.creatorId = authUser.id;
|
||||
group.workspaceId = workspaceId;
|
||||
|
||||
return await this.groupRepository.save(group);
|
||||
}
|
||||
|
||||
async updateGroup(
|
||||
workspaceId: string,
|
||||
updateGroupDto: UpdateGroupDto,
|
||||
): Promise<Group> {
|
||||
const group = new Group();
|
||||
|
||||
if (updateGroupDto.name) {
|
||||
group.name = updateGroupDto.name;
|
||||
}
|
||||
|
||||
if (updateGroupDto.description) {
|
||||
group.description = updateGroupDto.description;
|
||||
}
|
||||
|
||||
return await this.groupRepository.save(group);
|
||||
}
|
||||
|
||||
async getGroup(groupId: string, workspaceId: string): Promise<Group> {
|
||||
const group = await this.groupRepository.findOneBy({
|
||||
id: groupId,
|
||||
workspaceId: workspaceId,
|
||||
});
|
||||
|
||||
//TODO: get group member count
|
||||
|
||||
if (!group) {
|
||||
throw new NotFoundException('Group not found');
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
async getGroupsInWorkspace(
|
||||
workspaceId: string,
|
||||
paginationOptions: PaginationOptions,
|
||||
): Promise<PaginatedResult<Group>> {
|
||||
const [groupsInWorkspace, count] = await this.groupRepository.findAndCount({
|
||||
where: {
|
||||
workspaceId: workspaceId,
|
||||
},
|
||||
|
||||
take: paginationOptions.limit,
|
||||
skip: paginationOptions.skip,
|
||||
});
|
||||
|
||||
const paginationMeta = new PaginationMetaDto({ count, paginationOptions });
|
||||
|
||||
return new PaginatedResult(groupsInWorkspace, paginationMeta);
|
||||
}
|
||||
|
||||
async deleteGroup(groupId: string, workspaceId: string) {
|
||||
await this.getGroup(groupId, workspaceId);
|
||||
await this.groupRepository.delete(groupId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user