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

πŸš€ 1단계 - λ ˆκ±°μ‹œ μ½”λ“œ λ¦¬νŒ©ν„°λ§ #284

Merged
merged 5 commits into from
Dec 1, 2023
8 changes: 8 additions & 0 deletions src/main/java/nextstep/qna/domain/Answer.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package nextstep.qna.domain;

import nextstep.qna.CannotDeleteException;
import nextstep.qna.NotFoundException;
import nextstep.qna.UnAuthorizedException;
import nextstep.users.domain.NsUser;
Expand Down Expand Up @@ -76,4 +77,11 @@ public void toQuestion(Question question) {
public String toString() {
return "Answer [id=" + getId() + ", writer=" + writer + ", contents=" + contents + "]";
}

public Answer delete(NsUser writer) throws CannotDeleteException {
if (!this.isOwner(writer)) {
throw new CannotDeleteException("λ‹€λ₯Έ μ‚¬λžŒμ΄ μ“΄ 닡변이 μžˆμ–΄ μ‚­μ œν•  수 μ—†μŠ΅λ‹ˆλ‹€.");
}
return this.setDeleted(true);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

λ©”μ„œλ“œλŠ” ꡳ이 this μ•ˆλΆ™μ—¬λ„ λ κ²ƒκ°™μ•„μš”!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μš”κ±° 근데 없애도 λ˜μ§€ μ•Šμ„κΉŒμš” ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ₯² μš”κ΅¬μ‚¬ν•­μ— κΌ­ ν•„μš”ν•œ μ½”λ“œλΌκ³  μƒκ°ν–ˆλŠ”λ°
ν˜Ήμ‹œ μ–΄λ–€ μ΄μœ μΈμ§€ μ„ ν λ‹˜μ˜ 생각이 κΆκΈˆν•©λ‹ˆλ‹€! πŸ™‡πŸ™‡πŸ™‡πŸ™‡

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μ•„μ•„ μ–΄μ°¨ν”Ό deleteκ°€ deleted μ„ΈνŒ…ν•΄μ£ΌλŠ” 역할을 맑게 λμœΌλ‹ˆκΉŒ setterλŠ” 없어져도 λ˜μ§€ μ•Šλ‚˜ ν•˜λŠ” μ–˜κΈ°μ˜€μŠ΅λ‹ˆλ‹€!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘!! μ•„ν•˜!! λ§žμŠ΅λ‹ˆλ‹€!!
μΉœμ ˆν•œ μ½”λ©˜νŠΈ κ°μ‚¬ν•©λ‹ˆλ‹€!πŸ™‡

}
}
8 changes: 8 additions & 0 deletions src/main/java/nextstep/qna/domain/Question.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package nextstep.qna.domain;

import nextstep.qna.CannotDeleteException;
import nextstep.users.domain.NsUser;

import java.time.LocalDateTime;
Expand Down Expand Up @@ -89,4 +90,11 @@ public List<Answer> getAnswers() {
public String toString() {
return "Question [id=" + getId() + ", title=" + title + ", contents=" + contents + ", writer=" + writer + "]";
}

public Question delete(NsUser writer) throws CannotDeleteException {
if (!this.isOwner(writer)) {
throw new CannotDeleteException("μ§ˆλ¬Έμ„ μ‚­μ œν•  κΆŒν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€.");
}
return this.setDeleted(true);
}
}
18 changes: 18 additions & 0 deletions src/main/java/nextstep/qna/service/DeleteHistoryService.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package nextstep.qna.service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import nextstep.qna.domain.Answer;
import nextstep.qna.domain.ContentType;
import nextstep.qna.domain.DeleteHistory;
import nextstep.qna.domain.DeleteHistoryRepository;
import nextstep.qna.domain.Question;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -18,4 +23,17 @@ public class DeleteHistoryService {
public void saveAll(List<DeleteHistory> deleteHistories) {
deleteHistoryRepository.saveAll(deleteHistories);
}

public void saveAll(Question question, List<Answer> answers) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Questionκ³Ό Answerκ°€ 각각의 μ‚­μ œ 된 이λ ₯을 리턴해쀀닀면 이 λ©”μ„œλ“œλŠ” μ•ˆλ§Œλ“€μ–΄μ Έλ„ λ˜μ§€ μ•Šμ„κΉŒμš” ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μ—¬λŸ¬κ°€μ§€ 방법이 μžˆμ„κ²ƒ 같은데 μ €λŠ” 2가지 방법을 μƒκ°ν–ˆμŠ΅λ‹ˆλ‹€.

  1. QnAServiceμ—μ„œ List<DeleteHistory>λ₯Ό λ©”μ†Œλ“œμ— λ„˜κ²¨μ£Όκ³  Questionκ³Ό Answerκ°€ 각각 add λ©”μ„œλ“œ μˆ˜ν–‰ν•˜κΈ°.
  2. Questionμ—μ„œ ν•΄λ‹Ή Question을 μ‚­μ œν•˜κ³  κ΄€λ ¨λœ Answerλ₯Ό μ‚­μ œν•˜κ³  List<DeleteHistory>λ₯Ό λ§Œλ“€μ–΄μ„œ λ°˜ν™˜ν•˜κΈ°.

μ €λŠ” 이쀑에 두 번째 방법을 νƒν–ˆμŠ΅λ‹ˆλ‹€.
Question 도메인 μ•ˆμ—” Answerκ°€ λ“€μ–΄μžˆμ—ˆκ³  μ‚­μ œμ— κ΄€ν•΄μ„  λ‘˜μ΄ λ–¨μ–΄μ§ˆ 수 μ—†λŠ” 관계라고 νŒλ‹¨ν–ˆμŠ΅λ‹ˆλ‹€.
그쀑 주된 도메인인 Qusetion에 μ‚­μ œμž‘μ—…μ„ ν•˜κ³  List<DeleteHistory>λ₯Ό λ§Œλ“€μ–΄μ„œ λ°˜ν™˜ν•˜λ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€.

μ €λŠ” 이런 생각을 가지고 κ΅¬ν˜„μ„ ν–ˆλŠ”λ° ν˜Ήμ‹œ μ„ ν λ‹˜κ»˜μ„  λ‹€λ₯Έ 아이디어λ₯Ό 가지고 κ³„μ‹ κ°€μš”?
ν•œ 수 λΆ€νƒλ“œλ¦½λ‹ˆλ‹€! πŸ™‡πŸ™‡πŸ™‡πŸ™‡πŸ™‡

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

음 ? μ„€λͺ…ν•˜μ‹  2번 λ‚΄μš©μ€ μ œκ°€ μ˜λ„ν•œ 바와 μ •ν™•νžˆ μΌμΉ˜ν•˜λŠ”λ° κ΅¬ν˜„μ΄λž‘ λ‚΄μš©μ΄ λ‹€λ₯Έκ±° κ°™μ•„μš”? γ…‹γ…‹

μ•½κ°„ 생각해보면 .. μ•„λ§ˆ Question의 μ‚­μ œμ™€ Answers의 μ‚­μ œλŠ” 묢어놓고 DeleteHistoryλŠ” λ³„κ°œλ‘œ 보신 것 κ°™μ€λ°μš”..!
μ§€κΈˆ ꡬ쑰라면 μ„œλΉ„μŠ€μ—μ„œ κ΅¬ν˜„μ„ 잘λͺ»ν•˜κ²Œ 되면 μ‚­μ œλ˜μ§€ μ•Šμ€ Questionκ³Ό Answersλ₯Ό 여기에 λ„˜κ²¨λ„ μ‚­μ œ 된 이λ ₯을 λ‚¨κΈ°κ²Œ 될 것 κ°™μ•„μ„œ, μ–˜λ„ λ¬Άμ—¬μ•Ό ν•˜μ§€ μ•Šλ‚˜ μ‹ΆμŠ΅λ‹ˆλ‹€. κ·Έλž˜μ„œ Question의 delete λ©”μ„œλ“œκ°€ μ•„μ˜ˆ Listλ₯Ό λ¦¬ν„΄ν•΄μ£ΌλŠ” κ±Έ λ§μ”€λ“œλ Έμ—ˆμ–΄μš” ~

List<DeleteHistory> deleteHistories = new ArrayList<>();
deleteHistories.add(
new DeleteHistory(ContentType.QUESTION, question.getId(), question.getWriter(),
LocalDateTime.now()));
for (Answer answer : answers) {
deleteHistories.add(
new DeleteHistory(ContentType.ANSWER, answer.getId(), answer.getWriter(),
LocalDateTime.now()));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이λ ₯ ν…Œμ΄λΈ”μ€ 보톡 사후에 이슈 νŠΈλž˜ν‚Ήμ„ μœ„ν•΄ μ°Ύμ•„λ³΄λŠ” 데이터인데, μ΄λ ‡κ²Œ 맀번 nowλ₯Ό ν˜ΈμΆœν•˜λ©΄ λ°€λ¦¬μ„Έμ»¨λ“œλ‹¨μœ„λ‘œ μ‹œκ°„μ΄ λ‹¬λΌμ Έμ„œ λ‚˜μ€‘μ— λ™μΌν•œ μ‹œκ°„μ— μΌμ–΄λ‚œ μž‘μ—…λ“€μ„ 찾을 λ•Œ 쑰금 μ–΄λ €μ›Œμ§ˆ μˆ˜κ°€ μžˆμ–΄μ„œ μ™ΈλΆ€μ—μ„œ μ‹œκ°„μ„ μ£Όμž…λ°›μ•„μ„œ 같은 μ‹œκ°„μ„ μ‚¬μš©ν•˜λŠ”κ±Έ μΆ”μ²œλ“œλ¦½λ‹ˆλ‹€!

}
this.saveAll(deleteHistories);
}
}
45 changes: 20 additions & 25 deletions src/main/java/nextstep/qna/service/QnAService.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
package nextstep.qna.service;

import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import nextstep.qna.CannotDeleteException;
import nextstep.qna.NotFoundException;
import nextstep.qna.domain.*;
import nextstep.qna.domain.Answer;
import nextstep.qna.domain.AnswerRepository;
import nextstep.qna.domain.Question;
import nextstep.qna.domain.QuestionRepository;
import nextstep.users.domain.NsUser;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Service("qnaService")
public class QnAService {

@Resource(name = "questionRepository")
private QuestionRepository questionRepository;

Expand All @@ -25,25 +27,18 @@ public class QnAService {

@Transactional
public void deleteQuestion(NsUser loginUser, long questionId) throws CannotDeleteException {
Question question = questionRepository.findById(questionId).orElseThrow(NotFoundException::new);
if (!question.isOwner(loginUser)) {
throw new CannotDeleteException("μ§ˆλ¬Έμ„ μ‚­μ œν•  κΆŒν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€.");
}

List<Answer> answers = question.getAnswers();
for (Answer answer : answers) {
if (!answer.isOwner(loginUser)) {
throw new CannotDeleteException("λ‹€λ₯Έ μ‚¬λžŒμ΄ μ“΄ 닡변이 μžˆμ–΄ μ‚­μ œν•  수 μ—†μŠ΅λ‹ˆλ‹€.");
Question deletedQuestion = questionRepository.findById(questionId)
.orElseThrow(NotFoundException::new)
.delete(loginUser);

List<Answer> deletedAnswers = deletedQuestion.getAnswers().stream().map(answer -> {
try {
return answer.delete(loginUser);
} catch (CannotDeleteException e) {
throw new RuntimeException(e);
}
}

List<DeleteHistory> deleteHistories = new ArrayList<>();
question.setDeleted(true);
deleteHistories.add(new DeleteHistory(ContentType.QUESTION, questionId, question.getWriter(), LocalDateTime.now()));
for (Answer answer : answers) {
answer.setDeleted(true);
deleteHistories.add(new DeleteHistory(ContentType.ANSWER, answer.getId(), answer.getWriter(), LocalDateTime.now()));
}
deleteHistoryService.saveAll(deleteHistories);
}).collect(Collectors.toList());

deleteHistoryService.saveAll(deletedQuestion, deletedAnswers);
}
}
33 changes: 31 additions & 2 deletions src/test/java/nextstep/qna/domain/AnswerTest.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,37 @@
package nextstep.qna.domain;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import nextstep.qna.CannotDeleteException;
import nextstep.users.domain.NsUser;
import nextstep.users.domain.NsUserTest;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class AnswerTest {
public static final Answer A1 = new Answer(NsUserTest.JAVAJIGI, QuestionTest.Q1, "Answers Contents1");
public static final Answer A2 = new Answer(NsUserTest.SANJIGI, QuestionTest.Q1, "Answers Contents2");

public static final Answer A1 = new Answer(NsUserTest.JAVAJIGI, QuestionTest.Q1,
"Answers Contents1");
public static final Answer A2 = new Answer(NsUserTest.SANJIGI, QuestionTest.Q1,
"Answers Contents2");

@DisplayName("λ‹΅λ³€ λ“±λ‘μžμ™€ 둜그인 μœ μ €κ°€ μΌμΉ˜ν•  땐 μ˜ˆμ™Έλ₯Ό λ˜μ§€μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.")
@Test
void doesNotTrowExceptionWhenDeleteLoginUserMatchedWithAnswerWriter() {
NsUser writer = A1.getWriter();
assertThatNoException().isThrownBy(() -> A1.delete(writer));
assertThat(A1.isDeleted()).isTrue();
}

@DisplayName("λ‹΅λ³€ λ“±λ‘μžμ™€ 둜그인 μœ μ €κ°€ μΌμΉ˜ν•˜μ§€ μ•Šμ€λ° μ‚­μ œν•˜λ €κ³  ν•˜λ©΄ μ˜ˆμ™Έλ₯Ό λ˜μ§‘λ‹ˆλ‹€.")
@Test
void throwExceptionWhenDeleteLoginUserDoesNotMatchedWithAnswerWriter() {
NsUser writer = A1.getWriter();
assertThatThrownBy(() -> A2.delete(writer)).isInstanceOf(CannotDeleteException.class);
assertThat(A2.isDeleted()).isFalse();
}


}
27 changes: 27 additions & 0 deletions src/test/java/nextstep/qna/domain/QuestionTest.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,35 @@
package nextstep.qna.domain;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import nextstep.qna.CannotDeleteException;
import nextstep.users.domain.NsUser;
import nextstep.users.domain.NsUserTest;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class QuestionTest {

public static final Question Q1 = new Question(NsUserTest.JAVAJIGI, "title1", "contents1");
public static final Question Q2 = new Question(NsUserTest.SANJIGI, "title2", "contents2");

@DisplayName("μ‚­μ œν•  κΆŒν•œμ΄ μžˆλ‹€λ©΄ 질문이 μ‚­μ œλ©λ‹ˆλ‹€.")
@Test
void canDeleteIfYouHavePermission() throws CannotDeleteException {
NsUser writer = Q1.getWriter();
assertThatNoException().isThrownBy(() -> Q1.delete(writer));
assertThat(Q1.isDeleted()).isTrue();
}

@DisplayName("μ‚­μ œν•  κΆŒν•œμ΄ μ—†λ‹€λ©΄ μ§ˆλ¬Έμ„ μ‚­μ œν•  수 μ—†μŠ΅λ‹ˆλ‹€.")
@Test
void canNotDeleteIfYouDontHavePermission() throws CannotDeleteException {
NsUser writer = Q1.getWriter();
assertThatThrownBy(() -> Q2.delete(writer)).isInstanceOf(CannotDeleteException.class);
assertThat(Q2.isDeleted()).isFalse();
}

}