Skip to content

Commit 6595a87

Browse files
wlgh1553yu-yj215shl0501
authored
fix(be): toggle like (#39)
* feat: add unique keys in like table Co-authored-by: 유영재 <[email protected]> Co-authored-by: shl0501 <[email protected]> * fix: refactor like toggle logic Co-authored-by: 유영재 <[email protected]> Co-authored-by: shl0501 <[email protected]> * test: update toggle like test mocking Co-authored-by: 유영재 <[email protected]> Co-authored-by: shl0501 <[email protected]> --------- Co-authored-by: 유영재 <[email protected]> Co-authored-by: shl0501 <[email protected]>
1 parent b398005 commit 6595a87

File tree

7 files changed

+75
-15
lines changed

7 files changed

+75
-15
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
Warnings:
3+
4+
- A unique constraint covering the columns `[create_user_token,question_id]` on the table `QuestionLike` will be added. If there are existing duplicate values, this will fail.
5+
- A unique constraint covering the columns `[create_user_token,reply_id]` on the table `ReplyLike` will be added. If there are existing duplicate values, this will fail.
6+
7+
*/
8+
-- CreateIndex
9+
CREATE UNIQUE INDEX "QuestionLike_create_user_token_question_id_key" ON "QuestionLike"("create_user_token", "question_id");
10+
11+
-- CreateIndex
12+
CREATE UNIQUE INDEX "ReplyLike_create_user_token_reply_id_key" ON "ReplyLike"("create_user_token", "reply_id");
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
Warnings:
3+
4+
- A unique constraint covering the columns `[question_id,create_user_token]` on the table `QuestionLike` will be added. If there are existing duplicate values, this will fail.
5+
- A unique constraint covering the columns `[reply_id,create_user_token]` on the table `ReplyLike` will be added. If there are existing duplicate values, this will fail.
6+
7+
*/
8+
-- DropIndex
9+
DROP INDEX "QuestionLike_create_user_token_question_id_key";
10+
11+
-- DropIndex
12+
DROP INDEX "ReplyLike_create_user_token_reply_id_key";
13+
14+
-- CreateIndex
15+
CREATE INDEX "QuestionLike_question_id_create_user_token_idx" ON "QuestionLike"("question_id", "create_user_token");
16+
17+
-- CreateIndex
18+
CREATE UNIQUE INDEX "QuestionLike_question_id_create_user_token_key" ON "QuestionLike"("question_id", "create_user_token");
19+
20+
-- CreateIndex
21+
CREATE INDEX "ReplyLike_reply_id_create_user_token_idx" ON "ReplyLike"("reply_id", "create_user_token");
22+
23+
-- CreateIndex
24+
CREATE UNIQUE INDEX "ReplyLike_reply_id_create_user_token_key" ON "ReplyLike"("reply_id", "create_user_token");
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- DropIndex
2+
DROP INDEX "QuestionLike_question_id_create_user_token_idx";
3+
4+
-- DropIndex
5+
DROP INDEX "ReplyLike_reply_id_create_user_token_idx";

apps/server/prisma/schema.prisma

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ model QuestionLike {
9393
9494
question Question @relation("QuestionLikes", fields: [questionId], references: [questionId], onDelete: Cascade)
9595
createUserTokenEntity UserSessionToken @relation("TokenQuestionLikes", fields: [createUserToken], references: [token])
96+
97+
@@unique([questionId, createUserToken])
9698
}
9799

98100
model Reply {
@@ -117,6 +119,8 @@ model ReplyLike {
117119
118120
reply Reply @relation("ReplyLikes", fields: [replyId], references: [replyId], onDelete: Cascade)
119121
createUserTokenEntity UserSessionToken @relation("TokenReplyLikes", fields: [createUserToken], references: [token])
122+
123+
@@unique([replyId, createUserToken])
120124
}
121125

122126
model Chatting {

apps/server/src/questions/questions.repository.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,12 @@ export class QuestionsRepository {
124124
});
125125
}
126126

127-
async deleteLike(questionLikeId: number) {
128-
await this.prisma.questionLike.delete({
129-
where: { questionLikeId },
127+
async deleteLike(questionId: number, createUserToken: string) {
128+
await this.prisma.questionLike.deleteMany({
129+
where: {
130+
questionId,
131+
createUserToken,
132+
},
130133
});
131134
}
132135

apps/server/src/questions/questions.service.spec.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ForbiddenException } from '@nestjs/common';
22
import { Test, TestingModule } from '@nestjs/testing';
3+
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
34

45
import { QuestionsRepository } from './questions.repository';
56
import { QuestionsService } from './questions.service';
@@ -198,24 +199,24 @@ describe('QuestionsService', () => {
198199

199200
describe('toggleLike', () => {
200201
it('좋아요가 없을 경우 생성하고 liked: true를 반환해야 한다', async () => {
201-
questionsRepository.findLike.mockResolvedValue(null);
202-
203202
const result = await service.toggleLike(1, 'test-token');
204203

205204
expect(questionsRepository.createLike).toHaveBeenCalledWith(1, 'test-token');
206205
expect(result).toEqual({ liked: true });
207206
});
208207

209208
it('이미 좋아요가 있을 경우 삭제하고 liked: false를 반환해야 한다', async () => {
210-
questionsRepository.findLike.mockResolvedValue({
211-
questionLikeId: 1,
212-
createUserToken: 'test-token',
213-
questionId: 1,
214-
});
209+
questionsRepository.createLike.mockRejectedValueOnce(
210+
new PrismaClientKnownRequestError('Unique constraint violation', {
211+
code: 'P2002',
212+
clientVersion: '2.x.x',
213+
}),
214+
);
215215

216216
const result = await service.toggleLike(1, 'test-token');
217217

218-
expect(questionsRepository.deleteLike).toHaveBeenCalledWith(1);
218+
expect(questionsRepository.createLike).toHaveBeenCalledWith(1, 'test-token');
219+
expect(questionsRepository.deleteLike).toHaveBeenCalledWith(1, 'test-token');
219220
expect(result).toEqual({ liked: false });
220221
});
221222
});

apps/server/src/questions/questions.service.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ForbiddenException, Injectable, NotFoundException } from '@nestjs/common';
22
import { Question } from '@prisma/client';
3+
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
34

45
import { CreateQuestionDto } from './dto/create-question.dto';
56
import { GetQuestionDto } from './dto/get-question.dto';
@@ -15,6 +16,7 @@ import {
1516
import { RepliesRepository } from '@replies/replies.repository';
1617
import { SessionsRepository } from '@sessions/sessions.repository';
1718
import { SessionsAuthRepository } from '@sessions-auth/sessions-auth.repository';
19+
import { PRISMA_ERROR_CODE } from '@src/prisma/prisma.error';
1820

1921
@Injectable()
2022
export class QuestionsService {
@@ -172,10 +174,19 @@ export class QuestionsService {
172174
}
173175

174176
async toggleLike(questionId: number, createUserToken: string) {
175-
const exist = await this.questionRepository.findLike(questionId, createUserToken);
176-
if (exist) await this.questionRepository.deleteLike(exist.questionLikeId);
177-
else await this.questionRepository.createLike(questionId, createUserToken);
178-
return { liked: !exist };
177+
try {
178+
await this.questionRepository.createLike(questionId, createUserToken);
179+
return { liked: true };
180+
} catch (error) {
181+
if (
182+
error instanceof PrismaClientKnownRequestError &&
183+
error.code === PRISMA_ERROR_CODE.UNIQUE_CONSTRAINT_VIOLATION
184+
) {
185+
await this.questionRepository.deleteLike(questionId, createUserToken);
186+
return { liked: false };
187+
}
188+
throw error;
189+
}
179190
}
180191

181192
async getLikesCount(questionId: number) {

0 commit comments

Comments
 (0)