From 05e48b1e1a35a720a9d131b952a180be5f312aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=92=E1=85=A8=E1=84=8C?= =?UTF-8?q?=E1=85=B5=E1=86=AB=28Hyejin=20Kim=29/Escrow=E1=84=80=E1=85=A2?= =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=AF=E1=84=90=E1=85=B5=E1=86=B7/11ST?= Date: Wed, 3 Apr 2019 01:38:27 +0900 Subject: [PATCH 1/2] =?UTF-8?q?Step2=20Acceptance=20Test=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/domain/Question.java | 21 +++++ .../java/nextstep/service/QnaService.java | 12 ++- .../java/nextstep/web/HomeController.java | 15 ++++ .../java/nextstep/web/LoginController.java | 23 +++++ .../java/nextstep/web/QuestionController.java | 87 +++++++++++++++++++ .../resources/templates/qna/updateForm.html | 2 +- .../nextstep/web/LoginAcceptanceTest.java | 24 +++++ .../nextstep/web/QuestionAcceptanceTest.java | 83 ++++++++++++++++++ 8 files changed, 262 insertions(+), 5 deletions(-) create mode 100644 src/main/java/nextstep/web/LoginController.java create mode 100644 src/main/java/nextstep/web/QuestionController.java create mode 100644 src/test/java/nextstep/web/LoginAcceptanceTest.java create mode 100644 src/test/java/nextstep/web/QuestionAcceptanceTest.java diff --git a/src/main/java/nextstep/domain/Question.java b/src/main/java/nextstep/domain/Question.java index 2d4fecb..7048576 100644 --- a/src/main/java/nextstep/domain/Question.java +++ b/src/main/java/nextstep/domain/Question.java @@ -1,5 +1,8 @@ package nextstep.domain; +import nextstep.CannotDeleteException; +import nextstep.UnAuthenticationException; +import nextstep.UnAuthorizedException; import org.hibernate.annotations.Where; import support.domain.AbstractEntity; import support.domain.UrlGeneratable; @@ -38,6 +41,24 @@ public Question(String title, String contents) { this.contents = contents; } + public Question update(User loginUser, Question updatedQuestion) { + if (!isOwner(loginUser)) { + throw new UnAuthorizedException(); + } + + this.title = updatedQuestion.title; + this.contents = updatedQuestion.contents; + return this; + } + + public Question delete(User loginUser) throws CannotDeleteException { + if (!isOwner(loginUser)) { + throw new UnAuthorizedException(); + } + this.deleted = true; + return this; + } + public String getTitle() { return title; } diff --git a/src/main/java/nextstep/service/QnaService.java b/src/main/java/nextstep/service/QnaService.java index a910454..5edf9d2 100644 --- a/src/main/java/nextstep/service/QnaService.java +++ b/src/main/java/nextstep/service/QnaService.java @@ -1,6 +1,8 @@ package nextstep.service; import nextstep.CannotDeleteException; +import nextstep.UnAuthenticationException; +import nextstep.UnAuthorizedException; import nextstep.domain.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,14 +38,16 @@ public Optional findById(long id) { } @Transactional - public Question update(User loginUser, long id, Question updatedQuestion) { - // TODO 수정 기능 구현 - return null; + public Question update(User loginUser, long id, Question updatedQuestion) throws UnAuthorizedException { + Question question = findById(id).orElseThrow(UnAuthorizedException::new); + question.update(loginUser, updatedQuestion); + return question; } @Transactional public void deleteQuestion(User loginUser, long questionId) throws CannotDeleteException { - // TODO 삭제 기능 구현 + Question question = findById(questionId).orElseThrow(UnAuthorizedException::new); + question.delete(loginUser); } public Iterable findAll() { diff --git a/src/main/java/nextstep/web/HomeController.java b/src/main/java/nextstep/web/HomeController.java index 2ee7621..be22472 100755 --- a/src/main/java/nextstep/web/HomeController.java +++ b/src/main/java/nextstep/web/HomeController.java @@ -1,13 +1,28 @@ package nextstep.web; +import nextstep.domain.Question; +import nextstep.service.QnaService; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + @Controller public class HomeController { + + @Resource(name = "qnaService") + private QnaService qnaService; @GetMapping("/") public String home(Model model) { + Iterable questionsIterable = qnaService.findAll(); + List questions = new ArrayList<>(); + + questionsIterable.forEach(questions::add); + model.addAttribute("questions",questions); return "home"; } } diff --git a/src/main/java/nextstep/web/LoginController.java b/src/main/java/nextstep/web/LoginController.java new file mode 100644 index 0000000..91434f6 --- /dev/null +++ b/src/main/java/nextstep/web/LoginController.java @@ -0,0 +1,23 @@ +package nextstep.web; + +import nextstep.UnAuthenticationException; +import nextstep.domain.User; +import nextstep.security.HttpSessionUtils; +import nextstep.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; + +import javax.servlet.http.HttpSession; + +public class LoginController { + + @Autowired + private UserService userService; + + @PostMapping("/login") + public String login(String userId, String password, HttpSession session) throws UnAuthenticationException { + User loginUser = userService.login(userId, password); + session.setAttribute(HttpSessionUtils.USER_SESSION_KEY, loginUser); + return "redirect:/"; + } +} diff --git a/src/main/java/nextstep/web/QuestionController.java b/src/main/java/nextstep/web/QuestionController.java new file mode 100644 index 0000000..4e30550 --- /dev/null +++ b/src/main/java/nextstep/web/QuestionController.java @@ -0,0 +1,87 @@ +package nextstep.web; + +import nextstep.CannotDeleteException; +import nextstep.UnAuthorizedException; +import nextstep.domain.Question; +import nextstep.domain.QuestionRepository; +import nextstep.domain.User; +import nextstep.security.LoginUser; +import nextstep.service.QnaService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.persistence.EntityNotFoundException; +import javax.transaction.Transactional; +import java.util.ArrayList; +import java.util.List; + +@Controller +@RequestMapping("/questions") +public class QuestionController { + + private static final Logger log = LoggerFactory.getLogger(QuestionController.class); + + @Resource(name = "qnaService") + private QnaService qnaService; + + @Resource(name = "questionRepository") + private QuestionRepository questionRepository; + + + @GetMapping("/form") + public String form() { return "/qna/form"; } + + @PostMapping("") + public String create(Question question, @LoginUser User loginUser) { + qnaService.create(loginUser, question); + return "redirect:/questions"; + } + + @GetMapping("") + public String list(Model model) { + List questions = new ArrayList<>(); + Iterable questionsIterable = qnaService.findAll(); + questionsIterable.iterator().forEachRemaining(questions::add); + model.addAttribute("questions", questions); + return "home"; + } + + @GetMapping("/{id}") + public String showDetailQuestion(@PathVariable Long id, Model model) { + Question question = qnaService.findById(id).orElseThrow(EntityNotFoundException::new); + model.addAttribute("question", question); + return "/qna/show"; + } + + @PostMapping("/{id}") + public String update(@LoginUser User loginUser, @PathVariable long id, Question question) throws UnAuthorizedException{ + qnaService.update(loginUser, id, question); + return "redirect:/questions"; + } + + @GetMapping("/{id}/form") + public String formForUpdate(@LoginUser User loginUser, @PathVariable long id, Model model) { + Question question = qnaService.findById(id).orElseThrow(EntityNotFoundException::new); + if (!question.isOwner(loginUser)) { + throw new UnAuthorizedException(); + } + model.addAttribute("question", question); + return "/qna/updateForm"; + } + + @DeleteMapping("/{id}") + public String delete(@LoginUser User loginUser, @PathVariable long id) throws CannotDeleteException { + qnaService.deleteQuestion(loginUser, id); + return "redirect:/questions"; + } + + + + +} diff --git a/src/main/resources/templates/qna/updateForm.html b/src/main/resources/templates/qna/updateForm.html index cd1f3cd..9472955 100644 --- a/src/main/resources/templates/qna/updateForm.html +++ b/src/main/resources/templates/qna/updateForm.html @@ -3,7 +3,7 @@
{{# question }} -
+
> request = HtmlFormDataBuilder.urlEncodedForm() + .addParameter("userId", "javajigi") + .addParameter("password", "test") + .build(); + ResponseEntity response = template().postForEntity("/login", request, String.class); + + softly.assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FOUND); + softly.assertThat(response.getHeaders().getLocation().getPath()).startsWith("/"); + } +} diff --git a/src/test/java/nextstep/web/QuestionAcceptanceTest.java b/src/test/java/nextstep/web/QuestionAcceptanceTest.java new file mode 100644 index 0000000..cb85ba4 --- /dev/null +++ b/src/test/java/nextstep/web/QuestionAcceptanceTest.java @@ -0,0 +1,83 @@ +package nextstep.web; + +import nextstep.domain.QuestionRepository; +import nextstep.domain.User; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.MultiValueMap; +import support.test.AcceptanceTest; +import support.test.HtmlFormDataBuilder; + +public class QuestionAcceptanceTest extends AcceptanceTest { + private static final Logger log = LoggerFactory.getLogger(UserAcceptanceTest.class); + + @Autowired + private QuestionRepository questionRepository; + + @Test + public void createForm() { + ResponseEntity response = template().getForEntity("/questions/form", String.class); + softly.assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + log.debug("body : {}", response.getBody()); + } + + @Test + public void create() { + User loginUser = defaultUser(); + HttpEntity> request = HtmlFormDataBuilder.urlEncodedForm() + .addParameter("title", "테스트") + .addParameter("contents", "테스트내용") + .build(); + ResponseEntity response = basicAuthTemplate(loginUser) + .postForEntity("/questions", request, String.class); + + softly.assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FOUND); + softly.assertThat(questionRepository.findById(loginUser.getId()).isPresent()).isTrue(); + softly.assertThat(response.getHeaders().getLocation().getPath()).startsWith("/questions"); + } + + @Test + public void list() { + ResponseEntity response = template().getForEntity("/questions", String.class); + softly.assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + log.debug("body : {}", response.getBody()); + softly.assertThat(response.getBody().contains("Ruby")).isTrue(); + } + + @Test + public void update() throws Exception { + ResponseEntity response = update(basicAuthTemplate()); + + softly.assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FOUND); + softly.assertThat((response.getHeaders().getLocation()).getPath().startsWith("/questions")); + } + + private ResponseEntity update(TestRestTemplate template) throws Exception { + + HttpEntity> request = HtmlFormDataBuilder + .urlEncodedForm() + .addParameter("title", "테스트제목") + .addParameter("contents", "테스트내용") + .build(); + + return template.postForEntity("/questions/1", request, String.class); + } + + @Test + public void delete() { + HttpEntity request = HtmlFormDataBuilder.urlEncodedForm() + .addParameter("_method","delete") + .build(); + ResponseEntity response = basicAuthTemplate(defaultUser()) + .postForEntity("/questions/1", request, String.class); + + softly.assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FOUND); + softly.assertThat((response.getHeaders().getLocation()).getPath().startsWith("/questions")); + } +} From 80c1f69053c8aec5616ed6ade0f83ae9ddad3a9b Mon Sep 17 00:00:00 2001 From: decaffeine Date: Wed, 3 Apr 2019 01:44:57 +0900 Subject: [PATCH 2/2] =?UTF-8?q?Step2=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/web/HomeController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/nextstep/web/HomeController.java b/src/main/java/nextstep/web/HomeController.java index be22472..6309fc1 100755 --- a/src/main/java/nextstep/web/HomeController.java +++ b/src/main/java/nextstep/web/HomeController.java @@ -20,7 +20,6 @@ public class HomeController { public String home(Model model) { Iterable questionsIterable = qnaService.findAll(); List questions = new ArrayList<>(); - questionsIterable.forEach(questions::add); model.addAttribute("questions",questions); return "home";