Refactoring

* replace TypeORM with Kysely query builder
* refactor migrations
* other changes and fixes
This commit is contained in:
Philipinho
2024-03-29 01:46:11 +00:00
parent cacb5606b1
commit c18c9ae02b
122 changed files with 2619 additions and 3541 deletions

View File

@ -1,48 +1,35 @@
import {
BadRequestException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { DataSource, EntityManager } from 'typeorm';
import { GroupUserRepository } from '../respositories/group-user.repository';
import { BadRequestException, Injectable } from '@nestjs/common';
import { PaginationOptions } from '../../../helpers/pagination/pagination-options';
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';
import { Group } from '../entities/group.entity';
import { GroupService } from './group.service';
import { KyselyDB, KyselyTransaction } from '@docmost/db/types/kysely.types';
import { executeTx } from '@docmost/db/utils';
import { InjectKysely } from 'nestjs-kysely';
import { GroupRepo } from '@docmost/db/repos/group/group.repo';
import { GroupUserRepo } from '@docmost/db/repos/group/group-user.repo';
import { User } from '@docmost/db/types/entity.types';
@Injectable()
export class GroupUserService {
constructor(
private groupUserRepository: GroupUserRepository,
private groupRepo: GroupRepo,
private groupUserRepo: GroupUserRepo,
private groupService: GroupService,
private dataSource: DataSource,
@InjectKysely() private readonly db: KyselyDB,
) {}
async getGroupUsers(
groupId,
groupId: string,
workspaceId: string,
paginationOptions: PaginationOptions,
): Promise<PaginatedResult<User>> {
await this.groupService.findAndValidateGroup(groupId, workspaceId);
const [groupUsers, count] = await this.groupUserRepository.findAndCount({
relations: ['user'],
where: {
groupId: groupId,
group: {
workspaceId: workspaceId,
},
},
take: paginationOptions.limit,
skip: paginationOptions.skip,
});
const users = groupUsers.map((groupUser: GroupUser) => groupUser.user);
const { users, count } = await this.groupUserRepo.getGroupUsersPaginated(
groupId,
paginationOptions,
);
const paginationMeta = new PaginationMetaDto({ count, paginationOptions });
@ -52,23 +39,18 @@ export class GroupUserService {
async addUserToDefaultGroup(
userId: string,
workspaceId: string,
manager?: EntityManager,
trx?: KyselyTransaction,
): Promise<void> {
return await transactionWrapper(
async (manager) => {
const defaultGroup = await this.groupService.getDefaultGroup(
await executeTx(
this.db,
async (trx) => {
const defaultGroup = await this.groupRepo.getDefaultGroup(
workspaceId,
manager,
);
await this.addUserToGroup(
userId,
defaultGroup.id,
workspaceId,
manager,
trx,
);
await this.addUserToGroup(userId, defaultGroup.id, workspaceId, trx);
},
this.dataSource,
manager,
trx,
);
}
@ -76,46 +58,33 @@ export class GroupUserService {
userId: string,
groupId: string,
workspaceId: string,
manager?: EntityManager,
): Promise<GroupUser> {
return await transactionWrapper(
async (manager) => {
const group = await manager.findOneBy(Group, {
id: groupId,
workspaceId: workspaceId,
});
trx?: KyselyTransaction,
): Promise<void> {
await executeTx(
this.db,
async (trx) => {
await this.groupService.findAndValidateGroup(groupId, workspaceId);
const groupUserExists = await this.groupUserRepo.getGroupUserById(
userId,
groupId,
trx,
);
if (!group) {
throw new NotFoundException('Group not found');
}
const userExists = await manager.exists(User, {
where: { id: userId, workspaceId },
});
if (!userExists) {
throw new NotFoundException('User not found');
}
const existingGroupUser = await manager.findOneBy(GroupUser, {
userId: userId,
groupId: groupId,
});
if (existingGroupUser) {
if (groupUserExists) {
throw new BadRequestException(
'User is already a member of this group',
);
}
const groupUser = new GroupUser();
groupUser.userId = userId;
groupUser.groupId = groupId;
return manager.save(groupUser);
await this.groupUserRepo.insertGroupUser(
{
userId,
groupId,
},
trx,
);
},
this.dataSource,
manager,
trx,
);
}
@ -135,22 +104,15 @@ export class GroupUserService {
);
}
const groupUser = await this.getGroupUser(userId, groupId);
const groupUser = await this.groupUserRepo.getGroupUserById(
userId,
groupId,
);
if (!groupUser) {
throw new BadRequestException('Group member not found');
}
await this.groupUserRepository.delete({
userId,
groupId,
});
}
async getGroupUser(userId: string, groupId: string): Promise<GroupUser> {
return await this.groupUserRepository.findOneBy({
userId,
groupId,
});
await this.groupUserRepo.delete(userId, groupId);
}
}

View File

@ -4,87 +4,64 @@ import {
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';
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';
import { DataSource, EntityManager } from 'typeorm';
import { transactionWrapper } from '../../../helpers/db.helper';
import { KyselyTransaction } from '@docmost/db/types/kysely.types';
import { GroupRepo } from '@docmost/db/repos/group/group.repo';
import { Group, InsertableGroup, User } from '@docmost/db/types/entity.types';
@Injectable()
export class GroupService {
constructor(
private groupRepository: GroupRepository,
private dataSource: DataSource,
) {}
constructor(private groupRepo: GroupRepo) {}
async createGroup(
authUser: User,
workspaceId: string,
createGroupDto: CreateGroupDto,
trx?: KyselyTransaction,
): Promise<Group> {
const group = plainToInstance(Group, createGroupDto);
group.creatorId = authUser.id;
group.workspaceId = workspaceId;
const groupExists = await this.findGroupByName(
const groupExists = await this.groupRepo.findByName(
createGroupDto.name,
workspaceId,
);
if (groupExists) {
throw new BadRequestException('Group name already exists');
}
const insertableGroup: InsertableGroup = {
name: createGroupDto.name,
description: createGroupDto.description,
isDefault: false,
creatorId: authUser.id,
workspaceId: workspaceId,
};
return await this.groupRepository.save(group);
return await this.groupRepo.insertGroup(insertableGroup, trx);
}
async createDefaultGroup(
workspaceId: string,
userId?: string,
manager?: EntityManager,
trx?: KyselyTransaction,
): 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,
);
const insertableGroup: InsertableGroup = {
name: DefaultGroup.EVERYONE,
isDefault: true,
creatorId: userId ?? null,
workspaceId: workspaceId,
};
return await this.groupRepo.insertGroup(insertableGroup, trx);
}
async updateGroup(
workspaceId: string,
updateGroupDto: UpdateGroupDto,
): Promise<Group> {
const group = await this.groupRepository.findOneBy({
id: updateGroupDto.groupId,
workspaceId: workspaceId,
});
const group = await this.groupRepo.findById(
updateGroupDto.groupId,
workspaceId,
);
if (!group) {
throw new NotFoundException('Group not found');
@ -94,7 +71,7 @@ export class GroupService {
throw new BadRequestException('You cannot update a default group');
}
const groupExists = await this.findGroupByName(
const groupExists = await this.groupRepo.findByName(
updateGroupDto.name,
workspaceId,
);
@ -110,20 +87,21 @@ export class GroupService {
group.description = updateGroupDto.description;
}
return await this.groupRepository.save(group);
await this.groupRepo.update(
{
name: updateGroupDto.name,
description: updateGroupDto.description,
},
group.id,
workspaceId,
);
return group;
}
async getGroupInfo(groupId: string, workspaceId: string): Promise<Group> {
const group = await this.groupRepository
.createQueryBuilder('group')
.where('group.id = :groupId', { groupId })
.andWhere('group.workspaceId = :workspaceId', { workspaceId })
.loadRelationCountAndMap(
'group.memberCount',
'group.groupUsers',
'groupUsers',
)
.getOne();
// todo: add member count
const group = await this.groupRepo.findById(groupId, workspaceId);
if (!group) {
throw new NotFoundException('Group not found');
@ -136,17 +114,10 @@ export class GroupService {
workspaceId: string,
paginationOptions: PaginationOptions,
): Promise<PaginatedResult<Group>> {
const [groups, count] = await this.groupRepository
.createQueryBuilder('group')
.where('group.workspaceId = :workspaceId', { workspaceId })
.loadRelationCountAndMap(
'group.memberCount',
'group.groupUsers',
'groupUsers',
)
.take(paginationOptions.limit)
.skip(paginationOptions.skip)
.getManyAndCount();
const { groups, count } = await this.groupRepo.getGroupsPaginated(
workspaceId,
paginationOptions,
);
const paginationMeta = new PaginationMetaDto({ count, paginationOptions });
@ -158,34 +129,18 @@ export class GroupService {
if (group.isDefault) {
throw new BadRequestException('You cannot delete a default group');
}
await this.groupRepository.delete(groupId);
await this.groupRepo.delete(groupId, workspaceId);
}
async findAndValidateGroup(
groupId: string,
workspaceId: string,
): Promise<Group> {
const group = await this.groupRepository.findOne({
where: {
id: groupId,
workspaceId: workspaceId,
},
});
const group = await this.groupRepo.findById(groupId, workspaceId);
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();
}
}