From bd4fd8de961c3384aab82cc400273b60a27e6162 Mon Sep 17 00:00:00 2001 From: Gwanho Kim Date: Tue, 2 Jul 2024 21:42:51 +0900 Subject: [PATCH 1/9] Make like logic --- src/popo/notice/notice.controller.ts | 10 ++++++++++ src/popo/notice/notice.entity.ts | 3 +++ src/popo/notice/notice.service.ts | 16 ++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/src/popo/notice/notice.controller.ts b/src/popo/notice/notice.controller.ts index 2eccd7a..7aaaf45 100644 --- a/src/popo/notice/notice.controller.ts +++ b/src/popo/notice/notice.controller.ts @@ -70,6 +70,16 @@ export class NoticeController { return this.noticeService.increaseClickCount(id); } + @Patch('like/:id') + increaseLikeCount(@Param('id') id: number) { + return this.noticeService.increaseLikeCount(id); + } + + @Patch('unlike/:id') + decreaseLikeCount(@Param('id') id: number) { + return this.noticeService.decreaseLikeCount(id); + } + @Put(':id') @Roles(UserType.admin, UserType.association, UserType.staff) @UseGuards(JwtAuthGuard, RolesGuard) diff --git a/src/popo/notice/notice.entity.ts b/src/popo/notice/notice.entity.ts index 63d9a79..48eb63a 100644 --- a/src/popo/notice/notice.entity.ts +++ b/src/popo/notice/notice.entity.ts @@ -33,6 +33,9 @@ export class Notice extends BaseEntity { @Column({ default: 0 }) click_count: number; + @Column({ default: 0 }) + like_count: number; + @CreateDateColumn() createdAt: Date; diff --git a/src/popo/notice/notice.service.ts b/src/popo/notice/notice.service.ts index d6770b6..b029db0 100644 --- a/src/popo/notice/notice.service.ts +++ b/src/popo/notice/notice.service.ts @@ -72,6 +72,22 @@ export class NoticeService { ); } + async increaseLikeCount(id: number) { + const notice = await this.noticeRepo.findOneByOrFail({ id: id }); + return this.noticeRepo.update( + { id: id }, + { like_count: notice.like_count + 1 }, + ); + } + + async decreaseLikeCount(id: number) { + const notice = await this.noticeRepo.findOneByOrFail({ id: id }); + return this.noticeRepo.update( + { id: id }, + { like_count: notice.like_count - 1 }, + ); + } + async remove(id: number) { const existNotice = await this.findOneById(id); From 4ebbe6d954ae9863441d84ee340f1abdc3901561 Mon Sep 17 00:00:00 2001 From: Gwanho Kim Date: Mon, 15 Jul 2024 16:47:05 +0900 Subject: [PATCH 2/9] Add Like module for handling likes on notices --- src/popo/like/like.controller.ts | 57 ++++++++++++++++++++++++++++++++ src/popo/like/like.dto.ts | 4 +++ src/popo/like/like.entity.ts | 23 +++++++++++++ src/popo/like/like.module.ts | 16 +++++++++ src/popo/like/like.service.ts | 34 +++++++++++++++++++ src/popo/popo.module.ts | 2 ++ 6 files changed, 136 insertions(+) create mode 100644 src/popo/like/like.controller.ts create mode 100644 src/popo/like/like.dto.ts create mode 100644 src/popo/like/like.entity.ts create mode 100644 src/popo/like/like.module.ts create mode 100644 src/popo/like/like.service.ts diff --git a/src/popo/like/like.controller.ts b/src/popo/like/like.controller.ts new file mode 100644 index 0000000..4ccc0d2 --- /dev/null +++ b/src/popo/like/like.controller.ts @@ -0,0 +1,57 @@ +import { + BadRequestException, + Body, + Controller, + Delete, + Get, + Post, + Query, + UseGuards, +} from '@nestjs/common'; +import { ApiBody, ApiTags } from '@nestjs/swagger'; + +import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard'; +import { LikeDto } from './like.dto'; +import { LikeService } from './like.service'; + +@ApiTags('Like') +@Controller('like') +export class LikeController { + constructor(private readonly likeService: LikeService) {} + + @Post() + @UseGuards(JwtAuthGuard) + @ApiBody({ type: LikeDto }) + createCalendar(@Body() dto: LikeDto) { + return this.likeService.save(dto); + } + + @Get('count') + async getLikeCount(@Query('notice_id') notice_id: string): Promise { + return (await this.likeService.findAllByNoticeId(notice_id)).length; + } + + @Get('status') + async getLikeStatus( + @Query('user_id') user_id: string, + @Query('notice_id') notice_id: string, + ): Promise { + return this.likeService.findByUserIdAndNoticeId(user_id, notice_id) + ? true + : false; + } + + @Delete() + @UseGuards(JwtAuthGuard) + deleteCalendar( + @Query('user_id') user_id: string, + @Query('notice_id') notice_id: string, + ) { + if (!this.likeService.findByUserIdAndNoticeId(user_id, notice_id)) { + throw new BadRequestException( + '해당 게시글에 좋아요를 누른 기록이 없습니다.', + ); + } + return this.likeService.delete(user_id, notice_id); + } +} diff --git a/src/popo/like/like.dto.ts b/src/popo/like/like.dto.ts new file mode 100644 index 0000000..8238be7 --- /dev/null +++ b/src/popo/like/like.dto.ts @@ -0,0 +1,4 @@ +export class LikeDto { + readonly user_id: string; + readonly notice_id: string; +} diff --git a/src/popo/like/like.entity.ts b/src/popo/like/like.entity.ts new file mode 100644 index 0000000..8d00e1c --- /dev/null +++ b/src/popo/like/like.entity.ts @@ -0,0 +1,23 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + Index, +} from 'typeorm'; + +@Entity() +@Index(['user_id', 'notice_id'], { unique: true }) +export class Like { + @PrimaryGeneratedColumn('increment') + id: number; + + @Column({ nullable: false }) + user_id: string; + + @Column({ nullable: false }) + notice_id: string; + + @CreateDateColumn() + createdAt: Date; +} diff --git a/src/popo/like/like.module.ts b/src/popo/like/like.module.ts new file mode 100644 index 0000000..48823c6 --- /dev/null +++ b/src/popo/like/like.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { NestjsFormDataModule } from 'nestjs-form-data'; + +import { FileModule } from '../../file/file.module'; +import { Like } from './like.entity'; +import { LikeController } from './like.controller'; +import { LikeService } from './like.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([Like]), NestjsFormDataModule, FileModule], + controllers: [LikeController], + providers: [LikeService], + exports: [LikeService], +}) +export class LikeModule {} diff --git a/src/popo/like/like.service.ts b/src/popo/like/like.service.ts new file mode 100644 index 0000000..a52185c --- /dev/null +++ b/src/popo/like/like.service.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { Like } from './like.entity'; +import { LikeDto } from './like.dto'; + +@Injectable() +export class LikeService { + constructor( + @InjectRepository(Like) + private readonly likeRepo: Repository, + ) {} + + save(dto: LikeDto) { + return this.likeRepo.save(dto); + } + + findByUserIdAndNoticeId(user_id: string, notice_id: string) { + return this.likeRepo.findOne({ + where: { user_id: user_id, notice_id: notice_id }, + }); + } + + findAllByNoticeId(notice_id: string) { + return this.likeRepo.find({ + where: { notice_id: notice_id }, + }); + } + + delete(user_id: string, notice_id: string) { + return this.likeRepo.delete({ user_id: user_id, notice_id: notice_id }); + } +} diff --git a/src/popo/popo.module.ts b/src/popo/popo.module.ts index 0dc3d74..9d1e32c 100644 --- a/src/popo/popo.module.ts +++ b/src/popo/popo.module.ts @@ -10,6 +10,7 @@ import { ReservationModule } from './reservation/reservation.module'; import { SettingModule } from './setting/setting.module'; import { UserModule } from './user/user.module'; import { WhitebookModule } from './whitebook/whitebook.module'; +import { LikeModule } from './like/like.module'; @Module({ imports: [ @@ -23,6 +24,7 @@ import { WhitebookModule } from './whitebook/whitebook.module'; SettingModule, UserModule, WhitebookModule, + LikeModule, ], controllers: [], providers: [], From 8a1c9428200d2d077c02bb6912ace512535f1468 Mon Sep 17 00:00:00 2001 From: Gwanho Kim Date: Mon, 15 Jul 2024 17:01:43 +0900 Subject: [PATCH 3/9] Remove unused like-related code --- src/popo/notice/notice.controller.ts | 10 ---------- src/popo/notice/notice.entity.ts | 3 --- src/popo/notice/notice.service.ts | 16 ---------------- 3 files changed, 29 deletions(-) diff --git a/src/popo/notice/notice.controller.ts b/src/popo/notice/notice.controller.ts index 7aaaf45..2eccd7a 100644 --- a/src/popo/notice/notice.controller.ts +++ b/src/popo/notice/notice.controller.ts @@ -70,16 +70,6 @@ export class NoticeController { return this.noticeService.increaseClickCount(id); } - @Patch('like/:id') - increaseLikeCount(@Param('id') id: number) { - return this.noticeService.increaseLikeCount(id); - } - - @Patch('unlike/:id') - decreaseLikeCount(@Param('id') id: number) { - return this.noticeService.decreaseLikeCount(id); - } - @Put(':id') @Roles(UserType.admin, UserType.association, UserType.staff) @UseGuards(JwtAuthGuard, RolesGuard) diff --git a/src/popo/notice/notice.entity.ts b/src/popo/notice/notice.entity.ts index 48eb63a..63d9a79 100644 --- a/src/popo/notice/notice.entity.ts +++ b/src/popo/notice/notice.entity.ts @@ -33,9 +33,6 @@ export class Notice extends BaseEntity { @Column({ default: 0 }) click_count: number; - @Column({ default: 0 }) - like_count: number; - @CreateDateColumn() createdAt: Date; diff --git a/src/popo/notice/notice.service.ts b/src/popo/notice/notice.service.ts index b029db0..d6770b6 100644 --- a/src/popo/notice/notice.service.ts +++ b/src/popo/notice/notice.service.ts @@ -72,22 +72,6 @@ export class NoticeService { ); } - async increaseLikeCount(id: number) { - const notice = await this.noticeRepo.findOneByOrFail({ id: id }); - return this.noticeRepo.update( - { id: id }, - { like_count: notice.like_count + 1 }, - ); - } - - async decreaseLikeCount(id: number) { - const notice = await this.noticeRepo.findOneByOrFail({ id: id }); - return this.noticeRepo.update( - { id: id }, - { like_count: notice.like_count - 1 }, - ); - } - async remove(id: number) { const existNotice = await this.findOneById(id); From 86e62e28fd7fc9070bd4e3013c97359722a0e050 Mon Sep 17 00:00:00 2001 From: Gwanho Kim Date: Mon, 15 Jul 2024 19:12:31 +0900 Subject: [PATCH 4/9] Delete unused library --- src/popo/like/like.module.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/popo/like/like.module.ts b/src/popo/like/like.module.ts index 48823c6..1c59390 100644 --- a/src/popo/like/like.module.ts +++ b/src/popo/like/like.module.ts @@ -1,14 +1,12 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { NestjsFormDataModule } from 'nestjs-form-data'; -import { FileModule } from '../../file/file.module'; import { Like } from './like.entity'; import { LikeController } from './like.controller'; import { LikeService } from './like.service'; @Module({ - imports: [TypeOrmModule.forFeature([Like]), NestjsFormDataModule, FileModule], + imports: [TypeOrmModule.forFeature([Like])], controllers: [LikeController], providers: [LikeService], exports: [LikeService], From acd5a4249d33d2b1d174b68233ac3e26ed784deb Mon Sep 17 00:00:00 2001 From: Gwanho Kim Date: Thu, 18 Jul 2024 15:58:01 +0900 Subject: [PATCH 5/9] Rename functions --- src/popo/like/like.controller.ts | 20 +++++++++++--------- src/popo/like/like.service.ts | 4 ++++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/popo/like/like.controller.ts b/src/popo/like/like.controller.ts index 4ccc0d2..fa44f0b 100644 --- a/src/popo/like/like.controller.ts +++ b/src/popo/like/like.controller.ts @@ -14,6 +14,10 @@ import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard'; import { LikeDto } from './like.dto'; import { LikeService } from './like.service'; +const Message = { + FAIL_LIKE_DELETION_NEVER_LIKED: 'There is no record of liking the post.', +}; + @ApiTags('Like') @Controller('like') export class LikeController { @@ -22,20 +26,20 @@ export class LikeController { @Post() @UseGuards(JwtAuthGuard) @ApiBody({ type: LikeDto }) - createCalendar(@Body() dto: LikeDto) { + async create(@Body() dto: LikeDto): Promise { return this.likeService.save(dto); } @Get('count') - async getLikeCount(@Query('notice_id') notice_id: string): Promise { - return (await this.likeService.findAllByNoticeId(notice_id)).length; + countLikes(@Query('notice_id') notice_id: string): Promise { + return this.likeService.countLikes(notice_id); } @Get('status') - async getLikeStatus( + getStatus( @Query('user_id') user_id: string, @Query('notice_id') notice_id: string, - ): Promise { + ): boolean { return this.likeService.findByUserIdAndNoticeId(user_id, notice_id) ? true : false; @@ -43,14 +47,12 @@ export class LikeController { @Delete() @UseGuards(JwtAuthGuard) - deleteCalendar( + async delete( @Query('user_id') user_id: string, @Query('notice_id') notice_id: string, ) { if (!this.likeService.findByUserIdAndNoticeId(user_id, notice_id)) { - throw new BadRequestException( - '해당 게시글에 좋아요를 누른 기록이 없습니다.', - ); + throw new BadRequestException(Message.FAIL_LIKE_DELETION_NEVER_LIKED); } return this.likeService.delete(user_id, notice_id); } diff --git a/src/popo/like/like.service.ts b/src/popo/like/like.service.ts index a52185c..88390bb 100644 --- a/src/popo/like/like.service.ts +++ b/src/popo/like/like.service.ts @@ -28,6 +28,10 @@ export class LikeService { }); } + countLikes(notice_id: string) { + return this.likeRepo.count({ where: { notice_id: notice_id } }); + } + delete(user_id: string, notice_id: string) { return this.likeRepo.delete({ user_id: user_id, notice_id: notice_id }); } From 22bbeb57bdcd31e2f015cf737709e8e27de3341a Mon Sep 17 00:00:00 2001 From: Gwanho Kim Date: Thu, 18 Jul 2024 16:22:18 +0900 Subject: [PATCH 6/9] Make test --- src/popo/like/like.controller.spec.ts | 77 +++++++++++++++++++++++++++ src/popo/like/like.controller.ts | 7 ++- 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/popo/like/like.controller.spec.ts diff --git a/src/popo/like/like.controller.spec.ts b/src/popo/like/like.controller.spec.ts new file mode 100644 index 0000000..cc72b6f --- /dev/null +++ b/src/popo/like/like.controller.spec.ts @@ -0,0 +1,77 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { LikeController } from './like.controller'; +import { LikeService } from './like.service'; +import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { Like } from './like.entity'; + +describe('LikeController', () => { + let controller: LikeController; + let likeService: DeepMocked; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [LikeController], + providers: [ + { + provide: LikeService, + useValue: createMock(), + }, + ], + }).compile(); + + controller = module.get(LikeController); + likeService = module.get(LikeService); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + expect(likeService).toBeDefined(); + }); + + it('should count likes', async () => { + const result = 1; + const noticeId = '1'; + likeService.countLikes.mockResolvedValue(result); + + expect(await controller.countLikes(noticeId)).toBe(result); + }); + + it('should get status', async () => { + const result = true; + const like: Like = { + id: 1, + user_id: '1', + notice_id: '1', + createdAt: new Date(), + }; + const userId = '1'; + const noticeId = '1'; + likeService.findByUserIdAndNoticeId.mockResolvedValue(like); + + await expect(controller.getStatus(userId, noticeId)).toBe(result); + }); + + it('should throw error when delete if it is not liked', async () => { + const userId = '1'; + const noticeId = '1'; + likeService.findByUserIdAndNoticeId.mockResolvedValue(null); + + await expect(controller.delete(userId, noticeId)).rejects.toThrow(); + }); + + it('should delete like', async () => { + const userId = '1'; + const noticeId = '1'; + const like: Like = { + id: 1, + user_id: '1', + notice_id: '1', + createdAt: new Date(), + }; + const mockedDeletedResult = { affected: 1, raw: null }; + likeService.findByUserIdAndNoticeId.mockResolvedValue(like); + likeService.delete.mockResolvedValue(mockedDeletedResult); + + expect(await controller.delete(userId, noticeId)).toBe(mockedDeletedResult); + }); +}); diff --git a/src/popo/like/like.controller.ts b/src/popo/like/like.controller.ts index fa44f0b..ff0df0c 100644 --- a/src/popo/like/like.controller.ts +++ b/src/popo/like/like.controller.ts @@ -51,7 +51,12 @@ export class LikeController { @Query('user_id') user_id: string, @Query('notice_id') notice_id: string, ) { - if (!this.likeService.findByUserIdAndNoticeId(user_id, notice_id)) { + const target = await this.likeService.findByUserIdAndNoticeId( + user_id, + notice_id, + ); + + if (!target) { throw new BadRequestException(Message.FAIL_LIKE_DELETION_NEVER_LIKED); } return this.likeService.delete(user_id, notice_id); From 4beeabad592fde2708df3e73259eb5445f11191a Mon Sep 17 00:00:00 2001 From: Gwanho Kim Date: Wed, 24 Jul 2024 16:11:15 +0900 Subject: [PATCH 7/9] Rename like repository to noticeLike --- src/popo/like/like.module.ts | 14 ------- src/popo/like/like.service.ts | 38 ----------------- .../noticeLike.controller.spec.ts} | 30 +++++++------- .../noticeLike.controller.ts} | 26 ++++++------ .../noticeLike.dto.ts} | 2 +- .../noticeLike.entity.ts} | 4 +- src/popo/noticeLike/noticeLike.module.ts | 14 +++++++ src/popo/noticeLike/noticeLike.service.ts | 41 +++++++++++++++++++ src/popo/popo.module.ts | 4 +- 9 files changed, 88 insertions(+), 85 deletions(-) delete mode 100644 src/popo/like/like.module.ts delete mode 100644 src/popo/like/like.service.ts rename src/popo/{like/like.controller.spec.ts => noticeLike/noticeLike.controller.spec.ts} (70%) rename src/popo/{like/like.controller.ts => noticeLike/noticeLike.controller.ts} (57%) rename src/popo/{like/like.dto.ts => noticeLike/noticeLike.dto.ts} (67%) rename src/popo/{like/like.entity.ts => noticeLike/noticeLike.entity.ts} (88%) create mode 100644 src/popo/noticeLike/noticeLike.module.ts create mode 100644 src/popo/noticeLike/noticeLike.service.ts diff --git a/src/popo/like/like.module.ts b/src/popo/like/like.module.ts deleted file mode 100644 index 1c59390..0000000 --- a/src/popo/like/like.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; - -import { Like } from './like.entity'; -import { LikeController } from './like.controller'; -import { LikeService } from './like.service'; - -@Module({ - imports: [TypeOrmModule.forFeature([Like])], - controllers: [LikeController], - providers: [LikeService], - exports: [LikeService], -}) -export class LikeModule {} diff --git a/src/popo/like/like.service.ts b/src/popo/like/like.service.ts deleted file mode 100644 index 88390bb..0000000 --- a/src/popo/like/like.service.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; - -import { Like } from './like.entity'; -import { LikeDto } from './like.dto'; - -@Injectable() -export class LikeService { - constructor( - @InjectRepository(Like) - private readonly likeRepo: Repository, - ) {} - - save(dto: LikeDto) { - return this.likeRepo.save(dto); - } - - findByUserIdAndNoticeId(user_id: string, notice_id: string) { - return this.likeRepo.findOne({ - where: { user_id: user_id, notice_id: notice_id }, - }); - } - - findAllByNoticeId(notice_id: string) { - return this.likeRepo.find({ - where: { notice_id: notice_id }, - }); - } - - countLikes(notice_id: string) { - return this.likeRepo.count({ where: { notice_id: notice_id } }); - } - - delete(user_id: string, notice_id: string) { - return this.likeRepo.delete({ user_id: user_id, notice_id: notice_id }); - } -} diff --git a/src/popo/like/like.controller.spec.ts b/src/popo/noticeLike/noticeLike.controller.spec.ts similarity index 70% rename from src/popo/like/like.controller.spec.ts rename to src/popo/noticeLike/noticeLike.controller.spec.ts index cc72b6f..73ee642 100644 --- a/src/popo/like/like.controller.spec.ts +++ b/src/popo/noticeLike/noticeLike.controller.spec.ts @@ -1,26 +1,26 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { LikeController } from './like.controller'; -import { LikeService } from './like.service'; +import { NoticeLikeController } from './noticeLike.controller'; +import { NoticeLikeService } from './noticeLike.service'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { Like } from './like.entity'; +import { NoticeLike } from './noticeLike.entity'; -describe('LikeController', () => { - let controller: LikeController; - let likeService: DeepMocked; +describe('NoticeLikeController', () => { + let controller: NoticeLikeController; + let likeService: DeepMocked; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - controllers: [LikeController], + controllers: [NoticeLikeController], providers: [ { - provide: LikeService, - useValue: createMock(), + provide: NoticeLikeService, + useValue: createMock(), }, ], }).compile(); - controller = module.get(LikeController); - likeService = module.get(LikeService); + controller = module.get(NoticeLikeController); + likeService = module.get(NoticeLikeService); }); it('should be defined', () => { @@ -38,11 +38,11 @@ describe('LikeController', () => { it('should get status', async () => { const result = true; - const like: Like = { + const like: NoticeLike = { id: 1, user_id: '1', notice_id: '1', - createdAt: new Date(), + created_at: new Date(), }; const userId = '1'; const noticeId = '1'; @@ -62,11 +62,11 @@ describe('LikeController', () => { it('should delete like', async () => { const userId = '1'; const noticeId = '1'; - const like: Like = { + const like: NoticeLike = { id: 1, user_id: '1', notice_id: '1', - createdAt: new Date(), + created_at: new Date(), }; const mockedDeletedResult = { affected: 1, raw: null }; likeService.findByUserIdAndNoticeId.mockResolvedValue(like); diff --git a/src/popo/like/like.controller.ts b/src/popo/noticeLike/noticeLike.controller.ts similarity index 57% rename from src/popo/like/like.controller.ts rename to src/popo/noticeLike/noticeLike.controller.ts index ff0df0c..db4175f 100644 --- a/src/popo/like/like.controller.ts +++ b/src/popo/noticeLike/noticeLike.controller.ts @@ -11,28 +11,28 @@ import { import { ApiBody, ApiTags } from '@nestjs/swagger'; import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard'; -import { LikeDto } from './like.dto'; -import { LikeService } from './like.service'; +import { NoticeLikeDto } from './noticeLike.dto'; +import { NoticeLikeService } from './noticeLike.service'; const Message = { FAIL_LIKE_DELETION_NEVER_LIKED: 'There is no record of liking the post.', }; -@ApiTags('Like') -@Controller('like') -export class LikeController { - constructor(private readonly likeService: LikeService) {} +@ApiTags('NoticeLike') +@Controller('noticeLike') +export class NoticeLikeController { + constructor(private readonly noticeLikeService: NoticeLikeService) {} @Post() @UseGuards(JwtAuthGuard) - @ApiBody({ type: LikeDto }) - async create(@Body() dto: LikeDto): Promise { - return this.likeService.save(dto); + @ApiBody({ type: NoticeLikeDto }) + async create(@Body() dto: NoticeLikeDto): Promise { + return this.noticeLikeService.save(dto); } @Get('count') countLikes(@Query('notice_id') notice_id: string): Promise { - return this.likeService.countLikes(notice_id); + return this.noticeLikeService.countLikes(notice_id); } @Get('status') @@ -40,7 +40,7 @@ export class LikeController { @Query('user_id') user_id: string, @Query('notice_id') notice_id: string, ): boolean { - return this.likeService.findByUserIdAndNoticeId(user_id, notice_id) + return this.noticeLikeService.findByUserIdAndNoticeId(user_id, notice_id) ? true : false; } @@ -51,7 +51,7 @@ export class LikeController { @Query('user_id') user_id: string, @Query('notice_id') notice_id: string, ) { - const target = await this.likeService.findByUserIdAndNoticeId( + const target = await this.noticeLikeService.findByUserIdAndNoticeId( user_id, notice_id, ); @@ -59,6 +59,6 @@ export class LikeController { if (!target) { throw new BadRequestException(Message.FAIL_LIKE_DELETION_NEVER_LIKED); } - return this.likeService.delete(user_id, notice_id); + return this.noticeLikeService.delete(user_id, notice_id); } } diff --git a/src/popo/like/like.dto.ts b/src/popo/noticeLike/noticeLike.dto.ts similarity index 67% rename from src/popo/like/like.dto.ts rename to src/popo/noticeLike/noticeLike.dto.ts index 8238be7..2c067a9 100644 --- a/src/popo/like/like.dto.ts +++ b/src/popo/noticeLike/noticeLike.dto.ts @@ -1,4 +1,4 @@ -export class LikeDto { +export class NoticeLikeDto { readonly user_id: string; readonly notice_id: string; } diff --git a/src/popo/like/like.entity.ts b/src/popo/noticeLike/noticeLike.entity.ts similarity index 88% rename from src/popo/like/like.entity.ts rename to src/popo/noticeLike/noticeLike.entity.ts index 8d00e1c..c345fa6 100644 --- a/src/popo/like/like.entity.ts +++ b/src/popo/noticeLike/noticeLike.entity.ts @@ -8,7 +8,7 @@ import { @Entity() @Index(['user_id', 'notice_id'], { unique: true }) -export class Like { +export class NoticeLike { @PrimaryGeneratedColumn('increment') id: number; @@ -19,5 +19,5 @@ export class Like { notice_id: string; @CreateDateColumn() - createdAt: Date; + created_at: Date; } diff --git a/src/popo/noticeLike/noticeLike.module.ts b/src/popo/noticeLike/noticeLike.module.ts new file mode 100644 index 0000000..108a010 --- /dev/null +++ b/src/popo/noticeLike/noticeLike.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { NoticeLike } from './noticeLike.entity'; +import { NoticeLikeController } from './noticeLike.controller'; +import { NoticeLikeService } from './noticeLike.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([NoticeLike])], + controllers: [NoticeLikeController], + providers: [NoticeLikeService], + exports: [NoticeLikeService], +}) +export class NoticeLikeModule {} diff --git a/src/popo/noticeLike/noticeLike.service.ts b/src/popo/noticeLike/noticeLike.service.ts new file mode 100644 index 0000000..d7106ed --- /dev/null +++ b/src/popo/noticeLike/noticeLike.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { NoticeLike } from './noticeLike.entity'; +import { NoticeLikeDto } from './noticeLike.dto'; + +@Injectable() +export class NoticeLikeService { + constructor( + @InjectRepository(NoticeLike) + private readonly noticeLikeRepo: Repository, + ) {} + + save(dto: NoticeLikeDto) { + return this.noticeLikeRepo.save(dto); + } + + findByUserIdAndNoticeId(user_id: string, notice_id: string) { + return this.noticeLikeRepo.findOne({ + where: { user_id: user_id, notice_id: notice_id }, + }); + } + + findAllByNoticeId(notice_id: string) { + return this.noticeLikeRepo.find({ + where: { notice_id: notice_id }, + }); + } + + countLikes(notice_id: string) { + return this.noticeLikeRepo.count({ where: { notice_id: notice_id } }); + } + + delete(user_id: string, notice_id: string) { + return this.noticeLikeRepo.delete({ + user_id: user_id, + notice_id: notice_id, + }); + } +} diff --git a/src/popo/popo.module.ts b/src/popo/popo.module.ts index 9d1e32c..75ecbd9 100644 --- a/src/popo/popo.module.ts +++ b/src/popo/popo.module.ts @@ -10,7 +10,7 @@ import { ReservationModule } from './reservation/reservation.module'; import { SettingModule } from './setting/setting.module'; import { UserModule } from './user/user.module'; import { WhitebookModule } from './whitebook/whitebook.module'; -import { LikeModule } from './like/like.module'; +import { NoticeLikeModule } from './noticeLike/noticeLike.module'; @Module({ imports: [ @@ -24,7 +24,7 @@ import { LikeModule } from './like/like.module'; SettingModule, UserModule, WhitebookModule, - LikeModule, + NoticeLikeModule, ], controllers: [], providers: [], From d0a690b20ced3603ecc08957fced90a62f5d78bb Mon Sep 17 00:00:00 2001 From: Gwanho Kim Date: Wed, 24 Jul 2024 21:39:33 +0900 Subject: [PATCH 8/9] Update controller logic --- .../noticeLike/noticeLike.controller.spec.ts | 2 ++ src/popo/noticeLike/noticeLike.controller.ts | 35 +++++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/popo/noticeLike/noticeLike.controller.spec.ts b/src/popo/noticeLike/noticeLike.controller.spec.ts index 73ee642..7370a3e 100644 --- a/src/popo/noticeLike/noticeLike.controller.spec.ts +++ b/src/popo/noticeLike/noticeLike.controller.spec.ts @@ -36,6 +36,8 @@ describe('NoticeLikeController', () => { expect(await controller.countLikes(noticeId)).toBe(result); }); + // TODO: 테스트 DB를 만들어서 실제로 데이터를 DB에 넣고 findByUserIdAndNoticeId를 테스트하기 + // 해당 테스트는 service.spec으로 옮기기 it('should get status', async () => { const result = true; const like: NoticeLike = { diff --git a/src/popo/noticeLike/noticeLike.controller.ts b/src/popo/noticeLike/noticeLike.controller.ts index db4175f..a9d5c6e 100644 --- a/src/popo/noticeLike/noticeLike.controller.ts +++ b/src/popo/noticeLike/noticeLike.controller.ts @@ -4,6 +4,8 @@ import { Controller, Delete, Get, + HttpException, + HttpStatus, Post, Query, UseGuards, @@ -13,12 +15,16 @@ import { ApiBody, ApiTags } from '@nestjs/swagger'; import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard'; import { NoticeLikeDto } from './noticeLike.dto'; import { NoticeLikeService } from './noticeLike.service'; +import { QueryFailedError } from 'typeorm'; +import { NoticeLike } from './noticeLike.entity'; const Message = { FAIL_LIKE_DELETION_NEVER_LIKED: 'There is no record of liking the post.', + FAIL_DUPLICATE_ENTRY: 'Duplicate entry detected', + FAIL_INTERNAL_SERVER: 'Internal server error', }; -@ApiTags('NoticeLike') +@ApiTags('Notice Like') @Controller('noticeLike') export class NoticeLikeController { constructor(private readonly noticeLikeService: NoticeLikeService) {} @@ -26,8 +32,22 @@ export class NoticeLikeController { @Post() @UseGuards(JwtAuthGuard) @ApiBody({ type: NoticeLikeDto }) - async create(@Body() dto: NoticeLikeDto): Promise { - return this.noticeLikeService.save(dto); + async create(@Body() dto: NoticeLikeDto): Promise { + try { + return await this.noticeLikeService.save(dto); + } catch (e) { + if (e instanceof QueryFailedError) { + throw new HttpException( + Message.FAIL_DUPLICATE_ENTRY, + HttpStatus.CONFLICT, + ); + } else { + throw new HttpException( + Message.FAIL_INTERNAL_SERVER, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } @Get('count') @@ -36,11 +56,14 @@ export class NoticeLikeController { } @Get('status') - getStatus( + async getStatus( @Query('user_id') user_id: string, @Query('notice_id') notice_id: string, - ): boolean { - return this.noticeLikeService.findByUserIdAndNoticeId(user_id, notice_id) + ): Promise { + return (await this.noticeLikeService.findByUserIdAndNoticeId( + user_id, + notice_id, + )) ? true : false; } From bf9d38abed4124161c1326a458c7bf63d03617c8 Mon Sep 17 00:00:00 2001 From: Gwanho Kim Date: Wed, 24 Jul 2024 21:55:14 +0900 Subject: [PATCH 9/9] Remove inappropriate test --- .../noticeLike/noticeLike.controller.spec.ts | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/popo/noticeLike/noticeLike.controller.spec.ts b/src/popo/noticeLike/noticeLike.controller.spec.ts index 7370a3e..018f4af 100644 --- a/src/popo/noticeLike/noticeLike.controller.spec.ts +++ b/src/popo/noticeLike/noticeLike.controller.spec.ts @@ -6,7 +6,7 @@ import { NoticeLike } from './noticeLike.entity'; describe('NoticeLikeController', () => { let controller: NoticeLikeController; - let likeService: DeepMocked; + let noticeLikeService: DeepMocked; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -20,43 +20,43 @@ describe('NoticeLikeController', () => { }).compile(); controller = module.get(NoticeLikeController); - likeService = module.get(NoticeLikeService); + noticeLikeService = module.get(NoticeLikeService); }); it('should be defined', () => { expect(controller).toBeDefined(); - expect(likeService).toBeDefined(); + expect(noticeLikeService).toBeDefined(); }); it('should count likes', async () => { const result = 1; const noticeId = '1'; - likeService.countLikes.mockResolvedValue(result); + noticeLikeService.countLikes.mockResolvedValue(result); expect(await controller.countLikes(noticeId)).toBe(result); }); - // TODO: 테스트 DB를 만들어서 실제로 데이터를 DB에 넣고 findByUserIdAndNoticeId를 테스트하기 - // 해당 테스트는 service.spec으로 옮기기 - it('should get status', async () => { - const result = true; - const like: NoticeLike = { - id: 1, - user_id: '1', - notice_id: '1', - created_at: new Date(), - }; - const userId = '1'; - const noticeId = '1'; - likeService.findByUserIdAndNoticeId.mockResolvedValue(like); + // TODO: noticeLike.service.spec.ts로 테스트 옮기기 + // 테스트 DB를 만들어 실제로 데이터를 DB에 넣고 findByUserIdAndNoticeId를 테스트하기 + // it('should get status', async () => { + // const result = true; + // const like: Promise = { + // id: 1, + // user_id: '1', + // notice_id: '1', + // created_at: new Date(), + // }; + // const userId = '1'; + // const noticeId = '1'; + // noticeLikeService.findByUserIdAndNoticeId.mockResolvedValue(like); - await expect(controller.getStatus(userId, noticeId)).toBe(result); - }); + // await expect(controller.getStatus(userId, noticeId)).toBe(result); + // }); it('should throw error when delete if it is not liked', async () => { const userId = '1'; const noticeId = '1'; - likeService.findByUserIdAndNoticeId.mockResolvedValue(null); + noticeLikeService.findByUserIdAndNoticeId.mockResolvedValue(null); await expect(controller.delete(userId, noticeId)).rejects.toThrow(); }); @@ -71,8 +71,8 @@ describe('NoticeLikeController', () => { created_at: new Date(), }; const mockedDeletedResult = { affected: 1, raw: null }; - likeService.findByUserIdAndNoticeId.mockResolvedValue(like); - likeService.delete.mockResolvedValue(mockedDeletedResult); + noticeLikeService.findByUserIdAndNoticeId.mockResolvedValue(like); + noticeLikeService.delete.mockResolvedValue(mockedDeletedResult); expect(await controller.delete(userId, noticeId)).toBe(mockedDeletedResult); });