diff --git a/src/main/java/io/spring/api/CommentsApi.java b/src/main/java/io/spring/api/CommentsApi.java index 2d4466a..95e8c96 100644 --- a/src/main/java/io/spring/api/CommentsApi.java +++ b/src/main/java/io/spring/api/CommentsApi.java @@ -2,7 +2,9 @@ package io.spring.api; import com.fasterxml.jackson.annotation.JsonRootName; import io.spring.api.exception.InvalidRequestException; +import io.spring.api.exception.NoAuthorizationException; import io.spring.api.exception.ResourceNotFoundException; +import io.spring.application.AuthorizationService; import io.spring.application.comment.CommentData; import io.spring.application.comment.CommentQueryService; import io.spring.core.article.Article; @@ -22,6 +24,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; @@ -70,6 +73,20 @@ public class CommentsApi { }}); } + @RequestMapping(path = "{id}", method = RequestMethod.DELETE) + public ResponseEntity deleteComment(@PathVariable("slug") String slug, + @PathVariable("id") String commentId, + @AuthenticationPrincipal User user) { + Article article = findArticle(slug); + return commentRepository.findById(article.getId(), commentId).map(comment -> { + if (!AuthorizationService.canWriteComment(user, article, comment)) { + throw new NoAuthorizationException(); + } + commentRepository.remove(comment); + return ResponseEntity.noContent().build(); + }).orElseThrow(ResourceNotFoundException::new); + } + private Article findArticle(String slug) { return articleRepository.findBySlug(slug).map(article -> article).orElseThrow(ResourceNotFoundException::new); } diff --git a/src/main/java/io/spring/application/AuthorizationService.java b/src/main/java/io/spring/application/AuthorizationService.java index b1de760..f26b1c2 100644 --- a/src/main/java/io/spring/application/AuthorizationService.java +++ b/src/main/java/io/spring/application/AuthorizationService.java @@ -1,10 +1,15 @@ package io.spring.application; import io.spring.core.article.Article; +import io.spring.core.comment.Comment; import io.spring.core.user.User; public class AuthorizationService { public static boolean canWriteArticle(User user, Article article) { return user.getId().equals(article.getUserId()); } + + public static boolean canWriteComment(User user, Article article, Comment comment) { + return user.getId().equals(article.getUserId()) || user.getId().equals(comment.getUserId()); + } } diff --git a/src/main/java/io/spring/core/comment/CommentRepository.java b/src/main/java/io/spring/core/comment/CommentRepository.java index 0741f76..b720576 100644 --- a/src/main/java/io/spring/core/comment/CommentRepository.java +++ b/src/main/java/io/spring/core/comment/CommentRepository.java @@ -5,5 +5,7 @@ import java.util.Optional; public interface CommentRepository { void save(Comment comment); - Optional findById(String id); + Optional findById(String articleId, String id); + + void remove(Comment comment); } diff --git a/src/main/java/io/spring/infrastructure/comment/CommentMapper.java b/src/main/java/io/spring/infrastructure/comment/CommentMapper.java index 09a957e..bf48a15 100644 --- a/src/main/java/io/spring/infrastructure/comment/CommentMapper.java +++ b/src/main/java/io/spring/infrastructure/comment/CommentMapper.java @@ -10,5 +10,7 @@ import org.springframework.stereotype.Component; public interface CommentMapper { void insert(@Param("comment") Comment comment); - Comment findById(@Param("id") String id); + Comment findById(@Param("articleId") String articleId, @Param("id") String id); + + void delete(@Param("id") String id); } diff --git a/src/main/java/io/spring/infrastructure/comment/MyBatisCommentRepository.java b/src/main/java/io/spring/infrastructure/comment/MyBatisCommentRepository.java index dc40537..a7b95b1 100644 --- a/src/main/java/io/spring/infrastructure/comment/MyBatisCommentRepository.java +++ b/src/main/java/io/spring/infrastructure/comment/MyBatisCommentRepository.java @@ -22,7 +22,12 @@ public class MyBatisCommentRepository implements CommentRepository { } @Override - public Optional findById(String id) { - return Optional.ofNullable(commentMapper.findById(id)); + public Optional findById(String articleId, String id) { + return Optional.ofNullable(commentMapper.findById(articleId, id)); + } + + @Override + public void remove(Comment comment) { + commentMapper.delete(comment.getId()); } } diff --git a/src/main/resources/mapper/CommentMapper.xml b/src/main/resources/mapper/CommentMapper.xml index 4386399..a9ccaf2 100644 --- a/src/main/resources/mapper/CommentMapper.xml +++ b/src/main/resources/mapper/CommentMapper.xml @@ -12,6 +12,9 @@ #{comment.createdAt} ) + + delete from comments where id = #{id} + diff --git a/src/test/java/io/spring/api/CommentsApiTest.java b/src/test/java/io/spring/api/CommentsApiTest.java index 856a551..29fb06e 100644 --- a/src/test/java/io/spring/api/CommentsApiTest.java +++ b/src/test/java/io/spring/api/CommentsApiTest.java @@ -4,10 +4,12 @@ import io.restassured.RestAssured; import io.spring.application.comment.CommentData; import io.spring.application.comment.CommentQueryService; import io.spring.application.profile.ProfileData; +import io.spring.application.user.UserData; import io.spring.core.article.Article; import io.spring.core.article.ArticleRepository; +import io.spring.core.comment.Comment; import io.spring.core.comment.CommentRepository; -import org.joda.time.DateTime; +import io.spring.core.user.User; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -22,7 +24,6 @@ import java.util.Map; import java.util.Optional; import static io.restassured.RestAssured.given; -import static org.hamcrest.CoreMatchers.any; import static org.hamcrest.core.IsEqual.equalTo; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; @@ -49,6 +50,7 @@ public class CommentsApiTest extends TestWithCurrentUser { private Article article; private CommentData commentData; + private Comment comment; @Before public void setUp() throws Exception { @@ -60,12 +62,13 @@ public class CommentsApiTest extends TestWithCurrentUser { article = new Article("title", "desc", "body", new String[]{"test", "java"}, user.getId()); when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article)); + comment = new Comment("comment", user.getId(), article.getId()); commentData = new CommentData( - "123", - "comment", - article.getId(), - new DateTime(), - new DateTime(), + comment.getId(), + comment.getBody(), + comment.getArticleId(), + comment.getCreatedAt(), + comment.getCreatedAt(), new ProfileData(user.getId(), user.getUsername(), user.getBio(), user.getImage(), false)); } @@ -120,4 +123,38 @@ public class CommentsApiTest extends TestWithCurrentUser { .statusCode(200) .body("comments[0].id", equalTo(commentData.getId())); } + + @Test + public void should_delete_comment_success() throws Exception { + when(commentRepository.findById(article.getId(), eq(comment.getId()))).thenReturn(Optional.of(comment)); + + given() + .header("Authorization", "Token " + token) + .when() + .delete("/articles/{slug}/comments/{id}", article.getSlug(), comment.getId()) + .then() + .statusCode(204); + } + + @Test + public void should_get_403_if_not_author_of_article_or_author_of_comment_when_delete_comment() throws Exception { + User anotherUser = new User("other@example.com", "other", "123", "", ""); + when(userRepository.findByUsername(eq(anotherUser.getUsername()))).thenReturn(Optional.of(anotherUser)); + + when(commentRepository.findById(eq(article.getId()), eq(comment.getId()))).thenReturn(Optional.of(comment)); + String token = jwtService.toToken( + new UserData( + anotherUser.getId(), + anotherUser.getEmail(), + anotherUser.getUsername(), + anotherUser.getBio(), + anotherUser.getImage())); + given() + .header("Authorization", "Token " + token) + .when() + .delete("/articles/{slug}/comments/{id}", article.getSlug(), comment.getId()) + .then() + .statusCode(403); + + } } diff --git a/src/test/java/io/spring/api/TestWithCurrentUser.java b/src/test/java/io/spring/api/TestWithCurrentUser.java index a3297e6..cd4bc83 100644 --- a/src/test/java/io/spring/api/TestWithCurrentUser.java +++ b/src/test/java/io/spring/api/TestWithCurrentUser.java @@ -25,7 +25,7 @@ class TestWithCurrentUser { protected String token; @Autowired - private JwtService jwtService; + protected JwtService jwtService; protected void userFixture(String email, String username, String defaultAvatar) { user = new User(email, username, "123", "", defaultAvatar); diff --git a/src/test/java/io/spring/infrastructure/comment/MyBatisCommentRepositoryTest.java b/src/test/java/io/spring/infrastructure/comment/MyBatisCommentRepositoryTest.java index c044da7..690c4ee 100644 --- a/src/test/java/io/spring/infrastructure/comment/MyBatisCommentRepositoryTest.java +++ b/src/test/java/io/spring/infrastructure/comment/MyBatisCommentRepositoryTest.java @@ -26,7 +26,7 @@ public class MyBatisCommentRepositoryTest { Comment comment = new Comment("content", "123", "456"); commentRepository.save(comment); - Optional optional = commentRepository.findById(comment.getId()); + Optional optional = commentRepository.findById("456", comment.getId()); assertThat(optional.isPresent(), is(true)); assertThat(optional.get(), is(comment)); }