From a9242e971a482812896863594b3b9976d31efcf2 Mon Sep 17 00:00:00 2001 From: harris Date: Sun, 23 Aug 2020 20:51:25 +0900 Subject: [PATCH 1/8] =?UTF-8?q?[#108]=20=EB=B2=94=EC=9A=A9=EA=B2=8C?= =?UTF-8?q?=EC=8B=9C=ED=8C=90=20=EB=A7=8C=EB=93=A4=EA=B8=B0=20-=20?= =?UTF-8?q?=EB=B2=94=EC=9A=A9=EA=B2=8C=EC=8B=9C=ED=8C=90=20CRUD=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20-=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../article/api/ArticleController.java | 37 ++++++++ .../article/api/dto/ArticleRequestDto.java | 27 ++++++ .../article/api/dto/ArticleResponseDto.java | 26 ++++++ .../community/article/entity/Article.java | 27 ++++++ .../article/entity/ArticleRepository.java | 6 ++ .../article/exception/ArticleException.java | 7 ++ .../article/service/ArticleService.java | 38 ++++++++ .../community/article/entity/ArticleTest.java | 15 ++++ .../article/service/ArticleServiceTest.java | 89 +++++++++++++++++++ 9 files changed, 272 insertions(+) create mode 100644 community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java create mode 100644 community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleRequestDto.java create mode 100644 community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleResponseDto.java create mode 100644 community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/Article.java create mode 100644 community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/ArticleRepository.java create mode 100644 community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/exception/ArticleException.java create mode 100644 community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/service/ArticleService.java create mode 100644 community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/entity/ArticleTest.java create mode 100644 community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/service/ArticleServiceTest.java diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java new file mode 100644 index 00000000..50f51a1c --- /dev/null +++ b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java @@ -0,0 +1,37 @@ +package org.kiworkshop.community.article.api; + +import lombok.RequiredArgsConstructor; +import org.kiworkshop.community.article.api.dto.ArticleRequestDto; +import org.kiworkshop.community.article.api.dto.ArticleResponseDto; +import org.kiworkshop.community.article.service.ArticleService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/article") +@RequiredArgsConstructor +public class ArticleController { + private final ArticleService articleService; + + @GetMapping("/{id}") + public ResponseEntity read(@PathVariable Long id) { + return ResponseEntity.ok(articleService.read(id)); + } + + @PostMapping + public ResponseEntity create(@RequestBody ArticleRequestDto articleRequestDto) { + return ResponseEntity.ok(articleService.create(articleRequestDto)); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Long id) { + articleService.delete(id); + return ResponseEntity.ok().build(); + } + + @PutMapping("/{id}") + public ResponseEntity update(@PathVariable Long id, @RequestBody ArticleRequestDto articleRequestDto) { + articleService.update(id, articleRequestDto); + return ResponseEntity.ok().build(); + } +} diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleRequestDto.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleRequestDto.java new file mode 100644 index 00000000..ea3d872c --- /dev/null +++ b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleRequestDto.java @@ -0,0 +1,27 @@ +package org.kiworkshop.community.article.api.dto; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.kiworkshop.community.article.entity.Article; + +@Getter +@NoArgsConstructor +public class ArticleRequestDto { + private String title; + + @Builder + public ArticleRequestDto(String title, String content) { + this.title = title; + this.content = content; + } + + private String content; + + public Article toEntity() { + return Article.builder() + .title(title) + .content(content) + .build(); + } +} diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleResponseDto.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleResponseDto.java new file mode 100644 index 00000000..0ceb3348 --- /dev/null +++ b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleResponseDto.java @@ -0,0 +1,26 @@ +package org.kiworkshop.community.article.api.dto; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.kiworkshop.community.article.entity.Article; + +@Getter +@NoArgsConstructor +public class ArticleResponseDto { + private String title; + private String content; + + @Builder + public ArticleResponseDto(String title, String content) { + this.title = title; + this.content = content; + } + + public static ArticleResponseDto from(Article article) { + return ArticleResponseDto.builder() + .title(article.getTitle()) + .content(article.getContent()) + .build(); + } +} diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/Article.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/Article.java new file mode 100644 index 00000000..7b4fb9ec --- /dev/null +++ b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/Article.java @@ -0,0 +1,27 @@ +package org.kiworkshop.community.article.entity; + +import lombok.Builder; +import lombok.Getter; +import org.kiworkshop.community.common.domain.BaseEntity; + +import javax.persistence.Entity; + +@Entity +@Getter +public class Article extends BaseEntity { + private String title; + private String content; + private Long userId; + + @Builder + public Article(String title, String content, Long userId) { + this.title = title; + this.content = content; + this.userId = userId; + } + + public void update(Article article) { + this.title = article.title; + this.content = article.content; + } +} diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/ArticleRepository.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/ArticleRepository.java new file mode 100644 index 00000000..55faf8fe --- /dev/null +++ b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/ArticleRepository.java @@ -0,0 +1,6 @@ +package org.kiworkshop.community.article.entity; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ArticleRepository extends JpaRepository { +} diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/exception/ArticleException.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/exception/ArticleException.java new file mode 100644 index 00000000..1cab8af3 --- /dev/null +++ b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/exception/ArticleException.java @@ -0,0 +1,7 @@ +package org.kiworkshop.community.article.exception; + +public class ArticleException extends RuntimeException { + public ArticleException(String s) { + super(s); + } +} diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/service/ArticleService.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/service/ArticleService.java new file mode 100644 index 00000000..099ee67d --- /dev/null +++ b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/service/ArticleService.java @@ -0,0 +1,38 @@ +package org.kiworkshop.community.article.service; + +import lombok.RequiredArgsConstructor; +import org.kiworkshop.community.article.api.dto.ArticleRequestDto; +import org.kiworkshop.community.article.api.dto.ArticleResponseDto; +import org.kiworkshop.community.article.entity.Article; +import org.kiworkshop.community.article.entity.ArticleRepository; +import org.kiworkshop.community.article.exception.ArticleException; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ArticleService { + private final ArticleRepository articleRepository; + + public Long create(ArticleRequestDto articleRequestDto) { + Article article = articleRepository.save(articleRequestDto.toEntity()); + return article.getId(); + } + + public ArticleResponseDto read(Long id) { + Article article = findById(id); + return ArticleResponseDto.from(article); + } + + public void update(Long id, ArticleRequestDto articleRequestDto) { + Article article = findById(id); + article.update(articleRequestDto.toEntity()); + } + + public void delete(Long id) { + articleRepository.deleteById(id); + } + + private Article findById(Long id) { + return articleRepository.findById(id).orElseThrow(() -> new ArticleException("게시물을 찾을 수 없습니다.")); + } +} diff --git a/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/entity/ArticleTest.java b/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/entity/ArticleTest.java new file mode 100644 index 00000000..462b932f --- /dev/null +++ b/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/entity/ArticleTest.java @@ -0,0 +1,15 @@ +package org.kiworkshop.community.article.entity; + +import org.springframework.test.util.ReflectionTestUtils; + +import java.time.ZonedDateTime; + +public class ArticleTest { + public static Article createArticleTestFixture() { + Article article = Article.builder().title("title").content("content").build(); + ReflectionTestUtils.setField(article, "id", 1L); + ReflectionTestUtils.setField(article, "updatedAt", ZonedDateTime.now()); + ReflectionTestUtils.setField(article, "createdAt", ZonedDateTime.now()); + return article; + } +} \ No newline at end of file diff --git a/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/service/ArticleServiceTest.java b/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/service/ArticleServiceTest.java new file mode 100644 index 00000000..5d88834f --- /dev/null +++ b/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/service/ArticleServiceTest.java @@ -0,0 +1,89 @@ +package org.kiworkshop.community.article.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.kiworkshop.community.article.api.dto.ArticleRequestDto; +import org.kiworkshop.community.article.api.dto.ArticleResponseDto; +import org.kiworkshop.community.article.entity.Article; +import org.kiworkshop.community.article.entity.ArticleRepository; +import org.kiworkshop.community.article.exception.ArticleException; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.kiworkshop.community.article.entity.ArticleTest.createArticleTestFixture; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; + +@ExtendWith(MockitoExtension.class) +class ArticleServiceTest { + private ArticleService articleService; + @Mock + private ArticleRepository articleRepository; + + @BeforeEach + void setUp() { + this.articleService = new ArticleService(articleRepository); + } + + @Test + void create() { + //given + ArticleRequestDto articleRequestDto = ArticleRequestDto.builder().title("title").content("content").build(); + Article article = createArticleTestFixture(); + given(articleRepository.save(any(Article.class))).willReturn(article); + //when + Long id = articleService.create(articleRequestDto); + //then + assertThat(id).isNotNull(); + then(articleRepository).should().save(any(Article.class)); + } + + @Test + void read() { + //given + Article article = createArticleTestFixture(); + given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); + //when + ArticleResponseDto articleResponseDto = articleService.read(1L); + //then + assertThat(articleResponseDto.getTitle()).isEqualTo(article.getTitle()); + assertThat(articleResponseDto.getContent()).isEqualTo(article.getContent()); + then(articleRepository).should().findById(anyLong()); + } + + @Test + void update() { + //given + ArticleRequestDto articleRequestDto = ArticleRequestDto.builder().title("title1").content("content1").build(); + Article article = createArticleTestFixture(); + given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); + //when + articleService.update(1L, articleRequestDto); + //then + then(articleRepository).should().findById(anyLong()); + } + + @Test + void delete() { + //when + articleService.delete(1L); + //then + then(articleRepository).should().deleteById(anyLong()); + } + + @Test + void findByIdThrowsException() { + //given + ArticleRequestDto articleRequestDto = ArticleRequestDto.builder().title("title1").content("content1").build(); + //when & then + assertThrows(ArticleException.class, () -> articleService.read(1L)); + assertThrows(ArticleException.class, () -> articleService.update(1L, articleRequestDto)); + } +} \ No newline at end of file From 25326e65eec7109408723dff80f4ba10e75476ec Mon Sep 17 00:00:00 2001 From: harrisleesh Date: Mon, 31 Aug 2020 22:48:28 +0900 Subject: [PATCH 2/8] =?UTF-8?q?[#108]=20Article=EC=97=90=EC=84=9C=20userid?= =?UTF-8?q?=EB=A5=BC=20username=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Principal을 사용해서 user 검증 --- .../article/api/ArticleController.java | 14 +++--- .../article/api/dto/ArticleRequestDto.java | 6 +-- .../community/article/entity/Article.java | 20 +++++++-- .../article/service/ArticleService.java | 19 +++++--- .../community/article/entity/ArticleTest.java | 2 +- .../article/service/ArticleServiceTest.java | 44 ++++++++++++++++--- 6 files changed, 81 insertions(+), 24 deletions(-) diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java index 50f51a1c..e6be2e9a 100644 --- a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java +++ b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java @@ -7,6 +7,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.security.Principal; + @RestController @RequestMapping("/article") @RequiredArgsConstructor @@ -19,19 +21,19 @@ public ResponseEntity read(@PathVariable Long id) { } @PostMapping - public ResponseEntity create(@RequestBody ArticleRequestDto articleRequestDto) { - return ResponseEntity.ok(articleService.create(articleRequestDto)); + public ResponseEntity create(@RequestBody ArticleRequestDto articleRequestDto, Principal principal) { + return ResponseEntity.ok(articleService.create(articleRequestDto, principal)); } @DeleteMapping("/{id}") - public ResponseEntity delete(@PathVariable Long id) { - articleService.delete(id); + public ResponseEntity delete(@PathVariable Long id, Principal principal) { + articleService.delete(id, principal); return ResponseEntity.ok().build(); } @PutMapping("/{id}") - public ResponseEntity update(@PathVariable Long id, @RequestBody ArticleRequestDto articleRequestDto) { - articleService.update(id, articleRequestDto); + public ResponseEntity update(@PathVariable Long id, @RequestBody ArticleRequestDto articleRequestDto, Principal principal) { + articleService.update(id, articleRequestDto, principal); return ResponseEntity.ok().build(); } } diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleRequestDto.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleRequestDto.java index ea3d872c..277085c5 100644 --- a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleRequestDto.java +++ b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleRequestDto.java @@ -9,6 +9,7 @@ @NoArgsConstructor public class ArticleRequestDto { private String title; + private String content; @Builder public ArticleRequestDto(String title, String content) { @@ -16,12 +17,11 @@ public ArticleRequestDto(String title, String content) { this.content = content; } - private String content; - - public Article toEntity() { + public Article toEntity(String username) { return Article.builder() .title(title) .content(content) + .username(username) .build(); } } diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/Article.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/Article.java index 7b4fb9ec..1f25e502 100644 --- a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/Article.java +++ b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/Article.java @@ -3,25 +3,39 @@ import lombok.Builder; import lombok.Getter; import org.kiworkshop.community.common.domain.BaseEntity; +import org.springframework.util.Assert; +import javax.persistence.Column; import javax.persistence.Entity; @Entity @Getter public class Article extends BaseEntity { + @Column(nullable = false) private String title; + @Column(nullable = false) private String content; - private Long userId; + @Column(nullable = false) + private String username; @Builder - public Article(String title, String content, Long userId) { + private Article(String title, String content, String username) { + Assert.hasLength(title, "title must have length."); + Assert.hasLength(content, "content must have length."); + Assert.hasLength(username, "username must have length."); this.title = title; this.content = content; - this.userId = userId; + this.username = username; } public void update(Article article) { + // TODO: 20. 8. 31. Error Code 가 400이 되어야 할지? 403이 되어야 할지 고민 + Assert.isTrue(this.username.equals(article.username), "unauthorized username"); this.title = article.title; this.content = article.content; } + + public boolean isAuthor(String name) { + return this.username.equals(name); + } } diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/service/ArticleService.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/service/ArticleService.java index 099ee67d..26e71ee2 100644 --- a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/service/ArticleService.java +++ b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/service/ArticleService.java @@ -8,13 +8,15 @@ import org.kiworkshop.community.article.exception.ArticleException; import org.springframework.stereotype.Service; +import java.security.Principal; + @Service @RequiredArgsConstructor public class ArticleService { private final ArticleRepository articleRepository; - public Long create(ArticleRequestDto articleRequestDto) { - Article article = articleRepository.save(articleRequestDto.toEntity()); + public Long create(ArticleRequestDto articleRequestDto, Principal principal) { + Article article = articleRepository.save(articleRequestDto.toEntity(principal.getName())); return article.getId(); } @@ -23,13 +25,18 @@ public ArticleResponseDto read(Long id) { return ArticleResponseDto.from(article); } - public void update(Long id, ArticleRequestDto articleRequestDto) { + public void update(Long id, ArticleRequestDto articleRequestDto, Principal principal) { Article article = findById(id); - article.update(articleRequestDto.toEntity()); + article.update(articleRequestDto.toEntity(principal.getName())); } - public void delete(Long id) { - articleRepository.deleteById(id); + public void delete(Long id, Principal principal) { + Article article = findById(id); + // TODO: 20. 8. 31. soft delete를 할지? hard delete를 할지 고민 + if (!article.isAuthor(principal.getName())) { + throw new IllegalArgumentException("unauthorized user"); + } + articleRepository.delete(article); } private Article findById(Long id) { diff --git a/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/entity/ArticleTest.java b/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/entity/ArticleTest.java index 462b932f..fce04bac 100644 --- a/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/entity/ArticleTest.java +++ b/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/entity/ArticleTest.java @@ -6,7 +6,7 @@ public class ArticleTest { public static Article createArticleTestFixture() { - Article article = Article.builder().title("title").content("content").build(); + Article article = Article.builder().title("title").content("content").username("username").build(); ReflectionTestUtils.setField(article, "id", 1L); ReflectionTestUtils.setField(article, "updatedAt", ZonedDateTime.now()); ReflectionTestUtils.setField(article, "createdAt", ZonedDateTime.now()); diff --git a/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/service/ArticleServiceTest.java b/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/service/ArticleServiceTest.java index 5d88834f..6be3db86 100644 --- a/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/service/ArticleServiceTest.java +++ b/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/service/ArticleServiceTest.java @@ -11,6 +11,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.security.Principal; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @@ -26,6 +27,8 @@ class ArticleServiceTest { private ArticleService articleService; @Mock private ArticleRepository articleRepository; + @Mock + private Principal principal; @BeforeEach void setUp() { @@ -38,11 +41,13 @@ void create() { ArticleRequestDto articleRequestDto = ArticleRequestDto.builder().title("title").content("content").build(); Article article = createArticleTestFixture(); given(articleRepository.save(any(Article.class))).willReturn(article); + given(principal.getName()).willReturn("username"); //when - Long id = articleService.create(articleRequestDto); + Long id = articleService.create(articleRequestDto, principal); //then assertThat(id).isNotNull(); then(articleRepository).should().save(any(Article.class)); + then(principal).should().getName(); } @Test @@ -64,18 +69,47 @@ void update() { ArticleRequestDto articleRequestDto = ArticleRequestDto.builder().title("title1").content("content1").build(); Article article = createArticleTestFixture(); given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); + given(principal.getName()).willReturn("username"); //when - articleService.update(1L, articleRequestDto); + articleService.update(1L, articleRequestDto, principal); //then then(articleRepository).should().findById(anyLong()); } + @Test + void update_throw_exception() { + //given + ArticleRequestDto articleRequestDto = ArticleRequestDto.builder().title("title1").content("content1").build(); + Article article = createArticleTestFixture(); + given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); + given(principal.getName()).willReturn("username1"); + //when & then + assertThrows(IllegalArgumentException.class, () -> articleService.update(1L, articleRequestDto, principal)); + then(articleRepository).should().findById(anyLong()); + } + @Test void delete() { + //given + Article article = createArticleTestFixture(); + given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); + given(principal.getName()).willReturn("username"); //when - articleService.delete(1L); + articleService.delete(1L, principal); //then - then(articleRepository).should().deleteById(anyLong()); + then(articleRepository).should().findById(anyLong()); + then(articleRepository).should().delete(article); + } + + @Test + void delete_throw_exception() { + //given + Article article = createArticleTestFixture(); + given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); + given(principal.getName()).willReturn("username1"); + //when + assertThrows(IllegalArgumentException.class, () -> articleService.delete(1L, principal)); + then(articleRepository).should().findById(anyLong()); } @Test @@ -84,6 +118,6 @@ void findByIdThrowsException() { ArticleRequestDto articleRequestDto = ArticleRequestDto.builder().title("title1").content("content1").build(); //when & then assertThrows(ArticleException.class, () -> articleService.read(1L)); - assertThrows(ArticleException.class, () -> articleService.update(1L, articleRequestDto)); + assertThrows(ArticleException.class, () -> articleService.update(1L, articleRequestDto, principal)); } } \ No newline at end of file From 33de2db265dfb9d6b0679f70a4f076b3a9df921b Mon Sep 17 00:00:00 2001 From: "jaeju.jang" Date: Wed, 9 Sep 2020 15:49:17 +0900 Subject: [PATCH 3/8] =?UTF-8?q?[#108]=20article=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=BD=94=EC=96=B4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/core-article/build.gradle | 15 ++++++++ .../core/core-article/lombok.config | 3 ++ .../article/dto/ArticleRequestDto.java | 16 ++++++++ .../article/dto/ArticleResponseDto.java | 31 +++++++++++++++ .../article/dto/ArticleRequestDtoTest.java | 38 +++++++++++++++++++ .../article/dto/ArticleRequestDtoFixture.java | 15 ++++++++ .../dto/ArticleResponseDtoFixture.java | 16 ++++++++ community-backend/settings.gradle | 2 + 8 files changed, 136 insertions(+) create mode 100644 community-backend/core/core-article/build.gradle create mode 100644 community-backend/core/core-article/lombok.config create mode 100644 community-backend/core/core-article/src/main/java/org/kiworkshop/community/article/dto/ArticleRequestDto.java create mode 100644 community-backend/core/core-article/src/main/java/org/kiworkshop/community/article/dto/ArticleResponseDto.java create mode 100644 community-backend/core/core-article/src/test/java/org/kiworkshop/community/article/dto/ArticleRequestDtoTest.java create mode 100644 community-backend/core/core-article/src/testFixtures/java/org/kiworkshop/community/article/dto/ArticleRequestDtoFixture.java create mode 100644 community-backend/core/core-article/src/testFixtures/java/org/kiworkshop/community/article/dto/ArticleResponseDtoFixture.java diff --git a/community-backend/core/core-article/build.gradle b/community-backend/core/core-article/build.gradle new file mode 100644 index 00000000..51c419ea --- /dev/null +++ b/community-backend/core/core-article/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java' +} + +version 'unspecified' + +repositories { + mavenCentral() +} + +dependencies { + implementation "javax.validation:validation-api:${javaxValidationApiVersion}" + + testFixturesImplementation "org.springframework:spring-test:${springTestVersion}" +} diff --git a/community-backend/core/core-article/lombok.config b/community-backend/core/core-article/lombok.config new file mode 100644 index 00000000..189c0bef --- /dev/null +++ b/community-backend/core/core-article/lombok.config @@ -0,0 +1,3 @@ +# This file is generated by the 'io.freefair.lombok' Gradle plugin +config.stopBubbling = true +lombok.addLombokGeneratedAnnotation = true diff --git a/community-backend/core/core-article/src/main/java/org/kiworkshop/community/article/dto/ArticleRequestDto.java b/community-backend/core/core-article/src/main/java/org/kiworkshop/community/article/dto/ArticleRequestDto.java new file mode 100644 index 00000000..ee4a83c5 --- /dev/null +++ b/community-backend/core/core-article/src/main/java/org/kiworkshop/community/article/dto/ArticleRequestDto.java @@ -0,0 +1,16 @@ +package org.kiworkshop.community.article.dto; + + +import javax.validation.constraints.NotEmpty; + +import lombok.Getter; + +@Getter +public class ArticleRequestDto { + @NotEmpty + private String title; + @NotEmpty + private String content; + @NotEmpty + private String username; +} diff --git a/community-backend/core/core-article/src/main/java/org/kiworkshop/community/article/dto/ArticleResponseDto.java b/community-backend/core/core-article/src/main/java/org/kiworkshop/community/article/dto/ArticleResponseDto.java new file mode 100644 index 00000000..4473f228 --- /dev/null +++ b/community-backend/core/core-article/src/main/java/org/kiworkshop/community/article/dto/ArticleResponseDto.java @@ -0,0 +1,31 @@ +package org.kiworkshop.community.article.dto; + +import java.time.ZonedDateTime; + +import lombok.Builder; + +public class ArticleResponseDto { + private Long id; + private String title; + private String content; + private String username; + private ZonedDateTime createdAt; + private ZonedDateTime updatedAt; + + @Builder + private ArticleResponseDto( + Long id, + String title, + String content, + String username, + ZonedDateTime createdAt, + ZonedDateTime updatedAt + ) { + this.id = id; + this.title = title; + this.content =content; + this.username = username; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } +} diff --git a/community-backend/core/core-article/src/test/java/org/kiworkshop/community/article/dto/ArticleRequestDtoTest.java b/community-backend/core/core-article/src/test/java/org/kiworkshop/community/article/dto/ArticleRequestDtoTest.java new file mode 100644 index 00000000..be3ea5cd --- /dev/null +++ b/community-backend/core/core-article/src/test/java/org/kiworkshop/community/article/dto/ArticleRequestDtoTest.java @@ -0,0 +1,38 @@ +package org.kiworkshop.community.article.dto; + +import static org.assertj.core.api.BDDAssertions.then; + +import javax.validation.Validation; +import javax.validation.Validator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.springframework.test.util.ReflectionTestUtils; + +class ArticleRequestDtoTest { + + private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + private ArticleRequestDto articleRequestDto; + + @BeforeEach + void setUp() { + articleRequestDto = ArticleRequestDtoFixture.get(); + } + + @ParameterizedTest + @CsvSource({ + "title,empty", + "content,empty", + "username,empty" + }) + void validate_InvalidValue_ThrowException(String fieldName, String value) { + // given + String valueToInject = "empty".equals(value) ? "" : value; + ReflectionTestUtils.setField(articleRequestDto, fieldName, valueToInject); + + var violations = validator.validate(articleRequestDto); + + then(violations.size()).isEqualTo(1); + } +} diff --git a/community-backend/core/core-article/src/testFixtures/java/org/kiworkshop/community/article/dto/ArticleRequestDtoFixture.java b/community-backend/core/core-article/src/testFixtures/java/org/kiworkshop/community/article/dto/ArticleRequestDtoFixture.java new file mode 100644 index 00000000..13042468 --- /dev/null +++ b/community-backend/core/core-article/src/testFixtures/java/org/kiworkshop/community/article/dto/ArticleRequestDtoFixture.java @@ -0,0 +1,15 @@ +package org.kiworkshop.community.article.dto; + +import org.springframework.test.util.ReflectionTestUtils; + +public class ArticleRequestDtoFixture { + public static ArticleRequestDto get() { + ArticleRequestDto articleRequestDto = new ArticleRequestDto(); + + ReflectionTestUtils.setField(articleRequestDto, "title", "title"); + ReflectionTestUtils.setField(articleRequestDto, "content", "content"); + ReflectionTestUtils.setField(articleRequestDto, "username", "username"); + + return articleRequestDto; + } +} diff --git a/community-backend/core/core-article/src/testFixtures/java/org/kiworkshop/community/article/dto/ArticleResponseDtoFixture.java b/community-backend/core/core-article/src/testFixtures/java/org/kiworkshop/community/article/dto/ArticleResponseDtoFixture.java new file mode 100644 index 00000000..96a1aee0 --- /dev/null +++ b/community-backend/core/core-article/src/testFixtures/java/org/kiworkshop/community/article/dto/ArticleResponseDtoFixture.java @@ -0,0 +1,16 @@ +package org.kiworkshop.community.article.dto; + +import java.time.ZonedDateTime; + +public class ArticleResponseDtoFixture { + public static ArticleResponseDto get() { + return ArticleResponseDto.builder() + .id(1L) + .title("title") + .content("content") + .username("username") + .createdAt(ZonedDateTime.now()) + .updatedAt(ZonedDateTime.now()) + .build(); + } +} diff --git a/community-backend/settings.gradle b/community-backend/settings.gradle index 10ae2096..97e55d91 100644 --- a/community-backend/settings.gradle +++ b/community-backend/settings.gradle @@ -25,4 +25,6 @@ include 'core:core-user-resource' include 'in-system-available:common-web' include 'module:module-validation' +include 'core:core-article' +findProject(':core:core-article')?.name = 'core-article' From 9368a86b428060c07fed3fbdca0e22486c048973 Mon Sep 17 00:00:00 2001 From: "jaeju.jang" Date: Wed, 9 Sep 2020 17:26:31 +0900 Subject: [PATCH 4/8] =?UTF-8?q?[#108]=20article=20domain=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/app-monolith/build.gradle | 2 + .../article/dto/ArticleResponseDto.java | 2 + .../domain/domain-article/build.gradle | 21 ++++ .../domain/domain-article/lombok.config | 3 + .../exception/ArticleNotFoundException.java | 9 ++ .../article/domain/model/Article.java | 68 ++++++++++ .../domain/model/ArticleRepository.java | 6 + .../domain/service/ArticleConverter.java | 30 +++++ .../domain/service/ArticleService.java | 47 +++++++ .../domain/service/ArticleServiceTest.java | 117 ++++++++++++++++++ .../article/domain/model/ArticleFixture.java | 24 ++++ community-backend/settings.gradle | 4 +- 12 files changed, 331 insertions(+), 2 deletions(-) create mode 100644 community-backend/domain/domain-article/build.gradle create mode 100644 community-backend/domain/domain-article/lombok.config create mode 100644 community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/exception/ArticleNotFoundException.java create mode 100644 community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/Article.java create mode 100644 community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/ArticleRepository.java create mode 100644 community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/service/ArticleConverter.java create mode 100644 community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/service/ArticleService.java create mode 100644 community-backend/domain/domain-article/src/test/java/org/kiworkshop/community/article/domain/service/ArticleServiceTest.java create mode 100644 community-backend/domain/domain-article/src/testFixtures/java/org/kiworkshop/community/article/domain/model/ArticleFixture.java diff --git a/community-backend/app/app-monolith/build.gradle b/community-backend/app/app-monolith/build.gradle index 3102ede3..02d129cd 100644 --- a/community-backend/app/app-monolith/build.gradle +++ b/community-backend/app/app-monolith/build.gradle @@ -25,6 +25,8 @@ dependencies { implementation project(':core:core-user-resource') implementation project(':domain:domain-user-resource') + implementation project(':core:core-article') + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-actuator' diff --git a/community-backend/core/core-article/src/main/java/org/kiworkshop/community/article/dto/ArticleResponseDto.java b/community-backend/core/core-article/src/main/java/org/kiworkshop/community/article/dto/ArticleResponseDto.java index 4473f228..56671f83 100644 --- a/community-backend/core/core-article/src/main/java/org/kiworkshop/community/article/dto/ArticleResponseDto.java +++ b/community-backend/core/core-article/src/main/java/org/kiworkshop/community/article/dto/ArticleResponseDto.java @@ -3,7 +3,9 @@ import java.time.ZonedDateTime; import lombok.Builder; +import lombok.Getter; +@Getter public class ArticleResponseDto { private Long id; private String title; diff --git a/community-backend/domain/domain-article/build.gradle b/community-backend/domain/domain-article/build.gradle new file mode 100644 index 00000000..cc2dc428 --- /dev/null +++ b/community-backend/domain/domain-article/build.gradle @@ -0,0 +1,21 @@ +plugins { + id 'java' +} + +version 'unspecified' + +repositories { + mavenCentral() +} + +dependencies { + implementation project(':domain:domain-common') + implementation project(':core:core-article') + + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + + testImplementation testFixtures(project(":core:core-article")) + testFixturesImplementation project(":core:core-article") + testFixturesImplementation project(":domain:domain-common") + testFixturesImplementation "org.springframework:spring-test:${springTestVersion}" +} diff --git a/community-backend/domain/domain-article/lombok.config b/community-backend/domain/domain-article/lombok.config new file mode 100644 index 00000000..189c0bef --- /dev/null +++ b/community-backend/domain/domain-article/lombok.config @@ -0,0 +1,3 @@ +# This file is generated by the 'io.freefair.lombok' Gradle plugin +config.stopBubbling = true +lombok.addLombokGeneratedAnnotation = true diff --git a/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/exception/ArticleNotFoundException.java b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/exception/ArticleNotFoundException.java new file mode 100644 index 00000000..3194a95f --- /dev/null +++ b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/exception/ArticleNotFoundException.java @@ -0,0 +1,9 @@ +package org.kiworkshop.community.article.domain.exception; + +import javax.persistence.EntityNotFoundException; + +public class ArticleNotFoundException extends EntityNotFoundException { + public ArticleNotFoundException(Long id) { + super("Article of id:" + id + " is not found."); + } +} diff --git a/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/Article.java b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/Article.java new file mode 100644 index 00000000..39fb763d --- /dev/null +++ b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/Article.java @@ -0,0 +1,68 @@ +package org.kiworkshop.community.article.domain.model; + +import javax.persistence.Column; +import javax.persistence.Entity; + +import org.kiworkshop.community.common.domain.BaseEntity; +import org.springframework.util.Assert; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@Getter +@Entity +public class Article extends BaseEntity { + private static final String DELETED_ARTICLE_MESSAGE = "삭제된 게시글입니다."; + + @Column(nullable = false) + private String title; + @Column(nullable = false) + private String content; + @Column(nullable = false) + private String username; + private boolean active = true; + + @Builder + private Article(String title, String content, String username) { + Assert.hasLength(title, "title must have length."); + Assert.hasLength(content, "content must have length."); + Assert.hasLength(username, "username must have length."); + this.title = title; + this.content = content; + this.username = username; + } + + public void update(Article article) { + // TODO: 20. 8. 31. Error Code 가 400이 되어야 할지? 403이 되어야 할지 고민 + Assert.isTrue(this.username.equals(article.username), "unauthorized username"); + this.title = article.title; + this.content = article.content; + } + + public boolean isAuthor(String name) { + return this.username.equals(name); + } + + public void deactivate(String username) { + if (!this.username.equals(username)) { + throw new IllegalArgumentException("unauthorized user"); + } + this.active = false; + } + + public String getTitle() { + if (this.active) { + return DELETED_ARTICLE_MESSAGE; + } + return this.title; + } + + public String getContent() { + if (this.active) { + return DELETED_ARTICLE_MESSAGE; + } + return this.content; + } +} diff --git a/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/ArticleRepository.java b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/ArticleRepository.java new file mode 100644 index 00000000..3bef2ff0 --- /dev/null +++ b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/ArticleRepository.java @@ -0,0 +1,6 @@ +package org.kiworkshop.community.article.domain.model; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ArticleRepository extends JpaRepository { +} diff --git a/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/service/ArticleConverter.java b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/service/ArticleConverter.java new file mode 100644 index 00000000..fe7e82b8 --- /dev/null +++ b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/service/ArticleConverter.java @@ -0,0 +1,30 @@ +package org.kiworkshop.community.article.domain.service; + +import org.kiworkshop.community.article.domain.model.Article; +import org.kiworkshop.community.article.dto.ArticleRequestDto; +import org.kiworkshop.community.article.dto.ArticleResponseDto; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ArticleConverter { + public static Article toEntity(ArticleRequestDto articleRequestDto) { + return Article.builder() + .title(articleRequestDto.getTitle()) + .content(articleRequestDto.getContent()) + .username(articleRequestDto.getUsername()) + .build(); + } + + public static ArticleResponseDto from(Article article) { + return ArticleResponseDto.builder() + .id(article.getId()) + .title(article.getTitle()) + .content(article.getContent()) + .username(article.getUsername()) + .createdAt(article.getCreatedAt()) + .updatedAt(article.getUpdatedAt()) + .build(); + } +} diff --git a/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/service/ArticleService.java b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/service/ArticleService.java new file mode 100644 index 00000000..4ac054bf --- /dev/null +++ b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/service/ArticleService.java @@ -0,0 +1,47 @@ +package org.kiworkshop.community.article.domain.service; + +import java.security.Principal; + +import org.kiworkshop.community.article.domain.exception.ArticleNotFoundException; +import org.kiworkshop.community.article.domain.model.Article; +import org.kiworkshop.community.article.domain.model.ArticleRepository; +import org.kiworkshop.community.article.dto.ArticleRequestDto; +import org.kiworkshop.community.article.dto.ArticleResponseDto; +import org.springframework.stereotype.Service; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Service +public class ArticleService { + private final ArticleRepository articleRepository; + + public Long create(ArticleRequestDto articleRequestDto) { + Article article = articleRepository.save(ArticleConverter.toEntity(articleRequestDto)); + return article.getId(); + } + + public ArticleResponseDto read(Long id) { + Article article = findById(id); + return ArticleConverter.from(article); + } + + public void update(Long id, ArticleRequestDto articleRequestDto) { + Article article = findById(id); + article.update(ArticleConverter.toEntity(articleRequestDto)); + } + + public void delete(Long id, Principal principal) { + Article article = findById(id); + article.deactivate(principal.getName()); + // TODO: 20. 8. 31. soft delete를 할지? hard delete를 할지 고민 + // if (!article.isAuthor(principal.getName())) { + // throw new IllegalArgumentException("unauthorized user"); + // } + // articleRepository.delete(article); + } + + private Article findById(Long id) { + return articleRepository.findById(id).orElseThrow(() -> new ArticleNotFoundException(id)); + } +} diff --git a/community-backend/domain/domain-article/src/test/java/org/kiworkshop/community/article/domain/service/ArticleServiceTest.java b/community-backend/domain/domain-article/src/test/java/org/kiworkshop/community/article/domain/service/ArticleServiceTest.java new file mode 100644 index 00000000..d43947ee --- /dev/null +++ b/community-backend/domain/domain-article/src/test/java/org/kiworkshop/community/article/domain/service/ArticleServiceTest.java @@ -0,0 +1,117 @@ +package org.kiworkshop.community.article.domain.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.BDDMockito.*; + +import java.security.Principal; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.kiworkshop.community.article.domain.exception.ArticleNotFoundException; +import org.kiworkshop.community.article.domain.model.Article; +import org.kiworkshop.community.article.domain.model.ArticleFixture; +import org.kiworkshop.community.article.domain.model.ArticleRepository; +import org.kiworkshop.community.article.dto.ArticleRequestDto; +import org.kiworkshop.community.article.dto.ArticleRequestDtoFixture; +import org.kiworkshop.community.article.dto.ArticleResponseDto; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; + +@ExtendWith(MockitoExtension.class) +class ArticleServiceTest { + private ArticleService articleService; + @Mock + private ArticleRepository articleRepository; + @Mock + private Principal principal; + private Article article; + + @BeforeEach + void setUp() { + this.articleService = new ArticleService(articleRepository); + this.article = ArticleFixture.get(); + } + + @Test + void create() { + //given + ArticleRequestDto articleRequestDto = ArticleRequestDtoFixture.get(); + given(articleRepository.save(any(Article.class))).willReturn(article); + //when + Long id = articleService.create(articleRequestDto); + //then + assertThat(id).isNotNull(); + then(articleRepository).should().save(any(Article.class)); + } + + @Test + void read() { + //given + given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); + //when + ArticleResponseDto articleResponseDto = articleService.read(1L); + //then + assertThat(articleResponseDto.getTitle()).isEqualTo(article.getTitle()); + assertThat(articleResponseDto.getContent()).isEqualTo(article.getContent()); + then(articleRepository).should().findById(anyLong()); + } + + @Test + void update() { + //given + ArticleRequestDto articleRequestDto = ArticleRequestDtoFixture.get(); + given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); + //when + articleService.update(1L, articleRequestDto); + //then + then(articleRepository).should().findById(anyLong()); + } + + @Test + void update_throw_exception() { + //given + ArticleRequestDto articleRequestDto = ArticleRequestDtoFixture.get(); + given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); + + ReflectionTestUtils.setField(articleRequestDto, "username", "wrong username"); + //when & then + assertThrows(IllegalArgumentException.class, () -> articleService.update(1L, articleRequestDto)); + then(articleRepository).should().findById(anyLong()); + } + + @Test + void delete() { + //given + given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); + given(principal.getName()).willReturn("username"); + //when + articleService.delete(1L, principal); + //then + assertThat(article.isActive()).isEqualTo(false); + } + + @Test + void delete_throw_exception() { + //given + given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); + given(principal.getName()).willReturn("username1"); + //when + assertThrows(IllegalArgumentException.class, () -> articleService.delete(1L, principal)); + then(articleRepository).should().findById(anyLong()); + } + + @Test + void findByIdThrowsException() { + //given + ArticleRequestDto articleRequestDto = ArticleRequestDtoFixture.get(); + //when & then + assertThrows(ArticleNotFoundException.class, () -> articleService.read(1L)); + assertThrows(ArticleNotFoundException.class, () -> articleService.update(1L, articleRequestDto)); + } +} diff --git a/community-backend/domain/domain-article/src/testFixtures/java/org/kiworkshop/community/article/domain/model/ArticleFixture.java b/community-backend/domain/domain-article/src/testFixtures/java/org/kiworkshop/community/article/domain/model/ArticleFixture.java new file mode 100644 index 00000000..a9f8b7fb --- /dev/null +++ b/community-backend/domain/domain-article/src/testFixtures/java/org/kiworkshop/community/article/domain/model/ArticleFixture.java @@ -0,0 +1,24 @@ +package org.kiworkshop.community.article.domain.model; + +import java.time.ZonedDateTime; + +import org.springframework.test.util.ReflectionTestUtils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ArticleFixture { + public static Article get() { + Article article = new Article(); + + ReflectionTestUtils.setField(article, "id", 1L); + ReflectionTestUtils.setField(article, "title", "title"); + ReflectionTestUtils.setField(article, "content", "content"); + ReflectionTestUtils.setField(article, "username", "username"); + ReflectionTestUtils.setField(article, "createdAt", ZonedDateTime.now()); + ReflectionTestUtils.setField(article, "updatedAt", ZonedDateTime.now()); + + return article; + } +} diff --git a/community-backend/settings.gradle b/community-backend/settings.gradle index 97e55d91..7864aeff 100644 --- a/community-backend/settings.gradle +++ b/community-backend/settings.gradle @@ -15,16 +15,16 @@ include 'domain:domain-file' include 'domain:domain-comment' include 'domain:domain-common' include 'domain:domain-user-resource' +include 'domain:domain-article' include 'core:core-mother' include 'core:core-file' include 'core:core-comment' include 'core:core-auth' include 'core:core-user-resource' +include 'core:core-article' include 'in-system-available:common-web' include 'module:module-validation' -include 'core:core-article' -findProject(':core:core-article')?.name = 'core-article' From 0631be7cada4ed124eb6e17ab1d82a23fc76d8ed Mon Sep 17 00:00:00 2001 From: "jaeju.jang" Date: Wed, 9 Sep 2020 17:32:44 +0900 Subject: [PATCH 5/8] =?UTF-8?q?[#108]=20app=20monolith=EC=97=90=20article?= =?UTF-8?q?=20core=EC=99=80=20article=20domain=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/app-monolith/build.gradle | 1 + .../article/api/ArticleController.java | 10 +- .../article/api/dto/ArticleRequestDto.java | 27 ---- .../article/api/dto/ArticleResponseDto.java | 26 ---- .../community/article/entity/Article.java | 41 ------ .../article/entity/ArticleRepository.java | 6 - .../article/exception/ArticleException.java | 7 - .../article/service/ArticleService.java | 45 ------- .../article/api/ArticleControllerTest.java | 4 + .../community/article/entity/ArticleTest.java | 15 --- .../article/service/ArticleServiceTest.java | 123 ------------------ 11 files changed, 10 insertions(+), 295 deletions(-) delete mode 100644 community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleRequestDto.java delete mode 100644 community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleResponseDto.java delete mode 100644 community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/Article.java delete mode 100644 community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/ArticleRepository.java delete mode 100644 community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/exception/ArticleException.java delete mode 100644 community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/service/ArticleService.java create mode 100644 community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/api/ArticleControllerTest.java delete mode 100644 community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/entity/ArticleTest.java delete mode 100644 community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/service/ArticleServiceTest.java diff --git a/community-backend/app/app-monolith/build.gradle b/community-backend/app/app-monolith/build.gradle index 02d129cd..dde59071 100644 --- a/community-backend/app/app-monolith/build.gradle +++ b/community-backend/app/app-monolith/build.gradle @@ -26,6 +26,7 @@ dependencies { implementation project(':domain:domain-user-resource') implementation project(':core:core-article') + implementation project(':domain:domain-article') implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java index e6be2e9a..e6ad0ad5 100644 --- a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java +++ b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java @@ -1,9 +1,9 @@ package org.kiworkshop.community.article.api; import lombok.RequiredArgsConstructor; -import org.kiworkshop.community.article.api.dto.ArticleRequestDto; -import org.kiworkshop.community.article.api.dto.ArticleResponseDto; -import org.kiworkshop.community.article.service.ArticleService; +import org.kiworkshop.community.article.dto.ArticleRequestDto; +import org.kiworkshop.community.article.dto.ArticleResponseDto; +import org.kiworkshop.community.article.domain.service.ArticleService; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -22,7 +22,7 @@ public ResponseEntity read(@PathVariable Long id) { @PostMapping public ResponseEntity create(@RequestBody ArticleRequestDto articleRequestDto, Principal principal) { - return ResponseEntity.ok(articleService.create(articleRequestDto, principal)); + return ResponseEntity.ok(articleService.create(articleRequestDto)); } @DeleteMapping("/{id}") @@ -33,7 +33,7 @@ public ResponseEntity delete(@PathVariable Long id, Principal principal) { @PutMapping("/{id}") public ResponseEntity update(@PathVariable Long id, @RequestBody ArticleRequestDto articleRequestDto, Principal principal) { - articleService.update(id, articleRequestDto, principal); + articleService.update(id, articleRequestDto); return ResponseEntity.ok().build(); } } diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleRequestDto.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleRequestDto.java deleted file mode 100644 index 277085c5..00000000 --- a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleRequestDto.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.kiworkshop.community.article.api.dto; - -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.kiworkshop.community.article.entity.Article; - -@Getter -@NoArgsConstructor -public class ArticleRequestDto { - private String title; - private String content; - - @Builder - public ArticleRequestDto(String title, String content) { - this.title = title; - this.content = content; - } - - public Article toEntity(String username) { - return Article.builder() - .title(title) - .content(content) - .username(username) - .build(); - } -} diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleResponseDto.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleResponseDto.java deleted file mode 100644 index 0ceb3348..00000000 --- a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/dto/ArticleResponseDto.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.kiworkshop.community.article.api.dto; - -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.kiworkshop.community.article.entity.Article; - -@Getter -@NoArgsConstructor -public class ArticleResponseDto { - private String title; - private String content; - - @Builder - public ArticleResponseDto(String title, String content) { - this.title = title; - this.content = content; - } - - public static ArticleResponseDto from(Article article) { - return ArticleResponseDto.builder() - .title(article.getTitle()) - .content(article.getContent()) - .build(); - } -} diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/Article.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/Article.java deleted file mode 100644 index 1f25e502..00000000 --- a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/Article.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.kiworkshop.community.article.entity; - -import lombok.Builder; -import lombok.Getter; -import org.kiworkshop.community.common.domain.BaseEntity; -import org.springframework.util.Assert; - -import javax.persistence.Column; -import javax.persistence.Entity; - -@Entity -@Getter -public class Article extends BaseEntity { - @Column(nullable = false) - private String title; - @Column(nullable = false) - private String content; - @Column(nullable = false) - private String username; - - @Builder - private Article(String title, String content, String username) { - Assert.hasLength(title, "title must have length."); - Assert.hasLength(content, "content must have length."); - Assert.hasLength(username, "username must have length."); - this.title = title; - this.content = content; - this.username = username; - } - - public void update(Article article) { - // TODO: 20. 8. 31. Error Code 가 400이 되어야 할지? 403이 되어야 할지 고민 - Assert.isTrue(this.username.equals(article.username), "unauthorized username"); - this.title = article.title; - this.content = article.content; - } - - public boolean isAuthor(String name) { - return this.username.equals(name); - } -} diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/ArticleRepository.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/ArticleRepository.java deleted file mode 100644 index 55faf8fe..00000000 --- a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/entity/ArticleRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.kiworkshop.community.article.entity; - -import org.springframework.data.jpa.repository.JpaRepository; - -public interface ArticleRepository extends JpaRepository { -} diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/exception/ArticleException.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/exception/ArticleException.java deleted file mode 100644 index 1cab8af3..00000000 --- a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/exception/ArticleException.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.kiworkshop.community.article.exception; - -public class ArticleException extends RuntimeException { - public ArticleException(String s) { - super(s); - } -} diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/service/ArticleService.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/service/ArticleService.java deleted file mode 100644 index 26e71ee2..00000000 --- a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/service/ArticleService.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.kiworkshop.community.article.service; - -import lombok.RequiredArgsConstructor; -import org.kiworkshop.community.article.api.dto.ArticleRequestDto; -import org.kiworkshop.community.article.api.dto.ArticleResponseDto; -import org.kiworkshop.community.article.entity.Article; -import org.kiworkshop.community.article.entity.ArticleRepository; -import org.kiworkshop.community.article.exception.ArticleException; -import org.springframework.stereotype.Service; - -import java.security.Principal; - -@Service -@RequiredArgsConstructor -public class ArticleService { - private final ArticleRepository articleRepository; - - public Long create(ArticleRequestDto articleRequestDto, Principal principal) { - Article article = articleRepository.save(articleRequestDto.toEntity(principal.getName())); - return article.getId(); - } - - public ArticleResponseDto read(Long id) { - Article article = findById(id); - return ArticleResponseDto.from(article); - } - - public void update(Long id, ArticleRequestDto articleRequestDto, Principal principal) { - Article article = findById(id); - article.update(articleRequestDto.toEntity(principal.getName())); - } - - public void delete(Long id, Principal principal) { - Article article = findById(id); - // TODO: 20. 8. 31. soft delete를 할지? hard delete를 할지 고민 - if (!article.isAuthor(principal.getName())) { - throw new IllegalArgumentException("unauthorized user"); - } - articleRepository.delete(article); - } - - private Article findById(Long id) { - return articleRepository.findById(id).orElseThrow(() -> new ArticleException("게시물을 찾을 수 없습니다.")); - } -} diff --git a/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/api/ArticleControllerTest.java b/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/api/ArticleControllerTest.java new file mode 100644 index 00000000..90e94548 --- /dev/null +++ b/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/api/ArticleControllerTest.java @@ -0,0 +1,4 @@ +package org.kiworkshop.community.article.api; + +public class ArticleControllerTest { +} diff --git a/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/entity/ArticleTest.java b/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/entity/ArticleTest.java deleted file mode 100644 index fce04bac..00000000 --- a/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/entity/ArticleTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.kiworkshop.community.article.entity; - -import org.springframework.test.util.ReflectionTestUtils; - -import java.time.ZonedDateTime; - -public class ArticleTest { - public static Article createArticleTestFixture() { - Article article = Article.builder().title("title").content("content").username("username").build(); - ReflectionTestUtils.setField(article, "id", 1L); - ReflectionTestUtils.setField(article, "updatedAt", ZonedDateTime.now()); - ReflectionTestUtils.setField(article, "createdAt", ZonedDateTime.now()); - return article; - } -} \ No newline at end of file diff --git a/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/service/ArticleServiceTest.java b/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/service/ArticleServiceTest.java deleted file mode 100644 index 6be3db86..00000000 --- a/community-backend/app/app-monolith/src/test/java/org/kiworkshop/community/article/service/ArticleServiceTest.java +++ /dev/null @@ -1,123 +0,0 @@ -package org.kiworkshop.community.article.service; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.kiworkshop.community.article.api.dto.ArticleRequestDto; -import org.kiworkshop.community.article.api.dto.ArticleResponseDto; -import org.kiworkshop.community.article.entity.Article; -import org.kiworkshop.community.article.entity.ArticleRepository; -import org.kiworkshop.community.article.exception.ArticleException; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.security.Principal; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.kiworkshop.community.article.entity.ArticleTest.createArticleTestFixture; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.BDDMockito.given; -import static org.mockito.BDDMockito.then; - -@ExtendWith(MockitoExtension.class) -class ArticleServiceTest { - private ArticleService articleService; - @Mock - private ArticleRepository articleRepository; - @Mock - private Principal principal; - - @BeforeEach - void setUp() { - this.articleService = new ArticleService(articleRepository); - } - - @Test - void create() { - //given - ArticleRequestDto articleRequestDto = ArticleRequestDto.builder().title("title").content("content").build(); - Article article = createArticleTestFixture(); - given(articleRepository.save(any(Article.class))).willReturn(article); - given(principal.getName()).willReturn("username"); - //when - Long id = articleService.create(articleRequestDto, principal); - //then - assertThat(id).isNotNull(); - then(articleRepository).should().save(any(Article.class)); - then(principal).should().getName(); - } - - @Test - void read() { - //given - Article article = createArticleTestFixture(); - given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); - //when - ArticleResponseDto articleResponseDto = articleService.read(1L); - //then - assertThat(articleResponseDto.getTitle()).isEqualTo(article.getTitle()); - assertThat(articleResponseDto.getContent()).isEqualTo(article.getContent()); - then(articleRepository).should().findById(anyLong()); - } - - @Test - void update() { - //given - ArticleRequestDto articleRequestDto = ArticleRequestDto.builder().title("title1").content("content1").build(); - Article article = createArticleTestFixture(); - given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); - given(principal.getName()).willReturn("username"); - //when - articleService.update(1L, articleRequestDto, principal); - //then - then(articleRepository).should().findById(anyLong()); - } - - @Test - void update_throw_exception() { - //given - ArticleRequestDto articleRequestDto = ArticleRequestDto.builder().title("title1").content("content1").build(); - Article article = createArticleTestFixture(); - given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); - given(principal.getName()).willReturn("username1"); - //when & then - assertThrows(IllegalArgumentException.class, () -> articleService.update(1L, articleRequestDto, principal)); - then(articleRepository).should().findById(anyLong()); - } - - @Test - void delete() { - //given - Article article = createArticleTestFixture(); - given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); - given(principal.getName()).willReturn("username"); - //when - articleService.delete(1L, principal); - //then - then(articleRepository).should().findById(anyLong()); - then(articleRepository).should().delete(article); - } - - @Test - void delete_throw_exception() { - //given - Article article = createArticleTestFixture(); - given(articleRepository.findById(anyLong())).willReturn(Optional.of(article)); - given(principal.getName()).willReturn("username1"); - //when - assertThrows(IllegalArgumentException.class, () -> articleService.delete(1L, principal)); - then(articleRepository).should().findById(anyLong()); - } - - @Test - void findByIdThrowsException() { - //given - ArticleRequestDto articleRequestDto = ArticleRequestDto.builder().title("title1").content("content1").build(); - //when & then - assertThrows(ArticleException.class, () -> articleService.read(1L)); - assertThrows(ArticleException.class, () -> articleService.update(1L, articleRequestDto, principal)); - } -} \ No newline at end of file From ffc5788062788a65b8abc2b5638f455d04be0cd6 Mon Sep 17 00:00:00 2001 From: "jaeju.jang" Date: Wed, 9 Sep 2020 17:38:11 +0900 Subject: [PATCH 6/8] =?UTF-8?q?[#108]=20article=20table=20flyway=EC=9A=A9?= =?UTF-8?q?=20sql=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../db-migration/V8__Create_table_article.sql | 10 ++++++++++ .../community/article/domain/model/Article.java | 2 ++ 2 files changed, 12 insertions(+) create mode 100644 community-backend/db-migration/V8__Create_table_article.sql diff --git a/community-backend/db-migration/V8__Create_table_article.sql b/community-backend/db-migration/V8__Create_table_article.sql new file mode 100644 index 00000000..e17134dd --- /dev/null +++ b/community-backend/db-migration/V8__Create_table_article.sql @@ -0,0 +1,10 @@ +create table article ( + id bigint not null auto_increment, + created_at datetime(6), + updated_at datetime(6), + title varchar(255) not null, + content LONGTEXT not null, + username varchar(255) not null, + active bit, + primary key (id) +); diff --git a/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/Article.java b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/Article.java index 39fb763d..5a0d6e27 100644 --- a/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/Article.java +++ b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/Article.java @@ -2,6 +2,7 @@ import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.Lob; import org.kiworkshop.community.common.domain.BaseEntity; import org.springframework.util.Assert; @@ -18,6 +19,7 @@ public class Article extends BaseEntity { @Column(nullable = false) private String title; + @Lob @Column(nullable = false) private String content; @Column(nullable = false) From c3be74a903f8f38e2ff3871b1461459ef90e8362 Mon Sep 17 00:00:00 2001 From: "jaeju.jang" Date: Wed, 9 Sep 2020 18:09:10 +0900 Subject: [PATCH 7/8] =?UTF-8?q?[#108]=20article=20api=EC=97=90=20pageable?= =?UTF-8?q?=EB=A1=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=B0=9B=EC=95=84?= =?UTF-8?q?=EC=98=A4=EB=8A=94=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 --- .../article/api/ArticleController.java | 21 ++++++++++++---- .../domain/model/ArticleRepository.java | 4 +++ .../domain/service/ArticleService.java | 6 +++++ .../domain/service/ArticleServiceTest.java | 25 +++++++++++++++++++ .../article/domain/model/ArticleFixture.java | 6 ++++- 5 files changed, 56 insertions(+), 6 deletions(-) diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java index e6ad0ad5..73854348 100644 --- a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java +++ b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java @@ -4,6 +4,10 @@ import org.kiworkshop.community.article.dto.ArticleRequestDto; import org.kiworkshop.community.article.dto.ArticleResponseDto; import org.kiworkshop.community.article.domain.service.ArticleService; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -15,16 +19,23 @@ public class ArticleController { private final ArticleService articleService; - @GetMapping("/{id}") - public ResponseEntity read(@PathVariable Long id) { - return ResponseEntity.ok(articleService.read(id)); - } - @PostMapping public ResponseEntity create(@RequestBody ArticleRequestDto articleRequestDto, Principal principal) { return ResponseEntity.ok(articleService.create(articleRequestDto)); } + @GetMapping + public ResponseEntity> readPage( + @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable + ) { + return ResponseEntity.ok(articleService.readPage(pageable)); + } + + @GetMapping("/{id}") + public ResponseEntity read(@PathVariable Long id) { + return ResponseEntity.ok(articleService.read(id)); + } + @DeleteMapping("/{id}") public ResponseEntity delete(@PathVariable Long id, Principal principal) { articleService.delete(id, principal); diff --git a/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/ArticleRepository.java b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/ArticleRepository.java index 3bef2ff0..5558b531 100644 --- a/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/ArticleRepository.java +++ b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/model/ArticleRepository.java @@ -1,6 +1,10 @@ package org.kiworkshop.community.article.domain.model; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; public interface ArticleRepository extends JpaRepository { + Page
findAllByActiveIsTrue(Pageable pageable); } diff --git a/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/service/ArticleService.java b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/service/ArticleService.java index 4ac054bf..bc19180c 100644 --- a/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/service/ArticleService.java +++ b/community-backend/domain/domain-article/src/main/java/org/kiworkshop/community/article/domain/service/ArticleService.java @@ -7,6 +7,8 @@ import org.kiworkshop.community.article.domain.model.ArticleRepository; import org.kiworkshop.community.article.dto.ArticleRequestDto; import org.kiworkshop.community.article.dto.ArticleResponseDto; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import lombok.RequiredArgsConstructor; @@ -21,6 +23,10 @@ public Long create(ArticleRequestDto articleRequestDto) { return article.getId(); } + public Page readPage(Pageable pageable) { + return articleRepository.findAllByActiveIsTrue(pageable).map(ArticleConverter::from); + } + public ArticleResponseDto read(Long id) { Article article = findById(id); return ArticleConverter.from(article); diff --git a/community-backend/domain/domain-article/src/test/java/org/kiworkshop/community/article/domain/service/ArticleServiceTest.java b/community-backend/domain/domain-article/src/test/java/org/kiworkshop/community/article/domain/service/ArticleServiceTest.java index d43947ee..14729a76 100644 --- a/community-backend/domain/domain-article/src/test/java/org/kiworkshop/community/article/domain/service/ArticleServiceTest.java +++ b/community-backend/domain/domain-article/src/test/java/org/kiworkshop/community/article/domain/service/ArticleServiceTest.java @@ -7,7 +7,10 @@ import static org.mockito.BDDMockito.*; import java.security.Principal; +import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.LongStream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -21,6 +24,10 @@ import org.kiworkshop.community.article.dto.ArticleResponseDto; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.test.util.ReflectionTestUtils; @ExtendWith(MockitoExtension.class) @@ -50,6 +57,24 @@ void create() { then(articleRepository).should().save(any(Article.class)); } + @Test + void readPage() { + Long articleNum = 10L; + // given + List
articles = LongStream.rangeClosed(1L, articleNum) + .mapToObj(ArticleFixture::get) + .collect(Collectors.toList()); + PageImpl
articlePage = new PageImpl<>(articles); + given(articleRepository.findAllByActiveIsTrue(any())).willReturn(articlePage); + + // when + Pageable pageable = PageRequest.of(0, articleNum.intValue()); + Page articleResponseDtoPage = articleService.readPage(pageable); + + // then + assertThat(articleResponseDtoPage.getTotalElements()).isEqualTo(articleNum); + } + @Test void read() { //given diff --git a/community-backend/domain/domain-article/src/testFixtures/java/org/kiworkshop/community/article/domain/model/ArticleFixture.java b/community-backend/domain/domain-article/src/testFixtures/java/org/kiworkshop/community/article/domain/model/ArticleFixture.java index a9f8b7fb..09a122db 100644 --- a/community-backend/domain/domain-article/src/testFixtures/java/org/kiworkshop/community/article/domain/model/ArticleFixture.java +++ b/community-backend/domain/domain-article/src/testFixtures/java/org/kiworkshop/community/article/domain/model/ArticleFixture.java @@ -10,9 +10,13 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ArticleFixture { public static Article get() { + return get(1L); + } + + public static Article get(Long id) { Article article = new Article(); - ReflectionTestUtils.setField(article, "id", 1L); + ReflectionTestUtils.setField(article, "id", id); ReflectionTestUtils.setField(article, "title", "title"); ReflectionTestUtils.setField(article, "content", "content"); ReflectionTestUtils.setField(article, "username", "username"); From 2683ce6545050b77d3cbaa1e3d01a5a529b25ac2 Mon Sep 17 00:00:00 2001 From: "jaeju.jang" Date: Wed, 9 Sep 2020 18:18:12 +0900 Subject: [PATCH 8/8] =?UTF-8?q?[#108]=20article=20controller=EC=9D=98=20re?= =?UTF-8?q?turn=20type=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../article/api/ArticleController.java | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java index 73854348..7b0c477b 100644 --- a/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java +++ b/community-backend/app/app-monolith/src/main/java/org/kiworkshop/community/article/api/ArticleController.java @@ -20,31 +20,29 @@ public class ArticleController { private final ArticleService articleService; @PostMapping - public ResponseEntity create(@RequestBody ArticleRequestDto articleRequestDto, Principal principal) { - return ResponseEntity.ok(articleService.create(articleRequestDto)); + public Long create(@RequestBody ArticleRequestDto articleRequestDto, Principal principal) { + return articleService.create(articleRequestDto); } @GetMapping - public ResponseEntity> readPage( + public Page readPage( @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable ) { - return ResponseEntity.ok(articleService.readPage(pageable)); + return articleService.readPage(pageable); } @GetMapping("/{id}") - public ResponseEntity read(@PathVariable Long id) { - return ResponseEntity.ok(articleService.read(id)); + public ArticleResponseDto read(@PathVariable Long id) { + return articleService.read(id); } @DeleteMapping("/{id}") - public ResponseEntity delete(@PathVariable Long id, Principal principal) { + public void delete(@PathVariable Long id, Principal principal) { articleService.delete(id, principal); - return ResponseEntity.ok().build(); } @PutMapping("/{id}") - public ResponseEntity update(@PathVariable Long id, @RequestBody ArticleRequestDto articleRequestDto, Principal principal) { + public void update(@PathVariable Long id, @RequestBody ArticleRequestDto articleRequestDto, Principal principal) { articleService.update(id, articleRequestDto); - return ResponseEntity.ok().build(); } }