-
-
Notifications
You must be signed in to change notification settings - Fork 118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[IL-63][issue-4] Hibernate issue with authorities save and fixed issue with registration response #98
[IL-63][issue-4] Hibernate issue with authorities save and fixed issue with registration response #98
Changes from 6 commits
3771ec4
757e431
c5d024e
639b00d
308d9a0
62d0db9
a7332cc
fd4fe55
ce6100f
89e6788
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
package com.zufar.onlinestore.common.exception.handler; | ||
|
||
import com.zufar.onlinestore.common.response.ApiResponse; | ||
import com.zufar.onlinestore.user.exception.UserAlreadyRegisteredException; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.apache.logging.log4j.util.Strings; | ||
import org.springframework.context.support.DefaultMessageSourceResolvable; | ||
|
@@ -40,12 +41,19 @@ protected ApiResponse<Void> buildResponse(Exception exception, HttpStatus httpSt | |
} | ||
|
||
private List<String> collectErrorMessages(Exception exception) { | ||
return exception instanceof MethodArgumentNotValidException methodArgumentNotValidException ? | ||
methodArgumentNotValidException | ||
.getBindingResult() | ||
.getAllErrors().stream() | ||
.map(DefaultMessageSourceResolvable::getDefaultMessage) | ||
.toList() : List.of(exception.getMessage()); | ||
if (exception instanceof UserAlreadyRegisteredException userAlreadyRegisteredException) { | ||
return userAlreadyRegisteredException.getErrors(); | ||
} | ||
|
||
if (exception instanceof MethodArgumentNotValidException methodArgumentNotValidException) { | ||
return methodArgumentNotValidException | ||
.getBindingResult() | ||
.getAllErrors().stream() | ||
.map(DefaultMessageSourceResolvable::getDefaultMessage) | ||
.toList(); | ||
} | ||
|
||
return List.of(exception.getMessage()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use Collections.singletonList There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
} | ||
|
||
private String buildErrorDescription(Exception exception) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package com.zufar.onlinestore.user.api; | ||
|
||
import com.zufar.onlinestore.user.entity.Authority; | ||
import com.zufar.onlinestore.user.entity.UserEntity; | ||
import com.zufar.onlinestore.user.entity.UserGrantedAuthority; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
public class AuthorityService { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that this service may have a greater responsibility than setting the default role |
||
|
||
public void setDefaultAuthority(UserEntity savedUserEntity) { | ||
UserGrantedAuthority defaultAuthority = UserGrantedAuthority | ||
.builder() | ||
.authority(Authority.USER) | ||
.build(); | ||
|
||
savedUserEntity.addAuthority(defaultAuthority); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,12 +3,15 @@ | |
import com.zufar.onlinestore.user.converter.UserDtoConverter; | ||
import com.zufar.onlinestore.user.dto.UserDto; | ||
import com.zufar.onlinestore.user.entity.UserEntity; | ||
import com.zufar.onlinestore.user.exception.UserAlreadyRegisteredException; | ||
import com.zufar.onlinestore.user.exception.UserNotFoundException; | ||
import com.zufar.onlinestore.user.repository.UserRepository; | ||
import com.zufar.onlinestore.user.validation.UserDataValidator; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.UUID; | ||
|
||
|
@@ -19,11 +22,16 @@ public class UserService implements UserApi { | |
|
||
private final UserRepository userCrudRepository; | ||
private final UserDtoConverter userDtoConverter; | ||
private final AuthorityService authorityService; | ||
private final UserDataValidator userDataValidator; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Delete this validator from this class There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
||
@Override | ||
public UserDto saveUser(final UserDto userDto) { | ||
UserEntity userEntity = userDtoConverter.toEntity(userDto); | ||
userDataValidator.validate(userEntity); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should create A Custom Validator Annotation with Spring like that: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
UserEntity userEntityWithId = userCrudRepository.save(userEntity); | ||
authorityService.setDefaultAuthority(userEntityWithId); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is better to use before There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
||
return userDtoConverter.toDto(userEntityWithId); | ||
} | ||
|
||
|
@@ -36,4 +44,5 @@ public UserDto getUserById(final UUID userId) throws UserNotFoundException { | |
} | ||
return userDtoConverter.toDto(userEntity.get()); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,7 @@ | |
import org.apache.commons.lang3.builder.EqualsBuilder; | ||
import org.apache.commons.lang3.builder.HashCodeBuilder; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
|
||
import java.util.Iterator; | ||
import java.util.Set; | ||
import java.util.UUID; | ||
|
||
|
@@ -44,13 +44,13 @@ public class UserEntity implements UserDetails { | |
@Column(name = "last_name", nullable = false) | ||
private String lastName; | ||
|
||
@Column(name = "user_name", nullable = false) | ||
@Column(name = "user_name", nullable = false, unique = true) | ||
private String username; | ||
|
||
@Column(name = "stripe_customer_token", nullable = true, unique = true) | ||
private String stripeCustomerToken; | ||
|
||
@Column(name = "email", nullable = false) | ||
@Column(name = "email", nullable = false, unique = true) | ||
private String email; | ||
|
||
@Column(name = "password", nullable = false) | ||
|
@@ -75,6 +75,27 @@ public class UserEntity implements UserDetails { | |
@Column(name = "enabled", nullable = false) | ||
private boolean enabled; | ||
|
||
public void addAuthority(UserGrantedAuthority authority) { | ||
this.authorities.add(authority); | ||
authority.setUser(this); | ||
} | ||
|
||
public void removeAuthority(UserGrantedAuthority authority) { | ||
authority.setUser(null); | ||
this.authorities.remove(authority); | ||
} | ||
|
||
public void removeAuthorities() { | ||
Iterator<UserGrantedAuthority> iterator = this.authorities.iterator(); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
while (iterator.hasNext()){ | ||
UserGrantedAuthority authority = iterator.next(); | ||
|
||
authority.setUser(null); | ||
iterator.remove(); | ||
} | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,16 +11,14 @@ | |
import jakarta.persistence.JoinColumn; | ||
import jakarta.persistence.ManyToOne; | ||
import jakarta.persistence.Table; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.*; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dont use wildcard imports There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
import org.springframework.security.core.GrantedAuthority; | ||
|
||
import java.util.UUID; | ||
|
||
@Builder | ||
@Getter | ||
@Setter | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
@Entity | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.zufar.onlinestore.user.exception; | ||
|
||
import lombok.Getter; | ||
|
||
import java.util.List; | ||
|
||
@Getter | ||
public class UserAlreadyRegisteredException extends RuntimeException { | ||
|
||
private final List<String> errors; | ||
|
||
public UserAlreadyRegisteredException(List<String> errors) { | ||
this.errors = errors; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.zufar.onlinestore.user.exception.handler; | ||
|
||
import com.zufar.onlinestore.common.exception.handler.GlobalExceptionHandler; | ||
import com.zufar.onlinestore.common.response.ApiResponse; | ||
import com.zufar.onlinestore.payment.exception.PaymentNotFoundException; | ||
import com.zufar.onlinestore.user.exception.UserAlreadyRegisteredException; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
import org.springframework.web.bind.annotation.ResponseStatus; | ||
import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
|
||
@Slf4j | ||
@RestControllerAdvice | ||
public class UserExceptionHandler extends GlobalExceptionHandler { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this exception is related to security model more than to user model. That is why I think you should rename this class like |
||
|
||
@ExceptionHandler(UserAlreadyRegisteredException.class) | ||
@ResponseStatus(HttpStatus.BAD_REQUEST) | ||
public ApiResponse<Void> handleUserAlreadyRegisteredException(final UserAlreadyRegisteredException exception) { | ||
ApiResponse<Void> apiResponse = buildResponse(exception, HttpStatus.BAD_REQUEST); | ||
log.error("Handle user already registered exception: failed: messages: {}, description: {}.", | ||
apiResponse.messages(), apiResponse.description()); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove space There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
return apiResponse; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package com.zufar.onlinestore.user.validation; | ||
|
||
import com.zufar.onlinestore.user.entity.UserEntity; | ||
import com.zufar.onlinestore.user.exception.UserAlreadyRegisteredException; | ||
import com.zufar.onlinestore.user.repository.UserRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.UUID; | ||
|
||
@RequiredArgsConstructor | ||
@Component | ||
public class UserDataValidator { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should create A Custom Validator Annotation with Spring like that: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
||
private final List<String> errors; | ||
private final UserRepository userCrudRepository; | ||
|
||
public void validate(UserEntity user) { | ||
if (!isEmailUnique(user.getUserId(), user.getEmail())) { | ||
errors.add(String.format("User with email = %s is already registered", user.getEmail())); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. extra space There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
if (!isUsernameUnique(user.getUserId(), user.getUsername())) { | ||
errors.add(String.format("User with username = %s is already registered", user.getUsername())); | ||
} | ||
|
||
if (!errors.isEmpty()) { | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. extra space There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
throw new UserAlreadyRegisteredException(errors); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. extra space There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
} | ||
} | ||
|
||
public boolean isEmailUnique(UUID id, String email) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be great to create separate small class There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
Optional<UserEntity> userByEmail = userCrudRepository.findByEmail(email); | ||
|
||
if (userByEmail.isEmpty()) { | ||
return true; | ||
} | ||
|
||
return userByEmail.map(UserEntity::getUserId).filter(userId -> userId == id).isPresent(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This way please:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we need an id to perform this check? I dont think so because email is uniquely identifies the object There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
} | ||
|
||
public boolean isUsernameUnique(UUID id, String username) { | ||
UserEntity userByUsername = userCrudRepository.findUserByUsername(username); | ||
|
||
if (userByUsername == null) { | ||
return true; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
|
||
return userByUsername.getUserId().equals(id); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This exception relates to specific security module. Please, create new separate
SecurityExceptionHandler
class and create method:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done