Skip to content

Commit

Permalink
Merge pull request #68 from real-world-study/jinyoungchoi95-issue67
Browse files Browse the repository at this point in the history
[jinyoungchoi95-issue67] Comment 추가/조회/삭제 기능 구현
  • Loading branch information
jinyoungchoi95 committed Oct 29, 2021
2 parents 2919434 + 9cf244b commit 580adce
Show file tree
Hide file tree
Showing 17 changed files with 1,128 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.study.realworld.article.comment.controller;

import com.study.realworld.article.comment.controller.request.CommentCreateRequest;
import com.study.realworld.article.comment.controller.response.CommentResponse;
import com.study.realworld.article.comment.controller.response.CommentsResponse;
import com.study.realworld.article.comment.domain.Comment;
import com.study.realworld.article.comment.service.CommentService;
import com.study.realworld.article.domain.Slug;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/api")
@RestController
public class CommentController {

private final CommentService commentService;

public CommentController(CommentService commentService) {
this.commentService = commentService;
}

@GetMapping("/articles/{slug}/comments")
public ResponseEntity<CommentsResponse> getCommentByArticleSlug(@PathVariable String slug) {
List<Comment> comments = commentService.getCommentsByArticleSlug(Slug.of(slug));
return ResponseEntity.ok().body(CommentsResponse.fromComments(comments));
}

@PostMapping("/articles/{slug}/comments")
public ResponseEntity<CommentResponse> createComment(@PathVariable String slug,
@RequestBody CommentCreateRequest request,
@AuthenticationPrincipal Long loginId) {
Comment comment = commentService.createComment(loginId, Slug.of(slug), request.toCommentBody());
return ResponseEntity.ok().body(CommentResponse.fromComment(comment));
}

@DeleteMapping("/articles/{slug}/comments/{id}")
public void deleteComment(@PathVariable String slug, @PathVariable Long id,
@AuthenticationPrincipal Long loginId) {
commentService.deleteCommentByCommentId(loginId, Slug.of(slug), id);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.study.realworld.article.comment.controller.request;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.study.realworld.article.comment.domain.CommentBody;

@JsonTypeName(value = "comment")
@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME)
public class CommentCreateRequest {

@JsonProperty("body")
private String body;

CommentCreateRequest() {
}

public CommentBody toCommentBody() {
return CommentBody.of(body);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.study.realworld.article.comment.controller.response;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.study.realworld.article.comment.domain.Comment;
import com.study.realworld.article.comment.domain.CommentBody;
import com.study.realworld.user.controller.response.ProfileResponse.ProfileResponseNested;
import java.time.OffsetDateTime;

public class CommentResponse {

@JsonProperty("comment")
private CommentResponseNested commentResponseNested;

CommentResponse() {
}

private CommentResponse(CommentResponseNested commentResponseNested) {
this.commentResponseNested = commentResponseNested;
}

public static CommentResponse fromComment(Comment comment) {
return new CommentResponse(CommentResponseNested.fromComment(comment));
}

public static class CommentResponseNested {

@JsonProperty("id")
private Long id;

@JsonProperty("createdAt")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "UTC")
private OffsetDateTime createdAt;

@JsonProperty("updatedAt")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "UTC")
private OffsetDateTime updatedAt;

@JsonProperty("body")
private CommentBody commentBody;

@JsonProperty("author")
private ProfileResponseNested profileResponseNested;

CommentResponseNested() {
}

public CommentResponseNested(Long id, OffsetDateTime createdAt, OffsetDateTime updatedAt,
CommentBody commentBody,
ProfileResponseNested profileResponseNested) {
this.id = id;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
this.commentBody = commentBody;
this.profileResponseNested = profileResponseNested;
}

public static CommentResponseNested fromComment(Comment comment) {
return new CommentResponseNested(
comment.id(),
comment.createdAt(),
comment.updatedAt(),
comment.commentBody(),
ProfileResponseNested.ofProfile(comment.author().profile())
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.study.realworld.article.comment.controller.response;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.study.realworld.article.comment.controller.response.CommentResponse.CommentResponseNested;
import com.study.realworld.article.comment.domain.Comment;
import java.util.List;
import java.util.stream.Collectors;

public class CommentsResponse {

@JsonProperty("comments")
private List<CommentResponseNested> commentResponseNesteds;

CommentsResponse() {
}

private CommentsResponse(List<CommentResponseNested> commentResponseNesteds) {
this.commentResponseNesteds = commentResponseNesteds;
}

public static CommentsResponse fromComments(List<Comment> comments) {
return new CommentsResponse(
comments.stream()
.map(CommentResponseNested::fromComment)
.collect(Collectors.toList())
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.study.realworld.article.comment.domain;

import com.study.realworld.article.domain.Article;
import com.study.realworld.global.domain.BaseTimeEntity;
import com.study.realworld.global.exception.BusinessException;
import com.study.realworld.global.exception.ErrorCode;
import com.study.realworld.user.domain.User;
import java.time.OffsetDateTime;
import java.util.Objects;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import org.hibernate.annotations.Where;

@Entity
@Where(clause = "deleted_at is null")
public class Comment extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Embedded
private CommentBody commentBody;

@JoinColumn(name = "user_id", nullable = false, foreignKey = @ForeignKey(name = "fk_comment_to_user_id"))
@ManyToOne(fetch = FetchType.LAZY)
private User author;

@JoinColumn(name = "article_id", nullable = false, foreignKey = @ForeignKey(name = "fk_comment_to_article_id"))
@ManyToOne(fetch = FetchType.LAZY)
private Article article;

protected Comment() {
}

private Comment(CommentBody commentBody, User author, Article article) {
this.commentBody = commentBody;
this.author = author;
this.article = article;
}

public static Comment from(CommentBody commentBody, User author, Article article) {
return new Comment(commentBody, author, article);
}

public Long id() {
return id;
}

public CommentBody commentBody() {
return commentBody;
}

public User author() {
return author;
}

public Article article() {
return article;
}

public void deleteCommentByAuthor(User author) {
checkCommentAuthor(author);

saveDeletedTime(OffsetDateTime.now());
}

private void checkCommentAuthor(User author) {
if (!Objects.equals(this.author, author)) {
throw new BusinessException(ErrorCode.INVALID_COMMENT_AUTHOR_DISMATCH);
}
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Comment comment = (Comment) o;
return Objects.equals(commentBody, comment.commentBody) && Objects
.equals(author, comment.author) && Objects.equals(article, comment.article);
}

@Override
public int hashCode() {
return Objects.hash(commentBody, author, article);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.study.realworld.article.comment.domain;

import static com.google.common.base.Preconditions.checkArgument;

import com.fasterxml.jackson.annotation.JsonValue;
import com.study.realworld.global.exception.ErrorCode;
import java.util.Objects;
import javax.persistence.Column;
import javax.persistence.Embeddable;

@Embeddable
public class CommentBody {

@Column(name = "body", columnDefinition = "text", nullable = false)
private String body;

protected CommentBody() {
}

private CommentBody(String body) {
this.body = body;
}

public static CommentBody of(String body) {
checkBody(body);

return new CommentBody(body);
}

public static void checkBody(String body) {
checkArgument(Objects.nonNull(body), ErrorCode.INVALID_COMMENT_BODY_NULL);
}

@JsonValue
public String body() {
return body;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CommentBody body1 = (CommentBody) o;
return Objects.equals(body, body1.body);
}

@Override
public int hashCode() {
return Objects.hash(body);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.study.realworld.article.comment.domain;

import com.study.realworld.article.domain.Article;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CommentRepository extends JpaRepository<Comment, Long> {

List<Comment> findAllByArticle(Article article);

Optional<Comment> findByIdAndArticle(Long id, Article article);

}

0 comments on commit 580adce

Please sign in to comment.