From 583647b40aab0c02fb22d592ca4caae3c05921a5 Mon Sep 17 00:00:00 2001 From: nuyh Date: Thu, 20 Jul 2023 17:57:13 +0900 Subject: [PATCH 01/25] =?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 02/25] =?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 03/25] =?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 04/25] =?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 05/25] =?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 06/25] =?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 07/25] =?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 08/25] =?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 09/25] =?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 10/25] =?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 11/25] =?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 12/25] =?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 13/25] =?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 14/25] =?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 15/25] =?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 16/25] =?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 17/25] =?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 18/25] =?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 19/25] =?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 20/25] =?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 21/25] =?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 22/25] =?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 23/25] =?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 24/25] =?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 25/25] =?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; }