Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/main/java/nextstep/domain/Question.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -38,6 +41,24 @@ public Question(String title, String contents) {
this.contents = contents;
}

public Question update(User loginUser, Question updatedQuestion) {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

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;
}
Expand Down
12 changes: 8 additions & 4 deletions src/main/java/nextstep/service/QnaService.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -36,14 +38,16 @@ public Optional<Question> 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<Question> findAll() {
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/nextstep/web/HomeController.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
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<Question> questionsIterable = qnaService.findAll();
List<Question> questions = new ArrayList<>();
questionsIterable.forEach(questions::add);
model.addAttribute("questions",questions);
Copy link
Contributor

Choose a reason for hiding this comment

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

굳이 새로운 List에 담기보다 questionsIterable을 그냥 전달해도 되지 않을까?

Copy link
Contributor

Choose a reason for hiding this comment

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

space convention 위반

return "home";
}
}
23 changes: 23 additions & 0 deletions src/main/java/nextstep/web/LoginController.java
Original file line number Diff line number Diff line change
@@ -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:/";
Copy link
Contributor

Choose a reason for hiding this comment

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

로그인 실패하는 경우에 대한 예외처리도 하면 좋지 않을까?

}
}
87 changes: 87 additions & 0 deletions src/main/java/nextstep/web/QuestionController.java
Original file line number Diff line number Diff line change
@@ -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<Question> questions = new ArrayList<>();
Iterable<Question> 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";
}




}
2 changes: 1 addition & 1 deletion src/main/resources/templates/qna/updateForm.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<div class="col-md-12 col-sm-12 col-lg-10 col-lg-offset-1">
<div class="panel panel-default content-main">
{{# question }}
<form name="question" method="post" action="/questions">
<form name="question" method="post" action="/questions/{id}}">
<div class="form-group">
<label for="title">제목</label>
<input type="text" class="form-control" id="title" name="title" value="{{ title }}"
Expand Down
24 changes: 24 additions & 0 deletions src/test/java/nextstep/web/LoginAcceptanceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package nextstep.web;

import org.junit.Test;
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 LoginAcceptanceTest extends AcceptanceTest {

@Test
public void login() {
HttpEntity<MultiValueMap<String, Object>> request = HtmlFormDataBuilder.urlEncodedForm()
.addParameter("userId", "javajigi")
.addParameter("password", "test")
.build();
ResponseEntity<String> response = template().postForEntity("/login", request, String.class);

softly.assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FOUND);
softly.assertThat(response.getHeaders().getLocation().getPath()).startsWith("/");
}
}
83 changes: 83 additions & 0 deletions src/test/java/nextstep/web/QuestionAcceptanceTest.java
Original file line number Diff line number Diff line change
@@ -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<String> 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<MultiValueMap<String, Object>> request = HtmlFormDataBuilder.urlEncodedForm()
.addParameter("title", "테스트")
.addParameter("contents", "테스트내용")
.build();
ResponseEntity<String> 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<String> 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<String> response = update(basicAuthTemplate());

softly.assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FOUND);
softly.assertThat((response.getHeaders().getLocation()).getPath().startsWith("/questions"));
}

private ResponseEntity<String> update(TestRestTemplate template) throws Exception {

HttpEntity<MultiValueMap<String, Object>> 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<String> response = basicAuthTemplate(defaultUser())
.postForEntity("/questions/1", request, String.class);

softly.assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FOUND);
softly.assertThat((response.getHeaders().getLocation()).getPath().startsWith("/questions"));
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

도메인 객체에 로직을 구현했는데 이에 대한 단위 테스트도 구현하면 좋지 않을까?