Skip to content

Commit 2f02edb

Browse files
committed
[#24] GET /articles/:slug/comments
1 parent a14da3f commit 2f02edb

File tree

7 files changed

+91
-8
lines changed

7 files changed

+91
-8
lines changed

src/main/java/io/github/raeperd/realworld/application/article/comment/CommentRestController.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
import io.github.raeperd.realworld.domain.article.comment.CommentService;
44
import io.github.raeperd.realworld.infrastructure.jwt.UserJWTPayload;
55
import org.springframework.security.core.annotation.AuthenticationPrincipal;
6-
import org.springframework.web.bind.annotation.PathVariable;
7-
import org.springframework.web.bind.annotation.PostMapping;
8-
import org.springframework.web.bind.annotation.RequestBody;
9-
import org.springframework.web.bind.annotation.RestController;
6+
import org.springframework.web.bind.annotation.*;
107

118
import javax.validation.Valid;
129

@@ -25,4 +22,11 @@ public CommentModel postComments(@AuthenticationPrincipal UserJWTPayload jwtPayl
2522
final var commentAdded = commentService.createComment(jwtPayload.getUserId(), slug, dto.getBody());
2623
return CommentModel.fromComment(commentAdded);
2724
}
25+
26+
@GetMapping("/articles/{slug}/comments")
27+
public MultipleCommentModel getComments(@AuthenticationPrincipal UserJWTPayload jwtPayload,
28+
@PathVariable String slug) {
29+
final var comments = commentService.getComments(jwtPayload.getUserId(), slug);
30+
return MultipleCommentModel.fromComments(comments);
31+
}
2832
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.github.raeperd.realworld.application.article.comment;
2+
3+
import io.github.raeperd.realworld.application.article.comment.CommentModel.CommentModelNested;
4+
import io.github.raeperd.realworld.domain.article.comment.Comment;
5+
import lombok.Value;
6+
7+
import java.util.List;
8+
import java.util.Set;
9+
10+
import static java.util.stream.Collectors.toList;
11+
12+
@Value
13+
class MultipleCommentModel {
14+
15+
List<CommentModelNested> comments;
16+
17+
static MultipleCommentModel fromComments(Set<Comment> comments) {
18+
final var commentsCollected = comments.stream().map(CommentModelNested::fromComment)
19+
.collect(toList());
20+
return new MultipleCommentModel(commentsCollected);
21+
}
22+
}

src/main/java/io/github/raeperd/realworld/domain/article/Article.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ public void updateArticle(ArticleUpdateRequest updateRequest) {
8181
contents.updateArticleContentsIfPresent(updateRequest);
8282
}
8383

84+
public Article updateFavoriteByUser(User user) {
85+
favorited = userFavorited.contains(user);
86+
return this;
87+
}
88+
8489
public User getAuthor() {
8590
return author;
8691
}
@@ -105,9 +110,8 @@ public boolean isFavorited() {
105110
return favorited;
106111
}
107112

108-
public Article updateFavoriteByUser(User user) {
109-
favorited = userFavorited.contains(user);
110-
return this;
113+
public Set<Comment> getComments() {
114+
return comments;
111115
}
112116

113117
@Override

src/main/java/io/github/raeperd/realworld/domain/article/comment/CommentService.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package io.github.raeperd.realworld.domain.article.comment;
22

33
import io.github.raeperd.realworld.domain.article.ArticleFindService;
4+
import io.github.raeperd.realworld.domain.user.User;
45
import io.github.raeperd.realworld.domain.user.UserFindService;
56
import org.springframework.stereotype.Service;
67
import org.springframework.transaction.annotation.Transactional;
78

89
import java.util.NoSuchElementException;
10+
import java.util.Set;
911

1012
import static org.springframework.data.util.Optionals.mapIfAllPresent;
1113

@@ -26,4 +28,11 @@ public Comment createComment(long userId, String slug, String body) {
2628
(user, article) -> user.writeCommentToArticle(article, body))
2729
.orElseThrow(NoSuchElementException::new);
2830
}
31+
32+
@Transactional(readOnly = true)
33+
public Set<Comment> getComments(long userId, String slug) {
34+
return mapIfAllPresent(userFindService.findById(userId), articleFindService.getArticleBySlug(slug),
35+
User::viewArticleComments)
36+
.orElseThrow(NoSuchElementException::new);
37+
}
2938
}

src/main/java/io/github/raeperd/realworld/domain/user/User.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.Objects;
1212
import java.util.Set;
1313

14+
import static java.util.stream.Collectors.toSet;
1415
import static javax.persistence.CascadeType.REMOVE;
1516
import static javax.persistence.GenerationType.IDENTITY;
1617

@@ -89,6 +90,17 @@ User unfollowUser(User followee) {
8990
return this;
9091
}
9192

93+
public Set<Comment> viewArticleComments(Article article) {
94+
return article.getComments().stream()
95+
.map(this::viewComment)
96+
.collect(toSet());
97+
}
98+
99+
Comment viewComment(Comment comment) {
100+
viewProfile(comment.getAuthor());
101+
return comment;
102+
}
103+
92104
Profile viewProfile(User user) {
93105
return user.profile.withFollowing(followingUsers.contains(user));
94106
}

src/test/java/io/github/raeperd/realworld/IntegrationTest.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,18 @@ void create_comments_for_article() throws Exception {
217217
.header(AUTHORIZATION, "Token " + token)
218218
.contentType(APPLICATION_JSON)
219219
.content("{\"comment\":{\"body\":\"Thank you so much!\"}}"))
220-
.andExpect(status().isOk());
220+
.andExpect(status().isOk())
221+
.andExpect(validSingleCommentModel())
222+
.andExpect(jsonPath("comment.body", is("Thank you so much!")));
223+
}
224+
225+
@Order(12)
226+
@Test
227+
void all_comments_for_article() throws Exception {
228+
mockMvc.perform(get("/articles/{slug}/comments", "how-to-train-your-dragon")
229+
.header(AUTHORIZATION, "Token " + token))
230+
.andExpect(status().isOk())
231+
.andExpect(validMultipleCommentModel());
221232
}
222233

223234
@Order(12)

src/test/java/io/github/raeperd/realworld/IntegrationTestUtils.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,25 @@ private static ResultMatcher validArticleModelInPath(String path) {
7373
jsonPath(path + ".favoritesCount").isNumber(),
7474
validProfileModelInPath(path + ".author"));
7575
}
76+
77+
static ResultMatcher validSingleCommentModel() {
78+
return matchAll(
79+
jsonPath("comment").isMap(),
80+
validCommentModelInPath("comment"));
81+
}
82+
83+
static ResultMatcher validMultipleCommentModel() {
84+
return matchAll(
85+
jsonPath("comments").isArray(),
86+
validCommentModelInPath("comments[0]"));
87+
}
88+
89+
private static ResultMatcher validCommentModelInPath(String path) {
90+
return matchAll(
91+
jsonPath(path + ".id").isNumber(),
92+
jsonPath(path + ".body").isString(),
93+
jsonPath(path + ".createdAt", matchesPattern(ISO_8601_PATTERN)),
94+
jsonPath(path + ".updatedAt", matchesPattern(ISO_8601_PATTERN)),
95+
validProfileModelInPath(path + ".author"));
96+
}
7697
}

0 commit comments

Comments
 (0)