Skip to content
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

Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.zufar.onlinestore.common.validation.annotation;

import com.zufar.onlinestore.common.validation.validator.UniqueEmailValidator;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({ FIELD })
@Retention(RUNTIME)
@Constraint(validatedBy = UniqueEmailValidator.class)
@Documented
public @interface UniqueEmail {

String message() default "";

Class<?>[] groups() default { };

Class<? extends Payload>[] payload() default { };

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.zufar.onlinestore.common.validation.annotation;

import com.zufar.onlinestore.common.validation.validator.UniqueUsernameValidator;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({ FIELD })
@Retention(RUNTIME)
@Constraint(validatedBy = UniqueUsernameValidator.class)
@Documented
public @interface UniqueUsername {

String message() default "";

Class<?>[] groups() default { };

Class<? extends Payload>[] payload() default { };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.zufar.onlinestore.common.validation.validator;

import com.zufar.onlinestore.user.repository.UserRepository;
import com.zufar.onlinestore.common.validation.annotation.UniqueEmail;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@RequiredArgsConstructor
@Component
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {

private final UserRepository userCrudRepository;

@Override
public boolean isValid(String email, ConstraintValidatorContext constraintValidatorContext) {
return userCrudRepository
.findByEmail(email)
.isEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.zufar.onlinestore.common.validation.validator;

import com.zufar.onlinestore.common.validation.annotation.UniqueUsername;
import com.zufar.onlinestore.user.repository.UserRepository;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@RequiredArgsConstructor
@Component
public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {

private final UserRepository userCrudRepository;

@Override
public boolean isValid(String username, ConstraintValidatorContext constraintValidatorContext) {
return userCrudRepository.findUserByUsername(username) == null;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.zufar.onlinestore.security.dto.registration;

import com.zufar.onlinestore.common.validation.annotation.UniqueEmail;
import com.zufar.onlinestore.common.validation.annotation.UniqueUsername;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
Expand All @@ -15,10 +17,12 @@ public record UserRegistrationRequest(
@Size(max = 55, message = "LastName length must be less than 55 characters")
String lastName,

@UniqueUsername(message = "User with this username is already registered")
@NotBlank(message = "Username is the mandatory attribute")
@Size(max = 55, message = "Username length must be less than 55 characters")
String username,

@UniqueEmail(message = "User with this email is already registered")
@Email(message = "Email should be valid")
@NotBlank(message = "Email is the mandatory attribute")
String email,
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/com/zufar/onlinestore/user/api/AuthorityService.java
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 {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DefaultUserAuthoritySetter sound clearer

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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);
}
}
4 changes: 4 additions & 0 deletions src/main/java/com/zufar/onlinestore/user/api/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ public class UserService implements UserApi {

private final UserRepository userCrudRepository;
private final UserDtoConverter userDtoConverter;
private final AuthorityService authorityService;

@Override
public UserDto saveUser(final UserDto userDto) {
UserEntity userEntity = userDtoConverter.toEntity(userDto);
authorityService.setDefaultAuthority(userEntity);
UserEntity userEntityWithId = userCrudRepository.save(userEntity);

return userDtoConverter.toDto(userEntityWithId);
}

Expand All @@ -36,4 +39,5 @@ public UserDto getUserById(final UUID userId) throws UserNotFoundException {
}
return userDtoConverter.toDto(userEntity.get());
}

}
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
package com.zufar.onlinestore.user.converter;

import com.zufar.onlinestore.user.dto.UserDto;
import com.zufar.onlinestore.user.entity.Authority;
import com.zufar.onlinestore.user.entity.UserEntity;
import com.zufar.onlinestore.user.entity.UserGrantedAuthority;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingConstants;
import org.mapstruct.Named;

import java.util.Collections;
import java.util.Set;

@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, uses = AddressDtoConverter.class)
public interface UserDtoConverter {
Expand All @@ -23,32 +17,6 @@ public interface UserDtoConverter {
@Mapping(target = "credentialsNonExpired", constant = "true")
@Mapping(target = "enabled", constant = "true")
@Mapping(target = "address", source = "dto.address", qualifiedByName = "toAddress")
@Mapping(target = "authorities", source = "dto", qualifiedByName = "createAuthorities")
UserEntity toEntity(final UserDto dto);

@Named("createAuthorities")
@Mapping(target = "address", source = "dto.address", qualifiedByName = "toAddress")
default Set<UserGrantedAuthority> createAuthorities(UserDto dto) {
UserEntity userEntity = UserEntity.builder()
.firstName(dto.firstName())
.lastName(dto.lastName())
.email(dto.email())
.username(dto.username())
.password(dto.password())
.accountNonExpired(true)
.accountNonLocked(true)
.credentialsNonExpired(true)
.enabled(true)
.build();

Set<UserGrantedAuthority> authorities = Collections.singleton(UserGrantedAuthority
.builder()
.authority(Authority.USER)
.user(userEntity)
.build());

userEntity.setAuthorities(authorities);

return authorities;
}
}
26 changes: 21 additions & 5 deletions src/main/java/com/zufar/onlinestore/user/entity/UserEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;


@Builder
@Getter
@Setter
Expand All @@ -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)
Expand All @@ -60,8 +60,9 @@ public class UserEntity implements UserDetails {
@JoinColumn(name = "address_id", referencedColumnName = "id")
private transient Address address;

@Builder.Default
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<UserGrantedAuthority> authorities;
private Set<UserGrantedAuthority> authorities = new HashSet<>();

@Column(name = "account_non_expired", nullable = false)
private boolean accountNonExpired;
Expand All @@ -75,6 +76,21 @@ 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() {
authorities.forEach(authority -> authority.setUser(null));
authorities.clear();
}

@Override
public boolean equals(Object o) {
if (this == o)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.Builder;
import org.springframework.security.core.GrantedAuthority;

import java.util.UUID;

@Builder
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
import com.zufar.onlinestore.user.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;
import java.util.UUID;

public interface UserRepository extends JpaRepository<UserEntity, UUID> {

UserEntity findUserByUsername(String username);

Optional<UserEntity> findByEmail(String email);

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ CREATE TABLE user_details
account_non_locked BOOLEAN NOT NULL,
credentials_non_expired BOOLEAN NOT NULL,
enabled BOOLEAN NOT NULL,
UNIQUE(stripe_customer_token),
UNIQUE (user_name, email, stripe_customer_token),
CONSTRAINT fk_address
FOREIGN KEY (address_id)
REFERENCES address (id)
Expand Down