refactor: move logic to application level

This commit is contained in:
xushanchuan 2021-03-16 17:16:20 +08:00
parent 49cac729f1
commit 36e33e7730
No known key found for this signature in database
GPG Key ID: 44D23C44E00838D6
40 changed files with 1310 additions and 1114 deletions

View File

@ -0,0 +1,7 @@
package io.spring;
public class Util {
public static boolean isEmpty(String value) {
return value == null || value.isEmpty();
}
}

View File

@ -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 ArticleCommandService articleCommandService;
@Autowired
public ArticleApi(ArticleQueryService articleQueryService, ArticleRepository articleRepository) {
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)
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,
public ResponseEntity<?> updateArticle(
@PathVariable("slug") String slug,
@AuthenticationPrincipal User user,
@Valid @RequestBody UpdateArticleParam updateArticleParam) {
return articleRepository.findBySlug(slug).map(article -> {
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 -> {
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);
})
.orElseThrow(ResourceNotFoundException::new);
}
private Map<String, Object> articleResponse(ArticleData articleData) {
return new HashMap<String, Object>() {{
return new HashMap<String, Object>() {
{
put("article", articleData);
}};
}
};
}
}
@Getter
@NoArgsConstructor
@JsonRootName("article")
class UpdateArticleParam {
private String title = "";
private String body = "";
private String description = "";
}

View File

@ -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,8 +18,6 @@ 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 {
@ -27,7 +26,8 @@ public class ArticleFavoriteApi {
private ArticleQueryService articleQueryService;
@Autowired
public ArticleFavoriteApi(ArticleFavoriteRepository articleFavoriteRepository,
public ArticleFavoriteApi(
ArticleFavoriteRepository articleFavoriteRepository,
ArticleRepository articleRepository,
ArticleQueryService articleQueryService) {
this.articleFavoriteRepository = articleFavoriteRepository;
@ -36,32 +36,36 @@ public class ArticleFavoriteApi {
}
@PostMapping
public ResponseEntity favoriteArticle(@PathVariable("slug") String slug,
@AuthenticationPrincipal User user) {
Article article = getArticle(slug);
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 -> {
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<HashMap<String, Object>> responseArticleData(final ArticleData articleData) {
return ResponseEntity.ok(new HashMap<String, Object>() {{
private ResponseEntity<HashMap<String, Object>> responseArticleData(
final ArticleData articleData) {
return ResponseEntity.ok(
new HashMap<String, Object>() {
{
put("article", articleData);
}});
}
private Article getArticle(String slug) {
return articleRepository.findBySlug(slug).map(article -> article)
.orElseThrow(ResourceNotFoundException::new);
});
}
}

View File

@ -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<String, Object>() {
{
@ -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<? extends Payload>[] payload() default {};
}
class DuplicatedArticleValidator
implements ConstraintValidator<DuplicatedArticleConstraint, String> {
@Autowired private ArticleQueryService articleQueryService;
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return !articleQueryService.findBySlug(Article.toSlug(value), null).isPresent();
}
}

View File

@ -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<CommentData> comments = commentQueryService.findByArticleId(article.getId(), user);
return ResponseEntity.ok(
new HashMap<String, Object>() {
@ -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<String, Object> commentResponse(CommentData commentData) {
return new HashMap<String, Object>() {
{

View File

@ -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<UpdateUserConstraint, UpdateUserCommand> {
@Autowired private UserRepository userRepository;
@Override
public boolean isValid(UpdateUserCommand value, ConstraintValidatorContext context) {
String inputEmail = value.getParam().getEmail();
String inputUsername = value.getParam().getUsername();
final User targetUser = value.getTargetUser();
boolean isEmailValid =
userRepository.findByEmail(inputEmail).map(user -> user.equals(targetUser)).orElse(true);
boolean isUsernameValid =
userRepository
.findByUsername(inputUsername)
.map(user -> user.equals(targetUser))
.orElse(true);
if (isEmailValid && isUsernameValid) {
return true;
} else {
context.disableDefaultConstraintViolation();
if (!isEmailValid) {
context
.buildConstraintViolationWithTemplate("email already exist")
.addPropertyNode("email")
.addConstraintViolation();
}
if (!isUsernameValid) {
context
.buildConstraintViolationWithTemplate("username already exist")
.addPropertyNode("username")
.addConstraintViolation();
}
return false;
}
}
}
@Getter
@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 = "";
}

View File

@ -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<? extends Payload>[] payload() default {};
}
class DuplicatedEmailValidator implements ConstraintValidator<DuplicatedEmailConstraint, String> {
@Autowired private UserRepository userRepository;
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return (value == null || value.isEmpty()) || !userRepository.findByEmail(value).isPresent();
}
}
@Constraint(validatedBy = DuplicatedUsernameValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@interface DuplicatedUsernameConstraint {
String message() default "duplicated username";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
class DuplicatedUsernameValidator
implements ConstraintValidator<DuplicatedUsernameConstraint, String> {
@Autowired private UserRepository userRepository;
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return (value == null || value.isEmpty()) || !userRepository.findByUsername(value).isPresent();
}
}
@Getter
@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;
}

View File

@ -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");
}
}

View File

@ -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"));

View File

@ -2,21 +2,21 @@ 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;
@Autowired
public ProfileQueryService(UserReadService userReadService, UserRelationshipQueryService userRelationshipQueryService) {
public ProfileQueryService(
UserReadService userReadService, UserRelationshipQueryService userRelationshipQueryService) {
this.userReadService = userReadService;
this.userRelationshipQueryService = userRelationshipQueryService;
}
@ -26,12 +26,15 @@ public class ProfileQueryService {
if (userData == null) {
return Optional.empty();
} else {
ProfileData profileData = new ProfileData(
ProfileData profileData =
new ProfileData(
userData.getId(),
userData.getUsername(),
userData.getBio(),
userData.getImage(),
userRelationshipQueryService.isUserFollowing(currentUser.getId(), userData.getId()));
currentUser != null
&& userRelationshipQueryService.isUserFollowing(
currentUser.getId(), userData.getId()));
return Optional.of(profileData);
}
}

View File

@ -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;
}
}

View File

@ -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<? extends Payload>[] payload() default {};
}

View File

@ -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<DuplicatedArticleConstraint, String> {
@Autowired private ArticleQueryService articleQueryService;
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return !articleQueryService.findBySlug(Article.toSlug(value), null).isPresent();
}
}

View File

@ -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<String> tagList;
}

View File

@ -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 = "";
}

View File

@ -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<? extends Payload>[] payload() default {};
}

View File

@ -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<DuplicatedEmailConstraint, String> {
@Autowired private UserRepository userRepository;
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return (value == null || value.isEmpty()) || !userRepository.findByEmail(value).isPresent();
}
}

View File

@ -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<? extends Payload>[] payload() default {};
}

View File

@ -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<DuplicatedUsernameConstraint, String> {
@Autowired private UserRepository userRepository;
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return (value == null || value.isEmpty()) || !userRepository.findByUsername(value).isPresent();
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 = "";
}

View File

@ -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<UpdateUserConstraint, UpdateUserCommand> {
@Autowired private UserRepository userRepository;
@Override
public boolean isValid(UpdateUserCommand value, ConstraintValidatorContext context) {
String inputEmail = value.getParam().getEmail();
String inputUsername = value.getParam().getUsername();
final User targetUser = value.getTargetUser();
boolean isEmailValid =
userRepository.findByEmail(inputEmail).map(user -> user.equals(targetUser)).orElse(true);
boolean isUsernameValid =
userRepository
.findByUsername(inputUsername)
.map(user -> user.equals(targetUser))
.orElse(true);
if (isEmailValid && isUsernameValid) {
return true;
} else {
context.disableDefaultConstraintViolation();
if (!isEmailValid) {
context
.buildConstraintViolationWithTemplate("email already exist")
.addPropertyNode("email")
.addConstraintViolation();
}
if (!isUsernameValid) {
context
.buildConstraintViolationWithTemplate("username already exist")
.addPropertyNode("username")
.addConstraintViolation();
}
return false;
}
}
}

View File

@ -1,17 +1,16 @@
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"})
@ -26,35 +25,44 @@ public class Article {
private DateTime createdAt;
private DateTime updatedAt;
public Article(String title, String description, String body, String[] tagList, String userId) {
public Article(
String title, String description, String body, List<String> 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) {
public Article(
String title,
String description,
String body,
List<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.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)) {
if (!Util.isEmpty(title)) {
this.title = title;
this.slug = toSlug(title);
}
if (!"".equals(description)) {
this.description = description;
}
if (!"".equals(body)) {
this.body = body;
}
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\\?\\,\\.]+", "-");

View File

@ -1,11 +1,11 @@
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"})
@ -27,23 +27,23 @@ public class User {
}
public void update(String email, String username, String password, String bio, String image) {
if (!"".equals(email)) {
if (!Util.isEmpty(email)) {
this.email = email;
}
if (!"".equals(username)) {
if (!Util.isEmpty(username)) {
this.username = username;
}
if (!"".equals(password)) {
if (!Util.isEmpty(password)) {
this.password = password;
}
if (!"".equals(bio)) {
if (!Util.isEmpty(bio)) {
this.bio = bio;
}
if (!"".equals(image)) {
if (!Util.isEmpty(image)) {
this.image = image;
}
}

View File

@ -6,6 +6,7 @@
C.id commentId,
C.body commentBody,
C.created_at commentCreatedAt,
C.article_id commentArticleId,
<include refid="io.spring.infrastructure.mybatis.readservice.ArticleReadService.profileColumns"/>
from comments C
left join users U

View File

@ -34,6 +34,7 @@
<result column="commentBody" property="body"/>
<result column="commentCreatedAt" property="createdAt"/>
<result column="commentCreatedAt" property="updatedAt"/>
<result column="commentArticleId" property="articleId"/>
<association property="profileData" resultMap="profileData"/>
</resultMap>
</mapper>

View File

@ -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,30 +33,16 @@ 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;
@MockBean ArticleCommandService articleCommandService;
@Override
@Before
@ -56,7 +55,14 @@ public class ArticleApiTest extends TestWithCurrentUser {
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);
Article article =
new Article(
"Test New Article",
"Desc",
"Body",
Arrays.asList("java", "spring", "jpg"),
user.getId(),
time);
ArticleData articleData = TestHelper.getArticleDataFromArticleAndUser(article, user);
when(articleQueryService.findBySlug(eq(slug), eq(null))).thenReturn(Optional.of(articleData));
@ -68,16 +74,12 @@ public class ArticleApiTest extends TestWithCurrentUser {
.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);
RestAssuredMockMvc.when().get("/articles/not-exists").then().statusCode(404);
}
@Test
@ -87,12 +89,14 @@ public class ArticleApiTest extends TestWithCurrentUser {
String description = "new description";
Map<String, Object> 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);
when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article));
when(articleQueryService.findBySlug(eq(article.getSlug()), eq(user))).thenReturn(Optional.of(articleData));
when(articleQueryService.findBySlug(eq(article.getSlug()), eq(user)))
.thenReturn(Optional.of(articleData));
given()
.contentType("application/json")
@ -114,10 +118,13 @@ public class ArticleApiTest extends TestWithCurrentUser {
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(
ArticleData articleData =
new ArticleData(
article.getId(),
article.getSlug(),
article.getTitle(),
@ -128,10 +135,16 @@ 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(articleQueryService.findBySlug(eq(article.getSlug()), eq(user)))
.thenReturn(Optional.of(articleData));
given()
.contentType("application/json")
@ -149,7 +162,8 @@ public class ArticleApiTest extends TestWithCurrentUser {
String body = "body";
String description = "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());
when(articleRepository.findBySlug(eq(article.getSlug()))).thenReturn(Optional.of(article));
given()
@ -170,7 +184,9 @@ public class ArticleApiTest extends TestWithCurrentUser {
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()
@ -181,13 +197,20 @@ public class ArticleApiTest extends TestWithCurrentUser {
.statusCode(403);
}
private HashMap<String, Object> prepareUpdateParam(final String title, final String body, final String description) {
return new HashMap<String, Object>() {{
put("article", new HashMap<String, Object>() {{
private HashMap<String, Object> prepareUpdateParam(
final String title, final String body, final String description) {
return new HashMap<String, Object>() {
{
put(
"article",
new HashMap<String, Object>() {
{
put("title", title);
put("body", body);
put("description", description);
}});
}};
}
});
}
};
}
}

View File

@ -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,30 +30,16 @@ 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;
@ -52,9 +48,10 @@ public class ArticleFavoriteApiTest extends TestWithCurrentUser {
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());
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(
ArticleData articleData =
new ArticleData(
article.getId(),
article.getSlug(),
article.getTitle(),
@ -70,9 +67,9 @@ 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
@ -91,7 +88,8 @@ public class ArticleFavoriteApiTest extends TestWithCurrentUser {
@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())));
when(articleFavoriteRepository.find(eq(article.getId()), eq(user.getId())))
.thenReturn(Optional.of(new ArticleFavorite(article.getId(), user.getId())));
given()
.header("Authorization", "Token " + token)
.when()

View File

@ -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<String> tagList = asList("reactjs", "angularjs", "dragons");
Map<String, Object> 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<String, Object> param = prepareParam(title, description, body, tagList);
Map<String, Object> 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<String, Object> param = prepareParam(title, description, body, tagList);
Map<String, Object> 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<String, Object> prepareParam(
final String title, final String description, final String body, final String[] tagList) {
final String title, final String description, final String body, final List<String> tagList) {
return new HashMap<String, Object>() {
{
put(

View File

@ -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,59 +30,52 @@ 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;
@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());
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(
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<String, Object> param = new HashMap<String, Object>() {{
put("comment", new HashMap<String, Object>() {{
Map<String, Object> param =
new HashMap<String, Object>() {
{
put(
"comment",
new HashMap<String, Object>() {
{
put("body", "comment content");
}});
}};
}
});
}
};
when(commentQueryService.findById(anyString(), eq(user))).thenReturn(Optional.of(commentData));
@ -88,11 +92,18 @@ public class CommentsApiTest extends TestWithCurrentUser {
@Test
public void should_get_422_with_empty_body() throws Exception {
Map<String, Object> param = new HashMap<String, Object>() {{
put("comment", new HashMap<String, Object>() {{
Map<String, Object> param =
new HashMap<String, Object>() {
{
put(
"comment",
new HashMap<String, Object>() {
{
put("body", "");
}});
}};
}
});
}
};
given()
.contentType("application/json")
@ -103,12 +114,12 @@ public class CommentsApiTest extends TestWithCurrentUser {
.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));
when(commentQueryService.findByArticleId(anyString(), eq(null)))
.thenReturn(Arrays.asList(commentData));
RestAssuredMockMvc.when()
.get("/articles/{slug}/comments", article.getSlug())
.prettyPeek()
@ -119,7 +130,8 @@ public class CommentsApiTest extends TestWithCurrentUser {
@Test
public void should_delete_comment_success() throws Exception {
when(commentRepository.findById(eq(article.getId()), eq(comment.getId()))).thenReturn(Optional.of(comment));
when(commentRepository.findById(eq(article.getId()), eq(comment.getId())))
.thenReturn(Optional.of(comment));
given()
.header("Authorization", "Token " + token)
@ -130,13 +142,17 @@ public class CommentsApiTest extends TestWithCurrentUser {
}
@Test
public void should_get_403_if_not_author_of_article_or_author_of_comment_when_delete_comment() throws Exception {
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(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(userRepository.findById(eq(anotherUser.getId())))
.thenReturn(Optional.ofNullable(anotherUser));
when(commentRepository.findById(eq(article.getId()), eq(comment.getId()))).thenReturn(Optional.of(comment));
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()
@ -145,6 +161,5 @@ public class CommentsApiTest extends TestWithCurrentUser {
.delete("/articles/{slug}/comments/{id}", article.getSlug(), comment.getId())
.then()
.statusCode(403);
}
}

View File

@ -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 {

View File

@ -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,23 +22,16 @@ 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;
@Autowired private MockMvc mvc;
@Override
@Before
@ -42,30 +42,27 @@ public class ListArticleApiTest extends TestWithCurrentUser {
@Test
public void should_get_default_article_list() throws Exception {
ArticleDataList articleDataList = new ArticleDataList(
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);
RestAssuredMockMvc.when().get("/articles/feed").prettyPeek().then().statusCode(401);
}
@Test
public void should_get_feeds_success() throws Exception {
ArticleDataList articleDataList = new ArticleDataList(
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)

View File

@ -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

View File

@ -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,32 +30,22 @@ 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})
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;
@ -57,7 +54,9 @@ public class ArticleQueryServiceTest {
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());
article =
new Article(
"test", "desc", "body", Arrays.asList("java", "spring"), user.getId(), new DateTime());
articleRepository.save(article);
}
@ -67,7 +66,7 @@ public class ArticleQueryServiceTest {
assertTrue(optional.isPresent());
ArticleData fetched = optional.get();
assertEquals(fetched.getFavoritesCount(),0);
assertEquals(fetched.getFavoritesCount(), 0);
assertFalse(fetched.isFavorited());
assertNotNull(fetched.getCreatedAt());
assertNotNull(fetched.getUpdatedAt());
@ -90,16 +89,25 @@ public class ArticleQueryServiceTest {
@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));
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);
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);
ArticleDataList nodata =
queryService.findRecentArticles(null, null, null, new Page(2, 10), user);
assertEquals(nodata.getCount(), 2);
assertEquals(nodata.getArticleDatas().size(), 0);
}
@ -108,10 +116,12 @@ public class ArticleQueryServiceTest {
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());
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);
ArticleDataList recentArticles =
queryService.findRecentArticles(null, user.getUsername(), null, new Page(), user);
assertEquals(recentArticles.getArticleDatas().size(), 1);
assertEquals(recentArticles.getCount(), 1);
}
@ -121,27 +131,32 @@ public class ArticleQueryServiceTest {
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());
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);
ArticleDataList recentArticles = queryService.findRecentArticles(null, null, anotherUser.getUsername(), new Page(), anotherUser);
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);
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());
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);
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());
@ -158,7 +173,8 @@ public class ArticleQueryServiceTest {
FollowRelation followRelation = new FollowRelation(anotherUser.getId(), user.getId());
userRepository.saveRelation(followRelation);
ArticleDataList recentArticles = queryService.findRecentArticles(null, null, null, new Page(), anotherUser);
ArticleDataList recentArticles =
queryService.findRecentArticles(null, null, null, new Page(), anotherUser);
assertEquals(recentArticles.getCount(), 1);
ArticleData articleData = recentArticles.getArticleDatas().get(0);
assertTrue(articleData.getProfileData().isFollowing());

View File

@ -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,27 +26,22 @@ 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;
@ -63,7 +64,7 @@ public class CommentQueryServiceTest {
@Test
public void should_read_comments_of_article() {
Article article = new Article("title", "desc", "body", new String[]{"java"}, user.getId());
Article article = new Article("title", "desc", "body", Arrays.asList("java"), user.getId());
articleRepository.save(article);
User user2 = new User("user2@email.com", "user2", "123", "", "");
@ -77,6 +78,5 @@ public class CommentQueryServiceTest {
List<CommentData> comments = commentQueryService.findByArticleId(article.getId(), user);
assertEquals(comments.size(), 2);
}
}

View File

@ -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"));
articleRepository.save(new Article("test", "test", "test", Arrays.asList("java"), "123"));
assertTrue(tagsQueryService.allTags().contains("java"));
}
}

View File

@ -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");
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");
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");
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");
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");
Article article = new Article("what?the.hell,w", "desc", "body", Arrays.asList("java"), "123");
assertThat(article.getSlug(), is("what-the-hell-w"));
}
}

View File

@ -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 ArticleMapper articleMapper;
@Autowired private UserRepository userRepository;
@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());
Article article =
new Article("test", "desc", "body", Arrays.asList("java", "spring"), user.getId());
articleRepository.save(article);
Article anotherArticle = new Article("test", "desc", "body", new String[]{"java", "spring", "other"}, user.getId());
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"));
}
}
}

View File

@ -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,31 +22,21 @@ 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;
@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());
article = new Article("test", "desc", "body", Arrays.asList("java", "spring"), user.getId());
}
@Test