From 36e33e7730c836c045518963978bcbbf08252d3d Mon Sep 17 00:00:00 2001 From: xushanchuan Date: Tue, 16 Mar 2021 17:16:20 +0800 Subject: [PATCH] refactor: move logic to application level --- src/main/java/io/spring/Util.java | 7 + src/main/java/io/spring/api/ArticleApi.java | 127 ++++---- .../io/spring/api/ArticleFavoriteApi.java | 84 +++--- src/main/java/io/spring/api/ArticlesApi.java | 72 +---- src/main/java/io/spring/api/CommentsApi.java | 16 +- .../java/io/spring/api/CurrentUserApi.java | 112 +------ src/main/java/io/spring/api/UsersApi.java | 84 +----- .../io/spring/api/security/CORSConfig.java | 14 - .../api/security/WebSecurityConfig.java | 11 +- .../application/ProfileQueryService.java | 49 ++-- .../article/ArticleCommandService.java | 42 +++ .../article/DuplicatedArticleConstraint.java | 21 ++ .../article/DuplicatedArticleValidator.java | 18 ++ .../application/article/NewArticleParam.java | 28 ++ .../article/UpdateArticleParam.java | 16 + .../user/DuplicatedEmailConstraint.java | 16 + .../user/DuplicatedEmailValidator.java | 17 ++ .../user/DuplicatedUsernameConstraint.java | 16 + .../user/DuplicatedUsernameValidator.java | 17 ++ .../application/user/RegisterParam.java | 26 ++ .../application/user/UpdateUserCommand.java | 14 + .../application/user/UpdateUserParam.java | 25 ++ .../spring/application/user/UserService.java | 106 +++++++ .../java/io/spring/core/article/Article.java | 98 ++++--- src/main/java/io/spring/core/user/User.java | 70 ++--- .../resources/mapper/CommentReadService.xml | 1 + src/main/resources/mapper/TransferData.xml | 1 + .../java/io/spring/api/ArticleApiTest.java | 275 ++++++++++-------- .../io/spring/api/ArticleFavoriteApiTest.java | 110 ++++--- .../java/io/spring/api/ArticlesApiTest.java | 26 +- .../java/io/spring/api/CommentsApiTest.java | 227 ++++++++------- .../io/spring/api/CurrentUserApiTest.java | 5 +- .../io/spring/api/ListArticleApiTest.java | 93 +++--- src/test/java/io/spring/api/UsersApiTest.java | 8 +- .../article/ArticleQueryServiceTest.java | 264 +++++++++-------- .../comment/CommentQueryServiceTest.java | 92 +++--- .../application/tag/TagsQueryServiceTest.java | 23 +- .../io/spring/core/article/ArticleTest.java | 57 ++-- .../ArticleRepositoryTransactionTest.java | 43 ++- .../article/MyBatisArticleRepositoryTest.java | 93 +++--- 40 files changed, 1310 insertions(+), 1114 deletions(-) create mode 100644 src/main/java/io/spring/Util.java delete mode 100644 src/main/java/io/spring/api/security/CORSConfig.java create mode 100644 src/main/java/io/spring/application/article/ArticleCommandService.java create mode 100644 src/main/java/io/spring/application/article/DuplicatedArticleConstraint.java create mode 100644 src/main/java/io/spring/application/article/DuplicatedArticleValidator.java create mode 100644 src/main/java/io/spring/application/article/NewArticleParam.java create mode 100644 src/main/java/io/spring/application/article/UpdateArticleParam.java create mode 100644 src/main/java/io/spring/application/user/DuplicatedEmailConstraint.java create mode 100644 src/main/java/io/spring/application/user/DuplicatedEmailValidator.java create mode 100644 src/main/java/io/spring/application/user/DuplicatedUsernameConstraint.java create mode 100644 src/main/java/io/spring/application/user/DuplicatedUsernameValidator.java create mode 100644 src/main/java/io/spring/application/user/RegisterParam.java create mode 100644 src/main/java/io/spring/application/user/UpdateUserCommand.java create mode 100644 src/main/java/io/spring/application/user/UpdateUserParam.java create mode 100644 src/main/java/io/spring/application/user/UserService.java diff --git a/src/main/java/io/spring/Util.java b/src/main/java/io/spring/Util.java new file mode 100644 index 0000000..d2512ac --- /dev/null +++ b/src/main/java/io/spring/Util.java @@ -0,0 +1,7 @@ +package io.spring; + +public class Util { + public static boolean isEmpty(String value) { + return value == null || value.isEmpty(); + } +} diff --git a/src/main/java/io/spring/api/ArticleApi.java b/src/main/java/io/spring/api/ArticleApi.java index 724ea41..ebf2df5 100644 --- a/src/main/java/io/spring/api/ArticleApi.java +++ b/src/main/java/io/spring/api/ArticleApi.java @@ -1,15 +1,17 @@ package io.spring.api; -import com.fasterxml.jackson.annotation.JsonRootName; import io.spring.api.exception.NoAuthorizationException; import io.spring.api.exception.ResourceNotFoundException; -import io.spring.core.service.AuthorizationService; -import io.spring.application.data.ArticleData; import io.spring.application.ArticleQueryService; +import io.spring.application.article.ArticleCommandService; +import io.spring.application.article.UpdateArticleParam; +import io.spring.application.data.ArticleData; import io.spring.core.article.ArticleRepository; +import io.spring.core.service.AuthorizationService; import io.spring.core.user.User; -import lombok.Getter; -import lombok.NoArgsConstructor; +import java.util.HashMap; +import java.util.Map; +import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -21,71 +23,72 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; -import java.util.HashMap; -import java.util.Map; - @RestController @RequestMapping(path = "/articles/{slug}") public class ArticleApi { - private ArticleQueryService articleQueryService; - private ArticleRepository articleRepository; + private ArticleQueryService articleQueryService; + private ArticleRepository articleRepository; + private ArticleCommandService articleCommandService; - @Autowired - public ArticleApi(ArticleQueryService articleQueryService, ArticleRepository articleRepository) { - this.articleQueryService = articleQueryService; - this.articleRepository = articleRepository; - } + @Autowired + public ArticleApi( + ArticleQueryService articleQueryService, + ArticleRepository articleRepository, + ArticleCommandService articleCommandService) { + this.articleQueryService = articleQueryService; + this.articleRepository = articleRepository; + this.articleCommandService = articleCommandService; + } - @GetMapping - public ResponseEntity article(@PathVariable("slug") String slug, - @AuthenticationPrincipal User user) { - return articleQueryService.findBySlug(slug, user) - .map(articleData -> ResponseEntity.ok(articleResponse(articleData))) - .orElseThrow(ResourceNotFoundException::new); - } + @GetMapping + public ResponseEntity article( + @PathVariable("slug") String slug, @AuthenticationPrincipal User user) { + return articleQueryService + .findBySlug(slug, user) + .map(articleData -> ResponseEntity.ok(articleResponse(articleData))) + .orElseThrow(ResourceNotFoundException::new); + } - @PutMapping - public ResponseEntity updateArticle(@PathVariable("slug") String slug, - @AuthenticationPrincipal User user, - @Valid @RequestBody UpdateArticleParam updateArticleParam) { - return articleRepository.findBySlug(slug).map(article -> { - if (!AuthorizationService.canWriteArticle(user, article)) { + @PutMapping + public ResponseEntity updateArticle( + @PathVariable("slug") String slug, + @AuthenticationPrincipal User user, + @Valid @RequestBody UpdateArticleParam updateArticleParam) { + return articleRepository + .findBySlug(slug) + .map( + article -> { + if (!AuthorizationService.canWriteArticle(user, article)) { throw new NoAuthorizationException(); - } - article.update( - updateArticleParam.getTitle(), - updateArticleParam.getDescription(), - updateArticleParam.getBody()); - articleRepository.save(article); - return ResponseEntity.ok(articleResponse(articleQueryService.findBySlug(slug, user).get())); - }).orElseThrow(ResourceNotFoundException::new); - } + } + articleCommandService.updateArticle(article, updateArticleParam); + return ResponseEntity.ok( + articleResponse(articleQueryService.findBySlug(slug, user).get())); + }) + .orElseThrow(ResourceNotFoundException::new); + } - @DeleteMapping - public ResponseEntity deleteArticle(@PathVariable("slug") String slug, - @AuthenticationPrincipal User user) { - return articleRepository.findBySlug(slug).map(article -> { - if (!AuthorizationService.canWriteArticle(user, article)) { + @DeleteMapping + public ResponseEntity deleteArticle( + @PathVariable("slug") String slug, @AuthenticationPrincipal User user) { + return articleRepository + .findBySlug(slug) + .map( + article -> { + if (!AuthorizationService.canWriteArticle(user, article)) { throw new NoAuthorizationException(); - } - articleRepository.remove(article); - return ResponseEntity.noContent().build(); - }).orElseThrow(ResourceNotFoundException::new); - } + } + articleRepository.remove(article); + return ResponseEntity.noContent().build(); + }) + .orElseThrow(ResourceNotFoundException::new); + } - private Map articleResponse(ArticleData articleData) { - return new HashMap() {{ - put("article", articleData); - }}; - } -} - -@Getter -@NoArgsConstructor -@JsonRootName("article") -class UpdateArticleParam { - private String title = ""; - private String body = ""; - private String description = ""; + private Map articleResponse(ArticleData articleData) { + return new HashMap() { + { + put("article", articleData); + } + }; + } } diff --git a/src/main/java/io/spring/api/ArticleFavoriteApi.java b/src/main/java/io/spring/api/ArticleFavoriteApi.java index 34f1ef6..20d0119 100644 --- a/src/main/java/io/spring/api/ArticleFavoriteApi.java +++ b/src/main/java/io/spring/api/ArticleFavoriteApi.java @@ -1,13 +1,14 @@ package io.spring.api; import io.spring.api.exception.ResourceNotFoundException; -import io.spring.application.data.ArticleData; import io.spring.application.ArticleQueryService; +import io.spring.application.data.ArticleData; import io.spring.core.article.Article; import io.spring.core.article.ArticleRepository; import io.spring.core.favorite.ArticleFavorite; import io.spring.core.favorite.ArticleFavoriteRepository; import io.spring.core.user.User; +import java.util.HashMap; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -17,51 +18,54 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.HashMap; - @RestController @RequestMapping(path = "articles/{slug}/favorite") public class ArticleFavoriteApi { - private ArticleFavoriteRepository articleFavoriteRepository; - private ArticleRepository articleRepository; - private ArticleQueryService articleQueryService; + private ArticleFavoriteRepository articleFavoriteRepository; + private ArticleRepository articleRepository; + private ArticleQueryService articleQueryService; - @Autowired - public ArticleFavoriteApi(ArticleFavoriteRepository articleFavoriteRepository, - ArticleRepository articleRepository, - ArticleQueryService articleQueryService) { - this.articleFavoriteRepository = articleFavoriteRepository; - this.articleRepository = articleRepository; - this.articleQueryService = articleQueryService; - } + @Autowired + public ArticleFavoriteApi( + ArticleFavoriteRepository articleFavoriteRepository, + ArticleRepository articleRepository, + ArticleQueryService articleQueryService) { + this.articleFavoriteRepository = articleFavoriteRepository; + this.articleRepository = articleRepository; + this.articleQueryService = articleQueryService; + } - @PostMapping - public ResponseEntity favoriteArticle(@PathVariable("slug") String slug, - @AuthenticationPrincipal User user) { - Article article = getArticle(slug); - ArticleFavorite articleFavorite = new ArticleFavorite(article.getId(), user.getId()); - articleFavoriteRepository.save(articleFavorite); - return responseArticleData(articleQueryService.findBySlug(slug, user).get()); - } + @PostMapping + public ResponseEntity favoriteArticle( + @PathVariable("slug") String slug, @AuthenticationPrincipal User user) { + Article article = + articleRepository.findBySlug(slug).orElseThrow(ResourceNotFoundException::new); + ArticleFavorite articleFavorite = new ArticleFavorite(article.getId(), user.getId()); + articleFavoriteRepository.save(articleFavorite); + return responseArticleData(articleQueryService.findBySlug(slug, user).get()); + } - @DeleteMapping - public ResponseEntity unfavoriteArticle(@PathVariable("slug") String slug, - @AuthenticationPrincipal User user) { - Article article = getArticle(slug); - articleFavoriteRepository.find(article.getId(), user.getId()).ifPresent(favorite -> { - articleFavoriteRepository.remove(favorite); - }); - return responseArticleData(articleQueryService.findBySlug(slug, user).get()); - } + @DeleteMapping + public ResponseEntity unfavoriteArticle( + @PathVariable("slug") String slug, @AuthenticationPrincipal User user) { + Article article = + articleRepository.findBySlug(slug).orElseThrow(ResourceNotFoundException::new); + articleFavoriteRepository + .find(article.getId(), user.getId()) + .ifPresent( + favorite -> { + articleFavoriteRepository.remove(favorite); + }); + return responseArticleData(articleQueryService.findBySlug(slug, user).get()); + } - private ResponseEntity> responseArticleData(final ArticleData articleData) { - return ResponseEntity.ok(new HashMap() {{ + private ResponseEntity> responseArticleData( + final ArticleData articleData) { + return ResponseEntity.ok( + new HashMap() { + { put("article", articleData); - }}); - } - - private Article getArticle(String slug) { - return articleRepository.findBySlug(slug).map(article -> article) - .orElseThrow(ResourceNotFoundException::new); - } + } + }); + } } diff --git a/src/main/java/io/spring/api/ArticlesApi.java b/src/main/java/io/spring/api/ArticlesApi.java index c69092a..520aac1 100644 --- a/src/main/java/io/spring/api/ArticlesApi.java +++ b/src/main/java/io/spring/api/ArticlesApi.java @@ -1,25 +1,13 @@ package io.spring.api; -import com.fasterxml.jackson.annotation.JsonRootName; import io.spring.application.ArticleQueryService; import io.spring.application.Page; +import io.spring.application.article.ArticleCommandService; +import io.spring.application.article.NewArticleParam; import io.spring.core.article.Article; -import io.spring.core.article.ArticleRepository; 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.NoArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -33,26 +21,20 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping(path = "/articles") public class ArticlesApi { - private ArticleRepository articleRepository; + private ArticleCommandService articleCommandService; private ArticleQueryService articleQueryService; @Autowired - public ArticlesApi(ArticleRepository articleRepository, ArticleQueryService articleQueryService) { - this.articleRepository = articleRepository; + public ArticlesApi( + ArticleCommandService articleCommandService, ArticleQueryService articleQueryService) { + this.articleCommandService = articleCommandService; this.articleQueryService = articleQueryService; } @PostMapping public ResponseEntity createArticle( @Valid @RequestBody NewArticleParam newArticleParam, @AuthenticationPrincipal User user) { - Article article = - new Article( - newArticleParam.getTitle(), - newArticleParam.getDescription(), - newArticleParam.getBody(), - newArticleParam.getTagList(), - user.getId()); - articleRepository.save(article); + Article article = articleCommandService.createArticle(newArticleParam, user); return ResponseEntity.ok( new HashMap() { { @@ -82,43 +64,3 @@ public class ArticlesApi { tag, author, favoritedBy, new Page(offset, limit), user)); } } - -@Getter -@JsonRootName("article") -@NoArgsConstructor -class NewArticleParam { - @NotBlank(message = "can't be empty") - @DuplicatedArticleConstraint - private String title; - - @NotBlank(message = "can't be empty") - private String description; - - @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[] payload() default {}; -} - -class DuplicatedArticleValidator - implements ConstraintValidator { - - @Autowired private ArticleQueryService articleQueryService; - - @Override - public boolean isValid(String value, ConstraintValidatorContext context) { - return !articleQueryService.findBySlug(Article.toSlug(value), null).isPresent(); - } -} diff --git a/src/main/java/io/spring/api/CommentsApi.java b/src/main/java/io/spring/api/CommentsApi.java index 6af6372..92c8aa5 100644 --- a/src/main/java/io/spring/api/CommentsApi.java +++ b/src/main/java/io/spring/api/CommentsApi.java @@ -51,7 +51,8 @@ public class CommentsApi { @PathVariable("slug") String slug, @AuthenticationPrincipal User user, @Valid @RequestBody NewCommentParam newCommentParam) { - Article article = findArticle(slug); + Article article = + articleRepository.findBySlug(slug).orElseThrow(ResourceNotFoundException::new); Comment comment = new Comment(newCommentParam.getBody(), user.getId(), article.getId()); commentRepository.save(comment); return ResponseEntity.status(201) @@ -61,7 +62,8 @@ public class CommentsApi { @GetMapping public ResponseEntity getComments( @PathVariable("slug") String slug, @AuthenticationPrincipal User user) { - Article article = findArticle(slug); + Article article = + articleRepository.findBySlug(slug).orElseThrow(ResourceNotFoundException::new); List comments = commentQueryService.findByArticleId(article.getId(), user); return ResponseEntity.ok( new HashMap() { @@ -76,7 +78,8 @@ public class CommentsApi { @PathVariable("slug") String slug, @PathVariable("id") String commentId, @AuthenticationPrincipal User user) { - Article article = findArticle(slug); + Article article = + articleRepository.findBySlug(slug).orElseThrow(ResourceNotFoundException::new); return commentRepository .findById(article.getId(), commentId) .map( @@ -90,13 +93,6 @@ public class CommentsApi { .orElseThrow(ResourceNotFoundException::new); } - private Article findArticle(String slug) { - return articleRepository - .findBySlug(slug) - .map(article -> article) - .orElseThrow(ResourceNotFoundException::new); - } - private Map commentResponse(CommentData commentData) { return new HashMap() { { diff --git a/src/main/java/io/spring/api/CurrentUserApi.java b/src/main/java/io/spring/api/CurrentUserApi.java index e4e98cc..0b4b86b 100644 --- a/src/main/java/io/spring/api/CurrentUserApi.java +++ b/src/main/java/io/spring/api/CurrentUserApi.java @@ -1,28 +1,18 @@ package io.spring.api; -import com.fasterxml.jackson.annotation.JsonRootName; import io.spring.application.UserQueryService; import io.spring.application.data.UserData; import io.spring.application.data.UserWithToken; +import io.spring.application.user.UpdateUserCommand; +import io.spring.application.user.UpdateUserParam; +import io.spring.application.user.UserService; import io.spring.core.user.User; -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.NoArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -71,99 +61,3 @@ public class CurrentUserApi { }; } } - -@Validated -@Service -class UserService { - - private UserRepository userRepository; - - @Autowired - public UserService(UserRepository userRepository) { - this.userRepository = userRepository; - } - - public void updateUser(@Valid UpdateUserCommand command) { - User user = command.getTargetUser(); - UpdateUserParam updateUserParam = command.getParam(); - user.update( - updateUserParam.getEmail(), - updateUserParam.getUsername(), - updateUserParam.getPassword(), - updateUserParam.getBio(), - updateUserParam.getImage()); - userRepository.save(user); - } -} - -@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 { - - @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 -@JsonRootName("user") -@NoArgsConstructor -class UpdateUserParam { - - @Email(message = "should be an email") - private String email = ""; - - private String password = ""; - private String username = ""; - private String bio = ""; - private String image = ""; -} diff --git a/src/main/java/io/spring/api/UsersApi.java b/src/main/java/io/spring/api/UsersApi.java index 48dbc0f..49116dc 100644 --- a/src/main/java/io/spring/api/UsersApi.java +++ b/src/main/java/io/spring/api/UsersApi.java @@ -7,26 +7,21 @@ import io.spring.api.exception.InvalidAuthenticationException; import io.spring.application.UserQueryService; import io.spring.application.data.UserData; import io.spring.application.data.UserWithToken; +import io.spring.application.user.RegisterParam; +import io.spring.application.user.UserService; import io.spring.core.service.JwtService; import io.spring.core.user.EncryptService; import io.spring.core.user.User; 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 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.NotBlank; import lombok.Getter; import lombok.NoArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -36,34 +31,27 @@ import org.springframework.web.bind.annotation.RestController; public class UsersApi { private UserRepository userRepository; private UserQueryService userQueryService; - private String defaultImage; private EncryptService encryptService; private JwtService jwtService; + private UserService userService; @Autowired public UsersApi( UserRepository userRepository, UserQueryService userQueryService, EncryptService encryptService, - @Value("${image.default}") String defaultImage, - JwtService jwtService) { + JwtService jwtService, + UserService userService) { this.userRepository = userRepository; this.userQueryService = userQueryService; this.encryptService = encryptService; - this.defaultImage = defaultImage; this.jwtService = jwtService; + this.userService = userService; } @RequestMapping(path = "/users", method = POST) public ResponseEntity createUser(@Valid @RequestBody RegisterParam registerParam) { - User user = - new User( - registerParam.getEmail(), - registerParam.getUsername(), - encryptService.encrypt(registerParam.getPassword()), - "", - defaultImage); - userRepository.save(user); + User user = userService.createUser(registerParam); UserData userData = userQueryService.findById(user.getId()).get(); return ResponseEntity.status(201) .body(userResponse(new UserWithToken(userData, jwtService.toToken(user)))); @@ -91,47 +79,6 @@ public class UsersApi { } } -@Constraint(validatedBy = DuplicatedEmailValidator.class) -@Retention(RetentionPolicy.RUNTIME) -@interface DuplicatedEmailConstraint { - String message() default "duplicated email"; - - Class[] groups() default {}; - - Class[] payload() default {}; -} - -class DuplicatedEmailValidator implements ConstraintValidator { - - @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[] payload() default {}; -} - -class DuplicatedUsernameValidator - implements ConstraintValidator { - - @Autowired private UserRepository userRepository; - - @Override - public boolean isValid(String value, ConstraintValidatorContext context) { - return (value == null || value.isEmpty()) || !userRepository.findByUsername(value).isPresent(); - } -} - @Getter @JsonRootName("user") @NoArgsConstructor @@ -143,20 +90,3 @@ class LoginParam { @NotBlank(message = "can't be empty") private String password; } - -@Getter -@JsonRootName("user") -@NoArgsConstructor -class RegisterParam { - @NotBlank(message = "can't be empty") - @Email(message = "should be an email") - @DuplicatedEmailConstraint - private String email; - - @NotBlank(message = "can't be empty") - @DuplicatedUsernameConstraint - private String username; - - @NotBlank(message = "can't be empty") - private String password; -} diff --git a/src/main/java/io/spring/api/security/CORSConfig.java b/src/main/java/io/spring/api/security/CORSConfig.java deleted file mode 100644 index 2d30d41..0000000 --- a/src/main/java/io/spring/api/security/CORSConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.spring.api.security; - -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -public class CORSConfig implements WebMvcConfigurer { - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**") - .allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH"); - } -} diff --git a/src/main/java/io/spring/api/security/WebSecurityConfig.java b/src/main/java/io/spring/api/security/WebSecurityConfig.java index 02c599c..fa52492 100644 --- a/src/main/java/io/spring/api/security/WebSecurityConfig.java +++ b/src/main/java/io/spring/api/security/WebSecurityConfig.java @@ -1,5 +1,7 @@ package io.spring.api.security; +import static java.util.Arrays.asList; + import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -15,8 +17,6 @@ import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import static java.util.Arrays.asList; - @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -32,11 +32,12 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { - if (h2ConsoleEnabled) + if (h2ConsoleEnabled) { http.authorizeRequests() .antMatchers("/h2-console", "/h2-console/**").permitAll() .and() .headers().frameOptions().sameOrigin(); + } http.csrf().disable() .cors() @@ -46,6 +47,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .authorizeRequests() .antMatchers(HttpMethod.OPTIONS).permitAll() + .antMatchers("/graphiql").permitAll() + .antMatchers("/graphql").permitAll() .antMatchers(HttpMethod.GET, "/articles/feed").authenticated() .antMatchers(HttpMethod.POST, "/users", "/users/login").permitAll() .antMatchers(HttpMethod.GET, "/articles/**", "/profiles/**", "/tags").permitAll() @@ -62,7 +65,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { "GET", "POST", "PUT", "DELETE", "PATCH")); // setAllowCredentials(true) is important, otherwise: // The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. - configuration.setAllowCredentials(true); + configuration.setAllowCredentials(false); // setAllowedHeaders is important! Without it, OPTIONS preflight request // will fail with 403 Invalid CORS request configuration.setAllowedHeaders(asList("Authorization", "Cache-Control", "Content-Type")); diff --git a/src/main/java/io/spring/application/ProfileQueryService.java b/src/main/java/io/spring/application/ProfileQueryService.java index 548ac5f..179e009 100644 --- a/src/main/java/io/spring/application/ProfileQueryService.java +++ b/src/main/java/io/spring/application/ProfileQueryService.java @@ -2,37 +2,40 @@ package io.spring.application; import io.spring.application.data.ProfileData; import io.spring.application.data.UserData; -import io.spring.infrastructure.mybatis.readservice.UserReadService; import io.spring.core.user.User; +import io.spring.infrastructure.mybatis.readservice.UserReadService; import io.spring.infrastructure.mybatis.readservice.UserRelationshipQueryService; +import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import java.util.Optional; - @Component public class ProfileQueryService { - private UserReadService userReadService; - private UserRelationshipQueryService userRelationshipQueryService; + private UserReadService userReadService; + private UserRelationshipQueryService userRelationshipQueryService; - @Autowired - public ProfileQueryService(UserReadService userReadService, UserRelationshipQueryService userRelationshipQueryService) { - this.userReadService = userReadService; - this.userRelationshipQueryService = userRelationshipQueryService; - } + @Autowired + public ProfileQueryService( + UserReadService userReadService, UserRelationshipQueryService userRelationshipQueryService) { + this.userReadService = userReadService; + this.userRelationshipQueryService = userRelationshipQueryService; + } - public Optional findByUsername(String username, User currentUser) { - UserData userData = userReadService.findByUsername(username); - if (userData == null) { - return Optional.empty(); - } else { - ProfileData profileData = new ProfileData( - userData.getId(), - userData.getUsername(), - userData.getBio(), - userData.getImage(), - userRelationshipQueryService.isUserFollowing(currentUser.getId(), userData.getId())); - return Optional.of(profileData); - } + public Optional findByUsername(String username, User currentUser) { + UserData userData = userReadService.findByUsername(username); + if (userData == null) { + return Optional.empty(); + } else { + ProfileData profileData = + new ProfileData( + userData.getId(), + userData.getUsername(), + userData.getBio(), + userData.getImage(), + currentUser != null + && userRelationshipQueryService.isUserFollowing( + currentUser.getId(), userData.getId())); + return Optional.of(profileData); } + } } diff --git a/src/main/java/io/spring/application/article/ArticleCommandService.java b/src/main/java/io/spring/application/article/ArticleCommandService.java new file mode 100644 index 0000000..51c3692 --- /dev/null +++ b/src/main/java/io/spring/application/article/ArticleCommandService.java @@ -0,0 +1,42 @@ +package io.spring.application.article; + +import io.spring.core.article.Article; +import io.spring.core.article.ArticleRepository; +import io.spring.core.user.User; +import javax.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +@Service +@Validated +public class ArticleCommandService { + + private ArticleRepository articleRepository; + + @Autowired + public ArticleCommandService(ArticleRepository articleRepository) { + this.articleRepository = articleRepository; + } + + public Article createArticle(@Valid NewArticleParam newArticleParam, User creator) { + Article article = + new Article( + newArticleParam.getTitle(), + newArticleParam.getDescription(), + newArticleParam.getBody(), + newArticleParam.getTagList(), + creator.getId()); + articleRepository.save(article); + return article; + } + + public Article updateArticle(Article article, @Valid UpdateArticleParam updateArticleParam) { + article.update( + updateArticleParam.getTitle(), + updateArticleParam.getDescription(), + updateArticleParam.getBody()); + articleRepository.save(article); + return article; + } +} diff --git a/src/main/java/io/spring/application/article/DuplicatedArticleConstraint.java b/src/main/java/io/spring/application/article/DuplicatedArticleConstraint.java new file mode 100644 index 0000000..0eb1eda --- /dev/null +++ b/src/main/java/io/spring/application/article/DuplicatedArticleConstraint.java @@ -0,0 +1,21 @@ +package io.spring.application.article; + +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 javax.validation.Constraint; +import javax.validation.Payload; + +@Documented +@Constraint(validatedBy = DuplicatedArticleValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE_USE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface DuplicatedArticleConstraint { + String message() default "article name exists"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/io/spring/application/article/DuplicatedArticleValidator.java b/src/main/java/io/spring/application/article/DuplicatedArticleValidator.java new file mode 100644 index 0000000..0e3828e --- /dev/null +++ b/src/main/java/io/spring/application/article/DuplicatedArticleValidator.java @@ -0,0 +1,18 @@ +package io.spring.application.article; + +import io.spring.application.ArticleQueryService; +import io.spring.core.article.Article; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import org.springframework.beans.factory.annotation.Autowired; + +class DuplicatedArticleValidator + implements ConstraintValidator { + + @Autowired private ArticleQueryService articleQueryService; + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + return !articleQueryService.findBySlug(Article.toSlug(value), null).isPresent(); + } +} diff --git a/src/main/java/io/spring/application/article/NewArticleParam.java b/src/main/java/io/spring/application/article/NewArticleParam.java new file mode 100644 index 0000000..344d76c --- /dev/null +++ b/src/main/java/io/spring/application/article/NewArticleParam.java @@ -0,0 +1,28 @@ +package io.spring.application.article; + +import com.fasterxml.jackson.annotation.JsonRootName; +import java.util.List; +import javax.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@JsonRootName("article") +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class NewArticleParam { + @NotBlank(message = "can't be empty") + @DuplicatedArticleConstraint + private String title; + + @NotBlank(message = "can't be empty") + private String description; + + @NotBlank(message = "can't be empty") + private String body; + + private List tagList; +} diff --git a/src/main/java/io/spring/application/article/UpdateArticleParam.java b/src/main/java/io/spring/application/article/UpdateArticleParam.java new file mode 100644 index 0000000..14d5514 --- /dev/null +++ b/src/main/java/io/spring/application/article/UpdateArticleParam.java @@ -0,0 +1,16 @@ +package io.spring.application.article; + +import com.fasterxml.jackson.annotation.JsonRootName; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@JsonRootName("article") +public class UpdateArticleParam { + private String title = ""; + private String body = ""; + private String description = ""; +} diff --git a/src/main/java/io/spring/application/user/DuplicatedEmailConstraint.java b/src/main/java/io/spring/application/user/DuplicatedEmailConstraint.java new file mode 100644 index 0000000..e41eb00 --- /dev/null +++ b/src/main/java/io/spring/application/user/DuplicatedEmailConstraint.java @@ -0,0 +1,16 @@ +package io.spring.application.user; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import javax.validation.Constraint; +import javax.validation.Payload; + +@Constraint(validatedBy = DuplicatedEmailValidator.class) +@Retention(RetentionPolicy.RUNTIME) +public @interface DuplicatedEmailConstraint { + String message() default "duplicated email"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/io/spring/application/user/DuplicatedEmailValidator.java b/src/main/java/io/spring/application/user/DuplicatedEmailValidator.java new file mode 100644 index 0000000..e307114 --- /dev/null +++ b/src/main/java/io/spring/application/user/DuplicatedEmailValidator.java @@ -0,0 +1,17 @@ +package io.spring.application.user; + +import io.spring.core.user.UserRepository; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import org.springframework.beans.factory.annotation.Autowired; + +public class DuplicatedEmailValidator + implements ConstraintValidator { + + @Autowired private UserRepository userRepository; + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + return (value == null || value.isEmpty()) || !userRepository.findByEmail(value).isPresent(); + } +} diff --git a/src/main/java/io/spring/application/user/DuplicatedUsernameConstraint.java b/src/main/java/io/spring/application/user/DuplicatedUsernameConstraint.java new file mode 100644 index 0000000..4f365b7 --- /dev/null +++ b/src/main/java/io/spring/application/user/DuplicatedUsernameConstraint.java @@ -0,0 +1,16 @@ +package io.spring.application.user; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import javax.validation.Constraint; +import javax.validation.Payload; + +@Constraint(validatedBy = DuplicatedUsernameValidator.class) +@Retention(RetentionPolicy.RUNTIME) +@interface DuplicatedUsernameConstraint { + String message() default "duplicated username"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/io/spring/application/user/DuplicatedUsernameValidator.java b/src/main/java/io/spring/application/user/DuplicatedUsernameValidator.java new file mode 100644 index 0000000..ae1fd21 --- /dev/null +++ b/src/main/java/io/spring/application/user/DuplicatedUsernameValidator.java @@ -0,0 +1,17 @@ +package io.spring.application.user; + +import io.spring.core.user.UserRepository; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import org.springframework.beans.factory.annotation.Autowired; + +class DuplicatedUsernameValidator + implements ConstraintValidator { + + @Autowired private UserRepository userRepository; + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + return (value == null || value.isEmpty()) || !userRepository.findByUsername(value).isPresent(); + } +} diff --git a/src/main/java/io/spring/application/user/RegisterParam.java b/src/main/java/io/spring/application/user/RegisterParam.java new file mode 100644 index 0000000..3ba1234 --- /dev/null +++ b/src/main/java/io/spring/application/user/RegisterParam.java @@ -0,0 +1,26 @@ +package io.spring.application.user; + +import com.fasterxml.jackson.annotation.JsonRootName; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@JsonRootName("user") +@AllArgsConstructor +@NoArgsConstructor +public class RegisterParam { + @NotBlank(message = "can't be empty") + @Email(message = "should be an email") + @DuplicatedEmailConstraint + private String email; + + @NotBlank(message = "can't be empty") + @DuplicatedUsernameConstraint + private String username; + + @NotBlank(message = "can't be empty") + private String password; +} diff --git a/src/main/java/io/spring/application/user/UpdateUserCommand.java b/src/main/java/io/spring/application/user/UpdateUserCommand.java new file mode 100644 index 0000000..9df5230 --- /dev/null +++ b/src/main/java/io/spring/application/user/UpdateUserCommand.java @@ -0,0 +1,14 @@ +package io.spring.application.user; + +import io.spring.core.user.User; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +@UpdateUserConstraint +public class UpdateUserCommand { + + private User targetUser; + private UpdateUserParam param; +} diff --git a/src/main/java/io/spring/application/user/UpdateUserParam.java b/src/main/java/io/spring/application/user/UpdateUserParam.java new file mode 100644 index 0000000..54cd774 --- /dev/null +++ b/src/main/java/io/spring/application/user/UpdateUserParam.java @@ -0,0 +1,25 @@ +package io.spring.application.user; + +import com.fasterxml.jackson.annotation.JsonRootName; +import javax.validation.constraints.Email; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@JsonRootName("user") +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class UpdateUserParam { + + @Builder.Default + @Email(message = "should be an email") + private String email = ""; + + @Builder.Default private String password = ""; + @Builder.Default private String username = ""; + @Builder.Default private String bio = ""; + @Builder.Default private String image = ""; +} diff --git a/src/main/java/io/spring/application/user/UserService.java b/src/main/java/io/spring/application/user/UserService.java new file mode 100644 index 0000000..6e06cbc --- /dev/null +++ b/src/main/java/io/spring/application/user/UserService.java @@ -0,0 +1,106 @@ +package io.spring.application.user; + +import io.spring.core.user.EncryptService; +import io.spring.core.user.User; +import io.spring.core.user.UserRepository; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +@Service +@Validated +public class UserService { + private UserRepository userRepository; + private String defaultImage; + private EncryptService encryptService; + + @Autowired + public UserService( + UserRepository userRepository, + @Value("${image.default}") String defaultImage, + EncryptService encryptService) { + this.userRepository = userRepository; + this.defaultImage = defaultImage; + this.encryptService = encryptService; + } + + public User createUser(@Valid RegisterParam registerParam) { + User user = + new User( + registerParam.getEmail(), + registerParam.getUsername(), + encryptService.encrypt(registerParam.getPassword()), + "", + defaultImage); + userRepository.save(user); + return user; + } + + public void updateUser(@Valid UpdateUserCommand command) { + User user = command.getTargetUser(); + UpdateUserParam updateUserParam = command.getParam(); + user.update( + updateUserParam.getEmail(), + updateUserParam.getUsername(), + updateUserParam.getPassword(), + updateUserParam.getBio(), + updateUserParam.getImage()); + userRepository.save(user); + } +} + +@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 { + + @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; + } + } +} diff --git a/src/main/java/io/spring/core/article/Article.java b/src/main/java/io/spring/core/article/Article.java index 528430d..f23c2c6 100644 --- a/src/main/java/io/spring/core/article/Article.java +++ b/src/main/java/io/spring/core/article/Article.java @@ -1,62 +1,70 @@ package io.spring.core.article; +import static java.util.stream.Collectors.toList; + +import io.spring.Util; +import java.util.HashSet; +import java.util.List; +import java.util.UUID; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import org.joda.time.DateTime; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; - -import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toSet; - @Getter @NoArgsConstructor @EqualsAndHashCode(of = {"id"}) public class Article { - private String userId; - private String id; - private String slug; - private String title; - private String description; - private String body; - private List tags; - private DateTime createdAt; - private DateTime updatedAt; + private String userId; + private String id; + private String slug; + private String title; + private String description; + private String body; + private List tags; + private DateTime createdAt; + private DateTime updatedAt; - public Article(String title, String description, String body, String[] tagList, String userId) { - this(title, description, body, tagList, userId, new DateTime()); - } + public Article( + String title, String description, String body, List tagList, String userId) { + this(title, description, body, tagList, userId, new DateTime()); + } - public Article(String title, String description, String body, String[] tagList, String userId, DateTime createdAt) { - this.id = UUID.randomUUID().toString(); - this.slug = toSlug(title); - this.title = title; - this.description = description; - this.body = body; - this.tags = Arrays.stream(tagList).collect(toSet()).stream().map(Tag::new).collect(toList()); - this.userId = userId; - this.createdAt = createdAt; - this.updatedAt = createdAt; - } + public Article( + String title, + String description, + String body, + List tagList, + String userId, + DateTime createdAt) { + this.id = UUID.randomUUID().toString(); + this.slug = toSlug(title); + this.title = title; + this.description = description; + this.body = body; + this.tags = new HashSet<>(tagList).stream().map(Tag::new).collect(toList()); + this.userId = userId; + this.createdAt = createdAt; + this.updatedAt = createdAt; + } - public void update(String title, String description, String body) { - if (!"".equals(title)) { - this.title = title; - this.slug = toSlug(title); - } - if (!"".equals(description)) { - this.description = description; - } - if (!"".equals(body)) { - this.body = body; - } - this.updatedAt = new DateTime(); + public void update(String title, String description, String body) { + if (!Util.isEmpty(title)) { + this.title = title; + this.slug = toSlug(title); + this.updatedAt = new DateTime(); } + if (!Util.isEmpty(description)) { + this.description = description; + this.updatedAt = new DateTime(); + } + if (!Util.isEmpty(body)) { + this.body = body; + this.updatedAt = new DateTime(); + } + } - public static String toSlug(String title) { - return title.toLowerCase().replaceAll("[\\&|[\\uFE30-\\uFFA0]|\\’|\\”|\\s\\?\\,\\.]+", "-"); - } + public static String toSlug(String title) { + return title.toLowerCase().replaceAll("[\\&|[\\uFE30-\\uFFA0]|\\’|\\”|\\s\\?\\,\\.]+", "-"); + } } diff --git a/src/main/java/io/spring/core/user/User.java b/src/main/java/io/spring/core/user/User.java index 91e1563..3044d50 100644 --- a/src/main/java/io/spring/core/user/User.java +++ b/src/main/java/io/spring/core/user/User.java @@ -1,50 +1,50 @@ package io.spring.core.user; +import io.spring.Util; +import java.util.UUID; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; -import java.util.UUID; - @Getter @NoArgsConstructor @EqualsAndHashCode(of = {"id"}) public class User { - private String id; - private String email; - private String username; - private String password; - private String bio; - private String image; + private String id; + private String email; + private String username; + private String password; + private String bio; + private String image; - public User(String email, String username, String password, String bio, String image) { - this.id = UUID.randomUUID().toString(); - this.email = email; - this.username = username; - this.password = password; - this.bio = bio; - this.image = image; + public User(String email, String username, String password, String bio, String image) { + this.id = UUID.randomUUID().toString(); + this.email = email; + this.username = username; + this.password = password; + this.bio = bio; + this.image = image; + } + + public void update(String email, String username, String password, String bio, String image) { + if (!Util.isEmpty(email)) { + this.email = email; } - public void update(String email, String username, String password, String bio, String image) { - if (!"".equals(email)) { - this.email = email; - } - - if (!"".equals(username)) { - this.username = username; - } - - if (!"".equals(password)) { - this.password = password; - } - - if (!"".equals(bio)) { - this.bio = bio; - } - - if (!"".equals(image)) { - this.image = image; - } + if (!Util.isEmpty(username)) { + this.username = username; } + + if (!Util.isEmpty(password)) { + this.password = password; + } + + if (!Util.isEmpty(bio)) { + this.bio = bio; + } + + if (!Util.isEmpty(image)) { + this.image = image; + } + } } diff --git a/src/main/resources/mapper/CommentReadService.xml b/src/main/resources/mapper/CommentReadService.xml index 1e47227..acaf8dc 100644 --- a/src/main/resources/mapper/CommentReadService.xml +++ b/src/main/resources/mapper/CommentReadService.xml @@ -6,6 +6,7 @@ C.id commentId, C.body commentBody, C.created_at commentCreatedAt, + C.article_id commentArticleId, from comments C left join users U diff --git a/src/main/resources/mapper/TransferData.xml b/src/main/resources/mapper/TransferData.xml index a57a5cc..d3c7635 100644 --- a/src/main/resources/mapper/TransferData.xml +++ b/src/main/resources/mapper/TransferData.xml @@ -34,6 +34,7 @@ + \ No newline at end of file diff --git a/src/test/java/io/spring/api/ArticleApiTest.java b/src/test/java/io/spring/api/ArticleApiTest.java index dd63a7a..24520b1 100644 --- a/src/test/java/io/spring/api/ArticleApiTest.java +++ b/src/test/java/io/spring/api/ArticleApiTest.java @@ -1,15 +1,28 @@ 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.anyString; +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.spring.JacksonCustomizations; import io.spring.TestHelper; import io.spring.api.security.WebSecurityConfig; import io.spring.application.ArticleQueryService; +import io.spring.application.article.ArticleCommandService; import io.spring.application.data.ArticleData; import io.spring.application.data.ProfileData; import io.spring.core.article.Article; import io.spring.core.article.ArticleRepository; import io.spring.core.user.User; +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.format.ISODateTimeFormat; import org.junit.Before; @@ -20,104 +33,98 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; 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.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - @WebMvcTest({ArticleApi.class}) @Import({WebSecurityConfig.class, JacksonCustomizations.class}) public class ArticleApiTest extends TestWithCurrentUser { - @Autowired - private MockMvc mvc; + @Autowired private MockMvc mvc; - @MockBean - private ArticleQueryService articleQueryService; + @MockBean private ArticleQueryService articleQueryService; - @MockBean - private ArticleRepository articleRepository; + @MockBean private ArticleRepository articleRepository; - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - RestAssuredMockMvc.mockMvc(mvc); - } + @MockBean ArticleCommandService articleCommandService; - @Test - public void should_read_article_success() throws Exception { - String slug = "test-new-article"; - DateTime time = new DateTime(); - Article article = new Article("Test New Article", "Desc", "Body", new String[]{"java", "spring", "jpg"}, user.getId(), time); - ArticleData articleData = TestHelper.getArticleDataFromArticleAndUser(article, user); + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + RestAssuredMockMvc.mockMvc(mvc); + } - when(articleQueryService.findBySlug(eq(slug), eq(null))).thenReturn(Optional.of(articleData)); + @Test + public void should_read_article_success() throws Exception { + String slug = "test-new-article"; + DateTime time = new DateTime(); + Article article = + new Article( + "Test New Article", + "Desc", + "Body", + Arrays.asList("java", "spring", "jpg"), + user.getId(), + time); + ArticleData articleData = TestHelper.getArticleDataFromArticleAndUser(article, user); - RestAssuredMockMvc.when() - .get("/articles/{slug}", slug) - .then() - .statusCode(200) - .body("article.slug", equalTo(slug)) - .body("article.body", equalTo(articleData.getBody())) - .body("article.createdAt", equalTo(ISODateTimeFormat.dateTime().withZoneUTC().print(time))); + when(articleQueryService.findBySlug(eq(slug), eq(null))).thenReturn(Optional.of(articleData)); - } + RestAssuredMockMvc.when() + .get("/articles/{slug}", slug) + .then() + .statusCode(200) + .body("article.slug", equalTo(slug)) + .body("article.body", equalTo(articleData.getBody())) + .body("article.createdAt", equalTo(ISODateTimeFormat.dateTime().withZoneUTC().print(time))); + } - @Test - public void should_404_if_article_not_found() throws Exception { - when(articleQueryService.findBySlug(anyString(), any())).thenReturn(Optional.empty()); - RestAssuredMockMvc.when() - .get("/articles/not-exists") - .then() - .statusCode(404); - } + @Test + public void should_404_if_article_not_found() throws Exception { + when(articleQueryService.findBySlug(anyString(), any())).thenReturn(Optional.empty()); + RestAssuredMockMvc.when().get("/articles/not-exists").then().statusCode(404); + } - @Test - public void should_update_article_content_success() throws Exception { - String title = "new-title"; - String body = "new body"; - String description = "new description"; - Map updateParam = prepareUpdateParam(title, body, description); + @Test + public void should_update_article_content_success() throws Exception { + String title = "new-title"; + String body = "new body"; + String description = "new description"; + Map updateParam = prepareUpdateParam(title, body, description); - Article article = new Article(title, description, body, new String[]{"java", "spring", "jpg"}, user.getId()); + Article article = + new Article(title, description, body, Arrays.asList("java", "spring", "jpg"), user.getId()); - ArticleData articleData = TestHelper.getArticleDataFromArticleAndUser(article, user); + ArticleData articleData = TestHelper.getArticleDataFromArticleAndUser(article, user); - when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article)); - when(articleQueryService.findBySlug(eq(article.getSlug()), eq(user))).thenReturn(Optional.of(articleData)); + when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article)); + when(articleQueryService.findBySlug(eq(article.getSlug()), eq(user))) + .thenReturn(Optional.of(articleData)); - given() - .contentType("application/json") - .header("Authorization", "Token " + token) - .body(updateParam) - .when() - .put("/articles/{slug}", article.getSlug()) - .then() - .statusCode(200) - .body("article.slug", equalTo(articleData.getSlug())); - } + given() + .contentType("application/json") + .header("Authorization", "Token " + token) + .body(updateParam) + .when() + .put("/articles/{slug}", article.getSlug()) + .then() + .statusCode(200) + .body("article.slug", equalTo(articleData.getSlug())); + } - @Test - public void should_get_403_if_not_author_to_update_article() throws Exception { - String title = "new-title"; - String body = "new body"; - String description = "new description"; - Map updateParam = prepareUpdateParam(title, body, description); + @Test + public void should_get_403_if_not_author_to_update_article() throws Exception { + String title = "new-title"; + String body = "new body"; + String description = "new description"; + Map updateParam = prepareUpdateParam(title, body, description); - User anotherUser = new User("test@test.com", "test", "123123", "", ""); + User anotherUser = new User("test@test.com", "test", "123123", "", ""); - Article article = new Article(title, description, body, new String[]{"java", "spring", "jpg"}, anotherUser.getId()); + Article article = + new Article( + title, description, body, Arrays.asList("java", "spring", "jpg"), anotherUser.getId()); - DateTime time = new DateTime(); - ArticleData articleData = new ArticleData( + DateTime time = new DateTime(); + ArticleData articleData = + new ArticleData( article.getId(), article.getSlug(), article.getTitle(), @@ -128,66 +135,82 @@ public class ArticleApiTest extends TestWithCurrentUser { time, time, Arrays.asList("joda"), - new ProfileData(anotherUser.getId(), anotherUser.getUsername(), anotherUser.getBio(), anotherUser.getImage(), false)); + new ProfileData( + anotherUser.getId(), + anotherUser.getUsername(), + anotherUser.getBio(), + anotherUser.getImage(), + false)); - when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article)); - when(articleQueryService.findBySlug(eq(article.getSlug()), eq(user))).thenReturn(Optional.of(articleData)); + when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article)); + when(articleQueryService.findBySlug(eq(article.getSlug()), eq(user))) + .thenReturn(Optional.of(articleData)); - given() - .contentType("application/json") - .header("Authorization", "Token " + token) - .body(updateParam) - .when() - .put("/articles/{slug}", article.getSlug()) - .then() - .statusCode(403); - } + given() + .contentType("application/json") + .header("Authorization", "Token " + token) + .body(updateParam) + .when() + .put("/articles/{slug}", article.getSlug()) + .then() + .statusCode(403); + } - @Test - public void should_delete_article_success() throws Exception { - String title = "title"; - String body = "body"; - String description = "description"; + @Test + public void should_delete_article_success() throws Exception { + String title = "title"; + String body = "body"; + String description = "description"; - Article article = new Article(title, description, body, new String[]{"java", "spring", "jpg"}, user.getId()); - when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article)); + Article article = + new Article(title, description, body, Arrays.asList("java", "spring", "jpg"), user.getId()); + when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article)); - given() - .header("Authorization", "Token " + token) - .when() - .delete("/articles/{slug}", article.getSlug()) - .then() - .statusCode(204); + given() + .header("Authorization", "Token " + token) + .when() + .delete("/articles/{slug}", article.getSlug()) + .then() + .statusCode(204); - verify(articleRepository).remove(eq(article)); - } + verify(articleRepository).remove(eq(article)); + } - @Test - public void should_403_if_not_author_delete_article() throws Exception { - String title = "new-title"; - String body = "new body"; - String description = "new description"; + @Test + public void should_403_if_not_author_delete_article() throws Exception { + String title = "new-title"; + String body = "new body"; + String description = "new description"; - User anotherUser = new User("test@test.com", "test", "123123", "", ""); + User anotherUser = new User("test@test.com", "test", "123123", "", ""); - Article article = new Article(title, description, body, new String[]{"java", "spring", "jpg"}, anotherUser.getId()); + Article article = + new Article( + title, description, body, Arrays.asList("java", "spring", "jpg"), anotherUser.getId()); - when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article)); - given() - .header("Authorization", "Token " + token) - .when() - .delete("/articles/{slug}", article.getSlug()) - .then() - .statusCode(403); - } + when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article)); + given() + .header("Authorization", "Token " + token) + .when() + .delete("/articles/{slug}", article.getSlug()) + .then() + .statusCode(403); + } - private HashMap prepareUpdateParam(final String title, final String body, final String description) { - return new HashMap() {{ - put("article", new HashMap() {{ + private HashMap prepareUpdateParam( + final String title, final String body, final String description) { + return new HashMap() { + { + put( + "article", + new HashMap() { + { put("title", title); put("body", body); put("description", description); - }}); - }}; - } + } + }); + } + }; + } } diff --git a/src/test/java/io/spring/api/ArticleFavoriteApiTest.java b/src/test/java/io/spring/api/ArticleFavoriteApiTest.java index a1e25ae..0c1279f 100644 --- a/src/test/java/io/spring/api/ArticleFavoriteApiTest.java +++ b/src/test/java/io/spring/api/ArticleFavoriteApiTest.java @@ -1,5 +1,12 @@ 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.spring.JacksonCustomizations; import io.spring.api.security.WebSecurityConfig; @@ -12,6 +19,9 @@ import io.spring.core.article.Tag; import io.spring.core.favorite.ArticleFavorite; import io.spring.core.favorite.ArticleFavoriteRepository; import io.spring.core.user.User; +import java.util.Arrays; +import java.util.Optional; +import java.util.stream.Collectors; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -20,41 +30,28 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.test.web.servlet.MockMvc; -import java.util.Optional; -import java.util.stream.Collectors; - -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; - @WebMvcTest(ArticleFavoriteApi.class) @Import({WebSecurityConfig.class, JacksonCustomizations.class}) public class ArticleFavoriteApiTest extends TestWithCurrentUser { - @Autowired - private MockMvc mvc; + @Autowired private MockMvc mvc; - @MockBean - private ArticleFavoriteRepository articleFavoriteRepository; + @MockBean private ArticleFavoriteRepository articleFavoriteRepository; - @MockBean - private ArticleRepository articleRepository; + @MockBean private ArticleRepository articleRepository; - @MockBean - private ArticleQueryService articleQueryService; + @MockBean private ArticleQueryService articleQueryService; - private Article article; + private Article article; - @Before - public void setUp() throws Exception { - super.setUp(); - RestAssuredMockMvc.mockMvc(mvc); - User anotherUser = new User("other@test.com", "other", "123", "", ""); - article = new Article("title", "desc", "body", new String[]{"java"}, anotherUser.getId()); - when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article)); - ArticleData articleData = new ArticleData( + @Before + public void setUp() throws Exception { + super.setUp(); + RestAssuredMockMvc.mockMvc(mvc); + User anotherUser = new User("other@test.com", "other", "123", "", ""); + article = new Article("title", "desc", "body", Arrays.asList("java"), anotherUser.getId()); + when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article)); + ArticleData articleData = + new ArticleData( article.getId(), article.getSlug(), article.getTitle(), @@ -70,36 +67,37 @@ public class ArticleFavoriteApiTest extends TestWithCurrentUser { anotherUser.getUsername(), anotherUser.getBio(), anotherUser.getImage(), - false - )); - when(articleQueryService.findBySlug(eq(articleData.getSlug()), eq(user))).thenReturn(Optional.of(articleData)); - } + false)); + when(articleQueryService.findBySlug(eq(articleData.getSlug()), eq(user))) + .thenReturn(Optional.of(articleData)); + } - @Test - public void should_favorite_an_article_success() throws Exception { - given() - .header("Authorization", "Token " + token) - .when() - .post("/articles/{slug}/favorite", article.getSlug()) - .prettyPeek() - .then() - .statusCode(200) - .body("article.id", equalTo(article.getId())); + @Test + public void should_favorite_an_article_success() throws Exception { + given() + .header("Authorization", "Token " + token) + .when() + .post("/articles/{slug}/favorite", article.getSlug()) + .prettyPeek() + .then() + .statusCode(200) + .body("article.id", equalTo(article.getId())); - verify(articleFavoriteRepository).save(any()); - } + verify(articleFavoriteRepository).save(any()); + } - @Test - public void should_unfavorite_an_article_success() throws Exception { - when(articleFavoriteRepository.find(eq(article.getId()), eq(user.getId()))).thenReturn(Optional.of(new ArticleFavorite(article.getId(), user.getId()))); - given() - .header("Authorization", "Token " + token) - .when() - .delete("/articles/{slug}/favorite", article.getSlug()) - .prettyPeek() - .then() - .statusCode(200) - .body("article.id", equalTo(article.getId())); - verify(articleFavoriteRepository).remove(new ArticleFavorite(article.getId(), user.getId())); - } + @Test + public void should_unfavorite_an_article_success() throws Exception { + when(articleFavoriteRepository.find(eq(article.getId()), eq(user.getId()))) + .thenReturn(Optional.of(new ArticleFavorite(article.getId(), user.getId()))); + given() + .header("Authorization", "Token " + token) + .when() + .delete("/articles/{slug}/favorite", article.getSlug()) + .prettyPeek() + .then() + .statusCode(200) + .body("article.id", equalTo(article.getId())); + verify(articleFavoriteRepository).remove(new ArticleFavorite(article.getId(), user.getId())); + } } diff --git a/src/test/java/io/spring/api/ArticlesApiTest.java b/src/test/java/io/spring/api/ArticlesApiTest.java index ce5cbca..ee072e0 100644 --- a/src/test/java/io/spring/api/ArticlesApiTest.java +++ b/src/test/java/io/spring/api/ArticlesApiTest.java @@ -1,6 +1,7 @@ package io.spring.api; import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; +import static java.util.Arrays.asList; import static org.hamcrest.core.IsEqual.equalTo; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -11,12 +12,12 @@ import io.restassured.module.mockmvc.RestAssuredMockMvc; import io.spring.JacksonCustomizations; import io.spring.api.security.WebSecurityConfig; import io.spring.application.ArticleQueryService; +import io.spring.application.article.ArticleCommandService; import io.spring.application.data.ArticleData; import io.spring.application.data.ProfileData; import io.spring.core.article.Article; -import io.spring.core.article.ArticleRepository; -import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import org.joda.time.DateTime; @@ -33,10 +34,10 @@ import org.springframework.test.web.servlet.MockMvc; public class ArticlesApiTest extends TestWithCurrentUser { @Autowired private MockMvc mvc; - @MockBean private ArticleRepository articleRepository; - @MockBean private ArticleQueryService articleQueryService; + @MockBean private ArticleCommandService articleCommandService; + @Override @Before public void setUp() throws Exception { @@ -50,7 +51,7 @@ public class ArticlesApiTest extends TestWithCurrentUser { String slug = "how-to-train-your-dragon"; String description = "Ever wonder how?"; String body = "You have to believe"; - String[] tagList = {"reactjs", "angularjs", "dragons"}; + List tagList = asList("reactjs", "angularjs", "dragons"); Map param = prepareParam(title, description, body, tagList); ArticleData articleData = @@ -64,9 +65,12 @@ public class ArticlesApiTest extends TestWithCurrentUser { 0, new DateTime(), new DateTime(), - Arrays.asList(tagList), + tagList, new ProfileData("userid", user.getUsername(), user.getBio(), user.getImage(), false)); + when(articleCommandService.createArticle(any(), any())) + .thenReturn(new Article(title, description, body, tagList, user.getId())); + when(articleQueryService.findBySlug(eq(Article.toSlug(title)), any())) .thenReturn(Optional.empty()); @@ -87,7 +91,7 @@ public class ArticlesApiTest extends TestWithCurrentUser { .body("article.author.username", equalTo(user.getUsername())) .body("article.author.id", equalTo(null)); - verify(articleRepository).save(any()); + verify(articleCommandService).createArticle(any(), any()); } @Test @@ -96,7 +100,7 @@ public class ArticlesApiTest extends TestWithCurrentUser { String description = "Ever wonder how?"; String body = ""; String[] tagList = {"reactjs", "angularjs", "dragons"}; - Map param = prepareParam(title, description, body, tagList); + Map param = prepareParam(title, description, body, asList(tagList)); given() .contentType("application/json") @@ -117,7 +121,7 @@ public class ArticlesApiTest extends TestWithCurrentUser { String description = "Ever wonder how?"; String body = "You have to believe"; String[] tagList = {"reactjs", "angularjs", "dragons"}; - Map param = prepareParam(title, description, body, tagList); + Map param = prepareParam(title, description, body, asList(tagList)); ArticleData articleData = new ArticleData( @@ -130,7 +134,7 @@ public class ArticlesApiTest extends TestWithCurrentUser { 0, new DateTime(), new DateTime(), - Arrays.asList(tagList), + asList(tagList), new ProfileData("userid", user.getUsername(), user.getBio(), user.getImage(), false)); when(articleQueryService.findBySlug(eq(Article.toSlug(title)), any())) @@ -150,7 +154,7 @@ public class ArticlesApiTest extends TestWithCurrentUser { } private HashMap prepareParam( - final String title, final String description, final String body, final String[] tagList) { + final String title, final String description, final String body, final List tagList) { return new HashMap() { { put( diff --git a/src/test/java/io/spring/api/CommentsApiTest.java b/src/test/java/io/spring/api/CommentsApiTest.java index 6e4f4f0..2f4a81a 100644 --- a/src/test/java/io/spring/api/CommentsApiTest.java +++ b/src/test/java/io/spring/api/CommentsApiTest.java @@ -1,5 +1,12 @@ 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.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + import io.restassured.module.mockmvc.RestAssuredMockMvc; import io.spring.JacksonCustomizations; import io.spring.api.security.WebSecurityConfig; @@ -11,6 +18,10 @@ import io.spring.core.article.ArticleRepository; import io.spring.core.comment.Comment; import io.spring.core.comment.CommentRepository; import io.spring.core.user.User; +import java.util.Arrays; +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; @@ -19,132 +30,136 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; 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.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - @WebMvcTest(CommentsApi.class) @Import({WebSecurityConfig.class, JacksonCustomizations.class}) public class CommentsApiTest extends TestWithCurrentUser { - @MockBean - private ArticleRepository articleRepository; + @MockBean private ArticleRepository articleRepository; - @MockBean - private CommentRepository commentRepository; - @MockBean - private CommentQueryService commentQueryService; + @MockBean private CommentRepository commentRepository; + @MockBean private CommentQueryService commentQueryService; - private Article article; - private CommentData commentData; - private Comment comment; - @Autowired - private MockMvc mvc; + private Article article; + private CommentData commentData; + private Comment comment; + @Autowired private MockMvc mvc; - @Before - public void setUp() throws Exception { - RestAssuredMockMvc.mockMvc(mvc); - super.setUp(); - article = new Article("title", "desc", "body", new String[]{"test", "java"}, user.getId()); - when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article)); - comment = new Comment("comment", user.getId(), article.getId()); - commentData = new CommentData( + @Before + public void setUp() throws Exception { + RestAssuredMockMvc.mockMvc(mvc); + super.setUp(); + article = new Article("title", "desc", "body", Arrays.asList("test", "java"), user.getId()); + when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article)); + comment = new Comment("comment", user.getId(), article.getId()); + commentData = + new CommentData( comment.getId(), comment.getBody(), comment.getArticleId(), comment.getCreatedAt(), comment.getCreatedAt(), - new ProfileData(user.getId(), user.getUsername(), user.getBio(), user.getImage(), false)); - } + new ProfileData( + user.getId(), user.getUsername(), user.getBio(), user.getImage(), false)); + } - @Test - public void should_create_comment_success() throws Exception { - Map param = new HashMap() {{ - put("comment", new HashMap() {{ - put("body", "comment content"); - }}); - }}; + @Test + public void should_create_comment_success() throws Exception { + Map param = + new HashMap() { + { + put( + "comment", + new HashMap() { + { + put("body", "comment content"); + } + }); + } + }; - when(commentQueryService.findById(anyString(), eq(user))).thenReturn(Optional.of(commentData)); + when(commentQueryService.findById(anyString(), eq(user))).thenReturn(Optional.of(commentData)); - given() - .contentType("application/json") - .header("Authorization", "Token " + token) - .body(param) - .when() - .post("/articles/{slug}/comments", article.getSlug()) - .then() - .statusCode(201) - .body("comment.body", equalTo(commentData.getBody())); - } + given() + .contentType("application/json") + .header("Authorization", "Token " + token) + .body(param) + .when() + .post("/articles/{slug}/comments", article.getSlug()) + .then() + .statusCode(201) + .body("comment.body", equalTo(commentData.getBody())); + } - @Test - public void should_get_422_with_empty_body() throws Exception { - Map param = new HashMap() {{ - put("comment", new HashMap() {{ - put("body", ""); - }}); - }}; + @Test + public void should_get_422_with_empty_body() throws Exception { + Map param = + new HashMap() { + { + put( + "comment", + new HashMap() { + { + put("body", ""); + } + }); + } + }; - given() - .contentType("application/json") - .header("Authorization", "Token " + token) - .body(param) - .when() - .post("/articles/{slug}/comments", article.getSlug()) - .then() - .statusCode(422) - .body("errors.body[0]", equalTo("can't be empty")); + given() + .contentType("application/json") + .header("Authorization", "Token " + token) + .body(param) + .when() + .post("/articles/{slug}/comments", article.getSlug()) + .then() + .statusCode(422) + .body("errors.body[0]", equalTo("can't be empty")); + } - } + @Test + public void should_get_comments_of_article_success() throws Exception { + when(commentQueryService.findByArticleId(anyString(), eq(null))) + .thenReturn(Arrays.asList(commentData)); + RestAssuredMockMvc.when() + .get("/articles/{slug}/comments", article.getSlug()) + .prettyPeek() + .then() + .statusCode(200) + .body("comments[0].id", equalTo(commentData.getId())); + } - @Test - public void should_get_comments_of_article_success() throws Exception { - when(commentQueryService.findByArticleId(anyString(), eq(null))).thenReturn(Arrays.asList(commentData)); - RestAssuredMockMvc.when() - .get("/articles/{slug}/comments", article.getSlug()) - .prettyPeek() - .then() - .statusCode(200) - .body("comments[0].id", equalTo(commentData.getId())); - } + @Test + public void should_delete_comment_success() throws Exception { + when(commentRepository.findById(eq(article.getId()), eq(comment.getId()))) + .thenReturn(Optional.of(comment)); - @Test - public void should_delete_comment_success() throws Exception { - when(commentRepository.findById(eq(article.getId()), eq(comment.getId()))).thenReturn(Optional.of(comment)); + given() + .header("Authorization", "Token " + token) + .when() + .delete("/articles/{slug}/comments/{id}", article.getSlug(), comment.getId()) + .then() + .statusCode(204); + } - given() - .header("Authorization", "Token " + token) - .when() - .delete("/articles/{slug}/comments/{id}", article.getSlug(), comment.getId()) - .then() - .statusCode(204); - } + @Test + public void should_get_403_if_not_author_of_article_or_author_of_comment_when_delete_comment() + throws Exception { + User anotherUser = new User("other@example.com", "other", "123", "", ""); + when(userRepository.findByUsername(eq(anotherUser.getUsername()))) + .thenReturn(Optional.of(anotherUser)); + when(jwtService.getSubFromToken(any())).thenReturn(Optional.of(anotherUser.getId())); + when(userRepository.findById(eq(anotherUser.getId()))) + .thenReturn(Optional.ofNullable(anotherUser)); - @Test - public void should_get_403_if_not_author_of_article_or_author_of_comment_when_delete_comment() throws Exception { - User anotherUser = new User("other@example.com", "other", "123", "", ""); - when(userRepository.findByUsername(eq(anotherUser.getUsername()))).thenReturn(Optional.of(anotherUser)); - when(jwtService.getSubFromToken(any())).thenReturn(Optional.of(anotherUser.getId())); - when(userRepository.findById(eq(anotherUser.getId()))).thenReturn(Optional.ofNullable(anotherUser)); - - when(commentRepository.findById(eq(article.getId()), eq(comment.getId()))).thenReturn(Optional.of(comment)); - String token = jwtService.toToken(anotherUser); - when(userRepository.findById(eq(anotherUser.getId()))).thenReturn(Optional.of(anotherUser)); - given() - .header("Authorization", "Token " + token) - .when() - .delete("/articles/{slug}/comments/{id}", article.getSlug(), comment.getId()) - .then() - .statusCode(403); - - } + when(commentRepository.findById(eq(article.getId()), eq(comment.getId()))) + .thenReturn(Optional.of(comment)); + String token = jwtService.toToken(anotherUser); + when(userRepository.findById(eq(anotherUser.getId()))).thenReturn(Optional.of(anotherUser)); + given() + .header("Authorization", "Token " + token) + .when() + .delete("/articles/{slug}/comments/{id}", article.getSlug(), comment.getId()) + .then() + .statusCode(403); + } } diff --git a/src/test/java/io/spring/api/CurrentUserApiTest.java b/src/test/java/io/spring/api/CurrentUserApiTest.java index ed44369..9dba7b7 100644 --- a/src/test/java/io/spring/api/CurrentUserApiTest.java +++ b/src/test/java/io/spring/api/CurrentUserApiTest.java @@ -10,7 +10,9 @@ import io.restassured.module.mockmvc.RestAssuredMockMvc; import io.spring.JacksonCustomizations; import io.spring.api.security.WebSecurityConfig; import io.spring.application.UserQueryService; +import io.spring.application.user.UserService; import io.spring.core.user.User; +import io.spring.infrastructure.service.NaiveEncryptService; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -28,7 +30,8 @@ import org.springframework.test.web.servlet.MockMvc; WebSecurityConfig.class, JacksonCustomizations.class, UserService.class, - ValidationAutoConfiguration.class + ValidationAutoConfiguration.class, + NaiveEncryptService.class }) public class CurrentUserApiTest extends TestWithCurrentUser { diff --git a/src/test/java/io/spring/api/ListArticleApiTest.java b/src/test/java/io/spring/api/ListArticleApiTest.java index a0762de..a0a17eb 100644 --- a/src/test/java/io/spring/api/ListArticleApiTest.java +++ b/src/test/java/io/spring/api/ListArticleApiTest.java @@ -1,10 +1,17 @@ package io.spring.api; +import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; +import static io.spring.TestHelper.articleDataFixture; +import static java.util.Arrays.asList; +import static org.mockito.ArgumentMatchers.eq; +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.ArticleQueryService; import io.spring.application.Page; +import io.spring.application.article.ArticleCommandService; import io.spring.application.data.ArticleDataList; import io.spring.core.article.ArticleRepository; import org.junit.Before; @@ -15,64 +22,54 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.test.web.servlet.MockMvc; -import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; -import static io.spring.TestHelper.articleDataFixture; -import static java.util.Arrays.asList; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - @WebMvcTest(ArticlesApi.class) @Import({WebSecurityConfig.class, JacksonCustomizations.class}) public class ListArticleApiTest extends TestWithCurrentUser { - @MockBean - private ArticleRepository articleRepository; + @MockBean private ArticleRepository articleRepository; - @MockBean - private ArticleQueryService articleQueryService; + @MockBean private ArticleQueryService articleQueryService; - @Autowired - private MockMvc mvc; + @MockBean private ArticleCommandService articleCommandService; - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - RestAssuredMockMvc.mockMvc(mvc); - } + @Autowired private MockMvc mvc; - @Test - public void should_get_default_article_list() throws Exception { - ArticleDataList articleDataList = new ArticleDataList( + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + RestAssuredMockMvc.mockMvc(mvc); + } + + @Test + public void should_get_default_article_list() throws Exception { + ArticleDataList articleDataList = + new ArticleDataList( asList(articleDataFixture("1", user), articleDataFixture("2", user)), 2); - when(articleQueryService.findRecentArticles(eq(null), eq(null), eq(null), eq(new Page(0, 20)), eq(null))).thenReturn(articleDataList); - RestAssuredMockMvc.when() - .get("/articles") - .prettyPeek() - .then() - .statusCode(200); - } + when(articleQueryService.findRecentArticles( + eq(null), eq(null), eq(null), eq(new Page(0, 20)), eq(null))) + .thenReturn(articleDataList); + RestAssuredMockMvc.when().get("/articles").prettyPeek().then().statusCode(200); + } - @Test - public void should_get_feeds_401_without_login() throws Exception { - RestAssuredMockMvc.when() - .get("/articles/feed") - .prettyPeek() - .then() - .statusCode(401); - } + @Test + public void should_get_feeds_401_without_login() throws Exception { + RestAssuredMockMvc.when().get("/articles/feed").prettyPeek().then().statusCode(401); + } - @Test - public void should_get_feeds_success() throws Exception { - ArticleDataList articleDataList = new ArticleDataList( + @Test + public void should_get_feeds_success() throws Exception { + ArticleDataList articleDataList = + new ArticleDataList( asList(articleDataFixture("1", user), articleDataFixture("2", user)), 2); - when(articleQueryService.findUserFeed(eq(user), eq(new Page(0, 20)))).thenReturn(articleDataList); + when(articleQueryService.findUserFeed(eq(user), eq(new Page(0, 20)))) + .thenReturn(articleDataList); - given() - .header("Authorization", "Token " + token) - .when() - .get("/articles/feed") - .prettyPeek() - .then() - .statusCode(200); - } + given() + .header("Authorization", "Token " + token) + .when() + .get("/articles/feed") + .prettyPeek() + .then() + .statusCode(200); + } } diff --git a/src/test/java/io/spring/api/UsersApiTest.java b/src/test/java/io/spring/api/UsersApiTest.java index 934f86e..a4b6f15 100644 --- a/src/test/java/io/spring/api/UsersApiTest.java +++ b/src/test/java/io/spring/api/UsersApiTest.java @@ -12,6 +12,7 @@ import io.spring.JacksonCustomizations; import io.spring.api.security.WebSecurityConfig; import io.spring.application.UserQueryService; import io.spring.application.data.UserData; +import io.spring.application.user.UserService; import io.spring.core.service.JwtService; import io.spring.core.user.User; import io.spring.core.user.UserRepository; @@ -46,6 +47,9 @@ public class UsersApiTest { @MockBean private JwtService jwtService; @MockBean private UserReadService userReadService; + + @MockBean private UserService userService; + private String defaultAvatar; @Before @@ -64,6 +68,8 @@ public class UsersApiTest { UserData userData = new UserData(user.getId(), email, username, "", defaultAvatar); when(userReadService.findById(any())).thenReturn(userData); + when(userService.createUser(any())).thenReturn(user); + when(userRepository.findByUsername(eq(username))).thenReturn(Optional.empty()); when(userRepository.findByEmail(eq(email))).thenReturn(Optional.empty()); @@ -82,7 +88,7 @@ public class UsersApiTest { .body("user.image", equalTo(defaultAvatar)) .body("user.token", equalTo("123")); - verify(userRepository).save(any()); + verify(userService).createUser(any()); } @Test diff --git a/src/test/java/io/spring/application/article/ArticleQueryServiceTest.java b/src/test/java/io/spring/application/article/ArticleQueryServiceTest.java index d57269e..588b9b4 100644 --- a/src/test/java/io/spring/application/article/ArticleQueryServiceTest.java +++ b/src/test/java/io/spring/application/article/ArticleQueryServiceTest.java @@ -1,5 +1,10 @@ package io.spring.application.article; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + import io.spring.application.ArticleQueryService; import io.spring.application.Page; import io.spring.application.data.ArticleData; @@ -14,6 +19,8 @@ import io.spring.core.user.UserRepository; import io.spring.infrastructure.repository.MyBatisArticleFavoriteRepository; import io.spring.infrastructure.repository.MyBatisArticleRepository; import io.spring.infrastructure.repository.MyBatisUserRepository; +import java.util.Arrays; +import java.util.Optional; import org.joda.time.DateTime; import org.junit.Before; import org.junit.Test; @@ -23,161 +30,170 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; import org.springframework.test.context.junit4.SpringRunner; -import java.util.Optional; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - @RunWith(SpringRunner.class) @MybatisTest @Import({ - ArticleQueryService.class, - MyBatisUserRepository.class, - MyBatisArticleRepository.class, - MyBatisArticleFavoriteRepository.class}) + ArticleQueryService.class, + MyBatisUserRepository.class, + MyBatisArticleRepository.class, + MyBatisArticleFavoriteRepository.class +}) public class ArticleQueryServiceTest { - @Autowired - private ArticleQueryService queryService; + @Autowired private ArticleQueryService queryService; - @Autowired - private ArticleRepository articleRepository; + @Autowired private ArticleRepository articleRepository; - @Autowired - private UserRepository userRepository; + @Autowired private UserRepository userRepository; - @Autowired - private ArticleFavoriteRepository articleFavoriteRepository; + @Autowired private ArticleFavoriteRepository articleFavoriteRepository; - private User user; - private Article article; + private User user; + private Article article; - @Before - public void setUp() { - user = new User("aisensiy@gmail.com", "aisensiy", "123", "", ""); - userRepository.save(user); - article = new Article("test", "desc", "body", new String[]{"java", "spring"}, user.getId(), new DateTime()); - articleRepository.save(article); - } + @Before + public void setUp() { + user = new User("aisensiy@gmail.com", "aisensiy", "123", "", ""); + userRepository.save(user); + article = + new Article( + "test", "desc", "body", Arrays.asList("java", "spring"), user.getId(), new DateTime()); + articleRepository.save(article); + } - @Test - public void should_fetch_article_success() { - Optional optional = queryService.findById(article.getId(), user); - assertTrue(optional.isPresent()); + @Test + public void should_fetch_article_success() { + Optional optional = queryService.findById(article.getId(), user); + assertTrue(optional.isPresent()); - ArticleData fetched = optional.get(); - assertEquals(fetched.getFavoritesCount(),0); - assertFalse(fetched.isFavorited()); - assertNotNull(fetched.getCreatedAt()); - assertNotNull(fetched.getUpdatedAt()); - assertTrue(fetched.getTagList().contains("java")); - } + ArticleData fetched = optional.get(); + assertEquals(fetched.getFavoritesCount(), 0); + assertFalse(fetched.isFavorited()); + assertNotNull(fetched.getCreatedAt()); + assertNotNull(fetched.getUpdatedAt()); + assertTrue(fetched.getTagList().contains("java")); + } - @Test - public void should_get_article_with_right_favorite_and_favorite_count() { - User anotherUser = new User("other@test.com", "other", "123", "", ""); - userRepository.save(anotherUser); - articleFavoriteRepository.save(new ArticleFavorite(article.getId(), anotherUser.getId())); + @Test + public void should_get_article_with_right_favorite_and_favorite_count() { + User anotherUser = new User("other@test.com", "other", "123", "", ""); + userRepository.save(anotherUser); + articleFavoriteRepository.save(new ArticleFavorite(article.getId(), anotherUser.getId())); - Optional optional = queryService.findById(article.getId(), anotherUser); - assertTrue(optional.isPresent()); + Optional optional = queryService.findById(article.getId(), anotherUser); + assertTrue(optional.isPresent()); - ArticleData articleData = optional.get(); - assertEquals(articleData.getFavoritesCount(), 1); - assertTrue(articleData.isFavorited()); - } + ArticleData articleData = optional.get(); + assertEquals(articleData.getFavoritesCount(), 1); + assertTrue(articleData.isFavorited()); + } - @Test - public void should_get_default_article_list() { - Article anotherArticle = new Article("new article", "desc", "body", new String[]{"test"}, user.getId(), new DateTime().minusHours(1)); - articleRepository.save(anotherArticle); + @Test + public void should_get_default_article_list() { + Article anotherArticle = + new Article( + "new article", + "desc", + "body", + Arrays.asList("test"), + user.getId(), + new DateTime().minusHours(1)); + articleRepository.save(anotherArticle); - ArticleDataList recentArticles = queryService.findRecentArticles(null, null, null, new Page(), user); - assertEquals(recentArticles.getCount(), 2); - assertEquals(recentArticles.getArticleDatas().size(), 2); - assertEquals(recentArticles.getArticleDatas().get(0).getId(), article.getId()); + ArticleDataList recentArticles = + queryService.findRecentArticles(null, null, null, new Page(), user); + assertEquals(recentArticles.getCount(), 2); + assertEquals(recentArticles.getArticleDatas().size(), 2); + assertEquals(recentArticles.getArticleDatas().get(0).getId(), article.getId()); - ArticleDataList nodata = queryService.findRecentArticles(null, null, null, new Page(2, 10), user); - assertEquals(nodata.getCount(),2); - assertEquals(nodata.getArticleDatas().size(), 0); - } + ArticleDataList nodata = + queryService.findRecentArticles(null, null, null, new Page(2, 10), user); + assertEquals(nodata.getCount(), 2); + assertEquals(nodata.getArticleDatas().size(), 0); + } - @Test - public void should_query_article_by_author() { - User anotherUser = new User("other@email.com", "other", "123", "", ""); - userRepository.save(anotherUser); + @Test + public void should_query_article_by_author() { + User anotherUser = new User("other@email.com", "other", "123", "", ""); + userRepository.save(anotherUser); - Article anotherArticle = new Article("new article", "desc", "body", new String[]{"test"}, anotherUser.getId()); - articleRepository.save(anotherArticle); + Article anotherArticle = + new Article("new article", "desc", "body", Arrays.asList("test"), anotherUser.getId()); + articleRepository.save(anotherArticle); - ArticleDataList recentArticles = queryService.findRecentArticles(null, user.getUsername(), null, new Page(), user); - assertEquals(recentArticles.getArticleDatas().size(), 1); - assertEquals(recentArticles.getCount(), 1); - } + ArticleDataList recentArticles = + queryService.findRecentArticles(null, user.getUsername(), null, new Page(), user); + assertEquals(recentArticles.getArticleDatas().size(), 1); + assertEquals(recentArticles.getCount(), 1); + } - @Test - public void should_query_article_by_favorite() { - User anotherUser = new User("other@email.com", "other", "123", "", ""); - userRepository.save(anotherUser); + @Test + public void should_query_article_by_favorite() { + User anotherUser = new User("other@email.com", "other", "123", "", ""); + userRepository.save(anotherUser); - Article anotherArticle = new Article("new article", "desc", "body", new String[]{"test"}, anotherUser.getId()); - articleRepository.save(anotherArticle); + Article anotherArticle = + new Article("new article", "desc", "body", Arrays.asList("test"), anotherUser.getId()); + articleRepository.save(anotherArticle); - ArticleFavorite articleFavorite = new ArticleFavorite(article.getId(), anotherUser.getId()); - articleFavoriteRepository.save(articleFavorite); + ArticleFavorite articleFavorite = new ArticleFavorite(article.getId(), anotherUser.getId()); + articleFavoriteRepository.save(articleFavorite); - ArticleDataList recentArticles = queryService.findRecentArticles(null, null, anotherUser.getUsername(), new Page(), anotherUser); - assertEquals(recentArticles.getArticleDatas().size(), 1); - assertEquals(recentArticles.getCount(), 1); - ArticleData articleData = recentArticles.getArticleDatas().get(0); - assertEquals(articleData.getId(), article.getId()); - assertEquals(articleData.getFavoritesCount(),1); - assertTrue(articleData.isFavorited()); - } + ArticleDataList recentArticles = + queryService.findRecentArticles( + null, null, anotherUser.getUsername(), new Page(), anotherUser); + assertEquals(recentArticles.getArticleDatas().size(), 1); + assertEquals(recentArticles.getCount(), 1); + ArticleData articleData = recentArticles.getArticleDatas().get(0); + assertEquals(articleData.getId(), article.getId()); + assertEquals(articleData.getFavoritesCount(), 1); + assertTrue(articleData.isFavorited()); + } - @Test - public void should_query_article_by_tag() { - Article anotherArticle = new Article("new article", "desc", "body", new String[]{"test"}, user.getId()); - articleRepository.save(anotherArticle); + @Test + public void should_query_article_by_tag() { + Article anotherArticle = + new Article("new article", "desc", "body", Arrays.asList("test"), user.getId()); + articleRepository.save(anotherArticle); - ArticleDataList recentArticles = queryService.findRecentArticles("spring", null, null, new Page(), user); - assertEquals(recentArticles.getArticleDatas().size(), 1); - assertEquals(recentArticles.getCount(), 1); - assertEquals(recentArticles.getArticleDatas().get(0).getId(), article.getId()); + ArticleDataList recentArticles = + queryService.findRecentArticles("spring", null, null, new Page(), user); + assertEquals(recentArticles.getArticleDatas().size(), 1); + assertEquals(recentArticles.getCount(), 1); + assertEquals(recentArticles.getArticleDatas().get(0).getId(), article.getId()); - ArticleDataList notag = queryService.findRecentArticles("notag", null, null, new Page(), user); - assertEquals(notag.getCount(), 0); - } + ArticleDataList notag = queryService.findRecentArticles("notag", null, null, new Page(), user); + assertEquals(notag.getCount(), 0); + } - @Test - public void should_show_following_if_user_followed_author() { - User anotherUser = new User("other@email.com", "other", "123", "", ""); - userRepository.save(anotherUser); + @Test + public void should_show_following_if_user_followed_author() { + User anotherUser = new User("other@email.com", "other", "123", "", ""); + userRepository.save(anotherUser); - FollowRelation followRelation = new FollowRelation(anotherUser.getId(), user.getId()); - userRepository.saveRelation(followRelation); + FollowRelation followRelation = new FollowRelation(anotherUser.getId(), user.getId()); + userRepository.saveRelation(followRelation); - ArticleDataList recentArticles = queryService.findRecentArticles(null, null, null, new Page(), anotherUser); - assertEquals(recentArticles.getCount(), 1); - ArticleData articleData = recentArticles.getArticleDatas().get(0); - assertTrue(articleData.getProfileData().isFollowing()); - } + ArticleDataList recentArticles = + queryService.findRecentArticles(null, null, null, new Page(), anotherUser); + assertEquals(recentArticles.getCount(), 1); + ArticleData articleData = recentArticles.getArticleDatas().get(0); + assertTrue(articleData.getProfileData().isFollowing()); + } - @Test - public void should_get_user_feed() { - User anotherUser = new User("other@email.com", "other", "123", "", ""); - userRepository.save(anotherUser); + @Test + public void should_get_user_feed() { + User anotherUser = new User("other@email.com", "other", "123", "", ""); + userRepository.save(anotherUser); - FollowRelation followRelation = new FollowRelation(anotherUser.getId(), user.getId()); - userRepository.saveRelation(followRelation); + FollowRelation followRelation = new FollowRelation(anotherUser.getId(), user.getId()); + userRepository.saveRelation(followRelation); - ArticleDataList userFeed = queryService.findUserFeed(user, new Page()); - assertEquals(userFeed.getCount(), 0); + ArticleDataList userFeed = queryService.findUserFeed(user, new Page()); + assertEquals(userFeed.getCount(), 0); - ArticleDataList anotherUserFeed = queryService.findUserFeed(anotherUser, new Page()); - assertEquals(anotherUserFeed.getCount(), 1); - ArticleData articleData = anotherUserFeed.getArticleDatas().get(0); - assertTrue(articleData.getProfileData().isFollowing()); - } -} \ No newline at end of file + ArticleDataList anotherUserFeed = queryService.findUserFeed(anotherUser, new Page()); + assertEquals(anotherUserFeed.getCount(), 1); + ArticleData articleData = anotherUserFeed.getArticleDatas().get(0); + assertTrue(articleData.getProfileData().isFollowing()); + } +} diff --git a/src/test/java/io/spring/application/comment/CommentQueryServiceTest.java b/src/test/java/io/spring/application/comment/CommentQueryServiceTest.java index bdeabe7..b84be61 100644 --- a/src/test/java/io/spring/application/comment/CommentQueryServiceTest.java +++ b/src/test/java/io/spring/application/comment/CommentQueryServiceTest.java @@ -1,5 +1,8 @@ package io.spring.application.comment; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import io.spring.application.CommentQueryService; import io.spring.application.data.CommentData; import io.spring.core.article.Article; @@ -12,6 +15,9 @@ import io.spring.core.user.UserRepository; import io.spring.infrastructure.repository.MyBatisArticleRepository; import io.spring.infrastructure.repository.MyBatisCommentRepository; import io.spring.infrastructure.repository.MyBatisUserRepository; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -20,63 +26,57 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; import org.springframework.test.context.junit4.SpringRunner; -import java.util.List; -import java.util.Optional; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - @MybatisTest @RunWith(SpringRunner.class) -@Import({MyBatisCommentRepository.class, MyBatisUserRepository.class, CommentQueryService.class, MyBatisArticleRepository.class}) +@Import({ + MyBatisCommentRepository.class, + MyBatisUserRepository.class, + CommentQueryService.class, + MyBatisArticleRepository.class +}) public class CommentQueryServiceTest { - @Autowired - private CommentRepository commentRepository; + @Autowired private CommentRepository commentRepository; - @Autowired - private UserRepository userRepository; + @Autowired private UserRepository userRepository; - @Autowired - private CommentQueryService commentQueryService; + @Autowired private CommentQueryService commentQueryService; - @Autowired - private ArticleRepository articleRepository; + @Autowired private ArticleRepository articleRepository; - private User user; + private User user; - @Before - public void setUp() { - user = new User("aisensiy@test.com", "aisensiy", "123", "", ""); - userRepository.save(user); - } + @Before + public void setUp() { + user = new User("aisensiy@test.com", "aisensiy", "123", "", ""); + userRepository.save(user); + } - @Test - public void should_read_comment_success() { - Comment comment = new Comment("content", user.getId(), "123"); - commentRepository.save(comment); + @Test + public void should_read_comment_success() { + Comment comment = new Comment("content", user.getId(), "123"); + commentRepository.save(comment); - Optional optional = commentQueryService.findById(comment.getId(), user); - assertTrue(optional.isPresent()); - CommentData commentData = optional.get(); - assertEquals(commentData.getProfileData().getUsername(), user.getUsername()); - } + Optional optional = commentQueryService.findById(comment.getId(), user); + assertTrue(optional.isPresent()); + CommentData commentData = optional.get(); + assertEquals(commentData.getProfileData().getUsername(), user.getUsername()); + } - @Test - public void should_read_comments_of_article() { - Article article = new Article("title", "desc", "body", new String[]{"java"}, user.getId()); - articleRepository.save(article); + @Test + public void should_read_comments_of_article() { + Article article = new Article("title", "desc", "body", Arrays.asList("java"), user.getId()); + articleRepository.save(article); - User user2 = new User("user2@email.com", "user2", "123", "", ""); - userRepository.save(user2); - userRepository.saveRelation(new FollowRelation(user.getId(), user2.getId())); + User user2 = new User("user2@email.com", "user2", "123", "", ""); + userRepository.save(user2); + userRepository.saveRelation(new FollowRelation(user.getId(), user2.getId())); - Comment comment1 = new Comment("content1", user.getId(), article.getId()); - commentRepository.save(comment1); - Comment comment2 = new Comment("content2", user2.getId(), article.getId()); - commentRepository.save(comment2); + Comment comment1 = new Comment("content1", user.getId(), article.getId()); + commentRepository.save(comment1); + Comment comment2 = new Comment("content2", user2.getId(), article.getId()); + commentRepository.save(comment2); - List comments = commentQueryService.findByArticleId(article.getId(), user); - assertEquals(comments.size(), 2); - - } -} \ No newline at end of file + List comments = commentQueryService.findByArticleId(article.getId(), user); + assertEquals(comments.size(), 2); + } +} diff --git a/src/test/java/io/spring/application/tag/TagsQueryServiceTest.java b/src/test/java/io/spring/application/tag/TagsQueryServiceTest.java index d8a6397..b379313 100644 --- a/src/test/java/io/spring/application/tag/TagsQueryServiceTest.java +++ b/src/test/java/io/spring/application/tag/TagsQueryServiceTest.java @@ -1,9 +1,12 @@ package io.spring.application.tag; +import static org.junit.Assert.assertTrue; + import io.spring.application.TagsQueryService; import io.spring.core.article.Article; import io.spring.core.article.ArticleRepository; import io.spring.infrastructure.repository.MyBatisArticleRepository; +import java.util.Arrays; import org.junit.Test; import org.junit.runner.RunWith; import org.mybatis.spring.boot.test.autoconfigure.MybatisTest; @@ -11,21 +14,17 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; import org.springframework.test.context.junit4.SpringRunner; -import static org.junit.Assert.assertTrue; - @RunWith(SpringRunner.class) @MybatisTest @Import({TagsQueryService.class, MyBatisArticleRepository.class}) public class TagsQueryServiceTest { - @Autowired - private TagsQueryService tagsQueryService; + @Autowired private TagsQueryService tagsQueryService; - @Autowired - private ArticleRepository articleRepository; + @Autowired private ArticleRepository articleRepository; - @Test - public void should_get_all_tags() { - articleRepository.save(new Article("test", "test", "test", new String[]{"java"}, "123")); - assertTrue(tagsQueryService.allTags().contains("java")); - } -} \ No newline at end of file + @Test + public void should_get_all_tags() { + articleRepository.save(new Article("test", "test", "test", Arrays.asList("java"), "123")); + assertTrue(tagsQueryService.allTags().contains("java")); + } +} diff --git a/src/test/java/io/spring/core/article/ArticleTest.java b/src/test/java/io/spring/core/article/ArticleTest.java index 186b480..7046fa0 100644 --- a/src/test/java/io/spring/core/article/ArticleTest.java +++ b/src/test/java/io/spring/core/article/ArticleTest.java @@ -1,39 +1,40 @@ package io.spring.core.article; -import org.junit.Test; - import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import java.util.Arrays; +import org.junit.Test; + public class ArticleTest { - @Test - public void should_get_right_slug() { - Article article = new Article("a new title", "desc", "body", new String[]{"java"}, "123"); - assertThat(article.getSlug(), is("a-new-title")); - } + @Test + public void should_get_right_slug() { + Article article = new Article("a new title", "desc", "body", Arrays.asList("java"), "123"); + assertThat(article.getSlug(), is("a-new-title")); + } - @Test - public void should_get_right_slug_with_number_in_title() { - Article article = new Article("a new title 2", "desc", "body", new String[]{"java"}, "123"); - assertThat(article.getSlug(), is("a-new-title-2")); - } + @Test + public void should_get_right_slug_with_number_in_title() { + Article article = new Article("a new title 2", "desc", "body", Arrays.asList("java"), "123"); + assertThat(article.getSlug(), is("a-new-title-2")); + } - @Test - public void should_get_lower_case_slug() { - Article article = new Article("A NEW TITLE", "desc", "body", new String[]{"java"}, "123"); - assertThat(article.getSlug(), is("a-new-title")); - } + @Test + public void should_get_lower_case_slug() { + Article article = new Article("A NEW TITLE", "desc", "body", Arrays.asList("java"), "123"); + assertThat(article.getSlug(), is("a-new-title")); + } - @Test - public void should_handle_other_language() { - Article article = new Article("中文:标题", "desc", "body", new String[]{"java"}, "123"); - assertThat(article.getSlug(), is("中文-标题")); - } + @Test + public void should_handle_other_language() { + Article article = new Article("中文:标题", "desc", "body", Arrays.asList("java"), "123"); + assertThat(article.getSlug(), is("中文-标题")); + } - @Test - public void should_handle_commas() { - Article article = new Article("what?the.hell,w", "desc", "body", new String[]{"java"}, "123"); - assertThat(article.getSlug(), is("what-the-hell-w")); - } -} \ No newline at end of file + @Test + public void should_handle_commas() { + Article article = new Article("what?the.hell,w", "desc", "body", Arrays.asList("java"), "123"); + assertThat(article.getSlug(), is("what-the-hell-w")); + } +} diff --git a/src/test/java/io/spring/infrastructure/article/ArticleRepositoryTransactionTest.java b/src/test/java/io/spring/infrastructure/article/ArticleRepositoryTransactionTest.java index 4e4a67b..4675773 100644 --- a/src/test/java/io/spring/infrastructure/article/ArticleRepositoryTransactionTest.java +++ b/src/test/java/io/spring/infrastructure/article/ArticleRepositoryTransactionTest.java @@ -1,10 +1,13 @@ package io.spring.infrastructure.article; +import static org.junit.Assert.assertNull; + import io.spring.core.article.Article; import io.spring.core.article.ArticleRepository; import io.spring.core.user.User; import io.spring.core.user.UserRepository; import io.spring.infrastructure.mybatis.mapper.ArticleMapper; +import java.util.Arrays; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -12,35 +15,29 @@ import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabas import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; - @RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureTestDatabase public class ArticleRepositoryTransactionTest { - @Autowired - private ArticleRepository articleRepository; + @Autowired private ArticleRepository articleRepository; - @Autowired - private UserRepository userRepository; + @Autowired private UserRepository userRepository; - @Autowired - private ArticleMapper articleMapper; + @Autowired private ArticleMapper articleMapper; - - @Test - public void transactional_test() { - User user = new User("aisensiy@gmail.com", "aisensiy", "123", "bio", "default"); - userRepository.save(user); - Article article = new Article("test", "desc", "body", new String[]{"java", "spring"}, user.getId()); - articleRepository.save(article); - Article anotherArticle = new Article("test", "desc", "body", new String[]{"java", "spring", "other"}, user.getId()); - try { - articleRepository.save(anotherArticle); - } catch (Exception e) { - assertNull(articleMapper.findTag("other")); - } + @Test + public void transactional_test() { + User user = new User("aisensiy@gmail.com", "aisensiy", "123", "bio", "default"); + userRepository.save(user); + Article article = + new Article("test", "desc", "body", Arrays.asList("java", "spring"), user.getId()); + articleRepository.save(article); + Article anotherArticle = + new Article("test", "desc", "body", Arrays.asList("java", "spring", "other"), user.getId()); + try { + articleRepository.save(anotherArticle); + } catch (Exception e) { + assertNull(articleMapper.findTag("other")); } - + } } diff --git a/src/test/java/io/spring/infrastructure/article/MyBatisArticleRepositoryTest.java b/src/test/java/io/spring/infrastructure/article/MyBatisArticleRepositoryTest.java index 2eb6cde..12040d6 100644 --- a/src/test/java/io/spring/infrastructure/article/MyBatisArticleRepositoryTest.java +++ b/src/test/java/io/spring/infrastructure/article/MyBatisArticleRepositoryTest.java @@ -1,5 +1,10 @@ package io.spring.infrastructure.article; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + import io.spring.core.article.Article; import io.spring.core.article.ArticleRepository; import io.spring.core.article.Tag; @@ -7,6 +12,8 @@ import io.spring.core.user.User; import io.spring.core.user.UserRepository; import io.spring.infrastructure.repository.MyBatisArticleRepository; import io.spring.infrastructure.repository.MyBatisUserRepository; +import java.util.Arrays; +import java.util.Optional; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -15,63 +22,53 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; import org.springframework.test.context.junit4.SpringRunner; -import java.util.Optional; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; - @MybatisTest @RunWith(SpringRunner.class) @Import({MyBatisArticleRepository.class, MyBatisUserRepository.class}) public class MyBatisArticleRepositoryTest { - @Autowired - private ArticleRepository articleRepository; + @Autowired private ArticleRepository articleRepository; - @Autowired - private UserRepository userRepository; + @Autowired private UserRepository userRepository; - private Article article; + private Article article; + @Before + public void setUp() { + User user = new User("aisensiy@gmail.com", "aisensiy", "123", "bio", "default"); + userRepository.save(user); + article = new Article("test", "desc", "body", Arrays.asList("java", "spring"), user.getId()); + } - @Before - public void setUp() { - User user = new User("aisensiy@gmail.com", "aisensiy", "123", "bio", "default"); - userRepository.save(user); - article = new Article("test", "desc", "body", new String[]{"java", "spring"}, user.getId()); - } + @Test + public void should_create_and_fetch_article_success() { + articleRepository.save(article); + Optional
optional = articleRepository.findById(article.getId()); + assertTrue(optional.isPresent()); + assertEquals(optional.get(), article); + assertTrue(optional.get().getTags().contains(new Tag("java"))); + assertTrue(optional.get().getTags().contains(new Tag("spring"))); + } - @Test - public void should_create_and_fetch_article_success() { - articleRepository.save(article); - Optional
optional = articleRepository.findById(article.getId()); - assertTrue(optional.isPresent()); - assertEquals(optional.get(), article); - assertTrue(optional.get().getTags().contains(new Tag("java"))); - assertTrue(optional.get().getTags().contains(new Tag("spring"))); - } + @Test + public void should_update_and_fetch_article_success() { + articleRepository.save(article); - @Test - public void should_update_and_fetch_article_success() { - articleRepository.save(article); + String newTitle = "new test 2"; + article.update(newTitle, "", ""); + articleRepository.save(article); + System.out.println(article.getSlug()); + Optional
optional = articleRepository.findBySlug(article.getSlug()); + assertTrue(optional.isPresent()); + Article fetched = optional.get(); + assertEquals(fetched.getTitle(), newTitle); + assertNotEquals(fetched.getBody(), ""); + } - String newTitle = "new test 2"; - article.update(newTitle, "", ""); - articleRepository.save(article); - System.out.println(article.getSlug()); - Optional
optional = articleRepository.findBySlug(article.getSlug()); - assertTrue(optional.isPresent()); - Article fetched = optional.get(); - assertEquals(fetched.getTitle(), newTitle); - assertNotEquals(fetched.getBody(), ""); - } + @Test + public void should_delete_article() { + articleRepository.save(article); - @Test - public void should_delete_article() { - articleRepository.save(article); - - articleRepository.remove(article); - assertFalse(articleRepository.findById(article.getId()).isPresent()); - } -} \ No newline at end of file + articleRepository.remove(article); + assertFalse(articleRepository.findById(article.getId()).isPresent()); + } +}