Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

위키 및 답변 삭제 기능을 구현한다. #177

Merged
merged 10 commits into from
Jan 24, 2025
19 changes: 12 additions & 7 deletions maeil-wiki/src/main/java/maeilwiki/comment/Comment.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package maeilwiki.comment;

import java.time.LocalDateTime;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
Expand All @@ -15,7 +14,6 @@
import lombok.NoArgsConstructor;
import maeilsupport.BaseEntity;
import maeilwiki.member.Member;
import maeilwiki.wiki.Wiki;

@Entity
@Getter
Expand All @@ -38,21 +36,28 @@ public class Comment extends BaseEntity {
@JoinColumn(nullable = false)
private Member member;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(nullable = false)
private Wiki wiki;
@Column(nullable = false)
private Long wikiId;

public Comment(String answer, boolean isAnonymous, Member member, Wiki wiki) {
public Comment(String answer, boolean isAnonymous, Member member, Long wikiId) {
validateAnswer(answer);
this.answer = answer;
this.isAnonymous = isAnonymous;
this.member = member;
this.wiki = wiki;
this.wikiId = wikiId;
}

private void validateAnswer(String answer) {
if (answer == null || answer.isBlank()) {
throw new IllegalArgumentException("답변은 필수 입력값입니다.");
}
}

public void remove() {
if (deletedAt != null) {
throw new IllegalStateException("이미 삭제된 답변입니다.");
}

deletedAt = LocalDateTime.now();
}
}
29 changes: 0 additions & 29 deletions maeil-wiki/src/main/java/maeilwiki/comment/CommentApi.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@

import org.springframework.data.jpa.repository.JpaRepository;

interface CommentRepository extends JpaRepository<Comment, Long> {
public interface CommentRepository extends JpaRepository<Comment, Long> {

boolean existsByWikiIdAndDeletedAtIsNull(Long wikiId);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package maeilwiki.comment;

import maeilwiki.member.Member;
import maeilwiki.wiki.Wiki;

record CommentRequest(String answer, boolean isAnonymous) {
public record CommentRequest(String answer, boolean isAnonymous) {

public Comment toComment(Member member, Wiki wiki) {
return new Comment(answer, isAnonymous, member, wiki);
public Comment toComment(Member member, Long wikiId) {
return new Comment(answer, isAnonymous, member, wikiId);
}
}
20 changes: 12 additions & 8 deletions maeil-wiki/src/main/java/maeilwiki/comment/CommentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,16 @@
import lombok.RequiredArgsConstructor;
import maeilwiki.member.Member;
import maeilwiki.member.MemberRepository;
import maeilwiki.wiki.Wiki;
import maeilwiki.wiki.WikiRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Service
@RequiredArgsConstructor
class CommentService {
public class CommentService {

private final WikiRepository wikiRepository;
private final CommentRepository commentRepository;
private final MemberRepository memberRepository;
private final CommentRepository commentRepository;
private final CommentLikeRepository commentLikeRepository;

private final Map<String, Member> transactionTmpMemberMap = new ConcurrentHashMap<>();
Expand All @@ -29,13 +26,20 @@ public void comment(CommentRequest request, Long wikiId) {
String uuid = UUID.randomUUID().toString();
Member temporalMember = new Member(uuid, uuid, "GITHUB");
memberRepository.save(temporalMember);
Wiki wiki = wikiRepository.findById(wikiId)
.orElseThrow(NoSuchElementException::new);
Comment comment = request.toComment(temporalMember, wiki);
Comment comment = request.toComment(temporalMember, wikiId);

commentRepository.save(comment);
}

@Transactional
public void remove(Long commentId) {
// TODO: member 소유인지 확인해야한다.
Comment comment = commentRepository.findById(commentId)
.orElseThrow(NoSuchElementException::new);

comment.remove();
}

@Transactional
public void toggleLike(Long id) {
Member member = memberSetting();
Expand Down
9 changes: 8 additions & 1 deletion maeil-wiki/src/main/java/maeilwiki/wiki/Wiki.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package maeilwiki.wiki;

import java.time.LocalDateTime;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
Expand Down Expand Up @@ -70,4 +69,12 @@ private void validateQuestion(String question) {
throw new IllegalArgumentException("질문은 %d자 이하여야 합니다.".formatted(MAX_QUESTION_LENGTH));
}
}

public void remove() {
if (deletedAt != null) {
throw new IllegalStateException("이미 삭제된 위키입니다.");
}

deletedAt = LocalDateTime.now();
}
}
38 changes: 38 additions & 0 deletions maeil-wiki/src/main/java/maeilwiki/wiki/WikiApi.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package maeilwiki.wiki;

import lombok.RequiredArgsConstructor;
import maeilwiki.comment.CommentRequest;
import maeilwiki.comment.CommentService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -11,11 +15,45 @@
class WikiApi {

private final WikiService wikiService;
private final CommentService commentService;

@PostMapping("/wiki")
public ResponseEntity<Void> createWiki(@RequestBody WikiRequest request) {
wikiService.create(request);

return ResponseEntity.noContent().build();
}

@DeleteMapping("/wiki/{id}")
public ResponseEntity<Void> deleteWiki(@PathVariable Long id) {
wikiService.remove(id);

return ResponseEntity.noContent().build();
}

@PostMapping("/wiki/{wikiId}/comment")
public ResponseEntity<Void> createComment(@RequestBody CommentRequest request, @PathVariable Long wikiId) {
wikiService.comment(request, wikiId);

return ResponseEntity.noContent().build();
}

/**
* wikiId는 현재 사용 계획이 없지만, 입력 받는 이유는 다음과 같습니다.
* - 나중에 특정 위키가 아카이브 상태로 전환되는 기능을 구현할 때는 필요할 수 있습니다.
* - comment가 wiki에 속하는 개념이라는 것을 uri로 표현할 수 있습니다.
*/
@DeleteMapping("/wiki/{wikiId}/comment/{id}")
public ResponseEntity<Void> deleteComment(@PathVariable Long wikiId, @PathVariable Long id) {
commentService.remove(id);

return ResponseEntity.noContent().build();
}

@PostMapping("/wiki/{wikiId}/comment/{id}/like")
public ResponseEntity<Void> toggleLike(@PathVariable Long wikiId, @PathVariable Long id) {
commentService.toggleLike(id);

return ResponseEntity.noContent().build();
}
}
3 changes: 3 additions & 0 deletions maeil-wiki/src/main/java/maeilwiki/wiki/WikiRepository.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package maeilwiki.wiki;

import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface WikiRepository extends JpaRepository<Wiki, Long> {

Optional<Wiki> findByIdAndDeletedAtIsNull(Long id);
}
31 changes: 31 additions & 0 deletions maeil-wiki/src/main/java/maeilwiki/wiki/WikiService.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package maeilwiki.wiki;

import java.util.NoSuchElementException;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import maeilwiki.comment.CommentRepository;
import maeilwiki.comment.CommentRequest;
import maeilwiki.comment.CommentService;
import maeilwiki.member.Member;
import maeilwiki.member.MemberRepository;
import org.springframework.stereotype.Service;
Expand All @@ -13,6 +17,8 @@ class WikiService {

private final WikiRepository wikiRepository;
private final MemberRepository memberRepository;
private final CommentRepository commentRepository;
private final CommentService commentService;

@Transactional
public void create(WikiRequest request) {
Expand All @@ -23,4 +29,29 @@ public void create(WikiRequest request) {

wikiRepository.save(wiki);
}

@Transactional
public void remove(Long wikiId) {
// TODO : member 소유인지 확인해야한다.
validateHasComment(wikiId);
Wiki wiki = wikiRepository.findById(wikiId)
.orElseThrow(NoSuchElementException::new);

wiki.remove();
}

private void validateHasComment(Long wikiId) {
boolean hasComment = commentRepository.existsByWikiIdAndDeletedAtIsNull(wikiId);
if (hasComment) {
throw new IllegalStateException("답변이 존재하는 위키는 삭제할 수 없습니다.");
}
}

@Transactional
public void comment(CommentRequest request, Long wikiId) {
Wiki wiki = wikiRepository.findByIdAndDeletedAtIsNull(wikiId)
.orElseThrow(NoSuchElementException::new);

commentService.comment(request, wiki.getId());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package maeilwiki.comment;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;

import java.util.UUID;
import maeilwiki.member.Member;
import maeilwiki.member.MemberRepository;
import maeilwiki.support.IntegrationTestSupport;
import maeilwiki.wiki.Wiki;
import maeilwiki.wiki.WikiRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

class CommentRepositoryTest extends IntegrationTestSupport {

@Autowired
private WikiRepository wikiRepository;

@Autowired
private MemberRepository memberRepository;

@Autowired
private CommentRepository commentRepository;

@Test
@DisplayName("주어진 위키에 속하는 답변이 존재하는지 조회한다.")
void existsComment() {
Member member = createMember();
Wiki wiki = createWiki(member);
Wiki noCommentWiki = createWiki(member);
createComment(member, wiki);
Comment comment = createComment(member, noCommentWiki);
comment.remove();

assertAll(
() -> assertThat(commentRepository.existsByWikiIdAndDeletedAtIsNull(wiki.getId())).isTrue(),
() -> assertThat(commentRepository.existsByWikiIdAndDeletedAtIsNull(noCommentWiki.getId())).isFalse()
);
}

private Member createMember() {
Member member = new Member(UUID.randomUUID().toString(), UUID.randomUUID().toString(), "GITHUB");

return memberRepository.save(member);
}

private Wiki createWiki(Member member) {
Wiki wiki = new Wiki("question", "backend", false, member);

return wikiRepository.save(wiki);
}

private Comment createComment(Member member, Wiki wiki) {
Comment comment = new Comment("answer", false, member, wiki.getId());

return commentRepository.save(comment);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,17 @@ class CommentServiceTest extends IntegrationTestSupport {
private WikiRepository wikiRepository;

@Test
@DisplayName("존재하지 않는 위키에 답변을 작성할 수 없다.")
void notfound() {
CommentRequest request = new CommentRequest("답변을 작성합니다.", false);
Long unknownWikiId = -1L;
@DisplayName("존재하지 않는 답변을 삭제할 수 없다.")
void notFoundComment() {
Long unknownCommentId = -1L;

assertThatThrownBy(() -> commentService.comment(request, unknownWikiId))
assertThatThrownBy(() -> commentService.remove(unknownCommentId))
.isInstanceOf(NoSuchElementException.class);
}

@Test
@DisplayName("존재하지 않는 답변에 좋아요를 생성할 수 없다.")
void notFoundComment() {
void notFoundCommentForLike() {
Long unknownCommentId = -1L;

assertThatThrownBy(() -> commentService.toggleLike(unknownCommentId))
Expand Down Expand Up @@ -80,7 +79,7 @@ private Comment createComment() {
Wiki wiki = new Wiki("question", "backend", false, member);
wikiRepository.save(wiki);

Comment comment = new Comment("answer", false, member, wiki);
Comment comment = new Comment("answer", false, member, wiki.getId());
return commentRepository.save(comment);
}
}
Loading
Loading