diff --git a/BE/src/answer/controller/answer.controller.spec.ts b/BE/src/answer/controller/answer.controller.spec.ts index 19b0a18..1dfebf7 100644 --- a/BE/src/answer/controller/answer.controller.spec.ts +++ b/BE/src/answer/controller/answer.controller.spec.ts @@ -362,4 +362,183 @@ describe('AnswerController 통합테스트', () => { .expect(404); }); }); + + describe('질문의 답변 조회', () => { + it('질문을 조회할 때 회원 정보가 없으면 모든 답변이 최신순으로 정렬된다. ', async () => { + //given + const member = await memberRepository.save(memberFixture); + const category = await categoryRepository.save( + Category.from(beCategoryFixture, member), + ); + + const question = await questionRepository.save( + Question.of(category, null, 'question'), + ); + const answer = await answerRepository.save( + Answer.of('test', member, question), + ); + for (let index = 1; index <= 10; index++) { + await answerRepository.save( + Answer.of(`test${index}`, member, question), + ); + } + + //when&then + const token = await authService.login(memberFixturesOAuthRequest); + const agent = request.agent(app.getHttpServer()); + await agent + .get(`/api/answer/${question.id}`) + .set('Cookie', [`accessToken=${token}`]) + .expect(200); + }); + + it('질문을 조회할 때 회원 정보가 있으면 모든 등록한 DefaultAnswer부터 정렬된다. ', async () => { + //given + const member = await memberRepository.save(memberFixture); + const category = await categoryRepository.save( + Category.from(beCategoryFixture, member), + ); + + const question = await questionRepository.save( + Question.of(category, null, 'question'), + ); + const answer = await answerRepository.save( + Answer.of('test', member, question), + ); + question.setDefaultAnswer(answer); + await questionRepository.save(question); + for (let index = 1; index <= 10; index++) { + await answerRepository.save( + Answer.of(`test${index}`, member, question), + ); + } + + //when&then + const token = await authService.login(memberFixturesOAuthRequest); + const agent = request.agent(app.getHttpServer()); + await agent + .get(`/api/answer/${question.id}`) + .set('Cookie', [`accessToken=${token}`]) + .expect(200) + .then((response) => console.log(response.body)); + }); + + it('존재하지 않는 질문의 id를 조회하면 404에러를 반환한다. ', async () => { + //given + const member = await memberRepository.save(memberFixture); + const category = await categoryRepository.save( + Category.from(beCategoryFixture, member), + ); + + const question = await questionRepository.save( + Question.of(category, null, 'question'), + ); + const answer = await answerRepository.save( + Answer.of('test', member, question), + ); + for (let index = 1; index <= 10; index++) { + await answerRepository.save( + Answer.of(`test${index}`, member, question), + ); + } + + //when&then + const token = await authService.login(memberFixturesOAuthRequest); + const agent = request.agent(app.getHttpServer()); + await agent + .get(`/api/answer/130998`) + .set('Cookie', [`accessToken=${token}`]) + .expect(404); + }); + }); + + describe('답변 삭제', () => { + it('답변을 삭제할 때 자신의 댓글을 삭제하려고 하면 204코드와 함께 성공한다. ', async () => { + //given + const member = await memberRepository.save(memberFixture); + const category = await categoryRepository.save( + Category.from(beCategoryFixture, member), + ); + + const question = await questionRepository.save( + Question.of(category, null, 'question'), + ); + const answer = await answerRepository.save( + Answer.of('test', member, question), + ); + + //when&then + const token = await authService.login(memberFixturesOAuthRequest); + const agent = request.agent(app.getHttpServer()); + await agent + .delete(`/api/answer/${answer.id}`) + .set('Cookie', [`accessToken=${token}`]) + .expect(204); + }); + + it('쿠키가 없으면 401에러를 반환한다.', async () => { + //given + const member = await memberRepository.save(memberFixture); + const category = await categoryRepository.save( + Category.from(beCategoryFixture, member), + ); + + const question = await questionRepository.save( + Question.of(category, null, 'question'), + ); + const answer = await answerRepository.save( + Answer.of('test', member, question), + ); + + //when&then + const agent = request.agent(app.getHttpServer()); + await agent.delete(`/api/answer/${answer.id}`).expect(401); + }); + + it('다른 사람의 답변을 삭제하면 403에러를 반환한다.', async () => { + //given + const member = await memberRepository.save(memberFixture); + const category = await categoryRepository.save( + Category.from(beCategoryFixture, member), + ); + + const question = await questionRepository.save( + Question.of(category, null, 'question'), + ); + const answer = await answerRepository.save( + Answer.of('test', member, question), + ); + + //when&then + const token = await authService.login(oauthRequestFixture); + const agent = request.agent(app.getHttpServer()); + await agent + .delete(`/api/answer/${answer.id}`) + .set('Cookie', [`accessToken=${token}`]) + .expect(403); + }); + + it('답변이 없으면 404에러를 반환한다.', async () => { + //given + const member = await memberRepository.save(memberFixture); + const category = await categoryRepository.save( + Category.from(beCategoryFixture, member), + ); + + const question = await questionRepository.save( + Question.of(category, null, 'question'), + ); + const answer = await answerRepository.save( + Answer.of('test', member, question), + ); + + //when&then + const token = await authService.login(oauthRequestFixture); + const agent = request.agent(app.getHttpServer()); + await agent + .delete(`/api/answer/${100000}`) + .set('Cookie', [`accessToken=${token}`]) + .expect(404); + }); + }); }); diff --git a/BE/src/answer/controller/answer.controller.ts b/BE/src/answer/controller/answer.controller.ts index 418f4ac..bb194ff 100644 --- a/BE/src/answer/controller/answer.controller.ts +++ b/BE/src/answer/controller/answer.controller.ts @@ -1,4 +1,14 @@ -import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common'; +import { + Body, + Controller, + Delete, + Get, + Param, + Post, + Req, + Res, + UseGuards, +} from '@nestjs/common'; import { ApiBody, ApiCookieAuth, @@ -8,12 +18,13 @@ import { } from '@nestjs/swagger'; import { AnswerService } from '../service/answer.service'; import { CreateAnswerRequest } from '../dto/createAnswerRequest'; -import { Request } from 'express'; +import { Request, Response } from 'express'; import { AuthGuard } from '@nestjs/passport'; import { createApiResponseOption } from '../../util/swagger.util'; import { Member } from '../../member/entity/member'; import { AnswerResponse } from '../dto/answerResponse'; import { DefaultAnswerRequest } from '../dto/defaultAnswerRequest'; +import { AnswerListResponse } from '../dto/answerListResponse'; @ApiTags('answer') @Controller('/api/answer') @@ -55,4 +66,36 @@ export class AnswerController { req.user as Member, ); } + + @Get(':questionId') + @ApiOperation({ + summary: '질문의 답변 리스트 반환', + }) + @ApiResponse( + createApiResponseOption( + 200, + '답변 리스트 캡슐화해 반환', + AnswerListResponse, + ), + ) + async getQuestionAnswers(@Param('questionId') questionId: number) { + const answerList = await this.answerService.getAnswerList(questionId); + return AnswerListResponse.of(answerList); + } + + @Delete(':answerId') + @UseGuards(AuthGuard('jwt')) + @ApiCookieAuth() + @ApiOperation({ + summary: '답변 삭제', + }) + @ApiResponse(createApiResponseOption(204, '답변 삭제 완료', null)) + async deleteAnswer( + @Param('answerId') answerId: number, + @Req() req: Request, + @Res() res: Response, + ) { + await this.answerService.deleteAnswer(answerId, req.user as Member); + res.status(204).send(); + } } diff --git a/BE/src/answer/dto/answerListResponse.ts b/BE/src/answer/dto/answerListResponse.ts new file mode 100644 index 0000000..dfbf9e6 --- /dev/null +++ b/BE/src/answer/dto/answerListResponse.ts @@ -0,0 +1,30 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { createPropertyOption } from '../../util/swagger.util'; +import { AnswerResponse } from './answerResponse'; + +export class AnswerListResponse { + @ApiProperty( + createPropertyOption( + [ + new AnswerResponse( + 1, + 'answerContent', + 1, + '이장희', + 'https://jangsarchive.tistory.com', + ), + ], + '답변 ID', + [AnswerResponse], + ), + ) + answerResponseList: AnswerResponse[]; + + constructor(answerResponseList: AnswerResponse[]) { + this.answerResponseList = answerResponseList; + } + + static of(answerResponseList: AnswerResponse[]) { + return new AnswerListResponse(answerResponseList); + } +} diff --git a/BE/src/answer/entity/answer.ts b/BE/src/answer/entity/answer.ts index 65347d3..421a458 100644 --- a/BE/src/answer/entity/answer.ts +++ b/BE/src/answer/entity/answer.ts @@ -8,7 +8,7 @@ export class Answer extends DefaultEntity { @Column() content: string; - @ManyToOne(() => Member, { onDelete: 'CASCADE' }) + @ManyToOne(() => Member, { onDelete: 'CASCADE', eager: true }) @JoinColumn() member: Member; diff --git a/BE/src/answer/exception/answer.exception.ts b/BE/src/answer/exception/answer.exception.ts index ea7df5a..f9e42e3 100644 --- a/BE/src/answer/exception/answer.exception.ts +++ b/BE/src/answer/exception/answer.exception.ts @@ -6,4 +6,10 @@ class AnswerNotFoundException extends HttpException { } } -export { AnswerNotFoundException }; +class AnswerForbiddenException extends HttpException { + constructor() { + super('답변에 대한 권한이 없습니다', 403); + } +} + +export { AnswerNotFoundException, AnswerForbiddenException }; diff --git a/BE/src/answer/fixture/answer.fixture.ts b/BE/src/answer/fixture/answer.fixture.ts index 5c45f16..56c6694 100644 --- a/BE/src/answer/fixture/answer.fixture.ts +++ b/BE/src/answer/fixture/answer.fixture.ts @@ -1,6 +1,6 @@ import { Answer } from '../entity/answer'; import { memberFixture } from '../../member/fixture/member.fixture'; -import { questionFixture } from '../../question/util/question.util'; +import { questionFixture } from '../../question/fixture/question.fixture'; import { CreateAnswerRequest } from '../dto/createAnswerRequest'; import { DefaultAnswerRequest } from '../dto/defaultAnswerRequest'; diff --git a/BE/src/answer/repository/answer.repository.ts b/BE/src/answer/repository/answer.repository.ts index 5174165..d008679 100644 --- a/BE/src/answer/repository/answer.repository.ts +++ b/BE/src/answer/repository/answer.repository.ts @@ -28,4 +28,18 @@ export class AnswerRepository { question: { id: questionId }, }); } + + async findAllByQuestionId(questionId: number) { + return this.repository + .createQueryBuilder('answer') + .leftJoinAndSelect('answer.member', 'member') + .leftJoinAndSelect('answer.question', 'question') + .where('question.id = :questionId', { questionId }) + .orderBy('answer.createdAt', 'DESC') + .getMany(); + } + + async remove(answer: Answer) { + await this.repository.remove(answer); + } } diff --git a/BE/src/answer/service/answer.service.spec.ts b/BE/src/answer/service/answer.service.spec.ts index f76bd7d..ba3a7a0 100644 --- a/BE/src/answer/service/answer.service.spec.ts +++ b/BE/src/answer/service/answer.service.spec.ts @@ -4,7 +4,7 @@ import { AnswerRepository } from '../repository/answer.repository'; import { QuestionRepository } from '../../question/repository/question.repository'; import { Answer } from '../entity/answer'; import { memberFixture } from '../../member/fixture/member.fixture'; -import { questionFixture } from '../../question/util/question.util'; +import { questionFixture } from '../../question/fixture/question.fixture'; import { CreateAnswerRequest } from '../dto/createAnswerRequest'; import { AnswerResponse } from '../dto/answerResponse'; import { QuestionNotFoundException } from '../../question/exception/question.exception'; @@ -26,7 +26,11 @@ import { defaultAnswerRequestFixture, } from '../fixture/answer.fixture'; import { DefaultAnswerRequest } from '../dto/defaultAnswerRequest'; -import { ForbiddenException } from '../../token/exception/token.exception'; +import { CategoryForbiddenException } from '../../category/exception/category.exception'; +import { + AnswerForbiddenException, + AnswerNotFoundException, +} from '../exception/answer.exception'; describe('AnswerService 단위 테스트', () => { let service: AnswerService; @@ -158,7 +162,7 @@ describe('AnswerService 단위 테스트', () => { //then await expect( service.setDefaultAnswer(defaultAnswerRequestFixture, memberFixture), - ).rejects.toThrow(new ForbiddenException()); + ).rejects.toThrow(new CategoryForbiddenException()); }); }); }); @@ -288,4 +292,158 @@ describe('AnswerService 통합테스트', () => { expect(updatedQuestion.defaultAnswer.id).toEqual(answer.id); }); }); + + describe('질문에 대한 답변들 조회', () => { + it('질문에 대한 모든 답변들이 반환된다.', async () => { + //given + const member = await memberRepository.save(memberFixture); + const member1 = await memberRepository.save( + new Member( + null, + 'ja@ja.com', + 'ja', + 'https://jangsarchive.tistory.com', + new Date(), + ), + ); + const category = await categoryRepository.save( + Category.from(categoryFixtureWithId, member), + ); + const question = await questionRepository.save( + Question.of(category, null, 'test'), + ); + for (let index = 1; index <= 10; index++) { + await answerRepository.save( + Answer.of(`test${index}`, member, question), + ); + await answerRepository.save( + Answer.of(`TEST${index}`, member1, question), + ); + } + + //when + + //then + const list = await answerService.getAnswerList(question.id); + expect(list.length).toEqual(20); + }); + + it('대표답변으로 설정하면 처음으로 온다.', async () => { + //given + const member = await memberRepository.save(memberFixture); + const category = await categoryRepository.save( + Category.from(categoryFixtureWithId, member), + ); + const question = await questionRepository.save( + Question.of(category, null, 'test'), + ); + for (let index = 1; index <= 10; index++) { + await answerRepository.save( + Answer.of(`test${index}`, member, question), + ); + } + const answer = await answerRepository.save( + Answer.of(`defaultAnswer`, member, question), + ); + question.setDefaultAnswer(answer); + await questionRepository.save(question); + + //when + + //then + const list = await answerService.getAnswerList(question.id); + expect(list[0].content).toEqual('defaultAnswer'); + }); + }); + + describe('답변 삭제', () => { + it('답변을 삭제할 때 대표답변이라면 답변을 삭제하고 게시물의 대표답변은 null이 된다.', async () => { + //given + const member = await memberRepository.save(memberFixture); + const category = await categoryRepository.save( + Category.from(categoryFixtureWithId, member), + ); + const question = await questionRepository.save( + Question.of(category, null, 'test'), + ); + const answer = await answerRepository.save( + Answer.of(`defaultAnswer`, member, question), + ); + question.setDefaultAnswer(answer); + await questionRepository.save(question); + + //when + + //then + await answerService.deleteAnswer(answer.id, member); + const afterDeleteQuestion = await questionRepository.findById( + question.id, + ); + expect(afterDeleteQuestion.defaultAnswer).toBeNull(); + }); + + it('답변을 삭제할 때 다른 사람의 답변을 삭제하면 AnswerForbiddenException을 반환한다.', async () => { + //given + const member = await memberRepository.save(memberFixture); + const member1 = await memberRepository.save( + new Member( + 100, + 'janghee@janghee.com', + 'janghee', + 'https://jangsarchive.tistory.com', + new Date(), + ), + ); + const category = await categoryRepository.save( + Category.from(categoryFixtureWithId, member), + ); + const question = await questionRepository.save( + Question.of(category, null, 'test'), + ); + const answer = await answerRepository.save( + Answer.of(`defaultAnswer`, member, question), + ); + question.setDefaultAnswer(answer); + await questionRepository.save(question); + + //when + + //then + await expect( + answerService.deleteAnswer(answer.id, member1), + ).rejects.toThrow(new AnswerForbiddenException()); + }); + + it('답변을 삭제할 때 다른 사람의 답변을 삭제하면 AnswerForbiddenException을 반환한다.', async () => { + //given + const member = await memberRepository.save(memberFixture); + const member1 = await memberRepository.save( + new Member( + 100, + 'janghee@janghee.com', + 'janghee', + 'https://jangsarchive.tistory.com', + new Date(), + ), + ); + const category = await categoryRepository.save( + Category.from(categoryFixtureWithId, member), + ); + const question = await questionRepository.save( + Question.of(category, null, 'test'), + ); + const answer = await answerRepository.save( + Answer.of(`defaultAnswer`, member, question), + ); + question.setDefaultAnswer(answer); + await questionRepository.save(question); + + //when + + //then + await expect(answerService.deleteAnswer(128135, member1)).rejects.toThrow( + new AnswerNotFoundException(), + ); + }); + }); }); diff --git a/BE/src/answer/service/answer.service.ts b/BE/src/answer/service/answer.service.ts index db1312c..f2a15a8 100644 --- a/BE/src/answer/service/answer.service.ts +++ b/BE/src/answer/service/answer.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@nestjs/common'; import { AnswerRepository } from '../repository/answer.repository'; import { QuestionRepository } from '../../question/repository/question.repository'; -import { QuestionNotFoundException } from '../../question/exception/question.exception'; import { isEmpty } from 'class-validator'; import { CreateAnswerRequest } from '../dto/createAnswerRequest'; import { Member } from '../../member/entity/member'; @@ -10,8 +9,10 @@ import { Answer } from '../entity/answer'; import { AnswerResponse } from '../dto/answerResponse'; import { DefaultAnswerRequest } from '../dto/defaultAnswerRequest'; import { CategoryRepository } from '../../category/repository/category.repository'; -import { AnswerNotFoundException } from '../exception/answer.exception'; import { CategoryForbiddenException } from '../../category/exception/category.exception'; +import { validateAnswer } from '../util/answer.util'; +import { validateQuestion } from '../../question/util/question.util'; +import { AnswerForbiddenException } from '../exception/answer.exception'; @Injectable() export class AnswerService { @@ -26,9 +27,7 @@ export class AnswerService { createAnswerRequest.questionId, ); - if (isEmpty(question)) { - throw new QuestionNotFoundException(); - } + validateQuestion(question); const answer = await this.saveAnswerAndQuestion( createAnswerRequest, @@ -45,9 +44,7 @@ export class AnswerService { const question = await this.questionRepository.findById( defaultAnswerRequest.questionId, ); - if (isEmpty(question)) { - throw new QuestionNotFoundException(); - } + validateQuestion(question); const category = await this.categoryRepository.findByCategoryId( question.category.id, @@ -61,14 +58,45 @@ export class AnswerService { defaultAnswerRequest.answerId, ); - if (isEmpty(answer)) { - throw new AnswerNotFoundException(); - } + validateAnswer(answer); question.setDefaultAnswer(answer); await this.questionRepository.save(question); } + async deleteAnswer(id: number, member: Member) { + const answer = await this.answerRepository.findById(id); + + validateAnswer(answer); + + if (answer.isOwnedBy(member)) { + await this.answerRepository.remove(answer); + return; + } + + throw new AnswerForbiddenException(); + } + + async getAnswerList(questionId: number) { + const question = await this.questionRepository.findById(questionId); + const originalQuestion = + await this.questionRepository.findWithOriginById(questionId); + + validateQuestion(originalQuestion); + + const answers = ( + await this.answerRepository.findAllByQuestionId(originalQuestion.id) + ).map((answer) => AnswerResponse.from(answer, answer.member)); + + if (question.defaultAnswer) { + answers.unshift( + AnswerResponse.from(question.defaultAnswer, question.category.member), + ); + } + + return answers; + } + private async saveAnswerAndQuestion( createAnswerRequest: CreateAnswerRequest, question: Question, diff --git a/BE/src/answer/util/answer.util.ts b/BE/src/answer/util/answer.util.ts new file mode 100644 index 0000000..f1aac37 --- /dev/null +++ b/BE/src/answer/util/answer.util.ts @@ -0,0 +1,9 @@ +import { isEmpty } from 'class-validator'; +import { AnswerNotFoundException } from '../exception/answer.exception'; +import { Answer } from '../entity/answer'; + +export const validateAnswer = (answer: Answer) => { + if (isEmpty(answer)) { + throw new AnswerNotFoundException(); + } +}; diff --git a/BE/src/category/util/category.util.ts b/BE/src/category/util/category.util.ts new file mode 100644 index 0000000..7ad9671 --- /dev/null +++ b/BE/src/category/util/category.util.ts @@ -0,0 +1,7 @@ +import { Category } from '../entity/category'; +import { isEmpty } from 'class-validator'; +import { CategoryNotFoundException } from '../exception/category.exception'; + +export const validateCategory = (category: Category) => { + if (isEmpty(category)) throw new CategoryNotFoundException(); +}; diff --git a/BE/src/question/controller/question.controller.spec.ts b/BE/src/question/controller/question.controller.spec.ts index dfe41d8..2f86c8c 100644 --- a/BE/src/question/controller/question.controller.spec.ts +++ b/BE/src/question/controller/question.controller.spec.ts @@ -6,7 +6,7 @@ import { QuestionResponse } from '../dto/questionResponse'; import { createQuestionRequestFixture, questionFixture, -} from '../util/question.util'; +} from '../fixture/question.fixture'; import { INestApplication, ValidationPipe } from '@nestjs/common'; import { TokenModule } from '../../token/token.module'; import { Member } from '../../member/entity/member'; diff --git a/BE/src/question/dto/questionResponseList.ts b/BE/src/question/dto/questionResponseList.ts index 0a49177..15fefe0 100644 --- a/BE/src/question/dto/questionResponseList.ts +++ b/BE/src/question/dto/questionResponseList.ts @@ -1,7 +1,7 @@ import { QuestionResponse } from './questionResponse'; import { ApiProperty } from '@nestjs/swagger'; import { createPropertyOption } from '../../util/swagger.util'; -import { questionFixture } from '../util/question.util'; +import { questionFixture } from '../fixture/question.fixture'; export class QuestionResponseList { @ApiProperty( diff --git a/BE/src/question/fixture/question.fixture.ts b/BE/src/question/fixture/question.fixture.ts new file mode 100644 index 0000000..588c332 --- /dev/null +++ b/BE/src/question/fixture/question.fixture.ts @@ -0,0 +1,17 @@ +import { Question } from '../entity/question'; +import { categoryFixtureWithId } from '../../category/fixture/category.fixture'; +import { CreateQuestionRequest } from '../dto/createQuestionRequest'; + +export const questionFixture = new Question( + 1, + 'tester', + categoryFixtureWithId, + null, + new Date(), + null, +); + +export const createQuestionRequestFixture = new CreateQuestionRequest( + categoryFixtureWithId.id, + 'tester', +); diff --git a/BE/src/question/service/question.service.spec.ts b/BE/src/question/service/question.service.spec.ts index f7a0fd4..66a1122 100644 --- a/BE/src/question/service/question.service.spec.ts +++ b/BE/src/question/service/question.service.spec.ts @@ -4,7 +4,7 @@ import { QuestionRepository } from '../repository/question.repository'; import { createQuestionRequestFixture, questionFixture, -} from '../util/question.util'; +} from '../fixture/question.fixture'; import { QuestionResponse } from '../dto/questionResponse'; import { CategoryRepository } from '../../category/repository/category.repository'; import { categoryFixtureWithId } from '../../category/fixture/category.fixture'; diff --git a/BE/src/question/service/question.service.ts b/BE/src/question/service/question.service.ts index 934c8fa..001f45d 100644 --- a/BE/src/question/service/question.service.ts +++ b/BE/src/question/service/question.service.ts @@ -3,16 +3,13 @@ import { QuestionRepository } from '../repository/question.repository'; import { CreateQuestionRequest } from '../dto/createQuestionRequest'; import { CategoryRepository } from '../../category/repository/category.repository'; import { isEmpty } from 'class-validator'; -import { CategoryNotFoundException } from '../../category/exception/category.exception'; import { Question } from '../entity/question'; -import { Category } from '../../category/entity/category'; import { QuestionResponse } from '../dto/questionResponse'; import { Member } from '../../member/entity/member'; -import { - NeedToFindByCategoryIdException, - QuestionNotFoundException, -} from '../exception/question.exception'; +import { NeedToFindByCategoryIdException } from '../exception/question.exception'; import { validateManipulatedToken } from '../../util/token.util'; +import { validateQuestion } from '../util/question.util'; +import { validateCategory } from '../../category/util/category.util'; @Injectable() export class QuestionService { @@ -29,7 +26,7 @@ export class QuestionService { createQuestionRequest.categoryId, ); - this.validateCategory(category); + validateCategory(category); if (!category.isOwnedBy(member)) { throw new UnauthorizedException(); @@ -57,9 +54,7 @@ export class QuestionService { const question = await this.questionRepository.findById(questionId); - if (isEmpty(question)) { - throw new QuestionNotFoundException(); - } + validateQuestion(question); await this.validateMembersCategoryById(question.category.id, member); @@ -72,16 +67,10 @@ export class QuestionService { ) { const category = await this.categoryRepository.findByCategoryId(categoryId); - this.validateCategory(category); + validateCategory(category); if (!category.isOwnedBy(member)) { throw new UnauthorizedException(); } } - - private validateCategory(category: Category) { - if (isEmpty(category)) { - throw new CategoryNotFoundException(); - } - } } diff --git a/BE/src/question/util/question.util.ts b/BE/src/question/util/question.util.ts index 588c332..0819b60 100644 --- a/BE/src/question/util/question.util.ts +++ b/BE/src/question/util/question.util.ts @@ -1,17 +1,9 @@ import { Question } from '../entity/question'; -import { categoryFixtureWithId } from '../../category/fixture/category.fixture'; -import { CreateQuestionRequest } from '../dto/createQuestionRequest'; +import { isEmpty } from 'class-validator'; +import { QuestionNotFoundException } from '../exception/question.exception'; -export const questionFixture = new Question( - 1, - 'tester', - categoryFixtureWithId, - null, - new Date(), - null, -); - -export const createQuestionRequestFixture = new CreateQuestionRequest( - categoryFixtureWithId.id, - 'tester', -); +export const validateQuestion = (question: Question) => { + if (isEmpty(question)) { + throw new QuestionNotFoundException(); + } +};