From a625d17802023dcaf7a3f306b04c6b5dfe9599ca Mon Sep 17 00:00:00 2001 From: java-saeng Date: Fri, 14 Jul 2023 18:00:54 +0900 Subject: [PATCH 01/35] =?UTF-8?q?feat=20:=20essayAnswer=20=EB=8F=99?= =?UTF-8?q?=EC=A0=81=20=EC=BF=BC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/EssayAnswerService.java | 67 +++++++++-- .../dto/EssayAnswerSearchRequest.java | 18 +++ .../repository/EssayAnswerRepository.java | 4 +- .../repository/EssayAnswerSpecification.java | 68 +++++++++++ .../roadmap/ui/EssayAnswerController.java | 15 ++- .../EssayAnswerSpecificationTest.java | 109 ++++++++++++++++++ 6 files changed, 272 insertions(+), 9 deletions(-) create mode 100644 backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerSearchRequest.java create mode 100644 backend/src/main/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerSpecification.java create mode 100644 backend/src/test/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerSpecificationTest.java diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/EssayAnswerService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/EssayAnswerService.java index dd53c30b8..4cbd10c33 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/EssayAnswerService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/EssayAnswerService.java @@ -1,19 +1,27 @@ package wooteco.prolog.roadmap.application; import java.util.List; +import java.util.stream.Collectors; import org.hibernate.Hibernate; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import wooteco.prolog.member.application.MemberService; import wooteco.prolog.member.domain.Member; import wooteco.prolog.roadmap.application.dto.EssayAnswerRequest; +import wooteco.prolog.roadmap.application.dto.EssayAnswerResponse; +import wooteco.prolog.roadmap.application.dto.EssayAnswerSearchRequest; +import wooteco.prolog.roadmap.domain.Curriculum; import wooteco.prolog.roadmap.domain.EssayAnswer; import wooteco.prolog.roadmap.domain.Quiz; +import wooteco.prolog.roadmap.domain.repository.CurriculumRepository; import wooteco.prolog.roadmap.domain.repository.EssayAnswerRepository; +import wooteco.prolog.roadmap.domain.repository.EssayAnswerSpecification; +import wooteco.prolog.roadmap.domain.repository.KeywordRepository; import wooteco.prolog.roadmap.domain.repository.QuizRepository; - -import javax.persistence.PersistenceContext; +import wooteco.prolog.session.domain.Session; +import wooteco.prolog.session.domain.repository.SessionRepository; @Transactional @Service @@ -22,14 +30,20 @@ public class EssayAnswerService { private final EssayAnswerRepository essayAnswerRepository; private final QuizRepository quizRepository; private final MemberService memberService; + private final CurriculumRepository curriculumRepository; + private final SessionRepository sessionRepository; - @Autowired - public EssayAnswerService(EssayAnswerRepository essayAnswerRepository, - QuizRepository quizRepository, - MemberService memberService) { + public EssayAnswerService( + EssayAnswerRepository essayAnswerRepository, + QuizRepository quizRepository, + MemberService memberService, + CurriculumRepository curriculumRepository, + SessionRepository sessionRepository) { this.essayAnswerRepository = essayAnswerRepository; this.quizRepository = quizRepository; this.memberService = memberService; + this.curriculumRepository = curriculumRepository; + this.sessionRepository = sessionRepository; } @Transactional @@ -82,4 +96,43 @@ public List findByQuizId(Long quizId) { return essayAnswers; } + + public List searchEssayAnswers( + final EssayAnswerSearchRequest request, + final Pageable pageable + ) { + + final Long curriculumId = request.getCurriculumId(); + + final Curriculum curriculum = curriculumRepository.findById(curriculumId) + .orElseThrow(() -> new IllegalArgumentException( + "해당 커리큘럼이 존재하지 않습니다. curriculumId = " + curriculumId)); + + final List sessionIds = sessionRepository.findAllByCurriculumId(curriculum.getId()) + .stream() + .map(Session::getId) + .collect(Collectors.toList()); + + if (sessionIds.isEmpty()) { + throw new IllegalArgumentException("세션이 비어있으면 안됨"); + } + + final Specification essayAnswers = EssayAnswerSpecification.equalsSessionIdsIn( + sessionIds) + .and(EssayAnswerSpecification.equalsKeywordId(request.getKeywordId())) + .and(EssayAnswerSpecification.inQuizIds(request.getQuizIds())) + .and(EssayAnswerSpecification.inMemberIds(request.getMemberIds())) + .and(EssayAnswerSpecification.orderByIdDesc()); + + // 1. 키워드 없음 (커리큘럼만) + // 2. 키워드 있음, 퀴즈 없음 + // 3. 키워드 있음, 퀴즈 있음 + // 4. 키워드 있음, 퀴즈 없음, 멤버 있음 + // 5. 키워드 있음, 퀴즈 없음, 멤버 없음 + // 6. 키워드 있음, 퀴즈 있음, 멤버 있음 + // 7. 키워드 있음, 퀴즈 있음, 멤버 없음 + + return null; + + } } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerSearchRequest.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerSearchRequest.java new file mode 100644 index 000000000..9be4dd4d8 --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerSearchRequest.java @@ -0,0 +1,18 @@ +package wooteco.prolog.roadmap.application.dto; + +import java.util.List; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class EssayAnswerSearchRequest { + + private Long curriculumId; + private Long keywordId; + private List quizIds; + private List memberIds; +} diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerRepository.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerRepository.java index 441dd2198..d72d1e474 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerRepository.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerRepository.java @@ -3,9 +3,11 @@ import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import wooteco.prolog.roadmap.domain.EssayAnswer; -public interface EssayAnswerRepository extends JpaRepository { +public interface EssayAnswerRepository extends JpaRepository, + JpaSpecificationExecutor { Optional findByIdAndMemberId(Long id, Long memberId); diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerSpecification.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerSpecification.java new file mode 100644 index 000000000..c939b79f1 --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerSpecification.java @@ -0,0 +1,68 @@ +package wooteco.prolog.roadmap.domain.repository; + +import java.util.List; +import javax.persistence.criteria.JoinType; +import org.springframework.data.jpa.domain.Specification; +import wooteco.prolog.roadmap.domain.EssayAnswer; + +public class EssayAnswerSpecification { + + private EssayAnswerSpecification() { + } + + public static Specification equalsSessionIdsIn(final List sessionIds) { + return (root, query, builder) -> { + if (sessionIds == null || sessionIds.isEmpty()) { + return builder.and(); + } + + return root.join("quiz", JoinType.INNER) + .join("keyword", JoinType.INNER) + .get("sessionId").in(sessionIds); + }; + } + + public static Specification equalsKeywordId(Long keywordId) { + return (root, query, builder) -> { + if (keywordId == null || keywordId == 0L) { + return builder.and(); + } + + return root.get("quiz").get("keyword").get("id").in(keywordId); + }; + } + + public static Specification inQuizIds(List quizIds) { + return (root, query, builder) -> { + if (quizIds == null || quizIds.isEmpty()) { + return builder.and(); + } + + return root.get("quiz").get("id").in(quizIds); + }; + } + + public static Specification inMemberIds(List memberIds) { + return (root, query, builder) -> { + if (memberIds == null || memberIds.isEmpty()) { + return builder.and(); + } + + return root.join("member", JoinType.LEFT).get("id").in(memberIds); + }; + } + + public static Specification orderByIdDesc() { + return (root, query, builder) -> { + query.orderBy(builder.desc(root.get("id"))); + return null; + }; + } + + public static Specification distinct(final boolean distinct) { + return (root, query, builder) -> { + query.distinct(distinct); + return null; + }; + } +} diff --git a/backend/src/main/java/wooteco/prolog/roadmap/ui/EssayAnswerController.java b/backend/src/main/java/wooteco/prolog/roadmap/ui/EssayAnswerController.java index 5261095e5..7f9e86fc8 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/ui/EssayAnswerController.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/ui/EssayAnswerController.java @@ -1,6 +1,11 @@ package wooteco.prolog.roadmap.ui; +import static java.util.stream.Collectors.toList; + +import java.util.List; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import wooteco.prolog.login.domain.AuthMemberPrincipal; @@ -9,6 +14,7 @@ import wooteco.prolog.roadmap.application.QuizService; import wooteco.prolog.roadmap.application.dto.EssayAnswerRequest; import wooteco.prolog.roadmap.application.dto.EssayAnswerResponse; +import wooteco.prolog.roadmap.application.dto.EssayAnswerSearchRequest; import wooteco.prolog.roadmap.application.dto.EssayAnswerUpdateRequest; import wooteco.prolog.roadmap.application.dto.QuizResponse; import wooteco.prolog.roadmap.domain.EssayAnswer; @@ -38,6 +44,13 @@ public ResponseEntity create(@RequestBody EssayAnswerRequest request, return ResponseEntity.ok(essayAnswerService.createEssayAnswer(request, member.getId())); } + @GetMapping("/essay-answers") + public ResponseEntity> search( + EssayAnswerSearchRequest request, + @PageableDefault Pageable pageable) { + return ResponseEntity.ok(essayAnswerService.searchEssayAnswers(request, pageable)); + } + @GetMapping("/essay-answers/{essayAnswerId}") public ResponseEntity findById(@PathVariable Long essayAnswerId) { EssayAnswer essayAnswer = essayAnswerService.getById(essayAnswerId); @@ -52,7 +65,7 @@ public ResponseEntity updateById(@PathVariable Long essayAnswerId, essayAnswerService.updateEssayAnswer(essayAnswerId, request.getAnswer(), member.getId()); return ResponseEntity.ok().build(); } - + @DeleteMapping("/essay-answers/{essayAnswerId}") public ResponseEntity deleteEssayAnswerById(@PathVariable Long essayAnswerId, @AuthMemberPrincipal LoginMember member) { diff --git a/backend/src/test/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerSpecificationTest.java b/backend/src/test/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerSpecificationTest.java new file mode 100644 index 000000000..f3ea74a5e --- /dev/null +++ b/backend/src/test/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerSpecificationTest.java @@ -0,0 +1,109 @@ +package wooteco.prolog.roadmap.domain.repository; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.jpa.domain.Specification; +import wooteco.prolog.member.domain.Member; +import wooteco.prolog.member.domain.Role; +import wooteco.prolog.member.domain.repository.MemberRepository; +import wooteco.prolog.roadmap.domain.Curriculum; +import wooteco.prolog.roadmap.domain.EssayAnswer; +import wooteco.prolog.roadmap.domain.Keyword; +import wooteco.prolog.roadmap.domain.Quiz; +import wooteco.prolog.session.domain.Session; +import wooteco.prolog.session.domain.repository.SessionRepository; + +@SpringBootTest +class EssayAnswerSpecificationTest { + + @Autowired + EssayAnswerRepository essayAnswerRepository; + + @Autowired + CurriculumRepository curriculumRepository; + + @Autowired + KeywordRepository keywordRepository; + + @Autowired + MemberRepository memberRepository; + + @Autowired + SessionRepository sessionRepository; + @Autowired + private QuizRepository quizRepository; + + @BeforeEach + void init() { + final Curriculum curriculum = curriculumRepository.save(new Curriculum("커리큘럼1")); + final Session session1 = sessionRepository.save(new Session(curriculum.getId(), "세션1")); + final Session session2 = sessionRepository.save(new Session(curriculum.getId(), "세션2")); + + final Keyword keyword1 = keywordRepository.save( + Keyword.createKeyword("자바1", "자바 설명1", 1, 5, session1.getId(), null)); + final Keyword keyword2 = keywordRepository.save( + Keyword.createKeyword("자바2", "자바 설명2", 2, 5, session1.getId(), null)); + final Keyword keyword3 = keywordRepository.save( + Keyword.createKeyword("자바3", "자바 설명3", 3, 5, session1.getId(), null)); + final Keyword keyword4 = keywordRepository.save( + Keyword.createKeyword("자바4", "자바 설명4", 4, 5, session2.getId(), null)); + + final Quiz quiz1 = quizRepository.save(new Quiz(keyword1, "퀴즈1")); + final Quiz quiz2 = quizRepository.save(new Quiz(keyword2, "퀴즈2")); + final Quiz quiz3 = quizRepository.save(new Quiz(keyword2, "퀴즈3")); + final Quiz quiz4 = quizRepository.save(new Quiz(keyword3, "퀴즈4")); + final Quiz quiz5 = quizRepository.save(new Quiz(keyword3, "퀴즈5")); + final Quiz quiz6 = quizRepository.save(new Quiz(keyword4, "퀴즈6")); + final Quiz quiz7 = quizRepository.save(new Quiz(keyword4, "퀴즈7")); + final Quiz quiz8 = quizRepository.save(new Quiz(keyword4, "퀴즈8")); + final Quiz quiz9 = quizRepository.save(new Quiz(keyword4, "퀴즈9")); + + Member member = new Member(11L, "username", "nickname", Role.CREW, 101L, "https://"); + + EssayAnswer essayAnswer1 = new EssayAnswer(quiz1, "대답1", member); + EssayAnswer essayAnswer2 = new EssayAnswer(quiz2, "대답2", member); + EssayAnswer essayAnswer3 = new EssayAnswer(quiz2, "대답3", member); + EssayAnswer essayAnswer4 = new EssayAnswer(quiz2, "대답4", member); + EssayAnswer essayAnswer5 = new EssayAnswer(quiz2, "대답5", member); + EssayAnswer essayAnswer6 = new EssayAnswer(quiz3, "대답6", member); + EssayAnswer essayAnswer7 = new EssayAnswer(quiz4, "대답7", member); + EssayAnswer essayAnswer8 = new EssayAnswer(quiz4, "대답8", member); + EssayAnswer essayAnswer9 = new EssayAnswer(quiz5, "대답9", member); + EssayAnswer essayAnswer10 = new EssayAnswer(quiz5, "대답10", member); + EssayAnswer essayAnswer11 = new EssayAnswer(quiz6, "대답11", member); + EssayAnswer essayAnswer12 = new EssayAnswer(quiz6, "대답12", member); + EssayAnswer essayAnswer13 = new EssayAnswer(quiz6, "대답13", member); + EssayAnswer essayAnswer14 = new EssayAnswer(quiz7, "대답14", member); + EssayAnswer essayAnswer15 = new EssayAnswer(quiz7, "대답15", member); + EssayAnswer essayAnswer16 = new EssayAnswer(quiz8, "대답16", member); + EssayAnswer essayAnswer17 = new EssayAnswer(quiz8, "대답17", member); + EssayAnswer essayAnswer18 = new EssayAnswer(quiz9, "대답18", member); + EssayAnswer essayAnswer19 = new EssayAnswer(quiz9, "대답19", member); + EssayAnswer essayAnswer20 = new EssayAnswer(quiz9, "대답20", member); + + essayAnswerRepository.saveAll( + Arrays.asList( + essayAnswer1, essayAnswer2, essayAnswer3, essayAnswer4, essayAnswer5, essayAnswer6, + essayAnswer7, essayAnswer8, essayAnswer9, essayAnswer10, essayAnswer11, + essayAnswer12, essayAnswer13, essayAnswer14, essayAnswer15, essayAnswer16, + essayAnswer17, essayAnswer18, essayAnswer19, essayAnswer20) + ); + } + + @Test + void aa() throws Exception { + //given + final Specification essayAnswerSpecification = EssayAnswerSpecification.equalsSessionIdsIn( + Arrays.asList(1L)); + + //when + final List all = essayAnswerRepository.findAll(essayAnswerSpecification); + + //then + System.out.println("all = " + all); + } +} From 7f3fdad36e0e8acb2707d1d53e089ddcde5a2d02 Mon Sep 17 00:00:00 2001 From: jiwon Date: Mon, 17 Jul 2023 15:25:03 +0900 Subject: [PATCH 02/35] =?UTF-8?q?feat:=20EssayAnswerService=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=EB=B0=98=ED=99=98=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/EssayAnswerService.java | 21 +++------ .../roadmap/ui/EssayAnswerController.java | 16 ++++--- .../application/dto/EssayAnswersResponse.java | 45 +++++++++++++++++++ 3 files changed, 62 insertions(+), 20 deletions(-) create mode 100644 backend/src/main/java/wooteco/prolog/studylog/application/dto/EssayAnswersResponse.java diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/EssayAnswerService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/EssayAnswerService.java index 4cbd10c33..562e8dde1 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/EssayAnswerService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/EssayAnswerService.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.stream.Collectors; import org.hibernate.Hibernate; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; @@ -10,7 +11,6 @@ import wooteco.prolog.member.application.MemberService; import wooteco.prolog.member.domain.Member; import wooteco.prolog.roadmap.application.dto.EssayAnswerRequest; -import wooteco.prolog.roadmap.application.dto.EssayAnswerResponse; import wooteco.prolog.roadmap.application.dto.EssayAnswerSearchRequest; import wooteco.prolog.roadmap.domain.Curriculum; import wooteco.prolog.roadmap.domain.EssayAnswer; @@ -18,10 +18,10 @@ import wooteco.prolog.roadmap.domain.repository.CurriculumRepository; import wooteco.prolog.roadmap.domain.repository.EssayAnswerRepository; import wooteco.prolog.roadmap.domain.repository.EssayAnswerSpecification; -import wooteco.prolog.roadmap.domain.repository.KeywordRepository; import wooteco.prolog.roadmap.domain.repository.QuizRepository; import wooteco.prolog.session.domain.Session; import wooteco.prolog.session.domain.repository.SessionRepository; +import wooteco.prolog.studylog.application.dto.EssayAnswersResponse; @Transactional @Service @@ -97,7 +97,7 @@ public List findByQuizId(Long quizId) { return essayAnswers; } - public List searchEssayAnswers( + public EssayAnswersResponse searchEssayAnswers( final EssayAnswerSearchRequest request, final Pageable pageable ) { @@ -117,22 +117,15 @@ public List searchEssayAnswers( throw new IllegalArgumentException("세션이 비어있으면 안됨"); } - final Specification essayAnswers = EssayAnswerSpecification.equalsSessionIdsIn( + final Specification essayAnswerSpecification = EssayAnswerSpecification.equalsSessionIdsIn( sessionIds) .and(EssayAnswerSpecification.equalsKeywordId(request.getKeywordId())) .and(EssayAnswerSpecification.inQuizIds(request.getQuizIds())) .and(EssayAnswerSpecification.inMemberIds(request.getMemberIds())) .and(EssayAnswerSpecification.orderByIdDesc()); - // 1. 키워드 없음 (커리큘럼만) - // 2. 키워드 있음, 퀴즈 없음 - // 3. 키워드 있음, 퀴즈 있음 - // 4. 키워드 있음, 퀴즈 없음, 멤버 있음 - // 5. 키워드 있음, 퀴즈 없음, 멤버 없음 - // 6. 키워드 있음, 퀴즈 있음, 멤버 있음 - // 7. 키워드 있음, 퀴즈 있음, 멤버 없음 - - return null; - + final Page essayAnswers = essayAnswerRepository.findAll(essayAnswerSpecification, + pageable); + return EssayAnswersResponse.of(essayAnswers); } } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/ui/EssayAnswerController.java b/backend/src/main/java/wooteco/prolog/roadmap/ui/EssayAnswerController.java index 7f9e86fc8..8f6d6e4db 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/ui/EssayAnswerController.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/ui/EssayAnswerController.java @@ -7,7 +7,14 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import wooteco.prolog.login.domain.AuthMemberPrincipal; import wooteco.prolog.login.ui.LoginMember; import wooteco.prolog.roadmap.application.EssayAnswerService; @@ -18,10 +25,7 @@ import wooteco.prolog.roadmap.application.dto.EssayAnswerUpdateRequest; import wooteco.prolog.roadmap.application.dto.QuizResponse; import wooteco.prolog.roadmap.domain.EssayAnswer; - -import java.util.List; - -import static java.util.stream.Collectors.toList; +import wooteco.prolog.studylog.application.dto.EssayAnswersResponse; @RestController @RequestMapping @@ -45,7 +49,7 @@ public ResponseEntity create(@RequestBody EssayAnswerRequest request, } @GetMapping("/essay-answers") - public ResponseEntity> search( + public ResponseEntity search( EssayAnswerSearchRequest request, @PageableDefault Pageable pageable) { return ResponseEntity.ok(essayAnswerService.searchEssayAnswers(request, pageable)); diff --git a/backend/src/main/java/wooteco/prolog/studylog/application/dto/EssayAnswersResponse.java b/backend/src/main/java/wooteco/prolog/studylog/application/dto/EssayAnswersResponse.java new file mode 100644 index 000000000..2157e649a --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/studylog/application/dto/EssayAnswersResponse.java @@ -0,0 +1,45 @@ +package wooteco.prolog.studylog.application.dto; + + +import static java.util.stream.Collectors.toList; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import wooteco.prolog.roadmap.application.dto.EssayAnswerResponse; +import wooteco.prolog.roadmap.domain.EssayAnswer; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +public class EssayAnswersResponse { + + private static final int ONE_INDEXED_PARAMETER = 1; + + private List data; + private Long totalSize; + private int totalPage; + private int currPage; + + public static EssayAnswersResponse of(Page page) { + Page responsePage = new PageImpl<>( + toResponses(page.getContent()), + page.getPageable(), + page.getTotalElements() + ); + + return new EssayAnswersResponse(responsePage.getContent(), + responsePage.getTotalElements(), + responsePage.getTotalPages(), + responsePage.getNumber() + ONE_INDEXED_PARAMETER); + } + + private static List toResponses(List essayAnswers) { + return essayAnswers.stream() + .map(EssayAnswerResponse::of) + .collect(toList()); + } +} From 3277ee1a2f33a87cc139f12fe25e5f7a75b01821 Mon Sep 17 00:00:00 2001 From: jiwon Date: Mon, 17 Jul 2023 15:27:16 +0900 Subject: [PATCH 03/35] =?UTF-8?q?test:=20EssayAnswerService=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=9D=98=20=EB=8D=94?= =?UTF-8?q?=EB=AF=B8=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/EssayAnswerSearchRequest.java | 2 + .../application/EssayAnswerSearchTest.java | 120 ++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerSearchRequest.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerSearchRequest.java index 9be4dd4d8..289dd382f 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerSearchRequest.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerSearchRequest.java @@ -2,6 +2,7 @@ import java.util.List; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -9,6 +10,7 @@ @Setter @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor public class EssayAnswerSearchRequest { private Long curriculumId; diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java new file mode 100644 index 000000000..63665e4b9 --- /dev/null +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java @@ -0,0 +1,120 @@ +package wooteco.prolog.roadmap.application; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestConstructor; +import org.springframework.test.context.TestConstructor.AutowireMode; +import wooteco.prolog.member.domain.Member; +import wooteco.prolog.member.domain.Role; +import wooteco.prolog.roadmap.application.dto.EssayAnswerRequest; +import wooteco.prolog.roadmap.application.dto.EssayAnswerSearchRequest; +import wooteco.prolog.roadmap.domain.Curriculum; +import wooteco.prolog.roadmap.domain.Keyword; +import wooteco.prolog.roadmap.domain.Quiz; +import wooteco.prolog.roadmap.domain.repository.CurriculumRepository; +import wooteco.prolog.roadmap.domain.repository.KeywordRepository; +import wooteco.prolog.roadmap.domain.repository.QuizRepository; +import wooteco.prolog.session.domain.Session; +import wooteco.prolog.session.domain.repository.SessionRepository; + +@SpringBootTest +@TestConstructor(autowireMode = AutowireMode.ALL) +public class EssayAnswerSearchTest { + + private final CurriculumRepository curriculumRepository; + private final SessionRepository sessionRepository; + private final KeywordRepository keywordRepository; + private final QuizRepository quizRepository; + private final EssayAnswerService essayAnswerService; + + public EssayAnswerSearchTest(CurriculumRepository curriculumRepository, + SessionRepository sessionRepository, + KeywordRepository keywordRepository, QuizRepository quizRepository, + EssayAnswerService essayAnswerService) { + this.curriculumRepository = curriculumRepository; + this.sessionRepository = sessionRepository; + this.keywordRepository = keywordRepository; + this.quizRepository = quizRepository; + this.essayAnswerService = essayAnswerService; + } + + private Curriculum curriculum; + private Session session1, session2; + private Keyword keyword1, keyword2, keyword3, keyword4; + private Quiz quiz1, quiz2, quiz3, quiz4, quiz5, quiz6, quiz7, quiz8, quiz9; + private Member member; + private Long essayAnswerId1, essayAnswerId2, essayAnswerId3, essayAnswerId4, essayAnswerId5, + essayAnswerId6, essayAnswerId7, essayAnswerId8, essayAnswerId9, essayAnswerId10, + essayAnswerId11, essayAnswerId12, essayAnswerId13, essayAnswerId14, essayAnswerId15, + essayAnswerId16, essayAnswerId17, essayAnswerId18, essayAnswerId19, essayAnswerId20; + + @BeforeEach + void init() { + curriculum = curriculumRepository.save(new Curriculum("커리큘럼1")); + session1 = sessionRepository.save(new Session(curriculum.getId(), "세션1")); + session2 = sessionRepository.save(new Session(curriculum.getId(), "세션2")); + + keyword1 = Keyword.createKeyword("자바", "자바 설명", 1, 5, session1.getId(), null); + keyword2 = Keyword.createKeyword("키워드", "키워드 설명", 2, 5, session2.getId(), null); + keyword3 = keywordRepository.save( + Keyword.createKeyword("자바3", "자바 설명3", 3, 5, session1.getId(), null)); + keyword4 = keywordRepository.save( + Keyword.createKeyword("자바4", "자바 설명4", 4, 5, session2.getId(), null)); + + quiz1 = quizRepository.save(new Quiz(keyword1, "퀴즈1")); + quiz2 = quizRepository.save(new Quiz(keyword2, "퀴즈2")); + quiz3 = quizRepository.save(new Quiz(keyword2, "퀴즈3")); + quiz4 = quizRepository.save(new Quiz(keyword3, "퀴즈4")); + quiz5 = quizRepository.save(new Quiz(keyword3, "퀴즈5")); + quiz6 = quizRepository.save(new Quiz(keyword4, "퀴즈6")); + quiz7 = quizRepository.save(new Quiz(keyword4, "퀴즈7")); + quiz8 = quizRepository.save(new Quiz(keyword4, "퀴즈8")); + quiz9 = quizRepository.save(new Quiz(keyword4, "퀴즈9")); + + member = new Member(11L, "username", "nickname", Role.CREW, 101L, "https://"); + + final EssayAnswerRequest essayAnswer1 = new EssayAnswerRequest(quiz1.getId(), "대답1"); + final EssayAnswerRequest essayAnswer2 = new EssayAnswerRequest(quiz2.getId(), "대답2"); + final EssayAnswerRequest essayAnswer3 = new EssayAnswerRequest(quiz2.getId(), "대답3"); + final EssayAnswerRequest essayAnswer4 = new EssayAnswerRequest(quiz2.getId(), "대답4"); + final EssayAnswerRequest essayAnswer5 = new EssayAnswerRequest(quiz2.getId(), "대답5"); + final EssayAnswerRequest essayAnswer6 = new EssayAnswerRequest(quiz3.getId(), "대답6"); + final EssayAnswerRequest essayAnswer7 = new EssayAnswerRequest(quiz4.getId(), "대답7"); + final EssayAnswerRequest essayAnswer8 = new EssayAnswerRequest(quiz4.getId(), "대답8"); + final EssayAnswerRequest essayAnswer9 = new EssayAnswerRequest(quiz5.getId(), "대답9"); + final EssayAnswerRequest essayAnswer10 = new EssayAnswerRequest(quiz5.getId(), "대답10"); + final EssayAnswerRequest essayAnswer11 = new EssayAnswerRequest(quiz6.getId(), "대답11"); + final EssayAnswerRequest essayAnswer12 = new EssayAnswerRequest(quiz6.getId(), "대답12"); + final EssayAnswerRequest essayAnswer13 = new EssayAnswerRequest(quiz6.getId(), "대답13"); + final EssayAnswerRequest essayAnswer14 = new EssayAnswerRequest(quiz7.getId(), "대답14"); + final EssayAnswerRequest essayAnswer15 = new EssayAnswerRequest(quiz7.getId(), "대답15"); + final EssayAnswerRequest essayAnswer16 = new EssayAnswerRequest(quiz8.getId(), "대답16"); + final EssayAnswerRequest essayAnswer17 = new EssayAnswerRequest(quiz8.getId(), "대답17"); + final EssayAnswerRequest essayAnswer18 = new EssayAnswerRequest(quiz9.getId(), "대답18"); + final EssayAnswerRequest essayAnswer19 = new EssayAnswerRequest(quiz9.getId(), "대답19"); + final EssayAnswerRequest essayAnswer20 = new EssayAnswerRequest(quiz9.getId(), "대답20"); + + essayAnswerId1 = essayAnswerService.createEssayAnswer(essayAnswer1, member.getId()); + essayAnswerId2 = essayAnswerService.createEssayAnswer(essayAnswer2, member.getId()); + essayAnswerId3 = essayAnswerService.createEssayAnswer(essayAnswer3, member.getId()); + essayAnswerId4 = essayAnswerService.createEssayAnswer(essayAnswer4, member.getId()); + essayAnswerId5 = essayAnswerService.createEssayAnswer(essayAnswer5, member.getId()); + essayAnswerId6 = essayAnswerService.createEssayAnswer(essayAnswer6, member.getId()); + essayAnswerId7 = essayAnswerService.createEssayAnswer(essayAnswer7, member.getId()); + essayAnswerId8 = essayAnswerService.createEssayAnswer(essayAnswer8, member.getId()); + essayAnswerId9 = essayAnswerService.createEssayAnswer(essayAnswer9, member.getId()); + essayAnswerId10 = essayAnswerService.createEssayAnswer(essayAnswer10, member.getId()); + essayAnswerId11 = essayAnswerService.createEssayAnswer(essayAnswer11, member.getId()); + essayAnswerId12 = essayAnswerService.createEssayAnswer(essayAnswer12, member.getId()); + essayAnswerId13 = essayAnswerService.createEssayAnswer(essayAnswer13, member.getId()); + essayAnswerId14 = essayAnswerService.createEssayAnswer(essayAnswer14, member.getId()); + essayAnswerId15 = essayAnswerService.createEssayAnswer(essayAnswer15, member.getId()); + essayAnswerId16 = essayAnswerService.createEssayAnswer(essayAnswer16, member.getId()); + essayAnswerId17 = essayAnswerService.createEssayAnswer(essayAnswer17, member.getId()); + essayAnswerId18 = essayAnswerService.createEssayAnswer(essayAnswer18, member.getId()); + essayAnswerId19 = essayAnswerService.createEssayAnswer(essayAnswer19, member.getId()); + essayAnswerId20 = essayAnswerService.createEssayAnswer(essayAnswer20, member.getId()); + } +} From 7fc1e36ec3d649ae6f51ca83c032d562038904b9 Mon Sep 17 00:00:00 2001 From: jiwon Date: Mon, 17 Jul 2023 17:54:52 +0900 Subject: [PATCH 04/35] =?UTF-8?q?test:=20=EB=A1=9C=EB=93=9C=EB=A7=B5=20?= =?UTF-8?q?=EB=8B=B5=EB=B3=80=20=EB=AA=A9=EB=A1=9D=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/EssayAnswerResponse.java | 2 + .../application/EssayAnswerSearchTest.java | 187 ++++++++++++++---- 2 files changed, 156 insertions(+), 33 deletions(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerResponse.java index 5cd670803..3480ba7d9 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerResponse.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerResponse.java @@ -2,11 +2,13 @@ import java.time.LocalDateTime; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import wooteco.prolog.member.application.dto.MemberResponse; import wooteco.prolog.roadmap.domain.EssayAnswer; +@AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PRIVATE) @Getter public class EssayAnswerResponse { diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java index 63665e4b9..cd9bcfe99 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java @@ -1,15 +1,26 @@ package wooteco.prolog.roadmap.application; +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.PageRequest; import org.springframework.test.context.TestConstructor; import org.springframework.test.context.TestConstructor.AutowireMode; +import org.springframework.transaction.annotation.Transactional; +import wooteco.prolog.member.application.dto.MemberResponse; import wooteco.prolog.member.domain.Member; import wooteco.prolog.member.domain.Role; +import wooteco.prolog.member.domain.repository.MemberRepository; import wooteco.prolog.roadmap.application.dto.EssayAnswerRequest; +import wooteco.prolog.roadmap.application.dto.EssayAnswerResponse; import wooteco.prolog.roadmap.application.dto.EssayAnswerSearchRequest; +import wooteco.prolog.roadmap.application.dto.QuizResponse; import wooteco.prolog.roadmap.domain.Curriculum; import wooteco.prolog.roadmap.domain.Keyword; import wooteco.prolog.roadmap.domain.Quiz; @@ -18,33 +29,37 @@ import wooteco.prolog.roadmap.domain.repository.QuizRepository; import wooteco.prolog.session.domain.Session; import wooteco.prolog.session.domain.repository.SessionRepository; +import wooteco.prolog.studylog.application.dto.EssayAnswersResponse; @SpringBootTest +@Transactional @TestConstructor(autowireMode = AutowireMode.ALL) public class EssayAnswerSearchTest { - private final CurriculumRepository curriculumRepository; - private final SessionRepository sessionRepository; - private final KeywordRepository keywordRepository; - private final QuizRepository quizRepository; - private final EssayAnswerService essayAnswerService; + private CurriculumRepository curriculumRepository; + private SessionRepository sessionRepository; + private KeywordRepository keywordRepository; + private QuizRepository quizRepository; + private MemberRepository memberRepository; + private EssayAnswerService essayAnswerService; public EssayAnswerSearchTest(CurriculumRepository curriculumRepository, SessionRepository sessionRepository, - KeywordRepository keywordRepository, QuizRepository quizRepository, + KeywordRepository keywordRepository, + QuizRepository quizRepository, MemberRepository memberRepository, EssayAnswerService essayAnswerService) { this.curriculumRepository = curriculumRepository; this.sessionRepository = sessionRepository; this.keywordRepository = keywordRepository; this.quizRepository = quizRepository; + this.memberRepository = memberRepository; this.essayAnswerService = essayAnswerService; } private Curriculum curriculum; - private Session session1, session2; private Keyword keyword1, keyword2, keyword3, keyword4; private Quiz quiz1, quiz2, quiz3, quiz4, quiz5, quiz6, quiz7, quiz8, quiz9; - private Member member; + private Member member1, member2, member3, member4; private Long essayAnswerId1, essayAnswerId2, essayAnswerId3, essayAnswerId4, essayAnswerId5, essayAnswerId6, essayAnswerId7, essayAnswerId8, essayAnswerId9, essayAnswerId10, essayAnswerId11, essayAnswerId12, essayAnswerId13, essayAnswerId14, essayAnswerId15, @@ -53,11 +68,13 @@ public EssayAnswerSearchTest(CurriculumRepository curriculumRepository, @BeforeEach void init() { curriculum = curriculumRepository.save(new Curriculum("커리큘럼1")); - session1 = sessionRepository.save(new Session(curriculum.getId(), "세션1")); - session2 = sessionRepository.save(new Session(curriculum.getId(), "세션2")); + Session session1 = sessionRepository.save(new Session(curriculum.getId(), "세션1")); + Session session2 = sessionRepository.save(new Session(curriculum.getId(), "세션2")); - keyword1 = Keyword.createKeyword("자바", "자바 설명", 1, 5, session1.getId(), null); - keyword2 = Keyword.createKeyword("키워드", "키워드 설명", 2, 5, session2.getId(), null); + keyword1 = keywordRepository.save( + Keyword.createKeyword("자바", "자바 설명", 1, 5, session1.getId(), null)); + keyword2 = keywordRepository.save( + Keyword.createKeyword("키워드", "키워드 설명", 2, 5, session2.getId(), null)); keyword3 = keywordRepository.save( Keyword.createKeyword("자바3", "자바 설명3", 3, 5, session1.getId(), null)); keyword4 = keywordRepository.save( @@ -73,7 +90,14 @@ void init() { quiz8 = quizRepository.save(new Quiz(keyword4, "퀴즈8")); quiz9 = quizRepository.save(new Quiz(keyword4, "퀴즈9")); - member = new Member(11L, "username", "nickname", Role.CREW, 101L, "https://"); + member1 = memberRepository.save( + new Member(11L, "username1", "nickname1", Role.CREW, 111L, "https://")); + member2 = memberRepository.save( + new Member(12L, "username2", "nickname2", Role.CREW, 112L, "https://")); + member3 = memberRepository.save( + new Member(13L, "username3", "nickname3", Role.CREW, 113L, "https://")); + member4 = memberRepository.save( + new Member(14L, "username4", "nickname4", Role.CREW, 115L, "https://")); final EssayAnswerRequest essayAnswer1 = new EssayAnswerRequest(quiz1.getId(), "대답1"); final EssayAnswerRequest essayAnswer2 = new EssayAnswerRequest(quiz2.getId(), "대답2"); @@ -96,25 +120,122 @@ void init() { final EssayAnswerRequest essayAnswer19 = new EssayAnswerRequest(quiz9.getId(), "대답19"); final EssayAnswerRequest essayAnswer20 = new EssayAnswerRequest(quiz9.getId(), "대답20"); - essayAnswerId1 = essayAnswerService.createEssayAnswer(essayAnswer1, member.getId()); - essayAnswerId2 = essayAnswerService.createEssayAnswer(essayAnswer2, member.getId()); - essayAnswerId3 = essayAnswerService.createEssayAnswer(essayAnswer3, member.getId()); - essayAnswerId4 = essayAnswerService.createEssayAnswer(essayAnswer4, member.getId()); - essayAnswerId5 = essayAnswerService.createEssayAnswer(essayAnswer5, member.getId()); - essayAnswerId6 = essayAnswerService.createEssayAnswer(essayAnswer6, member.getId()); - essayAnswerId7 = essayAnswerService.createEssayAnswer(essayAnswer7, member.getId()); - essayAnswerId8 = essayAnswerService.createEssayAnswer(essayAnswer8, member.getId()); - essayAnswerId9 = essayAnswerService.createEssayAnswer(essayAnswer9, member.getId()); - essayAnswerId10 = essayAnswerService.createEssayAnswer(essayAnswer10, member.getId()); - essayAnswerId11 = essayAnswerService.createEssayAnswer(essayAnswer11, member.getId()); - essayAnswerId12 = essayAnswerService.createEssayAnswer(essayAnswer12, member.getId()); - essayAnswerId13 = essayAnswerService.createEssayAnswer(essayAnswer13, member.getId()); - essayAnswerId14 = essayAnswerService.createEssayAnswer(essayAnswer14, member.getId()); - essayAnswerId15 = essayAnswerService.createEssayAnswer(essayAnswer15, member.getId()); - essayAnswerId16 = essayAnswerService.createEssayAnswer(essayAnswer16, member.getId()); - essayAnswerId17 = essayAnswerService.createEssayAnswer(essayAnswer17, member.getId()); - essayAnswerId18 = essayAnswerService.createEssayAnswer(essayAnswer18, member.getId()); - essayAnswerId19 = essayAnswerService.createEssayAnswer(essayAnswer19, member.getId()); - essayAnswerId20 = essayAnswerService.createEssayAnswer(essayAnswer20, member.getId()); + essayAnswerId1 = essayAnswerService.createEssayAnswer(essayAnswer1, member1.getId()); + essayAnswerId2 = essayAnswerService.createEssayAnswer(essayAnswer2, member1.getId()); + essayAnswerId3 = essayAnswerService.createEssayAnswer(essayAnswer3, member2.getId()); + essayAnswerId4 = essayAnswerService.createEssayAnswer(essayAnswer4, member3.getId()); + essayAnswerId5 = essayAnswerService.createEssayAnswer(essayAnswer5, member4.getId()); + essayAnswerId6 = essayAnswerService.createEssayAnswer(essayAnswer6, member1.getId()); + essayAnswerId7 = essayAnswerService.createEssayAnswer(essayAnswer7, member1.getId()); + essayAnswerId8 = essayAnswerService.createEssayAnswer(essayAnswer8, member2.getId()); + essayAnswerId9 = essayAnswerService.createEssayAnswer(essayAnswer9, member1.getId()); + essayAnswerId10 = essayAnswerService.createEssayAnswer(essayAnswer10, member2.getId()); + essayAnswerId11 = essayAnswerService.createEssayAnswer(essayAnswer11, member1.getId()); + essayAnswerId12 = essayAnswerService.createEssayAnswer(essayAnswer12, member2.getId()); + essayAnswerId13 = essayAnswerService.createEssayAnswer(essayAnswer13, member3.getId()); + essayAnswerId14 = essayAnswerService.createEssayAnswer(essayAnswer14, member1.getId()); + essayAnswerId15 = essayAnswerService.createEssayAnswer(essayAnswer15, member2.getId()); + essayAnswerId16 = essayAnswerService.createEssayAnswer(essayAnswer16, member1.getId()); + essayAnswerId17 = essayAnswerService.createEssayAnswer(essayAnswer17, member2.getId()); + essayAnswerId18 = essayAnswerService.createEssayAnswer(essayAnswer18, member1.getId()); + essayAnswerId19 = essayAnswerService.createEssayAnswer(essayAnswer19, member2.getId()); + essayAnswerId20 = essayAnswerService.createEssayAnswer(essayAnswer20, member3.getId()); + } + + @Test + @DisplayName("커리큘럼 아이디를 받아 답변을 검색한다.") + void 커리큘럼_아이디를_받아서_답변을_검색한다() { + // given + final EssayAnswerSearchRequest 답변_검색_요청 = new EssayAnswerSearchRequest( + curriculum.getId(), null, null, null); + + // when + final EssayAnswersResponse 답변_응답들 = essayAnswerService.searchEssayAnswers( + 답변_검색_요청, PageRequest.of(0, 10)); + + // then + final List 예상_답변_응답_리스트 = Arrays.asList( + 예상_답변_응답(essayAnswerId20, quiz9, "대답20", member3), + 예상_답변_응답(essayAnswerId19, quiz9, "대답19", member2), + 예상_답변_응답(essayAnswerId18, quiz9, "대답18", member1), + 예상_답변_응답(essayAnswerId17, quiz8, "대답17", member2), + 예상_답변_응답(essayAnswerId16, quiz8, "대답16", member1), + 예상_답변_응답(essayAnswerId15, quiz7, "대답15", member2), + 예상_답변_응답(essayAnswerId14, quiz7, "대답14", member1), + 예상_답변_응답(essayAnswerId13, quiz6, "대답13", member3), + 예상_답변_응답(essayAnswerId12, quiz6, "대답12", member2), + 예상_답변_응답(essayAnswerId11, quiz6, "대답11", member1) + ); + final EssayAnswersResponse 예상되는_답변_응답들 = new EssayAnswersResponse(예상_답변_응답_리스트, 20L, 2, 1); + + assertThat(답변_응답들) + .usingRecursiveComparison() + .ignoringFields("data.createdAt", "data.updatedAt") + .isEqualTo(예상되는_답변_응답들); + } + + @Test + @DisplayName("커리큘럼 아이디와 키워드 아이디를 받아서 답변을 검색한다.") + void 커리큘럼_아이디와_키워드_아이디를_받아서_답변을_검색한다() { + // given + final EssayAnswerSearchRequest 답변_검색_요청 = new EssayAnswerSearchRequest( + curriculum.getId(), keyword3.getId(), null, null); + + // when + final EssayAnswersResponse 답변_응답들 = essayAnswerService.searchEssayAnswers( + 답변_검색_요청, PageRequest.of(0, 10)); + + // then + final List 예상_답변_응답_리스트 = Arrays.asList( + 예상_답변_응답(essayAnswerId10, quiz5, "대답10", member2), + 예상_답변_응답(essayAnswerId9, quiz5, "대답9", member1), + 예상_답변_응답(essayAnswerId8, quiz4, "대답8", member2), + 예상_답변_응답(essayAnswerId7, quiz4, "대답7", member1) + ); + + final EssayAnswersResponse 예상되는_답변_응답들 = new EssayAnswersResponse(예상_답변_응답_리스트, 4L, 1, 1); + + assertThat(답변_응답들) + .usingRecursiveComparison() + .ignoringFields("data.createdAt", "data.updatedAt") + .isEqualTo(예상되는_답변_응답들); + } + + @Test + @DisplayName("커리큘럼 아이디와 키워드 아이디와 퀴즈 아이디를 받아서 답변을 검색한다.") + void 커리큘럼_아이디와_키워드_아이디와_퀴즈_아이디를_받아서_답변을_검색한다() { + // given + final EssayAnswerSearchRequest 답변_검색_요청 = new EssayAnswerSearchRequest( + curriculum.getId(), keyword4.getId(), + Arrays.asList(quiz6.getId(), quiz9.getId()), null); + + // when + final EssayAnswersResponse 답변_응답들 = essayAnswerService.searchEssayAnswers( + 답변_검색_요청, PageRequest.of(0, 10)); + + // then + final List 예상_답변_응답_리스트 = Arrays.asList( + 예상_답변_응답(essayAnswerId20, quiz9, "대답20", member3), + 예상_답변_응답(essayAnswerId19, quiz9, "대답19", member2), + 예상_답변_응답(essayAnswerId18, quiz9, "대답18", member1), + 예상_답변_응답(essayAnswerId13, quiz6, "대답13", member3), + 예상_답변_응답(essayAnswerId12, quiz6, "대답12", member2), + 예상_답변_응답(essayAnswerId11, quiz6, "대답11", member1) + ); + + final EssayAnswersResponse 예상되는_답변_응답들 = new EssayAnswersResponse(예상_답변_응답_리스트, 6L, 1, 1); + + assertThat(답변_응답들) + .usingRecursiveComparison() + .ignoringFields("data.createdAt", "data.updatedAt") + .isEqualTo(예상되는_답변_응답들); + } + + private EssayAnswerResponse 예상_답변_응답(final Long essayAnswerId, final Quiz quiz, + final String answer, final Member member) { + return new EssayAnswerResponse(essayAnswerId, + new QuizResponse(quiz.getId(), quiz.getQuestion()), + answer, new MemberResponse(member.getId(), member.getUsername(), member.getNickname(), + Role.CREW, "https://"), LocalDateTime.now(), LocalDateTime.now()); } } From a671f3d7c566e097e4f998a7679d76fdb35ad6b2 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Wed, 19 Jul 2023 16:37:44 +0900 Subject: [PATCH 05/35] =?UTF-8?q?test:=20=EB=A1=9C=EB=93=9C=EB=A7=B5=20?= =?UTF-8?q?=EB=8B=B5=EB=B3=80=20=EB=AA=A9=EB=A1=9D=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/EssayAnswerSearchTest.java | 128 ++++++++++++++++++ .../EssayAnswerSpecificationTest.java | 109 --------------- 2 files changed, 128 insertions(+), 109 deletions(-) delete mode 100644 backend/src/test/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerSpecificationTest.java diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java index cd9bcfe99..5da24cbc1 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java @@ -238,4 +238,132 @@ void init() { answer, new MemberResponse(member.getId(), member.getUsername(), member.getNickname(), Role.CREW, "https://"), LocalDateTime.now(), LocalDateTime.now()); } + + @DisplayName("searchEssayAnswers() : 키워드 id가 있고, 퀴즈 id는 없으며, " + + "사용자의 id가 주어지면 해당 키워드의 답변들 중에 해당 사용자가 작성한 글을 조회할 수 있다.") + @Test + void test_searchEssayAnswers_keywordId_noQuizId_memberId() { + // given + final EssayAnswerSearchRequest request = new EssayAnswerSearchRequest( + curriculum.getId(), + keyword4.getId(), + null, + Arrays.asList(member1.getId()) + ); + + // when + final EssayAnswersResponse actual = essayAnswerService.searchEssayAnswers( + request, PageRequest.of(0, 10)); + + // then + final List expected = Arrays.asList( + 예상_답변_응답(essayAnswerId18, quiz9, "대답18", member1), + 예상_답변_응답(essayAnswerId16, quiz8, "대답16", member1), + 예상_답변_응답(essayAnswerId14, quiz7, "대답14", member1), + 예상_답변_응답(essayAnswerId11, quiz6, "대답11", member1) + ); + + final EssayAnswersResponse response = new EssayAnswersResponse(expected, 4L, 1, 1); + + assertThat(actual) + .usingRecursiveComparison() + .ignoringFields("data.createdAt", "data.updatedAt") + .isEqualTo(response); + } + + @DisplayName("searchEssayAnswers() : 키워드 id 있고, 퀴즈 id 없고, " + + "사용자의 id가 주어지지 않으면 해당 키워드의 답변들을 모두 조회할 수 있다.") + @Test + void test_searchEssayAnswers_keywordId_NoQuizId_noMemberId() { + // given + final EssayAnswerSearchRequest request = new EssayAnswerSearchRequest( + curriculum.getId(), + keyword2.getId(), + null, + null + ); + + // when + final EssayAnswersResponse actual = essayAnswerService.searchEssayAnswers( + request, PageRequest.of(0, 10)); + + // then + final List expected = Arrays.asList( + 예상_답변_응답(essayAnswerId6, quiz3, "대답6", member1), + 예상_답변_응답(essayAnswerId5, quiz2, "대답5", member4), + 예상_답변_응답(essayAnswerId4, quiz2, "대답4", member3), + 예상_답변_응답(essayAnswerId3, quiz2, "대답3", member2), + 예상_답변_응답(essayAnswerId2, quiz2, "대답2", member1) + ); + + final EssayAnswersResponse response = new EssayAnswersResponse(expected, 5L, 1, 1); + + assertThat(actual) + .usingRecursiveComparison() + .ignoringFields("data.createdAt", "data.updatedAt") + .isEqualTo(response); + } + + @DisplayName("searchEssayAnswers() : 키워드 id 있고, 퀴즈 id 있고, " + + "사용자의 id가 주어지면 사용자가 작성한 해당 퀴즈의 답변들을 모두 조회할 수 있다.") + @Test + void test_searchEssayAnswers_keywordId_quizId_memberId() { + // given + final EssayAnswerSearchRequest request = new EssayAnswerSearchRequest( + curriculum.getId(), + keyword4.getId(), + Arrays.asList(quiz6.getId(), quiz7.getId()), + Arrays.asList(member1.getId()) + ); + + // when + final EssayAnswersResponse actual = essayAnswerService.searchEssayAnswers( + request, PageRequest.of(0, 10)); + + // then + final List expected = Arrays.asList( + 예상_답변_응답(essayAnswerId14, quiz7, "대답14", member1), + 예상_답변_응답(essayAnswerId11, quiz6, "대답11", member1) + ); + + final EssayAnswersResponse response = new EssayAnswersResponse(expected, 2L, 1, 1); + + assertThat(actual) + .usingRecursiveComparison() + .ignoringFields("data.createdAt", "data.updatedAt") + .isEqualTo(response); + } + + @DisplayName("searchEssayAnswers() : 키워드 id 있고, 퀴즈 id 있고, " + + "사용자의 id가 주어지지 않으면 해당 퀴즈의 답변들을 모두 조회할 수 있다.") + @Test + void test_searchEssayAnswers_keywordId_quizId_noMemberId() { + // given + final EssayAnswerSearchRequest request = new EssayAnswerSearchRequest( + curriculum.getId(), + keyword4.getId(), + Arrays.asList(quiz6.getId(), quiz7.getId()), + null + ); + + // when + final EssayAnswersResponse actual = essayAnswerService.searchEssayAnswers( + request, PageRequest.of(0, 10)); + + // then + final List expected = Arrays.asList( + 예상_답변_응답(essayAnswerId15, quiz7, "대답15", member2), + 예상_답변_응답(essayAnswerId14, quiz7, "대답14", member1), + 예상_답변_응답(essayAnswerId13, quiz6, "대답13", member3), + 예상_답변_응답(essayAnswerId12, quiz6, "대답12", member2), + 예상_답변_응답(essayAnswerId11, quiz6, "대답11", member1) + ); + + final EssayAnswersResponse response = new EssayAnswersResponse(expected, 5L, 1, 1); + + assertThat(actual) + .usingRecursiveComparison() + .ignoringFields("data.createdAt", "data.updatedAt") + .isEqualTo(response); + } } diff --git a/backend/src/test/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerSpecificationTest.java b/backend/src/test/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerSpecificationTest.java deleted file mode 100644 index f3ea74a5e..000000000 --- a/backend/src/test/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerSpecificationTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package wooteco.prolog.roadmap.domain.repository; - -import java.util.Arrays; -import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.data.jpa.domain.Specification; -import wooteco.prolog.member.domain.Member; -import wooteco.prolog.member.domain.Role; -import wooteco.prolog.member.domain.repository.MemberRepository; -import wooteco.prolog.roadmap.domain.Curriculum; -import wooteco.prolog.roadmap.domain.EssayAnswer; -import wooteco.prolog.roadmap.domain.Keyword; -import wooteco.prolog.roadmap.domain.Quiz; -import wooteco.prolog.session.domain.Session; -import wooteco.prolog.session.domain.repository.SessionRepository; - -@SpringBootTest -class EssayAnswerSpecificationTest { - - @Autowired - EssayAnswerRepository essayAnswerRepository; - - @Autowired - CurriculumRepository curriculumRepository; - - @Autowired - KeywordRepository keywordRepository; - - @Autowired - MemberRepository memberRepository; - - @Autowired - SessionRepository sessionRepository; - @Autowired - private QuizRepository quizRepository; - - @BeforeEach - void init() { - final Curriculum curriculum = curriculumRepository.save(new Curriculum("커리큘럼1")); - final Session session1 = sessionRepository.save(new Session(curriculum.getId(), "세션1")); - final Session session2 = sessionRepository.save(new Session(curriculum.getId(), "세션2")); - - final Keyword keyword1 = keywordRepository.save( - Keyword.createKeyword("자바1", "자바 설명1", 1, 5, session1.getId(), null)); - final Keyword keyword2 = keywordRepository.save( - Keyword.createKeyword("자바2", "자바 설명2", 2, 5, session1.getId(), null)); - final Keyword keyword3 = keywordRepository.save( - Keyword.createKeyword("자바3", "자바 설명3", 3, 5, session1.getId(), null)); - final Keyword keyword4 = keywordRepository.save( - Keyword.createKeyword("자바4", "자바 설명4", 4, 5, session2.getId(), null)); - - final Quiz quiz1 = quizRepository.save(new Quiz(keyword1, "퀴즈1")); - final Quiz quiz2 = quizRepository.save(new Quiz(keyword2, "퀴즈2")); - final Quiz quiz3 = quizRepository.save(new Quiz(keyword2, "퀴즈3")); - final Quiz quiz4 = quizRepository.save(new Quiz(keyword3, "퀴즈4")); - final Quiz quiz5 = quizRepository.save(new Quiz(keyword3, "퀴즈5")); - final Quiz quiz6 = quizRepository.save(new Quiz(keyword4, "퀴즈6")); - final Quiz quiz7 = quizRepository.save(new Quiz(keyword4, "퀴즈7")); - final Quiz quiz8 = quizRepository.save(new Quiz(keyword4, "퀴즈8")); - final Quiz quiz9 = quizRepository.save(new Quiz(keyword4, "퀴즈9")); - - Member member = new Member(11L, "username", "nickname", Role.CREW, 101L, "https://"); - - EssayAnswer essayAnswer1 = new EssayAnswer(quiz1, "대답1", member); - EssayAnswer essayAnswer2 = new EssayAnswer(quiz2, "대답2", member); - EssayAnswer essayAnswer3 = new EssayAnswer(quiz2, "대답3", member); - EssayAnswer essayAnswer4 = new EssayAnswer(quiz2, "대답4", member); - EssayAnswer essayAnswer5 = new EssayAnswer(quiz2, "대답5", member); - EssayAnswer essayAnswer6 = new EssayAnswer(quiz3, "대답6", member); - EssayAnswer essayAnswer7 = new EssayAnswer(quiz4, "대답7", member); - EssayAnswer essayAnswer8 = new EssayAnswer(quiz4, "대답8", member); - EssayAnswer essayAnswer9 = new EssayAnswer(quiz5, "대답9", member); - EssayAnswer essayAnswer10 = new EssayAnswer(quiz5, "대답10", member); - EssayAnswer essayAnswer11 = new EssayAnswer(quiz6, "대답11", member); - EssayAnswer essayAnswer12 = new EssayAnswer(quiz6, "대답12", member); - EssayAnswer essayAnswer13 = new EssayAnswer(quiz6, "대답13", member); - EssayAnswer essayAnswer14 = new EssayAnswer(quiz7, "대답14", member); - EssayAnswer essayAnswer15 = new EssayAnswer(quiz7, "대답15", member); - EssayAnswer essayAnswer16 = new EssayAnswer(quiz8, "대답16", member); - EssayAnswer essayAnswer17 = new EssayAnswer(quiz8, "대답17", member); - EssayAnswer essayAnswer18 = new EssayAnswer(quiz9, "대답18", member); - EssayAnswer essayAnswer19 = new EssayAnswer(quiz9, "대답19", member); - EssayAnswer essayAnswer20 = new EssayAnswer(quiz9, "대답20", member); - - essayAnswerRepository.saveAll( - Arrays.asList( - essayAnswer1, essayAnswer2, essayAnswer3, essayAnswer4, essayAnswer5, essayAnswer6, - essayAnswer7, essayAnswer8, essayAnswer9, essayAnswer10, essayAnswer11, - essayAnswer12, essayAnswer13, essayAnswer14, essayAnswer15, essayAnswer16, - essayAnswer17, essayAnswer18, essayAnswer19, essayAnswer20) - ); - } - - @Test - void aa() throws Exception { - //given - final Specification essayAnswerSpecification = EssayAnswerSpecification.equalsSessionIdsIn( - Arrays.asList(1L)); - - //when - final List all = essayAnswerRepository.findAll(essayAnswerSpecification); - - //then - System.out.println("all = " + all); - } -} From d45a3d6553345d43cea0ad862057537c34ca9436 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Wed, 2 Aug 2023 17:46:27 +0900 Subject: [PATCH 06/35] =?UTF-8?q?fix=20:=20=EB=8B=A8=EC=9C=84=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존 코드 수정에 따라서 단위 테스트도 수정 #1461 --- .../prolog/roadmap/application/RoadMapServiceTest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java index 469f81277..2fb659eb7 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java @@ -6,6 +6,7 @@ import static org.mockito.Mockito.when; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.DisplayName; @@ -36,13 +37,13 @@ class RoadMapServiceTest { @Test @DisplayName("curriculumId가 주어지면 해당 커리큘럼의 키워드들을 전부 조회할 수 있다.") - void findAllKeywords() throws Exception { + void findAllKeywords() { //given final Curriculum curriculum = new Curriculum(1L, "커리큘럼1"); final Session session = new Session(1L, curriculum.getId(), "세션1"); final List sessions = Arrays.asList(session); - final Keyword keyword = Keyword.createKeyword("자바1", "자바 설명1", 1, 5, session.getId(), - null); + final Keyword keyword = new Keyword(1L, "자바1", "자바 설명1", 1, 5, session.getId(), + null, Collections.emptySet()); when(curriculumRepository.findById(anyLong())) .thenReturn(Optional.of(curriculum)); @@ -53,7 +54,7 @@ void findAllKeywords() throws Exception { when(keywordRepository.findBySessionIdIn(any())) .thenReturn(Arrays.asList(keyword)); - final KeywordsResponse expected = KeywordsResponse.createResponse(Arrays.asList(keyword)); + final KeywordsResponse expected = KeywordsResponse.createResponseWithChildren(Arrays.asList(keyword)); //when final KeywordsResponse actual = From 8b0d8b3875b5ce485e0064a98a472d90d0c8708a Mon Sep 17 00:00:00 2001 From: java-saeng Date: Thu, 3 Aug 2023 17:01:43 +0900 Subject: [PATCH 07/35] =?UTF-8?q?refactor=20:=20=EC=B5=9C=EC=83=81?= =?UTF-8?q?=EC=9C=84=20keyword=EC=9D=B8=EC=A7=80=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=EC=97=90=EC=84=9C=20=ED=8C=90=EB=8B=A8=ED=95=98?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1461 --- .../wooteco/prolog/roadmap/application/RoadMapService.java | 6 ++++-- .../prolog/roadmap/application/dto/KeywordsResponse.java | 2 +- .../main/java/wooteco/prolog/roadmap/domain/Keyword.java | 4 ++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java index a8f1b30c1..d352c1dc2 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java @@ -1,11 +1,14 @@ package wooteco.prolog.roadmap.application; +import static wooteco.prolog.common.exception.BadRequestCode.CURRICULUM_NOT_FOUND_EXCEPTION; + import java.util.List; import java.util.Set; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import wooteco.prolog.common.exception.BadRequestException; import wooteco.prolog.roadmap.application.dto.KeywordsResponse; import wooteco.prolog.roadmap.domain.Curriculum; import wooteco.prolog.roadmap.domain.Keyword; @@ -26,8 +29,7 @@ public class RoadMapService { @Transactional(readOnly = true) public KeywordsResponse findAllKeywords(final Long curriculumId) { final Curriculum curriculum = curriculumRepository.findById(curriculumId) - .orElseThrow(() -> new IllegalArgumentException( - "해당 커리큘럼이 존재하지 않습니다. curriculumId = " + curriculumId)); + .orElseThrow(() -> new BadRequestException(CURRICULUM_NOT_FOUND_EXCEPTION)); final Set sessionIds = sessionRepository.findAllByCurriculumId(curriculum.getId()) .stream() diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java index bbd9c3018..d904b67a3 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java @@ -26,7 +26,7 @@ public static KeywordsResponse createResponse(final List keywords) { public static KeywordsResponse createResponseWithChildren(final List keywords) { List keywordsResponse = keywords.stream() - .filter(it -> it.getParent() == null) + .filter(Keyword::isRoot) .map(KeywordResponse::createWithAllChildResponse) .collect(Collectors.toList()); return new KeywordsResponse(keywordsResponse); diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java index 8518c806c..e4ffb3222 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java @@ -124,6 +124,10 @@ private void validateKeywordParent(final Keyword parentKeyword) { } } + public boolean isRoot() { + return parent == null; + } + public Long getParentIdOrNull() { if (parent == null) { return null; From 3877b7d1e1e383771d32dbe8015aa23784b6b3da Mon Sep 17 00:00:00 2001 From: java-saeng Date: Thu, 3 Aug 2023 17:02:00 +0900 Subject: [PATCH 08/35] =?UTF-8?q?refactor=20:=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=9D=98=EB=AF=B8=EA=B0=80=20=EB=B3=B4=EC=9D=B4?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1461 --- .../main/java/wooteco/prolog/roadmap/ui/RoadmapController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/ui/RoadmapController.java b/backend/src/main/java/wooteco/prolog/roadmap/ui/RoadmapController.java index 16500a192..4fbfc82ee 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/ui/RoadmapController.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/ui/RoadmapController.java @@ -14,7 +14,7 @@ public class RoadmapController { private final RoadMapService roadMapService; @GetMapping("/roadmaps") - public KeywordsResponse findKeywords(@RequestParam final Long curriculumId) { + public KeywordsResponse findRoadMapKeyword(@RequestParam final Long curriculumId) { return roadMapService.findAllKeywords(curriculumId); } } From c54288937d885a5343d17ad9d6569df0e3c2c4dd Mon Sep 17 00:00:00 2001 From: jiwon Date: Mon, 7 Aug 2023 15:36:32 +0900 Subject: [PATCH 09/35] =?UTF-8?q?feat:=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/exception/BadRequestCode.java | 1 - .../application/EssayAnswerService.java | 14 +-- .../session/application/SessionService.java | 4 +- .../application/EssayAnswerSearchTest.java | 90 ++++++------------- .../application/SessionServiceTest.java | 32 ++++--- .../SessionMemberRepositoryTest.java | 4 +- 6 files changed, 55 insertions(+), 90 deletions(-) diff --git a/backend/src/main/java/wooteco/prolog/common/exception/BadRequestCode.java b/backend/src/main/java/wooteco/prolog/common/exception/BadRequestCode.java index 63d12ffda..96b2d71f7 100644 --- a/backend/src/main/java/wooteco/prolog/common/exception/BadRequestCode.java +++ b/backend/src/main/java/wooteco/prolog/common/exception/BadRequestCode.java @@ -75,7 +75,6 @@ public enum BadRequestCode { FILE_UPLOAD_FAIL_EXCEPTION(9003, "파일 업로드에 실패했습니다."), DUPLICATE_SESSION_EXCEPTION(10001, "중복되는 세션입니다."), - SESSION_NOT_FOUND_EXCEPTION(10002, "세션을 찾을 수 없습니다."), TOO_LONG_LEVEL_NAME_EXCEPTION(10003, String.format("세션 이름이 %d자 초과입니다.", Session.MAX_LENGTH)), SEARCH_ARGUMENT_PARSE_EXCEPTION(11001, "parsing 할 수 없는 argument입니다."), diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/EssayAnswerService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/EssayAnswerService.java index a052c946f..3042eab57 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/EssayAnswerService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/EssayAnswerService.java @@ -1,8 +1,12 @@ package wooteco.prolog.roadmap.application; +import static wooteco.prolog.common.exception.BadRequestCode.CURRICULUM_NOT_FOUND_EXCEPTION; import static wooteco.prolog.common.exception.BadRequestCode.ESSAY_ANSWER_NOT_FOUND_EXCEPTION; import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_QUIZ_NOT_FOUND_EXCEPTION; +import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_SESSION_NOT_FOUND_EXCEPTION; +import java.util.List; +import java.util.stream.Collectors; import org.hibernate.Hibernate; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -14,8 +18,8 @@ import wooteco.prolog.member.domain.Member; import wooteco.prolog.roadmap.application.dto.EssayAnswerRequest; import wooteco.prolog.roadmap.application.dto.EssayAnswerSearchRequest; -import wooteco.prolog.roadmap.domain.Curriculum; import wooteco.prolog.roadmap.application.dto.EssayAnswerUpdateRequest; +import wooteco.prolog.roadmap.domain.Curriculum; import wooteco.prolog.roadmap.domain.EssayAnswer; import wooteco.prolog.roadmap.domain.Quiz; import wooteco.prolog.roadmap.domain.repository.CurriculumRepository; @@ -26,9 +30,6 @@ import wooteco.prolog.session.domain.repository.SessionRepository; import wooteco.prolog.studylog.application.dto.EssayAnswersResponse; -import java.util.List; -import java.util.stream.Collectors; - @Transactional @Service public class EssayAnswerService { @@ -110,8 +111,7 @@ public EssayAnswersResponse searchEssayAnswers( final Long curriculumId = request.getCurriculumId(); final Curriculum curriculum = curriculumRepository.findById(curriculumId) - .orElseThrow(() -> new IllegalArgumentException( - "해당 커리큘럼이 존재하지 않습니다. curriculumId = " + curriculumId)); + .orElseThrow(() -> new BadRequestException(CURRICULUM_NOT_FOUND_EXCEPTION)); final List sessionIds = sessionRepository.findAllByCurriculumId(curriculum.getId()) .stream() @@ -119,7 +119,7 @@ public EssayAnswersResponse searchEssayAnswers( .collect(Collectors.toList()); if (sessionIds.isEmpty()) { - throw new IllegalArgumentException("세션이 비어있으면 안됨"); + throw new BadRequestException(ROADMAP_SESSION_NOT_FOUND_EXCEPTION); } final Specification essayAnswerSpecification = EssayAnswerSpecification.equalsSessionIdsIn( diff --git a/backend/src/main/java/wooteco/prolog/session/application/SessionService.java b/backend/src/main/java/wooteco/prolog/session/application/SessionService.java index ae28a2028..7d0592ed9 100644 --- a/backend/src/main/java/wooteco/prolog/session/application/SessionService.java +++ b/backend/src/main/java/wooteco/prolog/session/application/SessionService.java @@ -2,7 +2,7 @@ import static java.util.stream.Collectors.toList; import static wooteco.prolog.common.exception.BadRequestCode.DUPLICATE_SESSION_EXCEPTION; -import static wooteco.prolog.common.exception.BadRequestCode.SESSION_NOT_FOUND_EXCEPTION; +import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_SESSION_NOT_FOUND_EXCEPTION; import java.util.Collection; import java.util.List; @@ -43,7 +43,7 @@ private void validateName(String name) { public Session findById(Long id) { return sessionRepository.findById(id) - .orElseThrow(() -> new BadRequestException(SESSION_NOT_FOUND_EXCEPTION)); + .orElseThrow(() -> new BadRequestException(ROADMAP_SESSION_NOT_FOUND_EXCEPTION)); } public Optional findSessionById(Long id) { diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java index 5da24cbc1..a93b2f9cc 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java @@ -143,7 +143,7 @@ void init() { } @Test - @DisplayName("커리큘럼 아이디를 받아 답변을 검색한다.") + @DisplayName("커리큘럼 아이디 O, 키워드 아이디 X, 퀴즈 아이디 X, 멤버 아이디 X") void 커리큘럼_아이디를_받아서_답변을_검색한다() { // given final EssayAnswerSearchRequest 답변_검색_요청 = new EssayAnswerSearchRequest( @@ -175,7 +175,7 @@ void init() { } @Test - @DisplayName("커리큘럼 아이디와 키워드 아이디를 받아서 답변을 검색한다.") + @DisplayName("커리큘럼 아이디 O, 키워드 아이디 O, 퀴즈 아이디 X, 멤버 아이디 X") void 커리큘럼_아이디와_키워드_아이디를_받아서_답변을_검색한다() { // given final EssayAnswerSearchRequest 답변_검색_요청 = new EssayAnswerSearchRequest( @@ -202,7 +202,7 @@ void init() { } @Test - @DisplayName("커리큘럼 아이디와 키워드 아이디와 퀴즈 아이디를 받아서 답변을 검색한다.") + @DisplayName("커리큘럼 아이디 O, 키워드 아이디 O, 퀴즈 아이디 O, 멤버 아이디 X") void 커리큘럼_아이디와_키워드_아이디와_퀴즈_아이디를_받아서_답변을_검색한다() { // given final EssayAnswerSearchRequest 답변_검색_요청 = new EssayAnswerSearchRequest( @@ -231,18 +231,9 @@ void init() { .isEqualTo(예상되는_답변_응답들); } - private EssayAnswerResponse 예상_답변_응답(final Long essayAnswerId, final Quiz quiz, - final String answer, final Member member) { - return new EssayAnswerResponse(essayAnswerId, - new QuizResponse(quiz.getId(), quiz.getQuestion()), - answer, new MemberResponse(member.getId(), member.getUsername(), member.getNickname(), - Role.CREW, "https://"), LocalDateTime.now(), LocalDateTime.now()); - } - - @DisplayName("searchEssayAnswers() : 키워드 id가 있고, 퀴즈 id는 없으며, " - + "사용자의 id가 주어지면 해당 키워드의 답변들 중에 해당 사용자가 작성한 글을 조회할 수 있다.") @Test - void test_searchEssayAnswers_keywordId_noQuizId_memberId() { + @DisplayName("커리큘럼 아이디 O, 키워드 아이디 O, 퀴즈 아이디 X, 멤버 아이디 O") + void 커리큘럼_아이디와_키워드_아이디와_멤버_아이디를_받아서_답변을_검색한다() { // given final EssayAnswerSearchRequest request = new EssayAnswerSearchRequest( curriculum.getId(), @@ -271,43 +262,9 @@ void test_searchEssayAnswers_keywordId_noQuizId_memberId() { .isEqualTo(response); } - @DisplayName("searchEssayAnswers() : 키워드 id 있고, 퀴즈 id 없고, " - + "사용자의 id가 주어지지 않으면 해당 키워드의 답변들을 모두 조회할 수 있다.") - @Test - void test_searchEssayAnswers_keywordId_NoQuizId_noMemberId() { - // given - final EssayAnswerSearchRequest request = new EssayAnswerSearchRequest( - curriculum.getId(), - keyword2.getId(), - null, - null - ); - - // when - final EssayAnswersResponse actual = essayAnswerService.searchEssayAnswers( - request, PageRequest.of(0, 10)); - - // then - final List expected = Arrays.asList( - 예상_답변_응답(essayAnswerId6, quiz3, "대답6", member1), - 예상_답변_응답(essayAnswerId5, quiz2, "대답5", member4), - 예상_답변_응답(essayAnswerId4, quiz2, "대답4", member3), - 예상_답변_응답(essayAnswerId3, quiz2, "대답3", member2), - 예상_답변_응답(essayAnswerId2, quiz2, "대답2", member1) - ); - - final EssayAnswersResponse response = new EssayAnswersResponse(expected, 5L, 1, 1); - - assertThat(actual) - .usingRecursiveComparison() - .ignoringFields("data.createdAt", "data.updatedAt") - .isEqualTo(response); - } - - @DisplayName("searchEssayAnswers() : 키워드 id 있고, 퀴즈 id 있고, " - + "사용자의 id가 주어지면 사용자가 작성한 해당 퀴즈의 답변들을 모두 조회할 수 있다.") @Test - void test_searchEssayAnswers_keywordId_quizId_memberId() { + @DisplayName("커리큘럼 아이디 O, 키워드 아이디 O, 퀴즈 아이디 O, 멤버 아이디 O") + void 커리큘럼_아이디와_키워드_아이디와_퀴즈_아이디와_멤버_아이디를_받아서_답변을_검색한다() { // given final EssayAnswerSearchRequest request = new EssayAnswerSearchRequest( curriculum.getId(), @@ -334,16 +291,15 @@ void test_searchEssayAnswers_keywordId_quizId_memberId() { .isEqualTo(response); } - @DisplayName("searchEssayAnswers() : 키워드 id 있고, 퀴즈 id 있고, " - + "사용자의 id가 주어지지 않으면 해당 퀴즈의 답변들을 모두 조회할 수 있다.") @Test - void test_searchEssayAnswers_keywordId_quizId_noMemberId() { + @DisplayName("커리큘럼 아이디 O, 키워드 아이디 X, 퀴즈 아이디 X, 멤버 아이디 O") + void 커리큘럼_아이디와_멤버_아이디를_받아서_답변을_검색한다() { // given final EssayAnswerSearchRequest request = new EssayAnswerSearchRequest( curriculum.getId(), - keyword4.getId(), - Arrays.asList(quiz6.getId(), quiz7.getId()), - null + null, + null, + Arrays.asList(member1.getId()) ); // when @@ -352,18 +308,30 @@ void test_searchEssayAnswers_keywordId_quizId_noMemberId() { // then final List expected = Arrays.asList( - 예상_답변_응답(essayAnswerId15, quiz7, "대답15", member2), + 예상_답변_응답(essayAnswerId18, quiz9, "대답18", member1), + 예상_답변_응답(essayAnswerId16, quiz8, "대답16", member1), 예상_답변_응답(essayAnswerId14, quiz7, "대답14", member1), - 예상_답변_응답(essayAnswerId13, quiz6, "대답13", member3), - 예상_답변_응답(essayAnswerId12, quiz6, "대답12", member2), - 예상_답변_응답(essayAnswerId11, quiz6, "대답11", member1) + 예상_답변_응답(essayAnswerId11, quiz6, "대답11", member1), + 예상_답변_응답(essayAnswerId9, quiz5, "대답9", member1), + 예상_답변_응답(essayAnswerId7, quiz4, "대답7", member1), + 예상_답변_응답(essayAnswerId6, quiz3, "대답6", member1), + 예상_답변_응답(essayAnswerId2, quiz2, "대답2", member1), + 예상_답변_응답(essayAnswerId1, quiz1, "대답1", member1) ); - final EssayAnswersResponse response = new EssayAnswersResponse(expected, 5L, 1, 1); + final EssayAnswersResponse response = new EssayAnswersResponse(expected, 9L, 1, 1); assertThat(actual) .usingRecursiveComparison() .ignoringFields("data.createdAt", "data.updatedAt") .isEqualTo(response); } + + private EssayAnswerResponse 예상_답변_응답(final Long essayAnswerId, final Quiz quiz, + final String answer, final Member member) { + return new EssayAnswerResponse(essayAnswerId, + new QuizResponse(quiz.getId(), quiz.getQuestion()), + answer, new MemberResponse(member.getId(), member.getUsername(), member.getNickname(), + Role.CREW, "https://"), LocalDateTime.now(), LocalDateTime.now()); + } } diff --git a/backend/src/test/java/wooteco/prolog/session/application/SessionServiceTest.java b/backend/src/test/java/wooteco/prolog/session/application/SessionServiceTest.java index f949e3e0f..dd71e8d90 100644 --- a/backend/src/test/java/wooteco/prolog/session/application/SessionServiceTest.java +++ b/backend/src/test/java/wooteco/prolog/session/application/SessionServiceTest.java @@ -1,5 +1,19 @@ package wooteco.prolog.session.application; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.Mockito.doReturn; +import static wooteco.prolog.common.exception.BadRequestCode.DUPLICATE_SESSION_EXCEPTION; +import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_SESSION_NOT_FOUND_EXCEPTION; +import static wooteco.prolog.login.ui.LoginMember.Authority.ANONYMOUS; +import static wooteco.prolog.login.ui.LoginMember.Authority.MEMBER; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -16,22 +30,6 @@ import wooteco.prolog.session.domain.SessionMember; import wooteco.prolog.session.domain.repository.SessionRepository; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.doReturn; -import static wooteco.prolog.common.exception.BadRequestCode.DUPLICATE_SESSION_EXCEPTION; -import static wooteco.prolog.common.exception.BadRequestCode.SESSION_NOT_FOUND_EXCEPTION; -import static wooteco.prolog.login.ui.LoginMember.Authority.ANONYMOUS; -import static wooteco.prolog.login.ui.LoginMember.Authority.MEMBER; - @ExtendWith(MockitoExtension.class) class SessionServiceTest { @@ -91,7 +89,7 @@ void findByIdFail() { // when, then assertThatThrownBy(() -> sessionService.findById(1L)) .isInstanceOf(BadRequestException.class) - .hasMessage(SESSION_NOT_FOUND_EXCEPTION.getMessage()); + .hasMessage(ROADMAP_SESSION_NOT_FOUND_EXCEPTION.getMessage()); } @DisplayName("Id로 Optional을 조회한다.") diff --git a/backend/src/test/java/wooteco/prolog/session/domain/repository/SessionMemberRepositoryTest.java b/backend/src/test/java/wooteco/prolog/session/domain/repository/SessionMemberRepositoryTest.java index 76cf061d9..60a4e24ec 100644 --- a/backend/src/test/java/wooteco/prolog/session/domain/repository/SessionMemberRepositoryTest.java +++ b/backend/src/test/java/wooteco/prolog/session/domain/repository/SessionMemberRepositoryTest.java @@ -1,7 +1,7 @@ package wooteco.prolog.session.domain.repository; import static org.assertj.core.api.Assertions.assertThat; -import static wooteco.prolog.common.exception.BadRequestCode.SESSION_NOT_FOUND_EXCEPTION; +import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_SESSION_NOT_FOUND_EXCEPTION; import java.util.Optional; import org.junit.jupiter.api.DisplayName; @@ -43,7 +43,7 @@ void findSessionMemberBySessionIdAndMemberId() { // then assertThat(result.isPresent()).isTrue(); assertThat(result.orElseThrow( - () -> new BadRequestException(SESSION_NOT_FOUND_EXCEPTION))).isEqualTo(현구막_백엔드_레벨1); + () -> new BadRequestException(ROADMAP_SESSION_NOT_FOUND_EXCEPTION))).isEqualTo(현구막_백엔드_레벨1); } private Session 강의_생성(String name) { From eea9b9b4627d39bd5c273ae04f386394843e052b Mon Sep 17 00:00:00 2001 From: jiwon Date: Wed, 9 Aug 2023 16:34:11 +0900 Subject: [PATCH 10/35] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roadmap/application/EssayAnswerSearchTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java index a93b2f9cc..5a6be087b 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/EssayAnswerSearchTest.java @@ -91,14 +91,14 @@ void init() { quiz9 = quizRepository.save(new Quiz(keyword4, "퀴즈9")); member1 = memberRepository.save( - new Member(11L, "username1", "nickname1", Role.CREW, 111L, "https://")); + new Member("username1", "nickname1", Role.CREW, 111L, "https://")); member2 = memberRepository.save( - new Member(12L, "username2", "nickname2", Role.CREW, 112L, "https://")); + new Member("username2", "nickname2", Role.CREW, 112L, "https://")); member3 = memberRepository.save( - new Member(13L, "username3", "nickname3", Role.CREW, 113L, "https://")); + new Member("username3", "nickname3", Role.CREW, 113L, "https://")); member4 = memberRepository.save( - new Member(14L, "username4", "nickname4", Role.CREW, 115L, "https://")); - + new Member("username4", "nickname4", Role.CREW, 115L, "https://")); + final EssayAnswerRequest essayAnswer1 = new EssayAnswerRequest(quiz1.getId(), "대답1"); final EssayAnswerRequest essayAnswer2 = new EssayAnswerRequest(quiz2.getId(), "대답2"); final EssayAnswerRequest essayAnswer3 = new EssayAnswerRequest(quiz2.getId(), "대답3"); From 583647b40aab0c02fb22d592ca4caae3c05921a5 Mon Sep 17 00:00:00 2001 From: nuyh Date: Thu, 20 Jul 2023 17:57:13 +0900 Subject: [PATCH 11/35] =?UTF-8?q?feat:=20RecommendedPost=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../prolog/roadmap/domain/Keyword.java | 11 +--- .../roadmap/domain/RecommendedPost.java | 62 +++++++++++++++++++ .../V4__alter_table_keyword_reference.sql | 10 +++ 3 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java create mode 100644 backend/src/main/resources/db/migration/prod/V4__alter_table_keyword_reference.sql diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java index e4ffb3222..e7ee59f80 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java @@ -37,10 +37,7 @@ import java.util.Set; import javax.persistence.*; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; +import java.util.*; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -65,10 +62,8 @@ public class Keyword { @Column(name = "session_id", nullable = false) private Long sessionId; - @ElementCollection - @CollectionTable(name = "keyword_reference") - @Column(name = "url") - private List references; + @OneToMany(mappedBy = "keyword", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) + private List recommendedPosts = new ArrayList<>(); @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "parent_id") diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java new file mode 100644 index 000000000..3b28d789f --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java @@ -0,0 +1,62 @@ +package wooteco.prolog.roadmap.domain; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.util.Objects; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Getter +public class RecommendedPost { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String url; + + @ManyToOne + @JoinColumn(nullable = false) + private Keyword keyword; + + public RecommendedPost(final String url) { + this(null, url, null); + } + + public void updateUrl(final String url) { + this.url = url; + } + + public void remove() { + if (this.keyword == null) { + return; + } + + keyword.getRecommendedPosts().remove(this); + this.keyword = null; + } + + public void addKeyword(final Keyword keyword) { + this.keyword = keyword; + keyword.getRecommendedPosts().add(this); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (!(o instanceof RecommendedPost)) return false; + final RecommendedPost post = (RecommendedPost) o; + return Objects.equals(id, post.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/backend/src/main/resources/db/migration/prod/V4__alter_table_keyword_reference.sql b/backend/src/main/resources/db/migration/prod/V4__alter_table_keyword_reference.sql new file mode 100644 index 000000000..0ff33a5b0 --- /dev/null +++ b/backend/src/main/resources/db/migration/prod/V4__alter_table_keyword_reference.sql @@ -0,0 +1,10 @@ +drop table prolog.keyword_reference; + +create table if not exists prolog.recommended_post +( + id bigint auto_increment primary key, + url varchar(255) not null, + keyword_id bigint not null, + constraint FK_KEYWORD_ID + foreign key (keyword_id) references prolog.keyword (id) +); From f0edbca36f89efc72905cb0299abb9184809931a Mon Sep 17 00:00:00 2001 From: nuyh Date: Thu, 20 Jul 2023 17:58:20 +0900 Subject: [PATCH 12/35] =?UTF-8?q?feat:=20=EC=B6=94=EC=B2=9C=20=ED=8F=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=93=B1=EB=A1=9D,=20=EC=88=98=EC=A0=95,?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../application/RecommendedService.java | 59 ++++++++++++ .../application/dto/RecommendedRequest.java | 14 +++ .../dto/RecommendedUpdateRequest.java | 14 +++ .../repository/RecommendedRepository.java | 7 ++ .../RecommendedPostNotFoundException.java | 6 ++ .../application/RecommendedServiceTest.java | 93 +++++++++++++++++++ 6 files changed, 193 insertions(+) create mode 100644 backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedService.java create mode 100644 backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedRequest.java create mode 100644 backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedUpdateRequest.java create mode 100644 backend/src/main/java/wooteco/prolog/roadmap/domain/repository/RecommendedRepository.java create mode 100644 backend/src/main/java/wooteco/prolog/roadmap/exception/RecommendedPostNotFoundException.java create mode 100644 backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedServiceTest.java diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedService.java new file mode 100644 index 000000000..cd8134a77 --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedService.java @@ -0,0 +1,59 @@ +package wooteco.prolog.roadmap.application; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import wooteco.prolog.roadmap.application.dto.RecommendedRequest; +import wooteco.prolog.roadmap.application.dto.RecommendedUpdateRequest; +import wooteco.prolog.roadmap.domain.Keyword; +import wooteco.prolog.roadmap.domain.RecommendedPost; +import wooteco.prolog.roadmap.domain.repository.KeywordRepository; +import wooteco.prolog.roadmap.domain.repository.RecommendedRepository; +import wooteco.prolog.roadmap.exception.KeywordNotFoundException; +import wooteco.prolog.roadmap.exception.RecommendedPostNotFoundException; + +@Transactional(readOnly = true) +@Service +public class RecommendedService { + + private final RecommendedRepository recommendedRepository; + private final KeywordRepository keywordRepository; + + public RecommendedService(final RecommendedRepository recommendedRepository, final KeywordRepository keywordRepository) { + this.recommendedRepository = recommendedRepository; + this.keywordRepository = keywordRepository; + } + + @Transactional + public Long create(final Long keywordId, final RecommendedRequest request) { + final Keyword keyword = findKeywordOrThrow(keywordId); + + final RecommendedPost post = new RecommendedPost(request.getUrl()); + post.addKeyword(keyword); + + return recommendedRepository.save(post).getId(); + } + + private Keyword findKeywordOrThrow(final Long keywordId) { + return keywordRepository.findById(keywordId) + .orElseThrow(KeywordNotFoundException::new); + } + + @Transactional + public void update(final Long recommendedId, final RecommendedUpdateRequest request) { + final RecommendedPost post = findPostOrThrow(recommendedId); + + post.updateUrl(request.getUrl()); + } + + private RecommendedPost findPostOrThrow(final Long recommendedId) { + return recommendedRepository.findById(recommendedId) + .orElseThrow(RecommendedPostNotFoundException::new); + } + + @Transactional + public void delete(final Long recommendedId) { + final RecommendedPost recommendedPost = findPostOrThrow(recommendedId); + recommendedPost.remove(); + recommendedRepository.delete(recommendedPost); + } +} diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedRequest.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedRequest.java new file mode 100644 index 000000000..a515dfb65 --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedRequest.java @@ -0,0 +1,14 @@ +package wooteco.prolog.roadmap.application.dto; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class RecommendedRequest { + + private String url; +} diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedUpdateRequest.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedUpdateRequest.java new file mode 100644 index 000000000..44b2db78f --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedUpdateRequest.java @@ -0,0 +1,14 @@ +package wooteco.prolog.roadmap.application.dto; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class RecommendedUpdateRequest { + + private String url; +} diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/RecommendedRepository.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/RecommendedRepository.java new file mode 100644 index 000000000..5bb66066e --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/RecommendedRepository.java @@ -0,0 +1,7 @@ +package wooteco.prolog.roadmap.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import wooteco.prolog.roadmap.domain.RecommendedPost; + +public interface RecommendedRepository extends JpaRepository { +} diff --git a/backend/src/main/java/wooteco/prolog/roadmap/exception/RecommendedPostNotFoundException.java b/backend/src/main/java/wooteco/prolog/roadmap/exception/RecommendedPostNotFoundException.java new file mode 100644 index 000000000..57a336794 --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/roadmap/exception/RecommendedPostNotFoundException.java @@ -0,0 +1,6 @@ +package wooteco.prolog.roadmap.exception; + +import wooteco.prolog.common.exception.BadRequestException; + +public class RecommendedPostNotFoundException extends BadRequestException { +} diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedServiceTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedServiceTest.java new file mode 100644 index 000000000..ef10a52d2 --- /dev/null +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedServiceTest.java @@ -0,0 +1,93 @@ +package wooteco.prolog.roadmap.application; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; +import wooteco.prolog.roadmap.application.dto.RecommendedRequest; +import wooteco.prolog.roadmap.application.dto.RecommendedUpdateRequest; +import wooteco.prolog.roadmap.domain.Keyword; +import wooteco.prolog.roadmap.domain.RecommendedPost; +import wooteco.prolog.roadmap.domain.repository.KeywordRepository; +import wooteco.prolog.roadmap.domain.repository.RecommendedRepository; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + +@SpringBootTest +@Transactional +class RecommendedServiceTest { + + @Autowired + private RecommendedService recommendedService; + @Autowired + private RecommendedRepository recommendedRepository; + @Autowired + private KeywordRepository keywordRepository; + + private Keyword keyword; + + @BeforeEach + public void init() { + final Keyword keyword = Keyword.createKeyword("이름", "설명", 1, 1, 1L, null); + this.keyword = keywordRepository.save(keyword); + } + + @Test + @DisplayName("추천 포스트 생성 테스트") + void create() { + //given + final RecommendedRequest request = new RecommendedRequest("https//:example.com"); + + //when + Long recommendedPostId = recommendedService.create(keyword.getId(), request); + + final Keyword persistedKeyword = keywordRepository.findById(keyword.getId()).get(); + final RecommendedPost persistedPost = recommendedRepository.findById(recommendedPostId).get(); + + //then + assertSoftly(softAssertions -> { + assertThat(persistedPost.getUrl()).isEqualTo(request.getUrl()); + assertThat(persistedKeyword.getRecommendedPosts()).containsExactly(persistedPost); + }); + } + + @Test + @DisplayName("추천 포스트 수정 테스트") + void update() { + //given + final RecommendedRequest request = new RecommendedRequest("https//:example.com"); + Long recommendedPostId = recommendedService.create(keyword.getId(), request); + String newUrl = "https//:example222.com"; + final RecommendedUpdateRequest updateRrequest = new RecommendedUpdateRequest(newUrl); + + //when + recommendedService.update(recommendedPostId, updateRrequest); + Optional actual = recommendedRepository.findById(recommendedPostId); + + //then + assertThat(actual.get().getUrl()).isEqualTo(newUrl); + } + + @Test + @DisplayName("추천 포스트 삭제 테스트") + void delete() { + //given + final RecommendedRequest request = new RecommendedRequest("https//:example.com"); + Long recommendedPostId = recommendedService.create(keyword.getId(), request); + + //when + recommendedService.delete(recommendedPostId); + + //then + assertSoftly(softAssertions -> { + assertThat(recommendedRepository.findAll()).hasSize(0); + assertThat(keywordRepository.findById(keyword.getId()).get().getRecommendedPosts()) + .isEmpty(); + }); + } +} From 4498cc5ca6c4fda83c2d34bc5cc252764b56c61e Mon Sep 17 00:00:00 2001 From: nuyh Date: Thu, 20 Jul 2023 18:02:09 +0900 Subject: [PATCH 13/35] =?UTF-8?q?test:=20=ED=82=A4=EC=9B=8C=EB=93=9C?= =?UTF-8?q?=EB=B3=84=20=EC=B6=94=EC=B2=9C=20=ED=8F=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../roadmap/domain/RecommendedPostTest.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java diff --git a/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java b/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java new file mode 100644 index 000000000..958005548 --- /dev/null +++ b/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java @@ -0,0 +1,49 @@ +package wooteco.prolog.roadmap.domain; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + +class RecommendedPostTest { + + @Test + @DisplayName("삭제 기능 테스트") + void remove() { + //given + final RecommendedPost recommendedPost = new RecommendedPost(1L, "https://example.com", null); + final Keyword keyword = Keyword.createKeyword("이름", "설명", 1, 1, 1L, null); + recommendedPost.addKeyword(keyword); + + //when + final Keyword before = recommendedPost.getKeyword(); + recommendedPost.remove(); + final Keyword after = recommendedPost.getKeyword(); + + //then + Assertions.assertAll( + () -> assertThat(before).isNotNull(), + () -> assertThat(after).isNull() + ); + + } + + @Test + @DisplayName("소속 키워드를 추가한다") + void addKeyword() { + //given + final RecommendedPost post = new RecommendedPost("http://연어"); + final Keyword keyword = Keyword.createKeyword("name", "description", 1, 1, 1L, null); + + //when + post.addKeyword(keyword); + + //then + assertSoftly(soft -> { + assertThat(post.getKeyword()).isEqualTo(keyword); + assertThat(keyword.getRecommendedPosts()).containsExactly(post); + }); + } +} From 3ed16046f8119f33e68c61dc9b909e098ff25f32 Mon Sep 17 00:00:00 2001 From: nuyh Date: Thu, 20 Jul 2023 18:02:27 +0900 Subject: [PATCH 14/35] =?UTF-8?q?feat:=20=EC=96=B4=EB=93=9C=EB=AF=BC?= =?UTF-8?q?=EC=9A=A9=20=ED=82=A4=EC=9B=8C=EB=93=9C=EB=B3=84=20=EC=B6=94?= =?UTF-8?q?=EC=B2=9C=20=ED=8F=AC=EC=8A=A4=ED=8A=B8=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../roadmap/ui/RecommendedController.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java diff --git a/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java b/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java new file mode 100644 index 000000000..6838624d3 --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java @@ -0,0 +1,43 @@ +package wooteco.prolog.roadmap.ui; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import wooteco.prolog.roadmap.application.RecommendedService; +import wooteco.prolog.roadmap.application.dto.RecommendedRequest; +import wooteco.prolog.roadmap.application.dto.RecommendedUpdateRequest; + +import java.net.URI; + +@RestController +@RequestMapping("/keywords/{keywordId}/recommended-posts") +public class RecommendedController { + + private final RecommendedService recommendedService; + + public RecommendedController(final RecommendedService recommendedService) { + this.recommendedService = recommendedService; + } + + @PostMapping + public ResponseEntity createRecommendedPost(@PathVariable("keywordId") Long keywordId, + @RequestBody RecommendedRequest request) { + final Long id = recommendedService.create(keywordId, request); + return ResponseEntity.created( + URI.create("/keywords/" + keywordId + "/recommended-posts/" + id)).build(); + } + + @PutMapping("/{recommendedId}") + public ResponseEntity updateRecommendedPost(@PathVariable("keywordId") Long keywordId, + @PathVariable("recommendedId") Long recommendedId, + @RequestBody RecommendedUpdateRequest request) { + recommendedService.update(recommendedId, request); + return ResponseEntity.ok().build(); + } + + @DeleteMapping("/{recommendedId}") + public ResponseEntity deleteRecommendedPost(@PathVariable("keywordId") Long keywordId, + @PathVariable("recommendedId") Long recommendedId) { + recommendedService.delete(recommendedId); + return ResponseEntity.noContent().build(); + } +} From 6b5f6248a33b4b6f10351005daec26da373b0a18 Mon Sep 17 00:00:00 2001 From: nuyh Date: Fri, 28 Jul 2023 16:52:28 +0900 Subject: [PATCH 15/35] =?UTF-8?q?fix:=20=EC=B6=94=EC=B2=9C=20=ED=8F=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20URL=20=EA=B8=B8=EC=9D=B4=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20FK=20=EB=84=A4=EC=9E=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../db/migration/prod/V4__alter_table_keyword_reference.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/resources/db/migration/prod/V4__alter_table_keyword_reference.sql b/backend/src/main/resources/db/migration/prod/V4__alter_table_keyword_reference.sql index 0ff33a5b0..99090b659 100644 --- a/backend/src/main/resources/db/migration/prod/V4__alter_table_keyword_reference.sql +++ b/backend/src/main/resources/db/migration/prod/V4__alter_table_keyword_reference.sql @@ -3,8 +3,8 @@ drop table prolog.keyword_reference; create table if not exists prolog.recommended_post ( id bigint auto_increment primary key, - url varchar(255) not null, + url varchar(512) not null, keyword_id bigint not null, - constraint FK_KEYWORD_ID + constraint FK_RECOMMENDED_POST_PARENT_KEYWORD_ID foreign key (keyword_id) references prolog.keyword (id) ); From b268be41d98a6de495a0f2ddb4f1304b3668d348 Mon Sep 17 00:00:00 2001 From: nuyh Date: Fri, 28 Jul 2023 17:03:40 +0900 Subject: [PATCH 16/35] =?UTF-8?q?feat:=20=ED=82=A4=EC=9B=8C=EB=93=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=20=EC=B6=94=EC=B2=9C=20=ED=8F=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EB=8F=84=20=EC=A1=B0=ED=9A=8C=EB=90=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../prolog/docu/KeywordDocumentation.java | 19 ++++++++++++------- .../application/dto/KeywordResponse.java | 18 ++++++++++++++++-- .../dto/RecommendedPostResponse.java | 18 ++++++++++++++++++ 3 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedPostResponse.java diff --git a/backend/src/documentation/java/wooteco/prolog/docu/KeywordDocumentation.java b/backend/src/documentation/java/wooteco/prolog/docu/KeywordDocumentation.java index dd51f1199..f98318924 100644 --- a/backend/src/documentation/java/wooteco/prolog/docu/KeywordDocumentation.java +++ b/backend/src/documentation/java/wooteco/prolog/docu/KeywordDocumentation.java @@ -1,12 +1,5 @@ package wooteco.prolog.docu; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.doNothing; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; - -import java.util.Arrays; -import java.util.HashSet; import org.elasticsearch.common.collect.List; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -21,6 +14,14 @@ import wooteco.prolog.roadmap.application.dto.KeywordsResponse; import wooteco.prolog.roadmap.ui.KeywordController; +import java.util.Arrays; +import java.util.HashSet; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doNothing; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; + @WebMvcTest(controllers = KeywordController.class) public class KeywordDocumentation extends NewDocumentation { @@ -108,6 +109,7 @@ public class KeywordDocumentation extends NewDocumentation { 1, 1, null, + null, null ); @@ -133,6 +135,7 @@ public class KeywordDocumentation extends NewDocumentation { 1, 1, null, + null, new HashSet<>( Arrays.asList( new KeywordResponse( @@ -142,6 +145,7 @@ public class KeywordDocumentation extends NewDocumentation { 1, 1, 1L, + null, null ), new KeywordResponse( @@ -151,6 +155,7 @@ public class KeywordDocumentation extends NewDocumentation { 2, 1, 1L, + null, null )) ) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java index 68069a1d9..0e8d105fc 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java @@ -1,12 +1,15 @@ package wooteco.prolog.roadmap.application.dto; -import java.util.HashSet; -import java.util.Set; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import wooteco.prolog.roadmap.domain.Keyword; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter public class KeywordResponse { @@ -17,11 +20,13 @@ public class KeywordResponse { private int order; private int importance; private Long parentKeywordId; + private List recommendedPosts; private Set childrenKeywords; public KeywordResponse(final Long keywordId, final String name, final String description, final int order, final int importance, final Long parentKeywordId, + final List recommendedPosts, final Set childrenKeywords) { this.keywordId = keywordId; this.name = name; @@ -29,6 +34,7 @@ public KeywordResponse(final Long keywordId, final String name, final String des this.order = order; this.importance = importance; this.parentKeywordId = parentKeywordId; + this.recommendedPosts = recommendedPosts; this.childrenKeywords = childrenKeywords; } @@ -40,9 +46,16 @@ public static KeywordResponse createResponse(final Keyword keyword) { keyword.getSeq(), keyword.getImportance(), keyword.getParentIdOrNull(), + createRecommendedPostResponses(keyword), null); } + private static List createRecommendedPostResponses(final Keyword keyword) { + return keyword.getRecommendedPosts().stream() + .map(RecommendedPostResponse::from) + .collect(Collectors.toList()); + } + public static KeywordResponse createWithAllChildResponse(final Keyword keyword) { return new KeywordResponse( keyword.getId(), @@ -51,6 +64,7 @@ public static KeywordResponse createWithAllChildResponse(final Keyword keyword) keyword.getSeq(), keyword.getImportance(), keyword.getParentIdOrNull(), + createRecommendedPostResponses(keyword), createKeywordChild(keyword.getChildren())); } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedPostResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedPostResponse.java new file mode 100644 index 000000000..ca9835f6b --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedPostResponse.java @@ -0,0 +1,18 @@ +package wooteco.prolog.roadmap.application.dto; + +import lombok.Getter; +import wooteco.prolog.roadmap.domain.RecommendedPost; + +@Getter +public class RecommendedPostResponse { + + private final String url; + + public RecommendedPostResponse(final String url) { + this.url = url; + } + + public static RecommendedPostResponse from(final RecommendedPost recommendedPost) { + return new RecommendedPostResponse(recommendedPost.getUrl()); + } +} From fc6551abd259b5cf9bf5a2362bae0598f26e1b3f Mon Sep 17 00:00:00 2001 From: amaran-th Date: Sat, 29 Jul 2023 12:20:04 +0900 Subject: [PATCH 17/35] =?UTF-8?q?test:=20=ED=81=90=EC=BB=B4=EB=B2=84=20?= =?UTF-8?q?=EC=9D=B8=EC=88=98=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- ...KeywordRecommendedPostStepDefinitions.java | 66 +++++++++++++++++++ .../prolog/keyword-recommended-post.feature | 19 ++++++ 2 files changed, 85 insertions(+) create mode 100644 backend/src/acceptanceTest/java/wooteco/prolog/steps/KeywordRecommendedPostStepDefinitions.java create mode 100644 backend/src/acceptanceTest/resources/wooteco/prolog/keyword-recommended-post.feature diff --git a/backend/src/acceptanceTest/java/wooteco/prolog/steps/KeywordRecommendedPostStepDefinitions.java b/backend/src/acceptanceTest/java/wooteco/prolog/steps/KeywordRecommendedPostStepDefinitions.java new file mode 100644 index 000000000..47dd2f023 --- /dev/null +++ b/backend/src/acceptanceTest/java/wooteco/prolog/steps/KeywordRecommendedPostStepDefinitions.java @@ -0,0 +1,66 @@ +package wooteco.prolog.steps; + +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; +import org.springframework.http.HttpStatus; +import wooteco.prolog.AcceptanceSteps; +import wooteco.prolog.roadmap.application.dto.RecommendedRequest; + +import static org.assertj.core.api.Assertions.assertThat; +import static wooteco.prolog.fixtures.KeywordAcceptanceFixture.KEYWORD_REQUEST; + +public class KeywordRecommendedPostStepDefinitions extends AcceptanceSteps { + + @Given("{int}번 세션에 {string}라는 키워드를 순서 {int}, 중요도 {int}, 부모 키워드 {long}로 작성하고") + public void 키워드를_부모_키워드와_함께_작성하고(int sessionId, String keywordName, int seq, int importance, + long parentId) { + context.invokeHttpPost( + "/sessions/" + sessionId + "/keywords", + KEYWORD_REQUEST.getSaveChild(keywordName, seq, importance, parentId)); + } + + @Given("{int}번 키워드에 대해 추천 포스트 {string}를 작성하고") + @When("{int}번 키워드에 대해 추천 포스트 {string}를 작성하면") + public void 추천_포스트를_추가하면(int keywordId, String url) { + context.invokeHttpPost( + "/keywords/"+keywordId+"/recommended-posts", + new RecommendedRequest(url) + ); + } + + @When("{int}번 키워드에 대한 {int}번 추천 포스트를 {string}로 수정하면") + public void 추천_포스트를_수정하면(int keywordId, int recommendedId, String url) { + context.invokeHttpPut( + "/keywords/"+keywordId+"/recommended-posts/"+recommendedId, + new RecommendedRequest(url)); + } + + @When("{int}번 키워드에 대한 {int}번 추천 포스트를 삭제하면") + public void 추천_포스트를_삭제하면(int keywordId, int recommendedId) { + context.invokeHttpDelete( + "/keywords/" + keywordId + "/recommended-posts/" + recommendedId + ); + } + + @Then("추천 포스트가 생성된다") + public void 추천_포스트가_생성된다() { + int statusCode = context.response.statusCode(); + + assertThat(statusCode).isEqualTo(HttpStatus.CREATED.value()); + } + + @Then("추천 포스트가 수정된다") + public void 추천_포스트가_수정된다() { + int statusCode = context.response.statusCode(); + + assertThat(statusCode).isEqualTo(HttpStatus.OK.value()); + } + + @Then("추천 포스트가 삭제된다") + public void 추천_포스트가_삭제된다() { + int statusCode = context.response.statusCode(); + + assertThat(statusCode).isEqualTo(HttpStatus.NO_CONTENT.value()); + } +} diff --git a/backend/src/acceptanceTest/resources/wooteco/prolog/keyword-recommended-post.feature b/backend/src/acceptanceTest/resources/wooteco/prolog/keyword-recommended-post.feature new file mode 100644 index 000000000..b49513f8c --- /dev/null +++ b/backend/src/acceptanceTest/resources/wooteco/prolog/keyword-recommended-post.feature @@ -0,0 +1,19 @@ +@api +Feature: 로드맵 키워드 추천 포스트 관련 기능 + + Background: 사전 작업 + Given "2022 백엔드 레벨1" 세션을 생성하고 - 1번 세션 + And 1번 세션에 "자바"라는 키워드를 순서 1, 중요도 2로 작성하고 + + Scenario: 키워드 추천 포스트 생성하기 + When 1번 키워드에 대해 추천 포스트 "https://javajavajava"를 작성하면 + Then 추천 포스트가 생성된다 + + Scenario: 키워드 추천 포스트 수정하기 + Given 1번 키워드에 대해 추천 포스트 "https://javajavajava"를 작성하고 + When 1번 키워드에 대한 1번 추천 포스트를 "https://java2java2"로 수정하면 + Then 추천 포스트가 수정된다 + + Scenario: 키워드 추천 포스트 삭제하기 + When 1번 키워드에 대한 1번 추천 포스트를 삭제하면 + Then 추천 포스트가 삭제된다 From 35f6479c99d686eceab3763cc1cfbee6dccc9924 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Sat, 29 Jul 2023 12:25:08 +0900 Subject: [PATCH 18/35] =?UTF-8?q?test:=20=ED=81=90=EC=BB=B4=EB=B2=84=20?= =?UTF-8?q?=EC=9D=B8=EC=88=98=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../resources/wooteco/prolog/keyword-recommended-post.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/acceptanceTest/resources/wooteco/prolog/keyword-recommended-post.feature b/backend/src/acceptanceTest/resources/wooteco/prolog/keyword-recommended-post.feature index b49513f8c..9be2ee3e6 100644 --- a/backend/src/acceptanceTest/resources/wooteco/prolog/keyword-recommended-post.feature +++ b/backend/src/acceptanceTest/resources/wooteco/prolog/keyword-recommended-post.feature @@ -15,5 +15,6 @@ Feature: 로드맵 키워드 추천 포스트 관련 기능 Then 추천 포스트가 수정된다 Scenario: 키워드 추천 포스트 삭제하기 + Given 1번 키워드에 대해 추천 포스트 "https://javajavajava"를 작성하고 When 1번 키워드에 대한 1번 추천 포스트를 삭제하면 Then 추천 포스트가 삭제된다 From 4d0d6e58b89cedc95a75fe0115d73b76ce420caa Mon Sep 17 00:00:00 2001 From: amaran-th Date: Sat, 29 Jul 2023 13:08:52 +0900 Subject: [PATCH 19/35] =?UTF-8?q?test:=20=ED=81=90=EC=BB=B4=EB=B2=84=20?= =?UTF-8?q?=EC=9D=B8=EC=88=98=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=A1=B0=EA=B1=B4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../steps/KeywordRecommendedPostStepDefinitions.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/backend/src/acceptanceTest/java/wooteco/prolog/steps/KeywordRecommendedPostStepDefinitions.java b/backend/src/acceptanceTest/java/wooteco/prolog/steps/KeywordRecommendedPostStepDefinitions.java index 47dd2f023..2dfafcb0f 100644 --- a/backend/src/acceptanceTest/java/wooteco/prolog/steps/KeywordRecommendedPostStepDefinitions.java +++ b/backend/src/acceptanceTest/java/wooteco/prolog/steps/KeywordRecommendedPostStepDefinitions.java @@ -12,14 +12,6 @@ public class KeywordRecommendedPostStepDefinitions extends AcceptanceSteps { - @Given("{int}번 세션에 {string}라는 키워드를 순서 {int}, 중요도 {int}, 부모 키워드 {long}로 작성하고") - public void 키워드를_부모_키워드와_함께_작성하고(int sessionId, String keywordName, int seq, int importance, - long parentId) { - context.invokeHttpPost( - "/sessions/" + sessionId + "/keywords", - KEYWORD_REQUEST.getSaveChild(keywordName, seq, importance, parentId)); - } - @Given("{int}번 키워드에 대해 추천 포스트 {string}를 작성하고") @When("{int}번 키워드에 대해 추천 포스트 {string}를 작성하면") public void 추천_포스트를_추가하면(int keywordId, String url) { From 25c3f7e58c11ad94bf97cde7d4555eaef548e468 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Sun, 30 Jul 2023 12:26:49 +0900 Subject: [PATCH 20/35] =?UTF-8?q?fix:=20RecommendedPostResponse=20dto=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - id 필드 추가 #1397 --- .../roadmap/application/dto/RecommendedPostResponse.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedPostResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedPostResponse.java index ca9835f6b..75816135e 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedPostResponse.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/RecommendedPostResponse.java @@ -1,18 +1,17 @@ package wooteco.prolog.roadmap.application.dto; +import lombok.AllArgsConstructor; import lombok.Getter; import wooteco.prolog.roadmap.domain.RecommendedPost; +@AllArgsConstructor @Getter public class RecommendedPostResponse { + private final Long id; private final String url; - public RecommendedPostResponse(final String url) { - this.url = url; - } - public static RecommendedPostResponse from(final RecommendedPost recommendedPost) { - return new RecommendedPostResponse(recommendedPost.getUrl()); + return new RecommendedPostResponse(recommendedPost.getId(), recommendedPost.getUrl()); } } From 6e85c298444ac032d5ddc2c98c7c5c76d76a21e7 Mon Sep 17 00:00:00 2001 From: nuyh Date: Mon, 31 Jul 2023 15:30:03 +0900 Subject: [PATCH 21/35] =?UTF-8?q?fix:=20Keyword=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=EC=8B=9C=20=EC=84=B8=EC=85=98=EA=B3=BC=EC=9D=98=20=EC=97=B0?= =?UTF-8?q?=EA=B4=80=20=EA=B4=80=EA=B3=84=20violation=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../roadmap/application/RecommendedServiceTest.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedServiceTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedServiceTest.java index ef10a52d2..644c80785 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedServiceTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedServiceTest.java @@ -12,6 +12,8 @@ import wooteco.prolog.roadmap.domain.RecommendedPost; import wooteco.prolog.roadmap.domain.repository.KeywordRepository; import wooteco.prolog.roadmap.domain.repository.RecommendedRepository; +import wooteco.prolog.session.domain.Session; +import wooteco.prolog.session.domain.repository.SessionRepository; import java.util.Optional; @@ -28,13 +30,15 @@ class RecommendedServiceTest { private RecommendedRepository recommendedRepository; @Autowired private KeywordRepository keywordRepository; + @Autowired + private SessionRepository sessionRepository; private Keyword keyword; @BeforeEach public void init() { - final Keyword keyword = Keyword.createKeyword("이름", "설명", 1, 1, 1L, null); - this.keyword = keywordRepository.save(keyword); + final Session session = sessionRepository.save(new Session("레벨 1")); + this.keyword = keywordRepository.save(Keyword.createKeyword("이름", "설명", 1, 1, session.getId(), null)); } @Test From 7602e3dea8fe546204bd5766f177cf1d852aae3c Mon Sep 17 00:00:00 2001 From: nuyh Date: Wed, 2 Aug 2023 17:03:34 +0900 Subject: [PATCH 22/35] =?UTF-8?q?fix:=20=EC=98=88=EC=99=B8=20=EA=B3=A0?= =?UTF-8?q?=EB=8F=84=ED=99=94=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../prolog/common/exception/BadRequestCode.java | 2 ++ .../prolog/roadmap/application/RecommendedService.java | 10 ++++++---- .../exception/RecommendedPostNotFoundException.java | 6 ------ 3 files changed, 8 insertions(+), 10 deletions(-) delete mode 100644 backend/src/main/java/wooteco/prolog/roadmap/exception/RecommendedPostNotFoundException.java diff --git a/backend/src/main/java/wooteco/prolog/common/exception/BadRequestCode.java b/backend/src/main/java/wooteco/prolog/common/exception/BadRequestCode.java index 96b2d71f7..26c6e70c4 100644 --- a/backend/src/main/java/wooteco/prolog/common/exception/BadRequestCode.java +++ b/backend/src/main/java/wooteco/prolog/common/exception/BadRequestCode.java @@ -70,6 +70,8 @@ public enum BadRequestCode { NOT_EMPTY_ESSAY_ANSWER_EXCEPTION(8013, "답변은 공백일 수 없습니다."), ESSAY_ANSWER_NOT_VALID_USER(8014, "본인이 작성한 답변만 수정할 수 있습니다."), + ROADMAP_RECOMMENDED_POST_NOT_FOUND(8101, "해당 추천 포스트가 존재하지 않습니다."), + FILE_NAME_EMPTY_EXCEPTION(9001, "파일 이름이 존재하지 않습니다."), UNSUPPORTED_FILE_EXTENSION_EXCEPTION(9002, "지원하지 않는 파일 확장자입니다."), FILE_UPLOAD_FAIL_EXCEPTION(9003, "파일 업로드에 실패했습니다."), diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedService.java index cd8134a77..9877fa9cd 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedService.java @@ -2,14 +2,16 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import wooteco.prolog.common.exception.BadRequestException; import wooteco.prolog.roadmap.application.dto.RecommendedRequest; import wooteco.prolog.roadmap.application.dto.RecommendedUpdateRequest; import wooteco.prolog.roadmap.domain.Keyword; import wooteco.prolog.roadmap.domain.RecommendedPost; import wooteco.prolog.roadmap.domain.repository.KeywordRepository; import wooteco.prolog.roadmap.domain.repository.RecommendedRepository; -import wooteco.prolog.roadmap.exception.KeywordNotFoundException; -import wooteco.prolog.roadmap.exception.RecommendedPostNotFoundException; + +import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION; +import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_RECOMMENDED_POST_NOT_FOUND; @Transactional(readOnly = true) @Service @@ -35,7 +37,7 @@ public Long create(final Long keywordId, final RecommendedRequest request) { private Keyword findKeywordOrThrow(final Long keywordId) { return keywordRepository.findById(keywordId) - .orElseThrow(KeywordNotFoundException::new); + .orElseThrow(() -> new BadRequestException(ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION)); } @Transactional @@ -47,7 +49,7 @@ public void update(final Long recommendedId, final RecommendedUpdateRequest requ private RecommendedPost findPostOrThrow(final Long recommendedId) { return recommendedRepository.findById(recommendedId) - .orElseThrow(RecommendedPostNotFoundException::new); + .orElseThrow(() -> new BadRequestException(ROADMAP_RECOMMENDED_POST_NOT_FOUND)); } @Transactional diff --git a/backend/src/main/java/wooteco/prolog/roadmap/exception/RecommendedPostNotFoundException.java b/backend/src/main/java/wooteco/prolog/roadmap/exception/RecommendedPostNotFoundException.java deleted file mode 100644 index 57a336794..000000000 --- a/backend/src/main/java/wooteco/prolog/roadmap/exception/RecommendedPostNotFoundException.java +++ /dev/null @@ -1,6 +0,0 @@ -package wooteco.prolog.roadmap.exception; - -import wooteco.prolog.common.exception.BadRequestException; - -public class RecommendedPostNotFoundException extends BadRequestException { -} From 07d9bfa9f86a804c7fde81d820dc3c507861c876 Mon Sep 17 00:00:00 2001 From: nuyh Date: Thu, 3 Aug 2023 16:25:30 +0900 Subject: [PATCH 23/35] =?UTF-8?q?style:=20RecommendedPost=20=EB=A6=AC?= =?UTF-8?q?=ED=8F=AC=EC=A7=80=ED=86=A0=EB=A6=AC=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../roadmap/application/RecommendedService.java | 14 +++++++------- ...ository.java => RecommendedPostRepository.java} | 2 +- .../application/RecommendedServiceTest.java | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) rename backend/src/main/java/wooteco/prolog/roadmap/domain/repository/{RecommendedRepository.java => RecommendedPostRepository.java} (65%) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedService.java index 9877fa9cd..b22e4be5c 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedService.java @@ -8,7 +8,7 @@ import wooteco.prolog.roadmap.domain.Keyword; import wooteco.prolog.roadmap.domain.RecommendedPost; import wooteco.prolog.roadmap.domain.repository.KeywordRepository; -import wooteco.prolog.roadmap.domain.repository.RecommendedRepository; +import wooteco.prolog.roadmap.domain.repository.RecommendedPostRepository; import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION; import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_RECOMMENDED_POST_NOT_FOUND; @@ -17,11 +17,11 @@ @Service public class RecommendedService { - private final RecommendedRepository recommendedRepository; + private final RecommendedPostRepository recommendedPostRepository; private final KeywordRepository keywordRepository; - public RecommendedService(final RecommendedRepository recommendedRepository, final KeywordRepository keywordRepository) { - this.recommendedRepository = recommendedRepository; + public RecommendedService(final RecommendedPostRepository recommendedPostRepository, final KeywordRepository keywordRepository) { + this.recommendedPostRepository = recommendedPostRepository; this.keywordRepository = keywordRepository; } @@ -32,7 +32,7 @@ public Long create(final Long keywordId, final RecommendedRequest request) { final RecommendedPost post = new RecommendedPost(request.getUrl()); post.addKeyword(keyword); - return recommendedRepository.save(post).getId(); + return recommendedPostRepository.save(post).getId(); } private Keyword findKeywordOrThrow(final Long keywordId) { @@ -48,7 +48,7 @@ public void update(final Long recommendedId, final RecommendedUpdateRequest requ } private RecommendedPost findPostOrThrow(final Long recommendedId) { - return recommendedRepository.findById(recommendedId) + return recommendedPostRepository.findById(recommendedId) .orElseThrow(() -> new BadRequestException(ROADMAP_RECOMMENDED_POST_NOT_FOUND)); } @@ -56,6 +56,6 @@ private RecommendedPost findPostOrThrow(final Long recommendedId) { public void delete(final Long recommendedId) { final RecommendedPost recommendedPost = findPostOrThrow(recommendedId); recommendedPost.remove(); - recommendedRepository.delete(recommendedPost); + recommendedPostRepository.delete(recommendedPost); } } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/RecommendedRepository.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/RecommendedPostRepository.java similarity index 65% rename from backend/src/main/java/wooteco/prolog/roadmap/domain/repository/RecommendedRepository.java rename to backend/src/main/java/wooteco/prolog/roadmap/domain/repository/RecommendedPostRepository.java index 5bb66066e..44540d069 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/RecommendedRepository.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/RecommendedPostRepository.java @@ -3,5 +3,5 @@ import org.springframework.data.jpa.repository.JpaRepository; import wooteco.prolog.roadmap.domain.RecommendedPost; -public interface RecommendedRepository extends JpaRepository { +public interface RecommendedPostRepository extends JpaRepository { } diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedServiceTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedServiceTest.java index 644c80785..b552f10fa 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedServiceTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedServiceTest.java @@ -11,7 +11,7 @@ import wooteco.prolog.roadmap.domain.Keyword; import wooteco.prolog.roadmap.domain.RecommendedPost; import wooteco.prolog.roadmap.domain.repository.KeywordRepository; -import wooteco.prolog.roadmap.domain.repository.RecommendedRepository; +import wooteco.prolog.roadmap.domain.repository.RecommendedPostRepository; import wooteco.prolog.session.domain.Session; import wooteco.prolog.session.domain.repository.SessionRepository; @@ -27,7 +27,7 @@ class RecommendedServiceTest { @Autowired private RecommendedService recommendedService; @Autowired - private RecommendedRepository recommendedRepository; + private RecommendedPostRepository recommendedPostRepository; @Autowired private KeywordRepository keywordRepository; @Autowired @@ -51,7 +51,7 @@ void create() { Long recommendedPostId = recommendedService.create(keyword.getId(), request); final Keyword persistedKeyword = keywordRepository.findById(keyword.getId()).get(); - final RecommendedPost persistedPost = recommendedRepository.findById(recommendedPostId).get(); + final RecommendedPost persistedPost = recommendedPostRepository.findById(recommendedPostId).get(); //then assertSoftly(softAssertions -> { @@ -71,7 +71,7 @@ void update() { //when recommendedService.update(recommendedPostId, updateRrequest); - Optional actual = recommendedRepository.findById(recommendedPostId); + Optional actual = recommendedPostRepository.findById(recommendedPostId); //then assertThat(actual.get().getUrl()).isEqualTo(newUrl); @@ -89,7 +89,7 @@ void delete() { //then assertSoftly(softAssertions -> { - assertThat(recommendedRepository.findAll()).hasSize(0); + assertThat(recommendedPostRepository.findAll()).hasSize(0); assertThat(keywordRepository.findById(keyword.getId()).get().getRecommendedPosts()) .isEmpty(); }); From 2c77205f5524ebf1bb78d0ef18304aa79c84b43a Mon Sep 17 00:00:00 2001 From: nuyh Date: Thu, 3 Aug 2023 16:26:36 +0900 Subject: [PATCH 24/35] =?UTF-8?q?refactor:=20RecommendedPost=20=EC=82=BD?= =?UTF-8?q?=EC=9E=85=20=EC=9D=91=EB=8B=B5=EA=B0=92=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../prolog/roadmap/ui/RecommendedController.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java b/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java index 6838624d3..c86ebecd7 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java @@ -1,13 +1,18 @@ package wooteco.prolog.roadmap.ui; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import wooteco.prolog.roadmap.application.RecommendedService; import wooteco.prolog.roadmap.application.dto.RecommendedRequest; import wooteco.prolog.roadmap.application.dto.RecommendedUpdateRequest; -import java.net.URI; - @RestController @RequestMapping("/keywords/{keywordId}/recommended-posts") public class RecommendedController { @@ -22,8 +27,7 @@ public RecommendedController(final RecommendedService recommendedService) { public ResponseEntity createRecommendedPost(@PathVariable("keywordId") Long keywordId, @RequestBody RecommendedRequest request) { final Long id = recommendedService.create(keywordId, request); - return ResponseEntity.created( - URI.create("/keywords/" + keywordId + "/recommended-posts/" + id)).build(); + return ResponseEntity.status(HttpStatus.CREATED).build(); } @PutMapping("/{recommendedId}") From f3b068057289f0a52d406e2912cf0815947a6e7c Mon Sep 17 00:00:00 2001 From: nuyh Date: Thu, 3 Aug 2023 16:29:03 +0900 Subject: [PATCH 25/35] =?UTF-8?q?refactor:=20update=20=EC=9D=91=EB=8B=B5?= =?UTF-8?q?=EA=B0=92=20200=20->=20204=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../java/wooteco/prolog/roadmap/ui/RecommendedController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java b/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java index c86ebecd7..c984597d1 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java @@ -35,7 +35,7 @@ public ResponseEntity updateRecommendedPost(@PathVariable("keywordId") Lon @PathVariable("recommendedId") Long recommendedId, @RequestBody RecommendedUpdateRequest request) { recommendedService.update(recommendedId, request); - return ResponseEntity.ok().build(); + return ResponseEntity.noContent().build(); } @DeleteMapping("/{recommendedId}") From a8fd1e2b8f2ecbed524964025476b087b1c0a86b Mon Sep 17 00:00:00 2001 From: nuyh Date: Thu, 3 Aug 2023 16:35:08 +0900 Subject: [PATCH 26/35] =?UTF-8?q?style:=20recommended=20post=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- ...dedService.java => RecommendedPostService.java} | 4 ++-- .../prolog/roadmap/ui/RecommendedController.java | 14 +++++++------- ...ceTest.java => RecommendedPostServiceTest.java} | 14 +++++++------- 3 files changed, 16 insertions(+), 16 deletions(-) rename backend/src/main/java/wooteco/prolog/roadmap/application/{RecommendedService.java => RecommendedPostService.java} (93%) rename backend/src/test/java/wooteco/prolog/roadmap/application/{RecommendedServiceTest.java => RecommendedPostServiceTest.java} (86%) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedPostService.java similarity index 93% rename from backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedService.java rename to backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedPostService.java index b22e4be5c..de463aab9 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedPostService.java @@ -15,12 +15,12 @@ @Transactional(readOnly = true) @Service -public class RecommendedService { +public class RecommendedPostService { private final RecommendedPostRepository recommendedPostRepository; private final KeywordRepository keywordRepository; - public RecommendedService(final RecommendedPostRepository recommendedPostRepository, final KeywordRepository keywordRepository) { + public RecommendedPostService(final RecommendedPostRepository recommendedPostRepository, final KeywordRepository keywordRepository) { this.recommendedPostRepository = recommendedPostRepository; this.keywordRepository = keywordRepository; } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java b/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java index c984597d1..b781fd1a2 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java @@ -9,7 +9,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import wooteco.prolog.roadmap.application.RecommendedService; +import wooteco.prolog.roadmap.application.RecommendedPostService; import wooteco.prolog.roadmap.application.dto.RecommendedRequest; import wooteco.prolog.roadmap.application.dto.RecommendedUpdateRequest; @@ -17,16 +17,16 @@ @RequestMapping("/keywords/{keywordId}/recommended-posts") public class RecommendedController { - private final RecommendedService recommendedService; + private final RecommendedPostService recommendedPostService; - public RecommendedController(final RecommendedService recommendedService) { - this.recommendedService = recommendedService; + public RecommendedController(final RecommendedPostService recommendedPostService) { + this.recommendedPostService = recommendedPostService; } @PostMapping public ResponseEntity createRecommendedPost(@PathVariable("keywordId") Long keywordId, @RequestBody RecommendedRequest request) { - final Long id = recommendedService.create(keywordId, request); + final Long id = recommendedPostService.create(keywordId, request); return ResponseEntity.status(HttpStatus.CREATED).build(); } @@ -34,14 +34,14 @@ public ResponseEntity createRecommendedPost(@PathVariable("keywordId") Lon public ResponseEntity updateRecommendedPost(@PathVariable("keywordId") Long keywordId, @PathVariable("recommendedId") Long recommendedId, @RequestBody RecommendedUpdateRequest request) { - recommendedService.update(recommendedId, request); + recommendedPostService.update(recommendedId, request); return ResponseEntity.noContent().build(); } @DeleteMapping("/{recommendedId}") public ResponseEntity deleteRecommendedPost(@PathVariable("keywordId") Long keywordId, @PathVariable("recommendedId") Long recommendedId) { - recommendedService.delete(recommendedId); + recommendedPostService.delete(recommendedId); return ResponseEntity.noContent().build(); } } diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedServiceTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java similarity index 86% rename from backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedServiceTest.java rename to backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java index b552f10fa..7d0a24221 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedServiceTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java @@ -22,10 +22,10 @@ @SpringBootTest @Transactional -class RecommendedServiceTest { +class RecommendedPostServiceTest { @Autowired - private RecommendedService recommendedService; + private RecommendedPostService recommendedPostService; @Autowired private RecommendedPostRepository recommendedPostRepository; @Autowired @@ -48,7 +48,7 @@ void create() { final RecommendedRequest request = new RecommendedRequest("https//:example.com"); //when - Long recommendedPostId = recommendedService.create(keyword.getId(), request); + Long recommendedPostId = recommendedPostService.create(keyword.getId(), request); final Keyword persistedKeyword = keywordRepository.findById(keyword.getId()).get(); final RecommendedPost persistedPost = recommendedPostRepository.findById(recommendedPostId).get(); @@ -65,12 +65,12 @@ void create() { void update() { //given final RecommendedRequest request = new RecommendedRequest("https//:example.com"); - Long recommendedPostId = recommendedService.create(keyword.getId(), request); + Long recommendedPostId = recommendedPostService.create(keyword.getId(), request); String newUrl = "https//:example222.com"; final RecommendedUpdateRequest updateRrequest = new RecommendedUpdateRequest(newUrl); //when - recommendedService.update(recommendedPostId, updateRrequest); + recommendedPostService.update(recommendedPostId, updateRrequest); Optional actual = recommendedPostRepository.findById(recommendedPostId); //then @@ -82,10 +82,10 @@ void update() { void delete() { //given final RecommendedRequest request = new RecommendedRequest("https//:example.com"); - Long recommendedPostId = recommendedService.create(keyword.getId(), request); + Long recommendedPostId = recommendedPostService.create(keyword.getId(), request); //when - recommendedService.delete(recommendedPostId); + recommendedPostService.delete(recommendedPostId); //then assertSoftly(softAssertions -> { From 0c80f77abe57fd2fa037bde13a2122b29768595b Mon Sep 17 00:00:00 2001 From: nuyh Date: Thu, 3 Aug 2023 17:12:11 +0900 Subject: [PATCH 27/35] =?UTF-8?q?fix:=20test=20=EB=82=B4=EC=97=90=EC=84=9C?= =?UTF-8?q?=20Transactional=EB=A1=9C=20=EB=A1=A4=EB=B0=B1=ED=95=98?= =?UTF-8?q?=EB=8D=98=20=EA=B2=83=20DataInitializer=EB=A1=9C=20=EB=8C=80?= =?UTF-8?q?=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../application/RecommendedPostService.java | 1 - .../prolog/roadmap/domain/RecommendedPost.java | 13 +++++++------ .../application/RecommendedPostServiceTest.java | 15 ++++++++++++--- .../roadmap/domain/RecommendedPostTest.java | 11 ++--------- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedPostService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedPostService.java index de463aab9..fb34a6d81 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedPostService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedPostService.java @@ -56,6 +56,5 @@ private RecommendedPost findPostOrThrow(final Long recommendedId) { public void delete(final Long recommendedId) { final RecommendedPost recommendedPost = findPostOrThrow(recommendedId); recommendedPost.remove(); - recommendedPostRepository.delete(recommendedPost); } } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java index 3b28d789f..026c69324 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java @@ -5,7 +5,13 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; import java.util.Objects; @Entity @@ -34,12 +40,7 @@ public void updateUrl(final String url) { } public void remove() { - if (this.keyword == null) { - return; - } - keyword.getRecommendedPosts().remove(this); - this.keyword = null; } public void addKeyword(final Keyword keyword) { diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java index 7d0a24221..38f112528 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java @@ -1,11 +1,12 @@ package wooteco.prolog.roadmap.application; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.transaction.annotation.Transactional; +import wooteco.prolog.common.DataInitializer; import wooteco.prolog.roadmap.application.dto.RecommendedRequest; import wooteco.prolog.roadmap.application.dto.RecommendedUpdateRequest; import wooteco.prolog.roadmap.domain.Keyword; @@ -21,7 +22,6 @@ import static org.assertj.core.api.SoftAssertions.assertSoftly; @SpringBootTest -@Transactional class RecommendedPostServiceTest { @Autowired @@ -33,6 +33,10 @@ class RecommendedPostServiceTest { @Autowired private SessionRepository sessionRepository; + @Autowired + private DataInitializer dataInitializer; + + private Keyword keyword; @BeforeEach @@ -41,6 +45,11 @@ public void init() { this.keyword = keywordRepository.save(Keyword.createKeyword("이름", "설명", 1, 1, session.getId(), null)); } + @AfterEach + public void removeAll() { + dataInitializer.execute(); + } + @Test @DisplayName("추천 포스트 생성 테스트") void create() { @@ -81,7 +90,7 @@ void update() { @DisplayName("추천 포스트 삭제 테스트") void delete() { //given - final RecommendedRequest request = new RecommendedRequest("https//:example.com"); + final RecommendedRequest request = new RecommendedRequest("https://example.com"); Long recommendedPostId = recommendedPostService.create(keyword.getId(), request); //when diff --git a/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java b/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java index 958005548..3d74f08e0 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java @@ -1,6 +1,5 @@ package wooteco.prolog.roadmap.domain; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -13,21 +12,15 @@ class RecommendedPostTest { @DisplayName("삭제 기능 테스트") void remove() { //given - final RecommendedPost recommendedPost = new RecommendedPost(1L, "https://example.com", null); final Keyword keyword = Keyword.createKeyword("이름", "설명", 1, 1, 1L, null); + final RecommendedPost recommendedPost = new RecommendedPost(1L, "https://example.com", null); recommendedPost.addKeyword(keyword); //when - final Keyword before = recommendedPost.getKeyword(); recommendedPost.remove(); - final Keyword after = recommendedPost.getKeyword(); //then - Assertions.assertAll( - () -> assertThat(before).isNotNull(), - () -> assertThat(after).isNull() - ); - + assertThat(keyword.getRecommendedPosts()).isEmpty(); } @Test From d5c7e2f2185321b7b9c69be3ff88dc447ab04555 Mon Sep 17 00:00:00 2001 From: nuyh Date: Thu, 3 Aug 2023 17:38:52 +0900 Subject: [PATCH 28/35] =?UTF-8?q?refactor:=20RecommendedPost=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9E=90=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../prolog/roadmap/application/RecommendedPostService.java | 4 +--- .../java/wooteco/prolog/roadmap/domain/RecommendedPost.java | 4 ++-- .../wooteco/prolog/roadmap/domain/RecommendedPostTest.java | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedPostService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedPostService.java index fb34a6d81..e4e24d95f 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedPostService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedPostService.java @@ -28,9 +28,7 @@ public RecommendedPostService(final RecommendedPostRepository recommendedPostRep @Transactional public Long create(final Long keywordId, final RecommendedRequest request) { final Keyword keyword = findKeywordOrThrow(keywordId); - - final RecommendedPost post = new RecommendedPost(request.getUrl()); - post.addKeyword(keyword); + final RecommendedPost post = new RecommendedPost(request.getUrl(), keyword); return recommendedPostRepository.save(post).getId(); } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java index 026c69324..51c9b3f6b 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java @@ -31,8 +31,8 @@ public class RecommendedPost { @JoinColumn(nullable = false) private Keyword keyword; - public RecommendedPost(final String url) { - this(null, url, null); + public RecommendedPost(final String url, final Keyword keyword) { + this(null, url, keyword); } public void updateUrl(final String url) { diff --git a/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java b/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java index 3d74f08e0..3d6909d82 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java @@ -27,8 +27,8 @@ void remove() { @DisplayName("소속 키워드를 추가한다") void addKeyword() { //given - final RecommendedPost post = new RecommendedPost("http://연어"); final Keyword keyword = Keyword.createKeyword("name", "description", 1, 1, 1L, null); + final RecommendedPost post = new RecommendedPost("http://연어", keyword); //when post.addKeyword(keyword); From b34e979fffbe8799bd9acd9850d370b885ba0baa Mon Sep 17 00:00:00 2001 From: nuyh Date: Thu, 3 Aug 2023 17:40:34 +0900 Subject: [PATCH 29/35] =?UTF-8?q?style:=20final=20=EB=B9=A0=EC=A7=84=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../prolog/roadmap/ui/RecommendedController.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java b/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java index b781fd1a2..aa83c816d 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/ui/RecommendedController.java @@ -24,23 +24,23 @@ public RecommendedController(final RecommendedPostService recommendedPostService } @PostMapping - public ResponseEntity createRecommendedPost(@PathVariable("keywordId") Long keywordId, - @RequestBody RecommendedRequest request) { - final Long id = recommendedPostService.create(keywordId, request); + public ResponseEntity createRecommendedPost(@PathVariable("keywordId") final Long keywordId, + @RequestBody final RecommendedRequest request) { + recommendedPostService.create(keywordId, request); return ResponseEntity.status(HttpStatus.CREATED).build(); } @PutMapping("/{recommendedId}") - public ResponseEntity updateRecommendedPost(@PathVariable("keywordId") Long keywordId, - @PathVariable("recommendedId") Long recommendedId, - @RequestBody RecommendedUpdateRequest request) { + public ResponseEntity updateRecommendedPost(@PathVariable("keywordId") final Long keywordId, + @PathVariable("recommendedId") final Long recommendedId, + @RequestBody final RecommendedUpdateRequest request) { recommendedPostService.update(recommendedId, request); return ResponseEntity.noContent().build(); } @DeleteMapping("/{recommendedId}") - public ResponseEntity deleteRecommendedPost(@PathVariable("keywordId") Long keywordId, - @PathVariable("recommendedId") Long recommendedId) { + public ResponseEntity deleteRecommendedPost(@PathVariable("keywordId") final Long keywordId, + @PathVariable("recommendedId") final Long recommendedId) { recommendedPostService.delete(recommendedId); return ResponseEntity.noContent().build(); } From f0a9e62c60953be16de0d8c53f48660dfc784a66 Mon Sep 17 00:00:00 2001 From: nuyh Date: Thu, 3 Aug 2023 18:03:07 +0900 Subject: [PATCH 30/35] =?UTF-8?q?feat:=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../common/exception/BadRequestCode.java | 3 + .../roadmap/domain/RecommendedPost.java | 30 +++++++++- .../roadmap/domain/RecommendedPostTest.java | 57 ++++++++++++++++++- 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/backend/src/main/java/wooteco/prolog/common/exception/BadRequestCode.java b/backend/src/main/java/wooteco/prolog/common/exception/BadRequestCode.java index 26c6e70c4..430ab9861 100644 --- a/backend/src/main/java/wooteco/prolog/common/exception/BadRequestCode.java +++ b/backend/src/main/java/wooteco/prolog/common/exception/BadRequestCode.java @@ -2,6 +2,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; +import wooteco.prolog.roadmap.domain.RecommendedPost; import wooteco.prolog.session.domain.Mission; import wooteco.prolog.session.domain.Session; import wooteco.prolog.studylog.domain.TagName; @@ -71,6 +72,8 @@ public enum BadRequestCode { ESSAY_ANSWER_NOT_VALID_USER(8014, "본인이 작성한 답변만 수정할 수 있습니다."), ROADMAP_RECOMMENDED_POST_NOT_FOUND(8101, "해당 추천 포스트가 존재하지 않습니다."), + ROADMAP_RECOMMENDED_POST_INVALID_URL_LENGTH(8102, String.format( + "해당 추천 포스트의 URL 길이는 1 ~ %d여야 합니다.", RecommendedPost.URL_LENGTH_UPPER_BOUND)), FILE_NAME_EMPTY_EXCEPTION(9001, "파일 이름이 존재하지 않습니다."), UNSUPPORTED_FILE_EXTENSION_EXCEPTION(9002, "지원하지 않는 파일 확장자입니다."), diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java index 51c9b3f6b..69be9f18e 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java @@ -1,9 +1,9 @@ package wooteco.prolog.roadmap.domain; import lombok.AccessLevel; -import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; +import wooteco.prolog.common.exception.BadRequestException; import javax.persistence.Column; import javax.persistence.Entity; @@ -14,12 +14,18 @@ import javax.persistence.ManyToOne; import java.util.Objects; +import static java.util.Objects.hash; +import static java.util.Objects.isNull; +import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION; +import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_RECOMMENDED_POST_INVALID_URL_LENGTH; + @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor @Getter public class RecommendedPost { + public static final int URL_LENGTH_UPPER_BOUND = 512; + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -31,6 +37,24 @@ public class RecommendedPost { @JoinColumn(nullable = false) private Keyword keyword; + public RecommendedPost(final Long id, final String url, final Keyword keyword) { + final String trimmed = url.trim(); + validate(trimmed, keyword); + + this.id = id; + this.url = trimmed; + this.keyword = keyword; + } + + private void validate(final String url, final Keyword keyword) { + if (isNull(keyword)) { + throw new BadRequestException(ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION); + } + if (url.isEmpty() || url.length() > URL_LENGTH_UPPER_BOUND) { + throw new BadRequestException(ROADMAP_RECOMMENDED_POST_INVALID_URL_LENGTH); + } + } + public RecommendedPost(final String url, final Keyword keyword) { this(null, url, keyword); } @@ -58,6 +82,6 @@ public boolean equals(final Object o) { @Override public int hashCode() { - return Objects.hash(id); + return hash(id); } } diff --git a/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java b/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java index 3d6909d82..5b741a807 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java @@ -2,19 +2,72 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import wooteco.prolog.common.exception.BadRequestException; + +import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION; +import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_RECOMMENDED_POST_INVALID_URL_LENGTH; class RecommendedPostTest { + @Test + @DisplayName("추천 포스트 생성 시 키워드가 null이면 예외가 발생한다") + void construct_fail1() { + assertThatThrownBy(() -> new RecommendedPost("https://example.com", null)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION.getMessage()); + } + + @Test + @DisplayName("추천 포스트 생성 시 url의 길이가 공백 제외 0이면 예외가 발생한다") + void construct_fail2() { + //given + final String url = " "; + + //when, then + assertThatThrownBy(() -> new RecommendedPost(url, null)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION.getMessage()); + } + + @Test + @DisplayName("추천 포스트 생성 시 url의 길이가 공백 제외 512보다 크면 예외가 발생한다") + void construct_fail3() { + //given + final Keyword keyword = Keyword.createKeyword("name", "description", 1, 1, 1L, null); + final String url = Stream.generate(() -> "a") + .limit(513) + .collect(Collectors.joining()); + + //when, then + assertThatThrownBy(() -> new RecommendedPost(url, keyword)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ROADMAP_RECOMMENDED_POST_INVALID_URL_LENGTH.getMessage()); + } + + @Test + @DisplayName("추천 포스트 생성 테스트") + void construct() { + //given + final Keyword keyword = Keyword.createKeyword("name", "description", 1, 1, 1L, null); + final String url = "http://www.salmon"; + + //when, then + assertDoesNotThrow(() -> new RecommendedPost(url, keyword)); + } + @Test @DisplayName("삭제 기능 테스트") void remove() { //given final Keyword keyword = Keyword.createKeyword("이름", "설명", 1, 1, 1L, null); - final RecommendedPost recommendedPost = new RecommendedPost(1L, "https://example.com", null); - recommendedPost.addKeyword(keyword); + final RecommendedPost recommendedPost = new RecommendedPost("https://example.com", keyword); //when recommendedPost.remove(); From 7124da09f32607ca7abc1cb6ba09bfff317a2e24 Mon Sep 17 00:00:00 2001 From: nuyh Date: Fri, 11 Aug 2023 00:40:54 +0900 Subject: [PATCH 31/35] =?UTF-8?q?feat:=20List=EB=A1=9C=20=EA=B0=80?= =?UTF-8?q?=EC=A7=80=EB=8D=98=20=EC=97=B0=EA=B4=80=20=EA=B4=80=EA=B3=84=20?= =?UTF-8?q?Set=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD,=20Repository=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../prolog/steps/KeywordStepDefinitions.java | 6 ++-- .../roadmap/application/KeywordService.java | 13 ++++---- .../prolog/roadmap/domain/Keyword.java | 32 +++---------------- .../domain/repository/KeywordRepository.java | 22 +++++++++++-- .../application/KeywordServiceTest.java | 22 ++++++------- .../RecommendedPostServiceTest.java | 1 - .../repository/KeywordRepositoryTest.java | 4 +-- 7 files changed, 46 insertions(+), 54 deletions(-) diff --git a/backend/src/acceptanceTest/java/wooteco/prolog/steps/KeywordStepDefinitions.java b/backend/src/acceptanceTest/java/wooteco/prolog/steps/KeywordStepDefinitions.java index 4aff5b7f7..d6d5ef276 100644 --- a/backend/src/acceptanceTest/java/wooteco/prolog/steps/KeywordStepDefinitions.java +++ b/backend/src/acceptanceTest/java/wooteco/prolog/steps/KeywordStepDefinitions.java @@ -1,8 +1,5 @@ package wooteco.prolog.steps; -import static org.assertj.core.api.Assertions.assertThat; -import static wooteco.prolog.fixtures.KeywordAcceptanceFixture.KEYWORD_REQUEST; - import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; @@ -10,6 +7,9 @@ import wooteco.prolog.AcceptanceSteps; import wooteco.prolog.session.application.dto.SessionRequest; +import static org.assertj.core.api.Assertions.assertThat; +import static wooteco.prolog.fixtures.KeywordAcceptanceFixture.KEYWORD_REQUEST; + public class KeywordStepDefinitions extends AcceptanceSteps { /** diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java index 5d0578e48..87ea445b4 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java @@ -1,9 +1,5 @@ package wooteco.prolog.roadmap.application; -import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION; -import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_SESSION_NOT_FOUND_EXCEPTION; - -import java.util.List; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import wooteco.prolog.common.exception.BadRequestException; @@ -15,6 +11,11 @@ import wooteco.prolog.roadmap.domain.repository.KeywordRepository; import wooteco.prolog.session.domain.repository.SessionRepository; +import java.util.List; + +import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION; +import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_SESSION_NOT_FOUND_EXCEPTION; + @Transactional @Service public class KeywordService { @@ -55,7 +56,7 @@ public KeywordResponse findKeywordWithAllChild(final Long sessionId, final Long existSession(sessionId); existKeyword(keywordId); - Keyword keyword = keywordRepository.findFetchById(keywordId); + Keyword keyword = keywordRepository.findFetchByIdOrderBySeq(keywordId); return KeywordResponse.createWithAllChildResponse(keyword); } @@ -82,7 +83,7 @@ public void updateKeyword(final Long sessionId, final Long keywordId, public void deleteKeyword(final Long sessionId, final Long keywordId) { existSession(sessionId); - Keyword keyword = keywordRepository.findFetchById(keywordId); + Keyword keyword = keywordRepository.findFetchByIdOrderBySeq(keywordId); keywordRepository.delete(keyword); } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java index e7ee59f80..9fc136b14 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java @@ -1,23 +1,6 @@ package wooteco.prolog.roadmap.domain; -import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_AND_KEYWORD_PARENT_SAME_EXCEPTION; -import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_SEQUENCE_EXCEPTION; - -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; - import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -26,18 +9,11 @@ import javax.persistence.*; import java.util.HashSet; -import java.util.List; import java.util.Objects; import java.util.Set; -import javax.persistence.*; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -import javax.persistence.*; -import java.util.*; +import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_AND_KEYWORD_PARENT_SAME_EXCEPTION; +import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_SEQUENCE_EXCEPTION; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -62,8 +38,8 @@ public class Keyword { @Column(name = "session_id", nullable = false) private Long sessionId; - @OneToMany(mappedBy = "keyword", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) - private List recommendedPosts = new ArrayList<>(); + @OneToMany(mappedBy = "keyword", cascade = CascadeType.ALL, orphanRemoval = true) + private Set recommendedPosts = new HashSet<>(); @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "parent_id") diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java index d15d4e7ae..de65dac9d 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java @@ -2,20 +2,38 @@ import java.util.List; import java.util.Set; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import wooteco.prolog.roadmap.domain.Keyword; +import java.util.Optional; + +import static org.springframework.data.jpa.repository.EntityGraph.EntityGraphType.FETCH; + public interface KeywordRepository extends JpaRepository { + @EntityGraph(attributePaths = "recommendedPosts", type = FETCH) + Optional findById(final long id); + + @EntityGraph(attributePaths = "recommendedPosts", type = FETCH) + List findAll(); + @Query("SELECT k FROM Keyword k " + "LEFT JOIN FETCH k.children c " + + "LEFT JOIN FETCH k.recommendedPosts " + "LEFT JOIN FETCH k.parent p " - + "LEFT JOIN FETCH c.children lc WHERE k.id = :keywordId ORDER BY k.seq") - Keyword findFetchById(@Param("keywordId") Long keywordId); + + "LEFT JOIN FETCH p.recommendedPosts " + + "LEFT JOIN FETCH c.recommendedPosts " + + "LEFT JOIN FETCH c.children lc " + + "LEFT JOIN FETCH lc.recommendedPosts " + + "LEFT JOIN FETCH lc.children " + + "WHERE k.id = :keywordId ORDER BY k.seq") + Keyword findFetchByIdOrderBySeq(@Param("keywordId") Long keywordId); @Query("SELECT k FROM Keyword k " + + "LEFT JOIN FETCH k.recommendedPosts " + "WHERE k.sessionId = :sessionId AND k.parent IS NULL") List findBySessionIdAndParentIsNull(@Param("sessionId") Long sessionId); diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/KeywordServiceTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/KeywordServiceTest.java index 36477238a..2bb5f86ec 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/KeywordServiceTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/KeywordServiceTest.java @@ -1,14 +1,5 @@ package wooteco.prolog.roadmap.application; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION; - -import java.util.Collections; -import java.util.Optional; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -22,6 +13,13 @@ import wooteco.prolog.roadmap.domain.repository.KeywordRepository; import wooteco.prolog.session.domain.repository.SessionRepository; +import java.util.Collections; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.*; +import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION; + @ExtendWith(MockitoExtension.class) class KeywordServiceTest { @@ -105,13 +103,13 @@ void findKeywordWithAllChild() { when(keywordRepository.existsById(any())).thenReturn(true); Keyword keyword = new Keyword(1L, "", "", 1, 1, 1L, null, Collections.emptySet()); - when(keywordRepository.findFetchById(1L)).thenReturn(keyword); + when(keywordRepository.findFetchByIdOrderBySeq(1L)).thenReturn(keyword); //when keywordService.findKeywordWithAllChild(1L, 1L); //then - verify(keywordRepository, times(1)).findFetchById(any()); + verify(keywordRepository, times(1)).findFetchByIdOrderBySeq(any()); } @DisplayName("sessionId로 최상위 키워드들을 찾을 수 있다") @@ -178,7 +176,7 @@ void deleteKeyword() { keywordService.deleteKeyword(1L, 1L); //then - verify(keywordRepository, times(1)).findFetchById(any()); + verify(keywordRepository, times(1)).findFetchByIdOrderBySeq(any()); verify(keywordRepository, times(1)).delete(any()); } } diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java index 38f112528..2831d5b52 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java @@ -36,7 +36,6 @@ class RecommendedPostServiceTest { @Autowired private DataInitializer dataInitializer; - private Keyword keyword; @BeforeEach diff --git a/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java b/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java index 70255a8ba..1b4236b27 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java @@ -47,7 +47,7 @@ class KeywordRepositoryTest { em.clear(); // when - Keyword extract = keywordRepository.findFetchById(keywordParentId); + Keyword extract = keywordRepository.findFetchByIdOrderBySeq(keywordParentId); // then assertAll( @@ -79,7 +79,7 @@ class KeywordRepositoryTest { em.clear(); // when - Keyword extract = keywordRepository.findFetchById(keywordParentId); + Keyword extract = keywordRepository.findFetchByIdOrderBySeq(keywordParentId); // then assertAll( From 0137acb8f0a76b8e5e8099d9ad672f8fb34d98cc Mon Sep 17 00:00:00 2001 From: nuyh Date: Fri, 11 Aug 2023 01:12:53 +0900 Subject: [PATCH 32/35] =?UTF-8?q?fix:=20findById=20=EC=98=A4=EB=B2=84?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=94=A9=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../prolog/roadmap/domain/repository/KeywordRepository.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java index de65dac9d..fc0237937 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java @@ -1,21 +1,21 @@ package wooteco.prolog.roadmap.domain.repository; -import java.util.List; -import java.util.Set; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import wooteco.prolog.roadmap.domain.Keyword; +import java.util.List; import java.util.Optional; +import java.util.Set; import static org.springframework.data.jpa.repository.EntityGraph.EntityGraphType.FETCH; public interface KeywordRepository extends JpaRepository { @EntityGraph(attributePaths = "recommendedPosts", type = FETCH) - Optional findById(final long id); + Optional findById(final Long id); @EntityGraph(attributePaths = "recommendedPosts", type = FETCH) List findAll(); From 10839e5940b445d6e495599aa5985d45d6ca6ade Mon Sep 17 00:00:00 2001 From: nuyh Date: Fri, 11 Aug 2023 01:21:05 +0900 Subject: [PATCH 33/35] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EA=B0=84=EA=B2=B0=ED=99=94=20=EB=B0=8F?= =?UTF-8?q?=20=EA=B0=80=EB=8F=85=EC=84=B1=20=EC=A6=9D=EC=A7=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../RecommendedPostServiceTest.java | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java index 2831d5b52..5f3932c58 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java @@ -16,8 +16,6 @@ import wooteco.prolog.session.domain.Session; import wooteco.prolog.session.domain.repository.SessionRepository; -import java.util.Optional; - import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.SoftAssertions.assertSoftly; @@ -53,36 +51,32 @@ public void removeAll() { @DisplayName("추천 포스트 생성 테스트") void create() { //given - final RecommendedRequest request = new RecommendedRequest("https//:example.com"); + final RecommendedRequest request = new RecommendedRequest("https://example.com"); //when - Long recommendedPostId = recommendedPostService.create(keyword.getId(), request); - - final Keyword persistedKeyword = keywordRepository.findById(keyword.getId()).get(); - final RecommendedPost persistedPost = recommendedPostRepository.findById(recommendedPostId).get(); + recommendedPostService.create(keyword.getId(), request); //then - assertSoftly(softAssertions -> { - assertThat(persistedPost.getUrl()).isEqualTo(request.getUrl()); - assertThat(persistedKeyword.getRecommendedPosts()).containsExactly(persistedPost); - }); + assertThat(recommendedPostRepository.findAll()).hasSize(1); } @Test @DisplayName("추천 포스트 수정 테스트") void update() { //given - final RecommendedRequest request = new RecommendedRequest("https//:example.com"); - Long recommendedPostId = recommendedPostService.create(keyword.getId(), request); - String newUrl = "https//:example222.com"; - final RecommendedUpdateRequest updateRrequest = new RecommendedUpdateRequest(newUrl); + final Long recommendedPostId = recommendedPostService.create( + keyword.getId(), + new RecommendedRequest("https://example.com")); + + final String newUrl = "https://new.com"; + final RecommendedUpdateRequest updateRequest = new RecommendedUpdateRequest(newUrl); //when - recommendedPostService.update(recommendedPostId, updateRrequest); - Optional actual = recommendedPostRepository.findById(recommendedPostId); + recommendedPostService.update(recommendedPostId, updateRequest); //then - assertThat(actual.get().getUrl()).isEqualTo(newUrl); + final RecommendedPost post = recommendedPostRepository.findById(recommendedPostId).get(); + assertThat(post.getUrl()).isEqualTo(newUrl); } @Test @@ -90,7 +84,7 @@ void update() { void delete() { //given final RecommendedRequest request = new RecommendedRequest("https://example.com"); - Long recommendedPostId = recommendedPostService.create(keyword.getId(), request); + final Long recommendedPostId = recommendedPostService.create(keyword.getId(), request); //when recommendedPostService.delete(recommendedPostId); From c97e785889da3a18c449fdcf021f2cc5e98b39af Mon Sep 17 00:00:00 2001 From: nuyh Date: Fri, 11 Aug 2023 01:31:04 +0900 Subject: [PATCH 34/35] =?UTF-8?q?fix:=20RecommendedPost=EC=97=90=EC=84=9C?= =?UTF-8?q?=20url=EC=9D=98=20NPE=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../prolog/roadmap/domain/RecommendedPost.java | 8 ++++---- .../roadmap/domain/RecommendedPostTest.java | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java index 69be9f18e..727dca236 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/RecommendedPost.java @@ -14,6 +14,7 @@ import javax.persistence.ManyToOne; import java.util.Objects; +import static io.micrometer.core.instrument.util.StringUtils.isBlank; import static java.util.Objects.hash; import static java.util.Objects.isNull; import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION; @@ -38,11 +39,10 @@ public class RecommendedPost { private Keyword keyword; public RecommendedPost(final Long id, final String url, final Keyword keyword) { - final String trimmed = url.trim(); - validate(trimmed, keyword); + validate(url, keyword); this.id = id; - this.url = trimmed; + this.url = url.trim(); this.keyword = keyword; } @@ -50,7 +50,7 @@ private void validate(final String url, final Keyword keyword) { if (isNull(keyword)) { throw new BadRequestException(ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION); } - if (url.isEmpty() || url.length() > URL_LENGTH_UPPER_BOUND) { + if (isBlank(url) || url.trim().length() > URL_LENGTH_UPPER_BOUND) { throw new BadRequestException(ROADMAP_RECOMMENDED_POST_INVALID_URL_LENGTH); } } diff --git a/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java b/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java index 5b741a807..b9c234d9b 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/domain/RecommendedPostTest.java @@ -2,6 +2,8 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; import wooteco.prolog.common.exception.BadRequestException; import java.util.stream.Collectors; @@ -24,11 +26,20 @@ void construct_fail1() { .hasMessage(ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION.getMessage()); } + @ParameterizedTest + @NullAndEmptySource + @DisplayName("추천 포스트 생성 시 url이 null이면 예외가 발생한다") + void construct_fail2(final String url) { + assertThatThrownBy(() -> new RecommendedPost(url, null)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION.getMessage()); + } + @Test @DisplayName("추천 포스트 생성 시 url의 길이가 공백 제외 0이면 예외가 발생한다") - void construct_fail2() { + void construct_fail3() { //given - final String url = " "; + final String url = " "; //when, then assertThatThrownBy(() -> new RecommendedPost(url, null)) @@ -38,7 +49,7 @@ void construct_fail2() { @Test @DisplayName("추천 포스트 생성 시 url의 길이가 공백 제외 512보다 크면 예외가 발생한다") - void construct_fail3() { + void construct_fail4() { //given final Keyword keyword = Keyword.createKeyword("name", "description", 1, 1, 1L, null); final String url = Stream.generate(() -> "a") From e3c68a15534f9cf61a677c8a314792da1fd4b720 Mon Sep 17 00:00:00 2001 From: nuyh Date: Fri, 11 Aug 2023 01:35:37 +0900 Subject: [PATCH 35/35] =?UTF-8?q?style:=20=EA=B0=9C=ED=96=89=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1397 --- .../prolog/roadmap/application/RecommendedPostService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedPostService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedPostService.java index e4e24d95f..6d3c00cc1 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedPostService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/RecommendedPostService.java @@ -20,7 +20,8 @@ public class RecommendedPostService { private final RecommendedPostRepository recommendedPostRepository; private final KeywordRepository keywordRepository; - public RecommendedPostService(final RecommendedPostRepository recommendedPostRepository, final KeywordRepository keywordRepository) { + public RecommendedPostService(final RecommendedPostRepository recommendedPostRepository, + final KeywordRepository keywordRepository) { this.recommendedPostRepository = recommendedPostRepository; this.keywordRepository = keywordRepository; }