refactor(server): migrate album-user repo to kysely (#15351)

This commit is contained in:
Mert 2025-01-14 19:27:16 -05:00 committed by GitHub
parent c5476a99b1
commit 2903ad8156
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 78 additions and 36 deletions

View File

@ -86,6 +86,7 @@ class SqlGenerator {
this.sqlLogger.logQuery(event.query.sql); this.sqlLogger.logQuery(event.query.sql);
} else if (event.level === 'error') { } else if (event.level === 'error') {
this.sqlLogger.logQueryError(event.error as Error, event.query.sql); this.sqlLogger.logQueryError(event.error as Error, event.query.sql);
this.sqlLogger.logQuery(event.query.sql);
} }
}, },
}), }),

View File

@ -1,14 +1,18 @@
import { AlbumUserEntity } from 'src/entities/album-user.entity'; import { Insertable, Selectable, Updateable } from 'kysely';
import { AlbumsSharedUsersUsers } from 'src/db';
export const IAlbumUserRepository = 'IAlbumUserRepository'; export const IAlbumUserRepository = 'IAlbumUserRepository';
export type AlbumPermissionId = { export type AlbumPermissionId = {
albumId: string; albumsId: string;
userId: string; usersId: string;
}; };
export interface IAlbumUserRepository { export interface IAlbumUserRepository {
create(albumUser: Partial<AlbumUserEntity>): Promise<AlbumUserEntity>; create(albumUser: Insertable<AlbumsSharedUsersUsers>): Promise<Selectable<AlbumsSharedUsersUsers>>;
update({ userId, albumId }: AlbumPermissionId, albumPermission: Partial<AlbumUserEntity>): Promise<AlbumUserEntity>; update(
delete({ userId, albumId }: AlbumPermissionId): Promise<void>; id: AlbumPermissionId,
albumPermission: Updateable<AlbumsSharedUsersUsers>,
): Promise<Selectable<AlbumsSharedUsersUsers>>;
delete(id: AlbumPermissionId): Promise<void>;
} }

View File

@ -0,0 +1,25 @@
-- NOTE: This file is auto generated by ./sql-generator
-- AlbumUserRepository.create
insert into
"albums_shared_users_users" ("usersId", "albumsId")
values
($1, $2)
returning
*
-- AlbumUserRepository.update
update "albums_shared_users_users"
set
"role" = $1
where
"usersId" = $2
and "albumsId" = $3
returning
*
-- AlbumUserRepository.delete
delete from "albums_shared_users_users"
where
"usersId" = $1
and "albumsId" = $2

View File

@ -1,26 +1,40 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { Insertable, Kysely, Selectable, Updateable } from 'kysely';
import { AlbumUserEntity } from 'src/entities/album-user.entity'; import { InjectKysely } from 'nestjs-kysely';
import { AlbumsSharedUsersUsers, DB } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators';
import { AlbumUserRole } from 'src/enum';
import { AlbumPermissionId, IAlbumUserRepository } from 'src/interfaces/album-user.interface'; import { AlbumPermissionId, IAlbumUserRepository } from 'src/interfaces/album-user.interface';
import { Repository } from 'typeorm';
@Injectable() @Injectable()
export class AlbumUserRepository implements IAlbumUserRepository { export class AlbumUserRepository implements IAlbumUserRepository {
constructor(@InjectRepository(AlbumUserEntity) private repository: Repository<AlbumUserEntity>) {} constructor(@InjectKysely() private db: Kysely<DB>) {}
async create(albumUser: Partial<AlbumUserEntity>): Promise<AlbumUserEntity> { @GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }] })
const { userId, albumId } = await this.repository.save(albumUser); create(albumUser: Insertable<AlbumsSharedUsersUsers>): Promise<Selectable<AlbumsSharedUsersUsers>> {
return this.repository.findOneOrFail({ where: { userId, albumId } }); return this.db.insertInto('albums_shared_users_users').values(albumUser).returningAll().executeTakeFirstOrThrow();
} }
async update({ userId, albumId }: AlbumPermissionId, dto: Partial<AlbumUserEntity>): Promise<AlbumUserEntity> { @GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }, { role: AlbumUserRole.VIEWER }] })
await this.repository.update({ userId, albumId }, dto); update(
return this.repository.findOneOrFail({ { usersId, albumsId }: AlbumPermissionId,
where: { userId, albumId }, dto: Updateable<AlbumsSharedUsersUsers>,
}); ): Promise<Selectable<AlbumsSharedUsersUsers>> {
return this.db
.updateTable('albums_shared_users_users')
.set(dto)
.where('usersId', '=', usersId)
.where('albumsId', '=', albumsId)
.returningAll()
.executeTakeFirstOrThrow();
} }
async delete({ userId, albumId }: AlbumPermissionId): Promise<void> { @GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }] })
await this.repository.delete({ userId, albumId }); async delete({ usersId, albumsId }: AlbumPermissionId): Promise<void> {
await this.db
.deleteFrom('albums_shared_users_users')
.where('usersId', '=', usersId)
.where('albumsId', '=', albumsId)
.execute();
} }
} }

View File

@ -323,18 +323,16 @@ describe(AlbumService.name, () => {
albumMock.update.mockResolvedValue(albumStub.sharedWithAdmin); albumMock.update.mockResolvedValue(albumStub.sharedWithAdmin);
userMock.get.mockResolvedValue(userStub.user2); userMock.get.mockResolvedValue(userStub.user2);
albumUserMock.create.mockResolvedValue({ albumUserMock.create.mockResolvedValue({
userId: userStub.user2.id, usersId: userStub.user2.id,
user: userStub.user2, albumsId: albumStub.sharedWithAdmin.id,
albumId: albumStub.sharedWithAdmin.id,
album: albumStub.sharedWithAdmin,
role: AlbumUserRole.EDITOR, role: AlbumUserRole.EDITOR,
}); });
await sut.addUsers(authStub.user1, albumStub.sharedWithAdmin.id, { await sut.addUsers(authStub.user1, albumStub.sharedWithAdmin.id, {
albumUsers: [{ userId: authStub.user2.user.id }], albumUsers: [{ userId: authStub.user2.user.id }],
}); });
expect(albumUserMock.create).toHaveBeenCalledWith({ expect(albumUserMock.create).toHaveBeenCalledWith({
userId: authStub.user2.user.id, usersId: authStub.user2.user.id,
albumId: albumStub.sharedWithAdmin.id, albumsId: albumStub.sharedWithAdmin.id,
}); });
expect(eventMock.emit).toHaveBeenCalledWith('album.invite', { expect(eventMock.emit).toHaveBeenCalledWith('album.invite', {
id: albumStub.sharedWithAdmin.id, id: albumStub.sharedWithAdmin.id,
@ -361,8 +359,8 @@ describe(AlbumService.name, () => {
expect(albumUserMock.delete).toHaveBeenCalledTimes(1); expect(albumUserMock.delete).toHaveBeenCalledTimes(1);
expect(albumUserMock.delete).toHaveBeenCalledWith({ expect(albumUserMock.delete).toHaveBeenCalledWith({
albumId: albumStub.sharedWithUser.id, albumsId: albumStub.sharedWithUser.id,
userId: userStub.user1.id, usersId: userStub.user1.id,
}); });
expect(albumMock.getById).toHaveBeenCalledWith(albumStub.sharedWithUser.id, { withAssets: false }); expect(albumMock.getById).toHaveBeenCalledWith(albumStub.sharedWithUser.id, { withAssets: false });
}); });
@ -388,8 +386,8 @@ describe(AlbumService.name, () => {
expect(albumUserMock.delete).toHaveBeenCalledTimes(1); expect(albumUserMock.delete).toHaveBeenCalledTimes(1);
expect(albumUserMock.delete).toHaveBeenCalledWith({ expect(albumUserMock.delete).toHaveBeenCalledWith({
albumId: albumStub.sharedWithUser.id, albumsId: albumStub.sharedWithUser.id,
userId: authStub.user1.user.id, usersId: authStub.user1.user.id,
}); });
}); });
@ -400,8 +398,8 @@ describe(AlbumService.name, () => {
expect(albumUserMock.delete).toHaveBeenCalledTimes(1); expect(albumUserMock.delete).toHaveBeenCalledTimes(1);
expect(albumUserMock.delete).toHaveBeenCalledWith({ expect(albumUserMock.delete).toHaveBeenCalledWith({
albumId: albumStub.sharedWithUser.id, albumsId: albumStub.sharedWithUser.id,
userId: authStub.user1.user.id, usersId: authStub.user1.user.id,
}); });
}); });
@ -433,7 +431,7 @@ describe(AlbumService.name, () => {
role: AlbumUserRole.EDITOR, role: AlbumUserRole.EDITOR,
}); });
expect(albumUserMock.update).toHaveBeenCalledWith( expect(albumUserMock.update).toHaveBeenCalledWith(
{ albumId: albumStub.sharedWithAdmin.id, userId: userStub.admin.id }, { albumsId: albumStub.sharedWithAdmin.id, usersId: userStub.admin.id },
{ role: AlbumUserRole.EDITOR }, { role: AlbumUserRole.EDITOR },
); );
}); });

View File

@ -229,7 +229,7 @@ export class AlbumService extends BaseService {
throw new BadRequestException('User not found'); throw new BadRequestException('User not found');
} }
await this.albumUserRepository.create({ userId, albumId: id, role }); await this.albumUserRepository.create({ usersId: userId, albumsId: id, role });
await this.eventRepository.emit('album.invite', { id, userId }); await this.eventRepository.emit('album.invite', { id, userId });
} }
@ -257,12 +257,12 @@ export class AlbumService extends BaseService {
await this.requireAccess({ auth, permission: Permission.ALBUM_SHARE, ids: [id] }); await this.requireAccess({ auth, permission: Permission.ALBUM_SHARE, ids: [id] });
} }
await this.albumUserRepository.delete({ albumId: id, userId }); await this.albumUserRepository.delete({ albumsId: id, usersId: userId });
} }
async updateUser(auth: AuthDto, id: string, userId: string, dto: Partial<AlbumUserEntity>): Promise<void> { async updateUser(auth: AuthDto, id: string, userId: string, dto: Partial<AlbumUserEntity>): Promise<void> {
await this.requireAccess({ auth, permission: Permission.ALBUM_SHARE, ids: [id] }); await this.requireAccess({ auth, permission: Permission.ALBUM_SHARE, ids: [id] });
await this.albumUserRepository.update({ albumId: id, userId }, { role: dto.role }); await this.albumUserRepository.update({ albumsId: id, usersId: userId }, { role: dto.role });
} }
private async findOrFail(id: string, options: AlbumInfoOptions) { private async findOrFail(id: string, options: AlbumInfoOptions) {