Update bean validation
This commit is contained in:
parent
1d6e4af94a
commit
3602ebdc81
@ -1,19 +1,28 @@
|
|||||||
package io.spring.api;
|
package io.spring.api;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonRootName;
|
import com.fasterxml.jackson.annotation.JsonRootName;
|
||||||
import io.spring.api.exception.InvalidRequestException;
|
|
||||||
import io.spring.application.Page;
|
|
||||||
import io.spring.application.ArticleQueryService;
|
import io.spring.application.ArticleQueryService;
|
||||||
|
import io.spring.application.Page;
|
||||||
import io.spring.core.article.Article;
|
import io.spring.core.article.Article;
|
||||||
import io.spring.core.article.ArticleRepository;
|
import io.spring.core.article.ArticleRepository;
|
||||||
import io.spring.core.user.User;
|
import io.spring.core.user.User;
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import javax.validation.Constraint;
|
||||||
|
import javax.validation.ConstraintValidator;
|
||||||
|
import javax.validation.ConstraintValidatorContext;
|
||||||
|
import javax.validation.Payload;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import javax.validation.constraints.NotBlank;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
import org.springframework.validation.BindingResult;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
@ -21,73 +30,95 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping(path = "/articles")
|
@RequestMapping(path = "/articles")
|
||||||
public class ArticlesApi {
|
public class ArticlesApi {
|
||||||
private ArticleRepository articleRepository;
|
private ArticleRepository articleRepository;
|
||||||
private ArticleQueryService articleQueryService;
|
private ArticleQueryService articleQueryService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ArticlesApi(ArticleRepository articleRepository, ArticleQueryService articleQueryService) {
|
public ArticlesApi(ArticleRepository articleRepository, ArticleQueryService articleQueryService) {
|
||||||
this.articleRepository = articleRepository;
|
this.articleRepository = articleRepository;
|
||||||
this.articleQueryService = articleQueryService;
|
this.articleQueryService = articleQueryService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseEntity createArticle(@Valid @RequestBody NewArticleParam newArticleParam,
|
public ResponseEntity createArticle(
|
||||||
BindingResult bindingResult,
|
@Valid @RequestBody NewArticleParam newArticleParam, @AuthenticationPrincipal User user) {
|
||||||
@AuthenticationPrincipal User user) {
|
Article article =
|
||||||
if (bindingResult.hasErrors()) {
|
new Article(
|
||||||
throw new InvalidRequestException(bindingResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (articleQueryService.findBySlug(Article.toSlug(newArticleParam.getTitle()), null).isPresent()) {
|
|
||||||
bindingResult.rejectValue("title", "DUPLICATED", "article name exists");
|
|
||||||
throw new InvalidRequestException(bindingResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
Article article = new Article(
|
|
||||||
newArticleParam.getTitle(),
|
newArticleParam.getTitle(),
|
||||||
newArticleParam.getDescription(),
|
newArticleParam.getDescription(),
|
||||||
newArticleParam.getBody(),
|
newArticleParam.getBody(),
|
||||||
newArticleParam.getTagList(),
|
newArticleParam.getTagList(),
|
||||||
user.getId());
|
user.getId());
|
||||||
articleRepository.save(article);
|
articleRepository.save(article);
|
||||||
return ResponseEntity.ok(new HashMap<String, Object>() {{
|
return ResponseEntity.ok(
|
||||||
|
new HashMap<String, Object>() {
|
||||||
|
{
|
||||||
put("article", articleQueryService.findById(article.getId(), user).get());
|
put("article", articleQueryService.findById(article.getId(), user).get());
|
||||||
}});
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(path = "feed")
|
@GetMapping(path = "feed")
|
||||||
public ResponseEntity getFeed(@RequestParam(value = "offset", defaultValue = "0") int offset,
|
public ResponseEntity getFeed(
|
||||||
@RequestParam(value = "limit", defaultValue = "20") int limit,
|
@RequestParam(value = "offset", defaultValue = "0") int offset,
|
||||||
@AuthenticationPrincipal User user) {
|
@RequestParam(value = "limit", defaultValue = "20") int limit,
|
||||||
return ResponseEntity.ok(articleQueryService.findUserFeed(user, new Page(offset, limit)));
|
@AuthenticationPrincipal User user) {
|
||||||
}
|
return ResponseEntity.ok(articleQueryService.findUserFeed(user, new Page(offset, limit)));
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity getArticles(@RequestParam(value = "offset", defaultValue = "0") int offset,
|
public ResponseEntity getArticles(
|
||||||
@RequestParam(value = "limit", defaultValue = "20") int limit,
|
@RequestParam(value = "offset", defaultValue = "0") int offset,
|
||||||
@RequestParam(value = "tag", required = false) String tag,
|
@RequestParam(value = "limit", defaultValue = "20") int limit,
|
||||||
@RequestParam(value = "favorited", required = false) String favoritedBy,
|
@RequestParam(value = "tag", required = false) String tag,
|
||||||
@RequestParam(value = "author", required = false) String author,
|
@RequestParam(value = "favorited", required = false) String favoritedBy,
|
||||||
@AuthenticationPrincipal User user) {
|
@RequestParam(value = "author", required = false) String author,
|
||||||
return ResponseEntity.ok(articleQueryService.findRecentArticles(tag, author, favoritedBy, new Page(offset, limit), user));
|
@AuthenticationPrincipal User user) {
|
||||||
}
|
return ResponseEntity.ok(
|
||||||
|
articleQueryService.findRecentArticles(
|
||||||
|
tag, author, favoritedBy, new Page(offset, limit), user));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@JsonRootName("article")
|
@JsonRootName("article")
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
class NewArticleParam {
|
class NewArticleParam {
|
||||||
@NotBlank(message = "can't be empty")
|
@NotBlank(message = "can't be empty")
|
||||||
private String title;
|
@DuplicatedArticleConstraint
|
||||||
@NotBlank(message = "can't be empty")
|
private String title;
|
||||||
private String description;
|
|
||||||
@NotBlank(message = "can't be empty")
|
@NotBlank(message = "can't be empty")
|
||||||
private String body;
|
private String description;
|
||||||
private String[] tagList;
|
|
||||||
}
|
@NotBlank(message = "can't be empty")
|
||||||
|
private String body;
|
||||||
|
|
||||||
|
private String[] tagList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Documented
|
||||||
|
@Constraint(validatedBy = DuplicatedArticleValidator.class)
|
||||||
|
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE_USE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface DuplicatedArticleConstraint {
|
||||||
|
String message() default "article name exists";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
}
|
||||||
|
|
||||||
|
class DuplicatedArticleValidator
|
||||||
|
implements ConstraintValidator<DuplicatedArticleConstraint, String> {
|
||||||
|
|
||||||
|
@Autowired private ArticleQueryService articleQueryService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||||
|
return !articleQueryService.findBySlug(Article.toSlug(value), null).isPresent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,24 +1,26 @@
|
|||||||
package io.spring.api;
|
package io.spring.api;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonRootName;
|
import com.fasterxml.jackson.annotation.JsonRootName;
|
||||||
import io.spring.api.exception.InvalidRequestException;
|
|
||||||
import io.spring.api.exception.NoAuthorizationException;
|
import io.spring.api.exception.NoAuthorizationException;
|
||||||
import io.spring.api.exception.ResourceNotFoundException;
|
import io.spring.api.exception.ResourceNotFoundException;
|
||||||
import io.spring.core.service.AuthorizationService;
|
|
||||||
import io.spring.application.data.CommentData;
|
|
||||||
import io.spring.application.CommentQueryService;
|
import io.spring.application.CommentQueryService;
|
||||||
|
import io.spring.application.data.CommentData;
|
||||||
import io.spring.core.article.Article;
|
import io.spring.core.article.Article;
|
||||||
import io.spring.core.article.ArticleRepository;
|
import io.spring.core.article.ArticleRepository;
|
||||||
import io.spring.core.comment.Comment;
|
import io.spring.core.comment.Comment;
|
||||||
import io.spring.core.comment.CommentRepository;
|
import io.spring.core.comment.CommentRepository;
|
||||||
|
import io.spring.core.service.AuthorizationService;
|
||||||
import io.spring.core.user.User;
|
import io.spring.core.user.User;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import javax.validation.constraints.NotBlank;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
import org.springframework.validation.BindingResult;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@ -27,80 +29,87 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping(path = "/articles/{slug}/comments")
|
@RequestMapping(path = "/articles/{slug}/comments")
|
||||||
public class CommentsApi {
|
public class CommentsApi {
|
||||||
private ArticleRepository articleRepository;
|
private ArticleRepository articleRepository;
|
||||||
private CommentRepository commentRepository;
|
private CommentRepository commentRepository;
|
||||||
private CommentQueryService commentQueryService;
|
private CommentQueryService commentQueryService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public CommentsApi(ArticleRepository articleRepository,
|
public CommentsApi(
|
||||||
CommentRepository commentRepository,
|
ArticleRepository articleRepository,
|
||||||
CommentQueryService commentQueryService) {
|
CommentRepository commentRepository,
|
||||||
this.articleRepository = articleRepository;
|
CommentQueryService commentQueryService) {
|
||||||
this.commentRepository = commentRepository;
|
this.articleRepository = articleRepository;
|
||||||
this.commentQueryService = commentQueryService;
|
this.commentRepository = commentRepository;
|
||||||
}
|
this.commentQueryService = commentQueryService;
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseEntity<?> createComment(@PathVariable("slug") String slug,
|
public ResponseEntity<?> createComment(
|
||||||
@AuthenticationPrincipal User user,
|
@PathVariable("slug") String slug,
|
||||||
@Valid @RequestBody NewCommentParam newCommentParam,
|
@AuthenticationPrincipal User user,
|
||||||
BindingResult bindingResult) {
|
@Valid @RequestBody NewCommentParam newCommentParam) {
|
||||||
Article article = findArticle(slug);
|
Article article = findArticle(slug);
|
||||||
if (bindingResult.hasErrors()) {
|
Comment comment = new Comment(newCommentParam.getBody(), user.getId(), article.getId());
|
||||||
throw new InvalidRequestException(bindingResult);
|
commentRepository.save(comment);
|
||||||
}
|
return ResponseEntity.status(201)
|
||||||
Comment comment = new Comment(newCommentParam.getBody(), user.getId(), article.getId());
|
.body(commentResponse(commentQueryService.findById(comment.getId(), user).get()));
|
||||||
commentRepository.save(comment);
|
}
|
||||||
return ResponseEntity.status(201).body(commentResponse(commentQueryService.findById(comment.getId(), user).get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity getComments(@PathVariable("slug") String slug,
|
public ResponseEntity getComments(
|
||||||
@AuthenticationPrincipal User user) {
|
@PathVariable("slug") String slug, @AuthenticationPrincipal User user) {
|
||||||
Article article = findArticle(slug);
|
Article article = findArticle(slug);
|
||||||
List<CommentData> comments = commentQueryService.findByArticleId(article.getId(), user);
|
List<CommentData> comments = commentQueryService.findByArticleId(article.getId(), user);
|
||||||
return ResponseEntity.ok(new HashMap<String, Object>() {{
|
return ResponseEntity.ok(
|
||||||
|
new HashMap<String, Object>() {
|
||||||
|
{
|
||||||
put("comments", comments);
|
put("comments", comments);
|
||||||
}});
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@RequestMapping(path = "{id}", method = RequestMethod.DELETE)
|
@RequestMapping(path = "{id}", method = RequestMethod.DELETE)
|
||||||
public ResponseEntity deleteComment(@PathVariable("slug") String slug,
|
public ResponseEntity deleteComment(
|
||||||
@PathVariable("id") String commentId,
|
@PathVariable("slug") String slug,
|
||||||
@AuthenticationPrincipal User user) {
|
@PathVariable("id") String commentId,
|
||||||
Article article = findArticle(slug);
|
@AuthenticationPrincipal User user) {
|
||||||
return commentRepository.findById(article.getId(), commentId).map(comment -> {
|
Article article = findArticle(slug);
|
||||||
if (!AuthorizationService.canWriteComment(user, article, comment)) {
|
return commentRepository
|
||||||
|
.findById(article.getId(), commentId)
|
||||||
|
.map(
|
||||||
|
comment -> {
|
||||||
|
if (!AuthorizationService.canWriteComment(user, article, comment)) {
|
||||||
throw new NoAuthorizationException();
|
throw new NoAuthorizationException();
|
||||||
}
|
}
|
||||||
commentRepository.remove(comment);
|
commentRepository.remove(comment);
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}).orElseThrow(ResourceNotFoundException::new);
|
})
|
||||||
}
|
.orElseThrow(ResourceNotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
private Article findArticle(String slug) {
|
private Article findArticle(String slug) {
|
||||||
return articleRepository.findBySlug(slug).map(article -> article).orElseThrow(ResourceNotFoundException::new);
|
return articleRepository
|
||||||
}
|
.findBySlug(slug)
|
||||||
|
.map(article -> article)
|
||||||
|
.orElseThrow(ResourceNotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, Object> commentResponse(CommentData commentData) {
|
private Map<String, Object> commentResponse(CommentData commentData) {
|
||||||
return new HashMap<String, Object>() {{
|
return new HashMap<String, Object>() {
|
||||||
put("comment", commentData);
|
{
|
||||||
}};
|
put("comment", commentData);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@JsonRootName("comment")
|
@JsonRootName("comment")
|
||||||
class NewCommentParam {
|
class NewCommentParam {
|
||||||
@NotBlank(message = "can't be empty")
|
@NotBlank(message = "can't be empty")
|
||||||
private String body;
|
private String body;
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,28 @@
|
|||||||
package io.spring.api;
|
package io.spring.api;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonRootName;
|
import com.fasterxml.jackson.annotation.JsonRootName;
|
||||||
import io.spring.api.exception.InvalidRequestException;
|
|
||||||
import io.spring.application.UserQueryService;
|
import io.spring.application.UserQueryService;
|
||||||
import io.spring.application.data.UserWithToken;
|
|
||||||
import io.spring.application.data.UserData;
|
import io.spring.application.data.UserData;
|
||||||
|
import io.spring.application.data.UserWithToken;
|
||||||
import io.spring.core.user.User;
|
import io.spring.core.user.User;
|
||||||
import io.spring.core.user.UserRepository;
|
import io.spring.core.user.UserRepository;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.validation.Constraint;
|
||||||
|
import javax.validation.ConstraintValidator;
|
||||||
|
import javax.validation.ConstraintValidatorContext;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import javax.validation.constraints.Email;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import javax.validation.constraints.Email;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PutMapping;
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
@ -21,90 +30,140 @@ import org.springframework.web.bind.annotation.RequestHeader;
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping(path = "/user")
|
@RequestMapping(path = "/user")
|
||||||
public class CurrentUserApi {
|
public class CurrentUserApi {
|
||||||
private UserQueryService userQueryService;
|
|
||||||
private UserRepository userRepository;
|
|
||||||
|
|
||||||
@Autowired
|
private UserQueryService userQueryService;
|
||||||
public CurrentUserApi(UserQueryService userQueryService, UserRepository userRepository) {
|
private UserService userService;
|
||||||
this.userQueryService = userQueryService;
|
|
||||||
this.userRepository = userRepository;
|
@Autowired
|
||||||
}
|
public CurrentUserApi(UserQueryService userQueryService, UserService userService) {
|
||||||
|
this.userQueryService = userQueryService;
|
||||||
@GetMapping
|
this.userService = userService;
|
||||||
public ResponseEntity currentUser(@AuthenticationPrincipal User currentUser,
|
}
|
||||||
@RequestHeader(value = "Authorization") String authorization) {
|
|
||||||
UserData userData = userQueryService.findById(currentUser.getId()).get();
|
@GetMapping
|
||||||
return ResponseEntity.ok(userResponse(
|
public ResponseEntity currentUser(
|
||||||
new UserWithToken(userData, authorization.split(" ")[1])
|
@AuthenticationPrincipal User currentUser,
|
||||||
));
|
@RequestHeader(value = "Authorization") String authorization) {
|
||||||
}
|
UserData userData = userQueryService.findById(currentUser.getId()).get();
|
||||||
|
return ResponseEntity.ok(
|
||||||
@PutMapping
|
userResponse(new UserWithToken(userData, authorization.split(" ")[1])));
|
||||||
public ResponseEntity updateProfile(@AuthenticationPrincipal User currentUser,
|
}
|
||||||
@RequestHeader("Authorization") String token,
|
|
||||||
@Valid @RequestBody UpdateUserParam updateUserParam,
|
@PutMapping
|
||||||
BindingResult bindingResult) {
|
public ResponseEntity updateProfile(
|
||||||
if (bindingResult.hasErrors()) {
|
@AuthenticationPrincipal User currentUser,
|
||||||
throw new InvalidRequestException(bindingResult);
|
@RequestHeader("Authorization") String token,
|
||||||
}
|
@Valid @RequestBody UpdateUserParam updateUserParam) {
|
||||||
checkUniquenessOfUsernameAndEmail(currentUser, updateUserParam, bindingResult);
|
|
||||||
|
userService.updateUser(new UpdateUserCommand(currentUser, updateUserParam));
|
||||||
currentUser.update(
|
UserData userData = userQueryService.findById(currentUser.getId()).get();
|
||||||
updateUserParam.getEmail(),
|
return ResponseEntity.ok(userResponse(new UserWithToken(userData, token.split(" ")[1])));
|
||||||
updateUserParam.getUsername(),
|
}
|
||||||
updateUserParam.getPassword(),
|
|
||||||
updateUserParam.getBio(),
|
private Map<String, Object> userResponse(UserWithToken userWithToken) {
|
||||||
updateUserParam.getImage());
|
return new HashMap<String, Object>() {
|
||||||
userRepository.save(currentUser);
|
{
|
||||||
UserData userData = userQueryService.findById(currentUser.getId()).get();
|
put("user", userWithToken);
|
||||||
return ResponseEntity.ok(userResponse(
|
}
|
||||||
new UserWithToken(userData, token.split(" ")[1])
|
};
|
||||||
));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkUniquenessOfUsernameAndEmail(User currentUser, UpdateUserParam updateUserParam, BindingResult bindingResult) {
|
@Validated
|
||||||
if (!"".equals(updateUserParam.getUsername())) {
|
@Service
|
||||||
Optional<User> byUsername = userRepository.findByUsername(updateUserParam.getUsername());
|
class UserService {
|
||||||
if (byUsername.isPresent() && !byUsername.get().equals(currentUser)) {
|
|
||||||
bindingResult.rejectValue("username", "DUPLICATED", "username already exist");
|
private UserRepository userRepository;
|
||||||
}
|
|
||||||
}
|
@Autowired
|
||||||
|
public UserService(UserRepository userRepository) {
|
||||||
if (!"".equals(updateUserParam.getEmail())) {
|
this.userRepository = userRepository;
|
||||||
Optional<User> byEmail = userRepository.findByEmail(updateUserParam.getEmail());
|
}
|
||||||
if (byEmail.isPresent() && !byEmail.get().equals(currentUser)) {
|
|
||||||
bindingResult.rejectValue("email", "DUPLICATED", "email already exist");
|
public void updateUser(@Valid UpdateUserCommand command) {
|
||||||
}
|
User user = command.getTargetUser();
|
||||||
}
|
UpdateUserParam updateUserParam = command.getParam();
|
||||||
|
user.update(
|
||||||
if (bindingResult.hasErrors()) {
|
updateUserParam.getEmail(),
|
||||||
throw new InvalidRequestException(bindingResult);
|
updateUserParam.getUsername(),
|
||||||
}
|
updateUserParam.getPassword(),
|
||||||
}
|
updateUserParam.getBio(),
|
||||||
|
updateUserParam.getImage());
|
||||||
private Map<String, Object> userResponse(UserWithToken userWithToken) {
|
userRepository.save(user);
|
||||||
return new HashMap<String, Object>() {{
|
}
|
||||||
put("user", userWithToken);
|
}
|
||||||
}};
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@UpdateUserConstraint
|
||||||
|
class UpdateUserCommand {
|
||||||
|
|
||||||
|
private User targetUser;
|
||||||
|
private UpdateUserParam param;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Constraint(validatedBy = UpdateUserValidator.class)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface UpdateUserConstraint {
|
||||||
|
|
||||||
|
String message() default "invalid update param";
|
||||||
|
|
||||||
|
Class[] groups() default {};
|
||||||
|
|
||||||
|
Class[] payload() default {};
|
||||||
|
}
|
||||||
|
|
||||||
|
class UpdateUserValidator implements ConstraintValidator<UpdateUserConstraint, UpdateUserCommand> {
|
||||||
|
|
||||||
|
@Autowired private UserRepository userRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(UpdateUserCommand value, ConstraintValidatorContext context) {
|
||||||
|
String inputEmail = value.getParam().getEmail();
|
||||||
|
String inputUsername = value.getParam().getUsername();
|
||||||
|
final User targetUser = value.getTargetUser();
|
||||||
|
|
||||||
|
boolean isEmailValid =
|
||||||
|
userRepository.findByEmail(inputEmail).map(user -> user.equals(targetUser)).orElse(true);
|
||||||
|
boolean isUsernameValid =
|
||||||
|
userRepository
|
||||||
|
.findByUsername(inputUsername)
|
||||||
|
.map(user -> user.equals(targetUser))
|
||||||
|
.orElse(true);
|
||||||
|
if (isEmailValid && isUsernameValid) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
context.disableDefaultConstraintViolation();
|
||||||
|
if (!isEmailValid) {
|
||||||
|
context
|
||||||
|
.buildConstraintViolationWithTemplate("email already exist")
|
||||||
|
.addPropertyNode("email")
|
||||||
|
.addConstraintViolation();
|
||||||
|
}
|
||||||
|
if (!isUsernameValid) {
|
||||||
|
context
|
||||||
|
.buildConstraintViolationWithTemplate("username already exist")
|
||||||
|
.addPropertyNode("username")
|
||||||
|
.addConstraintViolation();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@JsonRootName("user")
|
@JsonRootName("user")
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
class UpdateUserParam {
|
class UpdateUserParam {
|
||||||
@Email(message = "should be an email")
|
|
||||||
private String email = "";
|
@Email(message = "should be an email")
|
||||||
private String password = "";
|
private String email = "";
|
||||||
private String username = "";
|
|
||||||
private String bio = "";
|
private String password = "";
|
||||||
private String image = "";
|
private String username = "";
|
||||||
|
private String bio = "";
|
||||||
|
private String image = "";
|
||||||
}
|
}
|
||||||
|
@ -1,125 +1,162 @@
|
|||||||
package io.spring.api;
|
package io.spring.api;
|
||||||
|
|
||||||
|
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonRootName;
|
import com.fasterxml.jackson.annotation.JsonRootName;
|
||||||
import io.spring.api.exception.InvalidRequestException;
|
import io.spring.api.exception.InvalidAuthenticationException;
|
||||||
import io.spring.application.UserQueryService;
|
import io.spring.application.UserQueryService;
|
||||||
import io.spring.application.data.UserWithToken;
|
|
||||||
import io.spring.application.data.UserData;
|
import io.spring.application.data.UserData;
|
||||||
|
import io.spring.application.data.UserWithToken;
|
||||||
import io.spring.core.service.JwtService;
|
import io.spring.core.service.JwtService;
|
||||||
import io.spring.core.user.EncryptService;
|
import io.spring.core.user.EncryptService;
|
||||||
import io.spring.core.user.User;
|
import io.spring.core.user.User;
|
||||||
import io.spring.core.user.UserRepository;
|
import io.spring.core.user.UserRepository;
|
||||||
import lombok.Getter;
|
import java.lang.annotation.Retention;
|
||||||
import lombok.NoArgsConstructor;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import javax.validation.Constraint;
|
||||||
|
import javax.validation.ConstraintValidator;
|
||||||
|
import javax.validation.ConstraintValidatorContext;
|
||||||
|
import javax.validation.Payload;
|
||||||
|
import javax.validation.Valid;
|
||||||
import javax.validation.constraints.Email;
|
import javax.validation.constraints.Email;
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.validation.BindingResult;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class UsersApi {
|
public class UsersApi {
|
||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
private UserQueryService userQueryService;
|
private UserQueryService userQueryService;
|
||||||
private String defaultImage;
|
private String defaultImage;
|
||||||
private EncryptService encryptService;
|
private EncryptService encryptService;
|
||||||
private JwtService jwtService;
|
private JwtService jwtService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public UsersApi(UserRepository userRepository,
|
public UsersApi(
|
||||||
UserQueryService userQueryService,
|
UserRepository userRepository,
|
||||||
EncryptService encryptService,
|
UserQueryService userQueryService,
|
||||||
@Value("${image.default}") String defaultImage,
|
EncryptService encryptService,
|
||||||
JwtService jwtService) {
|
@Value("${image.default}") String defaultImage,
|
||||||
this.userRepository = userRepository;
|
JwtService jwtService) {
|
||||||
this.userQueryService = userQueryService;
|
this.userRepository = userRepository;
|
||||||
this.encryptService = encryptService;
|
this.userQueryService = userQueryService;
|
||||||
this.defaultImage = defaultImage;
|
this.encryptService = encryptService;
|
||||||
this.jwtService = jwtService;
|
this.defaultImage = defaultImage;
|
||||||
}
|
this.jwtService = jwtService;
|
||||||
|
}
|
||||||
|
|
||||||
@RequestMapping(path = "/users", method = POST)
|
@RequestMapping(path = "/users", method = POST)
|
||||||
public ResponseEntity createUser(@Valid @RequestBody RegisterParam registerParam, BindingResult bindingResult) {
|
public ResponseEntity createUser(@Valid @RequestBody RegisterParam registerParam) {
|
||||||
checkInput(registerParam, bindingResult);
|
User user =
|
||||||
|
new User(
|
||||||
User user = new User(
|
|
||||||
registerParam.getEmail(),
|
registerParam.getEmail(),
|
||||||
registerParam.getUsername(),
|
registerParam.getUsername(),
|
||||||
encryptService.encrypt(registerParam.getPassword()),
|
encryptService.encrypt(registerParam.getPassword()),
|
||||||
"",
|
"",
|
||||||
defaultImage);
|
defaultImage);
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
UserData userData = userQueryService.findById(user.getId()).get();
|
UserData userData = userQueryService.findById(user.getId()).get();
|
||||||
return ResponseEntity.status(201).body(userResponse(new UserWithToken(userData, jwtService.toToken(user))));
|
return ResponseEntity.status(201)
|
||||||
|
.body(userResponse(new UserWithToken(userData, jwtService.toToken(user))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(path = "/users/login", method = POST)
|
||||||
|
public ResponseEntity userLogin(@Valid @RequestBody LoginParam loginParam) {
|
||||||
|
Optional<User> optional = userRepository.findByEmail(loginParam.getEmail());
|
||||||
|
if (optional.isPresent()
|
||||||
|
&& encryptService.check(loginParam.getPassword(), optional.get().getPassword())) {
|
||||||
|
UserData userData = userQueryService.findById(optional.get().getId()).get();
|
||||||
|
return ResponseEntity.ok(
|
||||||
|
userResponse(new UserWithToken(userData, jwtService.toToken(optional.get()))));
|
||||||
|
} else {
|
||||||
|
throw new InvalidAuthenticationException();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void checkInput(@Valid @RequestBody RegisterParam registerParam, BindingResult bindingResult) {
|
private Map<String, Object> userResponse(UserWithToken userWithToken) {
|
||||||
if (bindingResult.hasErrors()) {
|
return new HashMap<String, Object>() {
|
||||||
throw new InvalidRequestException(bindingResult);
|
{
|
||||||
}
|
put("user", userWithToken);
|
||||||
if (userRepository.findByUsername(registerParam.getUsername()).isPresent()) {
|
}
|
||||||
bindingResult.rejectValue("username", "DUPLICATED", "duplicated username");
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (userRepository.findByEmail(registerParam.getEmail()).isPresent()) {
|
@Constraint(validatedBy = DuplicatedEmailValidator.class)
|
||||||
bindingResult.rejectValue("email", "DUPLICATED", "duplicated email");
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
}
|
@interface DuplicatedEmailConstraint {
|
||||||
|
String message() default "duplicated email";
|
||||||
|
|
||||||
if (bindingResult.hasErrors()) {
|
Class<?>[] groups() default {};
|
||||||
throw new InvalidRequestException(bindingResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(path = "/users/login", method = POST)
|
Class<? extends Payload>[] payload() default {};
|
||||||
public ResponseEntity userLogin(@Valid @RequestBody LoginParam loginParam, BindingResult bindingResult) {
|
}
|
||||||
Optional<User> optional = userRepository.findByEmail(loginParam.getEmail());
|
|
||||||
if (optional.isPresent() && encryptService.check(loginParam.getPassword(), optional.get().getPassword())) {
|
|
||||||
UserData userData = userQueryService.findById(optional.get().getId()).get();
|
|
||||||
return ResponseEntity.ok(userResponse(new UserWithToken(userData, jwtService.toToken(optional.get()))));
|
|
||||||
} else {
|
|
||||||
bindingResult.rejectValue("password", "INVALID", "invalid email or password");
|
|
||||||
throw new InvalidRequestException(bindingResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, Object> userResponse(UserWithToken userWithToken) {
|
class DuplicatedEmailValidator implements ConstraintValidator<DuplicatedEmailConstraint, String> {
|
||||||
return new HashMap<String, Object>() {{
|
|
||||||
put("user", userWithToken);
|
@Autowired private UserRepository userRepository;
|
||||||
}};
|
|
||||||
}
|
@Override
|
||||||
|
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||||
|
return (value == null || value.isEmpty()) || !userRepository.findByEmail(value).isPresent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Constraint(validatedBy = DuplicatedUsernameValidator.class)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface DuplicatedUsernameConstraint {
|
||||||
|
String message() default "duplicated username";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
}
|
||||||
|
|
||||||
|
class DuplicatedUsernameValidator
|
||||||
|
implements ConstraintValidator<DuplicatedUsernameConstraint, String> {
|
||||||
|
|
||||||
|
@Autowired private UserRepository userRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||||
|
return (value == null || value.isEmpty()) || !userRepository.findByUsername(value).isPresent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@JsonRootName("user")
|
@JsonRootName("user")
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
class LoginParam {
|
class LoginParam {
|
||||||
@NotBlank(message = "can't be empty")
|
@NotBlank(message = "can't be empty")
|
||||||
@Email(message = "should be an email")
|
@Email(message = "should be an email")
|
||||||
private String email;
|
private String email;
|
||||||
@NotBlank(message = "can't be empty")
|
|
||||||
private String password;
|
@NotBlank(message = "can't be empty")
|
||||||
|
private String password;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@JsonRootName("user")
|
@JsonRootName("user")
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
class RegisterParam {
|
class RegisterParam {
|
||||||
@NotBlank(message = "can't be empty")
|
@NotBlank(message = "can't be empty")
|
||||||
@Email(message = "should be an email")
|
@Email(message = "should be an email")
|
||||||
private String email;
|
@DuplicatedEmailConstraint
|
||||||
@NotBlank(message = "can't be empty")
|
private String email;
|
||||||
private String username;
|
|
||||||
@NotBlank(message = "can't be empty")
|
@NotBlank(message = "can't be empty")
|
||||||
private String password;
|
@DuplicatedUsernameConstraint
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@NotBlank(message = "can't be empty")
|
||||||
|
private String password;
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,109 @@
|
|||||||
package io.spring.api.exception;
|
package io.spring.api.exception;
|
||||||
|
|
||||||
|
import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.validation.ConstraintViolation;
|
||||||
|
import javax.validation.ConstraintViolationException;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
import org.springframework.web.context.request.WebRequest;
|
import org.springframework.web.context.request.WebRequest;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY;
|
|
||||||
|
|
||||||
@RestControllerAdvice
|
@RestControllerAdvice
|
||||||
public class CustomizeExceptionHandler extends ResponseEntityExceptionHandler {
|
public class CustomizeExceptionHandler extends ResponseEntityExceptionHandler {
|
||||||
|
|
||||||
@ExceptionHandler({InvalidRequestException.class})
|
@ExceptionHandler({InvalidRequestException.class})
|
||||||
public ResponseEntity<Object> handleInvalidRequest(RuntimeException e, WebRequest request) {
|
public ResponseEntity<Object> handleInvalidRequest(RuntimeException e, WebRequest request) {
|
||||||
InvalidRequestException ire = (InvalidRequestException) e;
|
InvalidRequestException ire = (InvalidRequestException) e;
|
||||||
|
|
||||||
List<FieldErrorResource> errorResources = ire.getErrors().getFieldErrors().stream().map(fieldError ->
|
List<FieldErrorResource> errorResources =
|
||||||
new FieldErrorResource(
|
ire.getErrors().getFieldErrors().stream()
|
||||||
fieldError.getObjectName(),
|
.map(
|
||||||
fieldError.getField(),
|
fieldError ->
|
||||||
fieldError.getCode(),
|
new FieldErrorResource(
|
||||||
fieldError.getDefaultMessage())).collect(Collectors.toList());
|
fieldError.getObjectName(),
|
||||||
|
fieldError.getField(),
|
||||||
|
fieldError.getCode(),
|
||||||
|
fieldError.getDefaultMessage()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
ErrorResource error = new ErrorResource(errorResources);
|
ErrorResource error = new ErrorResource(errorResources);
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
|
||||||
return handleExceptionInternal(e, error, headers, UNPROCESSABLE_ENTITY, request);
|
return handleExceptionInternal(e, error, headers, UNPROCESSABLE_ENTITY, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(InvalidAuthenticationException.class)
|
||||||
|
public ResponseEntity<Object> handleInvalidAuthentication(
|
||||||
|
InvalidAuthenticationException e, WebRequest request) {
|
||||||
|
return ResponseEntity.status(UNPROCESSABLE_ENTITY)
|
||||||
|
.body(
|
||||||
|
new HashMap<String, Object>() {
|
||||||
|
{
|
||||||
|
put("message", e.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResponseEntity<Object> handleMethodArgumentNotValid(
|
||||||
|
MethodArgumentNotValidException e,
|
||||||
|
HttpHeaders headers,
|
||||||
|
HttpStatus status,
|
||||||
|
WebRequest request) {
|
||||||
|
List<FieldErrorResource> errorResources =
|
||||||
|
e.getBindingResult().getFieldErrors().stream()
|
||||||
|
.map(
|
||||||
|
fieldError ->
|
||||||
|
new FieldErrorResource(
|
||||||
|
fieldError.getObjectName(),
|
||||||
|
fieldError.getField(),
|
||||||
|
fieldError.getCode(),
|
||||||
|
fieldError.getDefaultMessage()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return ResponseEntity.status(UNPROCESSABLE_ENTITY).body(new ErrorResource(errorResources));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler({ConstraintViolationException.class})
|
||||||
|
@ResponseStatus(UNPROCESSABLE_ENTITY)
|
||||||
|
@ResponseBody
|
||||||
|
public ErrorResource handleConstraintViolation(
|
||||||
|
ConstraintViolationException ex, WebRequest request) {
|
||||||
|
List<FieldErrorResource> errors = new ArrayList<>();
|
||||||
|
for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
|
||||||
|
FieldErrorResource fieldErrorResource =
|
||||||
|
new FieldErrorResource(
|
||||||
|
violation.getRootBeanClass().getName(),
|
||||||
|
getParam(violation.getPropertyPath().toString()),
|
||||||
|
violation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName(),
|
||||||
|
violation.getMessage());
|
||||||
|
errors.add(fieldErrorResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new ErrorResource(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getParam(String s) {
|
||||||
|
String[] splits = s.split("\\.");
|
||||||
|
if (splits.length == 1) {
|
||||||
|
return s;
|
||||||
|
} else {
|
||||||
|
return String.join(".", Arrays.copyOfRange(splits, 2, splits.length));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package io.spring.api.exception;
|
||||||
|
|
||||||
|
public class InvalidAuthenticationException extends RuntimeException {
|
||||||
|
|
||||||
|
public InvalidAuthenticationException() {
|
||||||
|
super("invalid email or password");
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,12 @@
|
|||||||
package io.spring.api;
|
package io.spring.api;
|
||||||
|
|
||||||
|
import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
|
||||||
|
import static org.hamcrest.core.IsEqual.equalTo;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import io.restassured.module.mockmvc.RestAssuredMockMvc;
|
import io.restassured.module.mockmvc.RestAssuredMockMvc;
|
||||||
import io.spring.JacksonCustomizations;
|
import io.spring.JacksonCustomizations;
|
||||||
import io.spring.api.security.WebSecurityConfig;
|
import io.spring.api.security.WebSecurityConfig;
|
||||||
@ -8,6 +15,10 @@ import io.spring.application.data.ArticleData;
|
|||||||
import io.spring.application.data.ProfileData;
|
import io.spring.application.data.ProfileData;
|
||||||
import io.spring.core.article.Article;
|
import io.spring.core.article.Article;
|
||||||
import io.spring.core.article.ArticleRepository;
|
import io.spring.core.article.ArticleRepository;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -17,48 +28,33 @@ import org.springframework.boot.test.mock.mockito.MockBean;
|
|||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
|
|
||||||
import static org.hamcrest.core.IsEqual.equalTo;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
@WebMvcTest({ArticlesApi.class})
|
@WebMvcTest({ArticlesApi.class})
|
||||||
@Import({WebSecurityConfig.class, JacksonCustomizations.class})
|
@Import({WebSecurityConfig.class, JacksonCustomizations.class})
|
||||||
public class ArticlesApiTest extends TestWithCurrentUser {
|
public class ArticlesApiTest extends TestWithCurrentUser {
|
||||||
@Autowired
|
@Autowired private MockMvc mvc;
|
||||||
private MockMvc mvc;
|
|
||||||
|
|
||||||
@MockBean
|
@MockBean private ArticleRepository articleRepository;
|
||||||
private ArticleRepository articleRepository;
|
|
||||||
|
|
||||||
@MockBean
|
@MockBean private ArticleQueryService articleQueryService;
|
||||||
private ArticleQueryService articleQueryService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
RestAssuredMockMvc.mockMvc(mvc);
|
RestAssuredMockMvc.mockMvc(mvc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void should_create_article_success() throws Exception {
|
public void should_create_article_success() throws Exception {
|
||||||
String title = "How to train your dragon";
|
String title = "How to train your dragon";
|
||||||
String slug = "how-to-train-your-dragon";
|
String slug = "how-to-train-your-dragon";
|
||||||
String description = "Ever wonder how?";
|
String description = "Ever wonder how?";
|
||||||
String body = "You have to believe";
|
String body = "You have to believe";
|
||||||
String[] tagList = {"reactjs", "angularjs", "dragons"};
|
String[] tagList = {"reactjs", "angularjs", "dragons"};
|
||||||
Map<String, Object> param = prepareParam(title, description, body, tagList);
|
Map<String, Object> param = prepareParam(title, description, body, tagList);
|
||||||
|
|
||||||
ArticleData articleData = new ArticleData(
|
ArticleData articleData =
|
||||||
|
new ArticleData(
|
||||||
"123",
|
"123",
|
||||||
slug,
|
slug,
|
||||||
title,
|
title,
|
||||||
@ -71,59 +67,60 @@ public class ArticlesApiTest extends TestWithCurrentUser {
|
|||||||
Arrays.asList(tagList),
|
Arrays.asList(tagList),
|
||||||
new ProfileData("userid", user.getUsername(), user.getBio(), user.getImage(), false));
|
new ProfileData("userid", user.getUsername(), user.getBio(), user.getImage(), false));
|
||||||
|
|
||||||
when(articleQueryService.findBySlug(eq(Article.toSlug(title)), any())).thenReturn(Optional.empty());
|
when(articleQueryService.findBySlug(eq(Article.toSlug(title)), any()))
|
||||||
|
.thenReturn(Optional.empty());
|
||||||
|
|
||||||
when(articleQueryService.findById(any(), any())).thenReturn(Optional.of(articleData));
|
when(articleQueryService.findById(any(), any())).thenReturn(Optional.of(articleData));
|
||||||
|
|
||||||
given()
|
given()
|
||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
.header("Authorization", "Token " + token)
|
.header("Authorization", "Token " + token)
|
||||||
.body(param)
|
.body(param)
|
||||||
.when()
|
.when()
|
||||||
.post("/articles")
|
.post("/articles")
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.body("article.title", equalTo(title))
|
.body("article.title", equalTo(title))
|
||||||
.body("article.favorited", equalTo(false))
|
.body("article.favorited", equalTo(false))
|
||||||
.body("article.body", equalTo(body))
|
.body("article.body", equalTo(body))
|
||||||
.body("article.favoritesCount", equalTo(0))
|
.body("article.favoritesCount", equalTo(0))
|
||||||
.body("article.author.username", equalTo(user.getUsername()))
|
.body("article.author.username", equalTo(user.getUsername()))
|
||||||
.body("article.author.id", equalTo(null));
|
.body("article.author.id", equalTo(null));
|
||||||
|
|
||||||
verify(articleRepository).save(any());
|
verify(articleRepository).save(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void should_get_error_message_with_wrong_parameter() throws Exception {
|
public void should_get_error_message_with_wrong_parameter() throws Exception {
|
||||||
String title = "How to train your dragon";
|
String title = "How to train your dragon";
|
||||||
String description = "Ever wonder how?";
|
String description = "Ever wonder how?";
|
||||||
String body = "";
|
String body = "";
|
||||||
String[] tagList = {"reactjs", "angularjs", "dragons"};
|
String[] tagList = {"reactjs", "angularjs", "dragons"};
|
||||||
Map<String, Object> param = prepareParam(title, description, body, tagList);
|
Map<String, Object> param = prepareParam(title, description, body, tagList);
|
||||||
|
|
||||||
given()
|
given()
|
||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
.header("Authorization", "Token " + token)
|
.header("Authorization", "Token " + token)
|
||||||
.body(param)
|
.body(param)
|
||||||
.when()
|
.when()
|
||||||
.post("/articles")
|
.post("/articles")
|
||||||
.prettyPeek()
|
.prettyPeek()
|
||||||
.then()
|
.then()
|
||||||
.statusCode(422)
|
.statusCode(422)
|
||||||
.body("errors.body[0]", equalTo("can't be empty"));
|
.body("errors.body[0]", equalTo("can't be empty"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
@Test
|
||||||
|
public void should_get_error_message_with_duplicated_title() {
|
||||||
|
String title = "How to train your dragon";
|
||||||
|
String slug = "how-to-train-your-dragon";
|
||||||
|
String description = "Ever wonder how?";
|
||||||
|
String body = "You have to believe";
|
||||||
|
String[] tagList = {"reactjs", "angularjs", "dragons"};
|
||||||
|
Map<String, Object> param = prepareParam(title, description, body, tagList);
|
||||||
|
|
||||||
@Test
|
ArticleData articleData =
|
||||||
public void should_get_error_message_with_duplicated_title() {
|
new ArticleData(
|
||||||
String title = "How to train your dragon";
|
|
||||||
String slug = "how-to-train-your-dragon";
|
|
||||||
String description = "Ever wonder how?";
|
|
||||||
String body = "You have to believe";
|
|
||||||
String[] tagList = {"reactjs", "angularjs", "dragons"};
|
|
||||||
Map<String, Object> param = prepareParam(title, description, body, tagList);
|
|
||||||
|
|
||||||
ArticleData articleData = new ArticleData(
|
|
||||||
"123",
|
"123",
|
||||||
slug,
|
slug,
|
||||||
title,
|
title,
|
||||||
@ -136,29 +133,37 @@ public class ArticlesApiTest extends TestWithCurrentUser {
|
|||||||
Arrays.asList(tagList),
|
Arrays.asList(tagList),
|
||||||
new ProfileData("userid", user.getUsername(), user.getBio(), user.getImage(), false));
|
new ProfileData("userid", user.getUsername(), user.getBio(), user.getImage(), false));
|
||||||
|
|
||||||
when(articleQueryService.findBySlug(eq(Article.toSlug(title)), any())).thenReturn(Optional.of(articleData));
|
when(articleQueryService.findBySlug(eq(Article.toSlug(title)), any()))
|
||||||
|
.thenReturn(Optional.of(articleData));
|
||||||
|
|
||||||
when(articleQueryService.findById(any(), any())).thenReturn(Optional.of(articleData));
|
when(articleQueryService.findById(any(), any())).thenReturn(Optional.of(articleData));
|
||||||
|
|
||||||
given()
|
given()
|
||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
.header("Authorization", "Token " + token)
|
.header("Authorization", "Token " + token)
|
||||||
.body(param)
|
.body(param)
|
||||||
.when()
|
.when()
|
||||||
.post("/articles")
|
.post("/articles")
|
||||||
.then()
|
.prettyPeek()
|
||||||
.statusCode(422);
|
.then()
|
||||||
|
.statusCode(422);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
private HashMap<String, Object> prepareParam(
|
||||||
|
final String title, final String description, final String body, final String[] tagList) {
|
||||||
private HashMap<String, Object> prepareParam(final String title, final String description, final String body, final String[] tagList) {
|
return new HashMap<String, Object>() {
|
||||||
return new HashMap<String, Object>() {{
|
{
|
||||||
put("article", new HashMap<String, Object>() {{
|
put(
|
||||||
|
"article",
|
||||||
|
new HashMap<String, Object>() {
|
||||||
|
{
|
||||||
put("title", title);
|
put("title", title);
|
||||||
put("description", description);
|
put("description", description);
|
||||||
put("body", body);
|
put("body", body);
|
||||||
put("tagList", tagList);
|
put("tagList", tagList);
|
||||||
}});
|
}
|
||||||
}};
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,161 +1,176 @@
|
|||||||
package io.spring.api;
|
package io.spring.api;
|
||||||
|
|
||||||
import io.restassured.module.mockmvc.RestAssuredMockMvc;
|
|
||||||
import io.spring.JacksonCustomizations;
|
|
||||||
import io.spring.api.security.WebSecurityConfig;
|
|
||||||
import io.spring.application.UserQueryService;
|
|
||||||
import io.spring.core.user.User;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
|
||||||
import org.springframework.context.annotation.Import;
|
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
|
import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
|
||||||
import static org.hamcrest.core.IsEqual.equalTo;
|
import static org.hamcrest.core.IsEqual.equalTo;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import io.restassured.module.mockmvc.RestAssuredMockMvc;
|
||||||
|
import io.spring.JacksonCustomizations;
|
||||||
|
import io.spring.api.security.WebSecurityConfig;
|
||||||
|
import io.spring.application.UserQueryService;
|
||||||
|
import io.spring.core.user.User;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
@WebMvcTest(CurrentUserApi.class)
|
@WebMvcTest(CurrentUserApi.class)
|
||||||
@Import({WebSecurityConfig.class, JacksonCustomizations.class})
|
@Import({
|
||||||
|
WebSecurityConfig.class,
|
||||||
|
JacksonCustomizations.class,
|
||||||
|
UserService.class,
|
||||||
|
ValidationAutoConfiguration.class
|
||||||
|
})
|
||||||
public class CurrentUserApiTest extends TestWithCurrentUser {
|
public class CurrentUserApiTest extends TestWithCurrentUser {
|
||||||
|
|
||||||
@Autowired
|
@Autowired private MockMvc mvc;
|
||||||
private MockMvc mvc;
|
|
||||||
|
|
||||||
@MockBean
|
@MockBean private UserQueryService userQueryService;
|
||||||
private UserQueryService userQueryService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
RestAssuredMockMvc.mockMvc(mvc);
|
RestAssuredMockMvc.mockMvc(mvc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void should_get_current_user_with_token() throws Exception {
|
public void should_get_current_user_with_token() throws Exception {
|
||||||
when(userQueryService.findById(any())).thenReturn(Optional.of(userData));
|
when(userQueryService.findById(any())).thenReturn(Optional.of(userData));
|
||||||
|
|
||||||
given()
|
given()
|
||||||
.header("Authorization", "Token " + token)
|
.header("Authorization", "Token " + token)
|
||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
.when()
|
.when()
|
||||||
.get("/user")
|
.get("/user")
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.body("user.email", equalTo(email))
|
.body("user.email", equalTo(email))
|
||||||
.body("user.username", equalTo(username))
|
.body("user.username", equalTo(username))
|
||||||
.body("user.bio", equalTo(""))
|
.body("user.bio", equalTo(""))
|
||||||
.body("user.image", equalTo(defaultAvatar))
|
.body("user.image", equalTo(defaultAvatar))
|
||||||
.body("user.token", equalTo(token));
|
.body("user.token", equalTo(token));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void should_get_401_without_token() throws Exception {
|
public void should_get_401_without_token() throws Exception {
|
||||||
given()
|
given().contentType("application/json").when().get("/user").then().statusCode(401);
|
||||||
.contentType("application/json")
|
}
|
||||||
.when()
|
|
||||||
.get("/user")
|
|
||||||
.then()
|
|
||||||
.statusCode(401);
|
|
||||||
|
|
||||||
}
|
@Test
|
||||||
|
public void should_get_401_with_invalid_token() throws Exception {
|
||||||
|
String invalidToken = "asdfasd";
|
||||||
|
when(jwtService.getSubFromToken(eq(invalidToken))).thenReturn(Optional.empty());
|
||||||
|
given()
|
||||||
|
.contentType("application/json")
|
||||||
|
.header("Authorization", "Token " + invalidToken)
|
||||||
|
.when()
|
||||||
|
.get("/user")
|
||||||
|
.then()
|
||||||
|
.statusCode(401);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void should_get_401_with_invalid_token() throws Exception {
|
public void should_update_current_user_profile() throws Exception {
|
||||||
String invalidToken = "asdfasd";
|
String newEmail = "newemail@example.com";
|
||||||
when(jwtService.getSubFromToken(eq(invalidToken))).thenReturn(Optional.empty());
|
String newBio = "updated";
|
||||||
given()
|
String newUsername = "newusernamee";
|
||||||
.contentType("application/json")
|
|
||||||
.header("Authorization", "Token " + invalidToken)
|
|
||||||
.when()
|
|
||||||
.get("/user")
|
|
||||||
.then()
|
|
||||||
.statusCode(401);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
Map<String, Object> param =
|
||||||
public void should_update_current_user_profile() throws Exception {
|
new HashMap<String, Object>() {
|
||||||
String newEmail = "newemail@example.com";
|
{
|
||||||
String newBio = "updated";
|
put(
|
||||||
String newUsername = "newusernamee";
|
"user",
|
||||||
|
new HashMap<String, Object>() {
|
||||||
|
{
|
||||||
|
put("email", newEmail);
|
||||||
|
put("bio", newBio);
|
||||||
|
put("username", newUsername);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Map<String, Object> param = new HashMap<String, Object>() {{
|
when(userRepository.findByUsername(eq(newUsername))).thenReturn(Optional.empty());
|
||||||
put("user", new HashMap<String, Object>() {{
|
when(userRepository.findByEmail(eq(newEmail))).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
when(userQueryService.findById(eq(user.getId()))).thenReturn(Optional.of(userData));
|
||||||
|
|
||||||
|
given()
|
||||||
|
.contentType("application/json")
|
||||||
|
.header("Authorization", "Token " + token)
|
||||||
|
.body(param)
|
||||||
|
.when()
|
||||||
|
.put("/user")
|
||||||
|
.then()
|
||||||
|
.statusCode(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void should_get_error_if_email_exists_when_update_user_profile() throws Exception {
|
||||||
|
String newEmail = "newemail@example.com";
|
||||||
|
String newBio = "updated";
|
||||||
|
String newUsername = "newusernamee";
|
||||||
|
|
||||||
|
Map<String, Object> param = prepareUpdateParam(newEmail, newBio, newUsername);
|
||||||
|
|
||||||
|
when(userRepository.findByEmail(eq(newEmail)))
|
||||||
|
.thenReturn(Optional.of(new User(newEmail, "username", "123", "", "")));
|
||||||
|
when(userRepository.findByUsername(eq(newUsername))).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
when(userQueryService.findById(eq(user.getId()))).thenReturn(Optional.of(userData));
|
||||||
|
|
||||||
|
given()
|
||||||
|
.contentType("application/json")
|
||||||
|
.header("Authorization", "Token " + token)
|
||||||
|
.body(param)
|
||||||
|
.when()
|
||||||
|
.put("/user")
|
||||||
|
.prettyPeek()
|
||||||
|
.then()
|
||||||
|
.statusCode(422)
|
||||||
|
.body("errors.email[0]", equalTo("email already exist"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap<String, Object> prepareUpdateParam(
|
||||||
|
final String newEmail, final String newBio, final String newUsername) {
|
||||||
|
return new HashMap<String, Object>() {
|
||||||
|
{
|
||||||
|
put(
|
||||||
|
"user",
|
||||||
|
new HashMap<String, Object>() {
|
||||||
|
{
|
||||||
put("email", newEmail);
|
put("email", newEmail);
|
||||||
put("bio", newBio);
|
put("bio", newBio);
|
||||||
put("username", newUsername);
|
put("username", newUsername);
|
||||||
}});
|
}
|
||||||
}};
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
when(userRepository.findByUsername(eq(newUsername))).thenReturn(Optional.empty());
|
@Test
|
||||||
when(userRepository.findByEmail(eq(newEmail))).thenReturn(Optional.empty());
|
public void should_get_401_if_not_login() throws Exception {
|
||||||
|
given()
|
||||||
when(userQueryService.findById(eq(user.getId()))).thenReturn(Optional.of(userData));
|
.contentType("application/json")
|
||||||
|
.body(
|
||||||
given()
|
new HashMap<String, Object>() {
|
||||||
.contentType("application/json")
|
{
|
||||||
.header("Authorization", "Token " + token)
|
|
||||||
.body(param)
|
|
||||||
.when()
|
|
||||||
.put("/user")
|
|
||||||
.then()
|
|
||||||
.statusCode(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void should_get_error_if_email_exists_when_update_user_profile() throws Exception {
|
|
||||||
String newEmail = "newemail@example.com";
|
|
||||||
String newBio = "updated";
|
|
||||||
String newUsername = "newusernamee";
|
|
||||||
|
|
||||||
Map<String, Object> param = prepareUpdateParam(newEmail, newBio, newUsername);
|
|
||||||
|
|
||||||
when(userRepository.findByEmail(eq(newEmail))).thenReturn(Optional.of(new User(newEmail, "username", "123", "", "")));
|
|
||||||
when(userRepository.findByUsername(eq(newUsername))).thenReturn(Optional.empty());
|
|
||||||
|
|
||||||
when(userQueryService.findById(eq(user.getId()))).thenReturn(Optional.of(userData));
|
|
||||||
|
|
||||||
given()
|
|
||||||
.contentType("application/json")
|
|
||||||
.header("Authorization", "Token " + token)
|
|
||||||
.body(param)
|
|
||||||
.when()
|
|
||||||
.put("/user")
|
|
||||||
.prettyPeek()
|
|
||||||
.then()
|
|
||||||
.statusCode(422)
|
|
||||||
.body("errors.email[0]", equalTo("email already exist"));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private HashMap<String, Object> prepareUpdateParam(final String newEmail, final String newBio, final String newUsername) {
|
|
||||||
return new HashMap<String, Object>() {{
|
|
||||||
put("user", new HashMap<String, Object>() {{
|
|
||||||
put("email", newEmail);
|
|
||||||
put("bio", newBio);
|
|
||||||
put("username", newUsername);
|
|
||||||
}});
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void should_get_401_if_not_login() throws Exception {
|
|
||||||
given()
|
|
||||||
.contentType("application/json")
|
|
||||||
.body(new HashMap<String, Object>() {{
|
|
||||||
put("user", new HashMap<String, Object>());
|
put("user", new HashMap<String, Object>());
|
||||||
}})
|
}
|
||||||
.when()
|
})
|
||||||
.put("/user")
|
.when()
|
||||||
.then().statusCode(401);
|
.put("/user")
|
||||||
}
|
.then()
|
||||||
|
.statusCode(401);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
package io.spring.api;
|
package io.spring.api;
|
||||||
|
|
||||||
|
import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
|
||||||
|
import static org.hamcrest.core.IsEqual.equalTo;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import io.restassured.module.mockmvc.RestAssuredMockMvc;
|
import io.restassured.module.mockmvc.RestAssuredMockMvc;
|
||||||
import io.spring.JacksonCustomizations;
|
import io.spring.JacksonCustomizations;
|
||||||
import io.spring.api.security.WebSecurityConfig;
|
import io.spring.api.security.WebSecurityConfig;
|
||||||
@ -10,6 +17,9 @@ import io.spring.core.user.User;
|
|||||||
import io.spring.core.user.UserRepository;
|
import io.spring.core.user.UserRepository;
|
||||||
import io.spring.infrastructure.mybatis.readservice.UserReadService;
|
import io.spring.infrastructure.mybatis.readservice.UserReadService;
|
||||||
import io.spring.infrastructure.service.NaiveEncryptService;
|
import io.spring.infrastructure.service.NaiveEncryptService;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -20,224 +30,236 @@ import org.springframework.context.annotation.Import;
|
|||||||
import org.springframework.test.context.junit4.SpringRunner;
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
|
|
||||||
import static org.hamcrest.core.IsEqual.equalTo;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
@WebMvcTest(UsersApi.class)
|
@WebMvcTest(UsersApi.class)
|
||||||
@Import({WebSecurityConfig.class, UserQueryService.class, NaiveEncryptService.class, JacksonCustomizations.class})
|
@Import({
|
||||||
|
WebSecurityConfig.class,
|
||||||
|
UserQueryService.class,
|
||||||
|
NaiveEncryptService.class,
|
||||||
|
JacksonCustomizations.class
|
||||||
|
})
|
||||||
public class UsersApiTest {
|
public class UsersApiTest {
|
||||||
@Autowired
|
@Autowired private MockMvc mvc;
|
||||||
private MockMvc mvc;
|
|
||||||
|
|
||||||
@MockBean
|
@MockBean private UserRepository userRepository;
|
||||||
private UserRepository userRepository;
|
|
||||||
|
|
||||||
@MockBean
|
@MockBean private JwtService jwtService;
|
||||||
private JwtService jwtService;
|
|
||||||
|
|
||||||
@MockBean
|
@MockBean private UserReadService userReadService;
|
||||||
private UserReadService userReadService;
|
private String defaultAvatar;
|
||||||
private String defaultAvatar;
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
RestAssuredMockMvc.mockMvc(mvc);
|
||||||
|
defaultAvatar = "https://static.productionready.io/images/smiley-cyrus.jpg";
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Test
|
||||||
public void setUp() throws Exception {
|
public void should_create_user_success() throws Exception {
|
||||||
RestAssuredMockMvc.mockMvc(mvc);
|
String email = "john@jacob.com";
|
||||||
defaultAvatar = "https://static.productionready.io/images/smiley-cyrus.jpg";
|
String username = "johnjacob";
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
when(jwtService.toToken(any())).thenReturn("123");
|
||||||
public void should_create_user_success() throws Exception {
|
User user = new User(email, username, "123", "", defaultAvatar);
|
||||||
String email = "john@jacob.com";
|
UserData userData = new UserData(user.getId(), email, username, "", defaultAvatar);
|
||||||
String username = "johnjacob";
|
when(userReadService.findById(any())).thenReturn(userData);
|
||||||
|
|
||||||
when(jwtService.toToken(any())).thenReturn("123");
|
when(userRepository.findByUsername(eq(username))).thenReturn(Optional.empty());
|
||||||
User user = new User(email, username, "123", "", defaultAvatar);
|
when(userRepository.findByEmail(eq(email))).thenReturn(Optional.empty());
|
||||||
UserData userData = new UserData(user.getId(), email, username, "", defaultAvatar);
|
|
||||||
when(userReadService.findById(any())).thenReturn(userData);
|
|
||||||
|
|
||||||
when(userRepository.findByUsername(eq(username))).thenReturn(Optional.empty());
|
Map<String, Object> param = prepareRegisterParameter(email, username);
|
||||||
when(userRepository.findByEmail(eq(email))).thenReturn(Optional.empty());
|
|
||||||
|
|
||||||
Map<String, Object> param = prepareRegisterParameter(email, username);
|
given()
|
||||||
|
.contentType("application/json")
|
||||||
|
.body(param)
|
||||||
|
.when()
|
||||||
|
.post("/users")
|
||||||
|
.then()
|
||||||
|
.statusCode(201)
|
||||||
|
.body("user.email", equalTo(email))
|
||||||
|
.body("user.username", equalTo(username))
|
||||||
|
.body("user.bio", equalTo(""))
|
||||||
|
.body("user.image", equalTo(defaultAvatar))
|
||||||
|
.body("user.token", equalTo("123"));
|
||||||
|
|
||||||
given()
|
verify(userRepository).save(any());
|
||||||
.contentType("application/json")
|
}
|
||||||
.body(param)
|
|
||||||
.when()
|
|
||||||
.post("/users")
|
|
||||||
.then()
|
|
||||||
.statusCode(201)
|
|
||||||
.body("user.email", equalTo(email))
|
|
||||||
.body("user.username", equalTo(username))
|
|
||||||
.body("user.bio", equalTo(""))
|
|
||||||
.body("user.image", equalTo(defaultAvatar))
|
|
||||||
.body("user.token", equalTo("123"));
|
|
||||||
|
|
||||||
verify(userRepository).save(any());
|
@Test
|
||||||
}
|
public void should_show_error_message_for_blank_username() throws Exception {
|
||||||
|
|
||||||
@Test
|
String email = "john@jacob.com";
|
||||||
public void should_show_error_message_for_blank_username() throws Exception {
|
String username = "";
|
||||||
|
|
||||||
String email = "john@jacob.com";
|
Map<String, Object> param = prepareRegisterParameter(email, username);
|
||||||
String username = "";
|
|
||||||
|
|
||||||
Map<String, Object> param = prepareRegisterParameter(email, username);
|
given()
|
||||||
|
.contentType("application/json")
|
||||||
|
.body(param)
|
||||||
|
.when()
|
||||||
|
.post("/users")
|
||||||
|
.prettyPeek()
|
||||||
|
.then()
|
||||||
|
.statusCode(422)
|
||||||
|
.body("errors.username[0]", equalTo("can't be empty"));
|
||||||
|
}
|
||||||
|
|
||||||
given()
|
@Test
|
||||||
.contentType("application/json")
|
public void should_show_error_message_for_invalid_email() throws Exception {
|
||||||
.body(param)
|
String email = "johnxjacob.com";
|
||||||
.when()
|
String username = "johnjacob";
|
||||||
.post("/users")
|
|
||||||
.then()
|
|
||||||
.statusCode(422)
|
|
||||||
.body("errors.username[0]", equalTo("can't be empty"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
Map<String, Object> param = prepareRegisterParameter(email, username);
|
||||||
public void should_show_error_message_for_invalid_email() throws Exception {
|
|
||||||
String email = "johnxjacob.com";
|
|
||||||
String username = "johnjacob";
|
|
||||||
|
|
||||||
Map<String, Object> param = prepareRegisterParameter(email, username);
|
given()
|
||||||
|
.contentType("application/json")
|
||||||
|
.body(param)
|
||||||
|
.when()
|
||||||
|
.post("/users")
|
||||||
|
.prettyPeek()
|
||||||
|
.then()
|
||||||
|
.statusCode(422)
|
||||||
|
.body("errors.email[0]", equalTo("should be an email"));
|
||||||
|
}
|
||||||
|
|
||||||
given()
|
@Test
|
||||||
.contentType("application/json")
|
public void should_show_error_for_duplicated_username() throws Exception {
|
||||||
.body(param)
|
String email = "john@jacob.com";
|
||||||
.when()
|
String username = "johnjacob";
|
||||||
.post("/users")
|
|
||||||
.then()
|
|
||||||
.statusCode(422)
|
|
||||||
.body("errors.email[0]", equalTo("should be an email"));
|
|
||||||
|
|
||||||
}
|
when(userRepository.findByUsername(eq(username)))
|
||||||
|
.thenReturn(Optional.of(new User(email, username, "123", "bio", "")));
|
||||||
|
when(userRepository.findByEmail(any())).thenReturn(Optional.empty());
|
||||||
|
|
||||||
@Test
|
Map<String, Object> param = prepareRegisterParameter(email, username);
|
||||||
public void should_show_error_for_duplicated_username() throws Exception {
|
|
||||||
String email = "john@jacob.com";
|
|
||||||
String username = "johnjacob";
|
|
||||||
|
|
||||||
when(userRepository.findByUsername(eq(username))).thenReturn(Optional.of(new User(
|
given()
|
||||||
email, username, "123", "bio", ""
|
.contentType("application/json")
|
||||||
)));
|
.body(param)
|
||||||
when(userRepository.findByEmail(any())).thenReturn(Optional.empty());
|
.when()
|
||||||
|
.post("/users")
|
||||||
|
.prettyPeek()
|
||||||
|
.then()
|
||||||
|
.statusCode(422)
|
||||||
|
.body("errors.username[0]", equalTo("duplicated username"));
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, Object> param = prepareRegisterParameter(email, username);
|
@Test
|
||||||
|
public void should_show_error_for_duplicated_email() throws Exception {
|
||||||
|
String email = "john@jacob.com";
|
||||||
|
String username = "johnjacob2";
|
||||||
|
|
||||||
given()
|
when(userRepository.findByEmail(eq(email)))
|
||||||
.contentType("application/json")
|
.thenReturn(Optional.of(new User(email, username, "123", "bio", "")));
|
||||||
.body(param)
|
|
||||||
.when()
|
|
||||||
.post("/users")
|
|
||||||
.then()
|
|
||||||
.statusCode(422)
|
|
||||||
.body("errors.username[0]", equalTo("duplicated username"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
when(userRepository.findByUsername(eq(username))).thenReturn(Optional.empty());
|
||||||
public void should_show_error_for_duplicated_email() throws Exception {
|
|
||||||
String email = "john@jacob.com";
|
|
||||||
String username = "johnjacob2";
|
|
||||||
|
|
||||||
when(userRepository.findByEmail(eq(email))).thenReturn(Optional.of(new User(
|
Map<String, Object> param = prepareRegisterParameter(email, username);
|
||||||
email, username, "123", "bio", ""
|
|
||||||
)));
|
|
||||||
|
|
||||||
when(userRepository.findByUsername(eq(username))).thenReturn(Optional.empty());
|
given()
|
||||||
|
.contentType("application/json")
|
||||||
|
.body(param)
|
||||||
|
.when()
|
||||||
|
.post("/users")
|
||||||
|
.then()
|
||||||
|
.statusCode(422)
|
||||||
|
.body("errors.email[0]", equalTo("duplicated email"));
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, Object> param = prepareRegisterParameter(email, username);
|
private HashMap<String, Object> prepareRegisterParameter(
|
||||||
|
final String email, final String username) {
|
||||||
given()
|
return new HashMap<String, Object>() {
|
||||||
.contentType("application/json")
|
{
|
||||||
.body(param)
|
put(
|
||||||
.when()
|
"user",
|
||||||
.post("/users")
|
new HashMap<String, Object>() {
|
||||||
.then()
|
{
|
||||||
.statusCode(422)
|
|
||||||
.body("errors.email[0]", equalTo("duplicated email"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private HashMap<String, Object> prepareRegisterParameter(final String email, final String username) {
|
|
||||||
return new HashMap<String, Object>() {{
|
|
||||||
put("user", new HashMap<String, Object>() {{
|
|
||||||
put("email", email);
|
put("email", email);
|
||||||
put("password", "johnnyjacob");
|
put("password", "johnnyjacob");
|
||||||
put("username", username);
|
put("username", username);
|
||||||
}});
|
}
|
||||||
}};
|
});
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void should_login_success() throws Exception {
|
public void should_login_success() throws Exception {
|
||||||
String email = "john@jacob.com";
|
String email = "john@jacob.com";
|
||||||
String username = "johnjacob2";
|
String username = "johnjacob2";
|
||||||
String password = "123";
|
String password = "123";
|
||||||
|
|
||||||
User user = new User(email, username, password, "", defaultAvatar);
|
User user = new User(email, username, password, "", defaultAvatar);
|
||||||
UserData userData = new UserData("123", email, username, "", defaultAvatar);
|
UserData userData = new UserData("123", email, username, "", defaultAvatar);
|
||||||
|
|
||||||
when(userRepository.findByEmail(eq(email))).thenReturn(Optional.of(user));
|
when(userRepository.findByEmail(eq(email))).thenReturn(Optional.of(user));
|
||||||
when(userReadService.findByUsername(eq(username))).thenReturn(userData);
|
when(userReadService.findByUsername(eq(username))).thenReturn(userData);
|
||||||
when(userReadService.findById(eq(user.getId()))).thenReturn(userData);
|
when(userReadService.findById(eq(user.getId()))).thenReturn(userData);
|
||||||
when(jwtService.toToken(any())).thenReturn("123");
|
when(jwtService.toToken(any())).thenReturn("123");
|
||||||
|
|
||||||
Map<String, Object> param = new HashMap<String, Object>() {{
|
Map<String, Object> param =
|
||||||
put("user", new HashMap<String, Object>() {{
|
new HashMap<String, Object>() {
|
||||||
put("email", email);
|
{
|
||||||
put("password", password);
|
put(
|
||||||
}});
|
"user",
|
||||||
}};
|
new HashMap<String, Object>() {
|
||||||
|
{
|
||||||
|
put("email", email);
|
||||||
|
put("password", password);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
given()
|
given()
|
||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
.body(param)
|
.body(param)
|
||||||
.when()
|
.when()
|
||||||
.post("/users/login")
|
.post("/users/login")
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.body("user.email", equalTo(email))
|
.body("user.email", equalTo(email))
|
||||||
.body("user.username", equalTo(username))
|
.body("user.username", equalTo(username))
|
||||||
.body("user.bio", equalTo(""))
|
.body("user.bio", equalTo(""))
|
||||||
.body("user.image", equalTo(defaultAvatar))
|
.body("user.image", equalTo(defaultAvatar))
|
||||||
.body("user.token", equalTo("123"));;
|
.body("user.token", equalTo("123"));
|
||||||
}
|
;
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void should_fail_login_with_wrong_password() throws Exception {
|
public void should_fail_login_with_wrong_password() throws Exception {
|
||||||
String email = "john@jacob.com";
|
String email = "john@jacob.com";
|
||||||
String username = "johnjacob2";
|
String username = "johnjacob2";
|
||||||
String password = "123";
|
String password = "123";
|
||||||
|
|
||||||
User user = new User(email, username, password, "", defaultAvatar);
|
User user = new User(email, username, password, "", defaultAvatar);
|
||||||
UserData userData = new UserData(user.getId(), email, username, "", defaultAvatar);
|
UserData userData = new UserData(user.getId(), email, username, "", defaultAvatar);
|
||||||
|
|
||||||
when(userRepository.findByEmail(eq(email))).thenReturn(Optional.of(user));
|
when(userRepository.findByEmail(eq(email))).thenReturn(Optional.of(user));
|
||||||
when(userReadService.findByUsername(eq(username))).thenReturn(userData);
|
when(userReadService.findByUsername(eq(username))).thenReturn(userData);
|
||||||
|
|
||||||
Map<String, Object> param = new HashMap<String, Object>() {{
|
Map<String, Object> param =
|
||||||
put("user", new HashMap<String, Object>() {{
|
new HashMap<String, Object>() {
|
||||||
put("email", email);
|
{
|
||||||
put("password", "123123");
|
put(
|
||||||
}});
|
"user",
|
||||||
}};
|
new HashMap<String, Object>() {
|
||||||
|
{
|
||||||
|
put("email", email);
|
||||||
|
put("password", "123123");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
given()
|
given()
|
||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
.body(param)
|
.body(param)
|
||||||
.when()
|
.when()
|
||||||
.post("/users/login")
|
.post("/users/login")
|
||||||
.then()
|
.prettyPeek()
|
||||||
.statusCode(422)
|
.then()
|
||||||
.body("errors.password[0]", equalTo("invalid email or password"));
|
.statusCode(422)
|
||||||
}
|
.body("message", equalTo("invalid email or password"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user