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.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; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping(path = "/user") public class CurrentUserApi { private UserQueryService userQueryService; private UserService userService; @Autowired public CurrentUserApi(UserQueryService userQueryService, UserService userService) { this.userQueryService = userQueryService; this.userService = userService; } @GetMapping public ResponseEntity currentUser( @AuthenticationPrincipal User currentUser, @RequestHeader(value = "Authorization") String authorization) { UserData userData = userQueryService.findById(currentUser.getId()).get(); return ResponseEntity.ok( userResponse(new UserWithToken(userData, authorization.split(" ")[1]))); } @PutMapping public ResponseEntity updateProfile( @AuthenticationPrincipal User currentUser, @RequestHeader("Authorization") String token, @Valid @RequestBody UpdateUserParam updateUserParam) { userService.updateUser(new UpdateUserCommand(currentUser, updateUserParam)); UserData userData = userQueryService.findById(currentUser.getId()).get(); return ResponseEntity.ok(userResponse(new UserWithToken(userData, token.split(" ")[1]))); } private Map userResponse(UserWithToken userWithToken) { return new HashMap() { { put("user", userWithToken); } }; } } @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 = ""; }