fix: Request validation in UserService
This commit is contained in:
parent
1eee6b1b48
commit
7e38b5a2e0
9 changed files with 126 additions and 26 deletions
|
@ -4,8 +4,8 @@ import eu.ztsh.wymiana.data.repository.UserRepository;
|
||||||
import eu.ztsh.wymiana.exception.UserAlreadyExistsException;
|
import eu.ztsh.wymiana.exception.UserAlreadyExistsException;
|
||||||
import eu.ztsh.wymiana.model.User;
|
import eu.ztsh.wymiana.model.User;
|
||||||
import eu.ztsh.wymiana.util.UserMapper;
|
import eu.ztsh.wymiana.util.UserMapper;
|
||||||
|
import eu.ztsh.wymiana.validation.InstanceValidator;
|
||||||
import eu.ztsh.wymiana.web.model.UserCreateRequest;
|
import eu.ztsh.wymiana.web.model.UserCreateRequest;
|
||||||
import jakarta.validation.Valid;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@ -16,8 +16,10 @@ import java.util.Optional;
|
||||||
public class UserService {
|
public class UserService {
|
||||||
|
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
|
private final InstanceValidator validator;
|
||||||
|
|
||||||
public User create(@Valid UserCreateRequest request) {
|
public User create(UserCreateRequest request) {
|
||||||
|
validator.validate(request);
|
||||||
if (userRepository.findById(request.pesel()).isPresent()) {
|
if (userRepository.findById(request.pesel()).isPresent()) {
|
||||||
throw new UserAlreadyExistsException(request);
|
throw new UserAlreadyExistsException(request);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,12 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
@Constraint(validatedBy = AdultValidator.class)
|
@Constraint(validatedBy = AdultValidator.class)
|
||||||
@Documented
|
@Documented
|
||||||
public @interface Adult {
|
public @interface Adult {
|
||||||
String message() default "{jakarta.validation.constraints.Adult.message}";
|
String message() default MESSAGE;
|
||||||
|
|
||||||
Class<?>[] groups() default { };
|
Class<?>[] groups() default { };
|
||||||
|
|
||||||
Class<? extends Payload>[] payload() default { };
|
Class<? extends Payload>[] payload() default { };
|
||||||
|
|
||||||
|
String MESSAGE = "The person has not reached the age of 18";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package eu.ztsh.wymiana.validation;
|
||||||
|
|
||||||
|
import jakarta.validation.Validator;
|
||||||
|
|
||||||
|
public class InstanceValidator {
|
||||||
|
|
||||||
|
private final Validator validator;
|
||||||
|
|
||||||
|
public InstanceValidator(Validator validator) {
|
||||||
|
this.validator = validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void validate(T t, Class<?>... classes) {
|
||||||
|
var violations = validator.validate(t, classes);
|
||||||
|
if (!violations.isEmpty()) {
|
||||||
|
throw new ValidationFailedException(violations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package eu.ztsh.wymiana.validation;
|
||||||
|
|
||||||
|
import jakarta.validation.Validation;
|
||||||
|
import jakarta.validation.ValidatorFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class InstanceValidatorFactory {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public InstanceValidator instanceValidator() {
|
||||||
|
try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
|
||||||
|
return new InstanceValidator(factory.getValidator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package eu.ztsh.wymiana.validation;
|
||||||
|
|
||||||
|
import jakarta.validation.ConstraintViolation;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class ValidationFailedException extends RuntimeException {
|
||||||
|
|
||||||
|
private final transient Set<?> violations;
|
||||||
|
|
||||||
|
public <T> ValidationFailedException(Set<ConstraintViolation<T>> violations) {
|
||||||
|
super("Validation failed: %s".formatted(violations.stream()
|
||||||
|
.map(ConstraintViolation::getMessage).collect(Collectors.joining(System.lineSeparator()))));
|
||||||
|
this.violations = violations;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,7 +3,9 @@ package eu.ztsh.wymiana.web.model;
|
||||||
import eu.ztsh.wymiana.validation.Adult;
|
import eu.ztsh.wymiana.validation.Adult;
|
||||||
import jakarta.validation.constraints.Min;
|
import jakarta.validation.constraints.Min;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Builder;
|
||||||
|
|
||||||
|
@Builder
|
||||||
public record UserCreateRequest(
|
public record UserCreateRequest(
|
||||||
@NotNull String name,
|
@NotNull String name,
|
||||||
@NotNull String surname,
|
@NotNull String surname,
|
||||||
|
|
|
@ -23,8 +23,11 @@ public class EntityCreator {
|
||||||
return new UserEntityBuilder();
|
return new UserEntityBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UserCreateRequest userRequest() {
|
public static UserCreateRequest.UserCreateRequestBuilder userRequest() {
|
||||||
return new UserCreateRequest(Constants.NAME, Constants.SURNAME, Constants.PESEL, Constants.PLN);
|
return UserCreateRequest.builder().name(Constants.NAME)
|
||||||
|
.surname(Constants.SURNAME)
|
||||||
|
.pesel(Constants.PESEL)
|
||||||
|
.pln(Constants.PLN);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class UserEntityBuilder {
|
public static class UserEntityBuilder {
|
||||||
|
|
|
@ -5,7 +5,11 @@ import eu.ztsh.wymiana.RepositoryBasedTest;
|
||||||
import eu.ztsh.wymiana.data.repository.UserRepository;
|
import eu.ztsh.wymiana.data.repository.UserRepository;
|
||||||
import eu.ztsh.wymiana.exception.UserAlreadyExistsException;
|
import eu.ztsh.wymiana.exception.UserAlreadyExistsException;
|
||||||
import eu.ztsh.wymiana.util.UserMapper;
|
import eu.ztsh.wymiana.util.UserMapper;
|
||||||
|
import eu.ztsh.wymiana.validation.Adult;
|
||||||
|
import eu.ztsh.wymiana.validation.InstanceValidator;
|
||||||
|
import eu.ztsh.wymiana.validation.ValidationFailedException;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
@ -17,44 +21,72 @@ class UserServiceTest extends RepositoryBasedTest {
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public UserServiceTest(UserRepository userRepository) {
|
public UserServiceTest(UserRepository userRepository, InstanceValidator instanceValidator) {
|
||||||
super(userRepository);
|
super(userRepository);
|
||||||
userService = new UserService(userRepository);
|
userService = new UserService(userRepository, instanceValidator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Transactional
|
@Transactional
|
||||||
|
@DisplayName("Create new user")
|
||||||
void createNewUserTest() {
|
void createNewUserTest() {
|
||||||
userService.create(EntityCreator.userRequest());
|
userService.create(EntityCreator.userRequest().build());
|
||||||
var entity = EntityCreator.user().build();
|
var entity = EntityCreator.user().build();
|
||||||
expect(entity);
|
expect(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createDuplicatedUser() {
|
@DisplayName("Try to create user that already exists")
|
||||||
var first = EntityCreator.userRequest();
|
void createDuplicatedUserTest() {
|
||||||
var second = EntityCreator.userRequest();
|
var first = EntityCreator.userRequest().build();
|
||||||
|
var second = EntityCreator.userRequest().build();
|
||||||
userService.create(first);
|
userService.create(first);
|
||||||
assertThatThrownBy(() -> userService.create(second))
|
assertThatThrownBy(() -> userService.create(second))
|
||||||
.isInstanceOf(UserAlreadyExistsException.class)
|
.isInstanceOf(UserAlreadyExistsException.class)
|
||||||
.hasMessage("User with PESEL %s already exists".formatted(EntityCreator.Constants.PESEL));
|
.hasMessage("User with PESEL %s already exists".formatted(EntityCreator.Constants.PESEL));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Try to create another user with same PESEL")
|
||||||
|
void createDuplicatedPeselTest() {
|
||||||
|
var first = EntityCreator.userRequest().build();
|
||||||
|
var second = EntityCreator.userRequest()
|
||||||
|
.name("Jan")
|
||||||
|
.surname("Kowalski")
|
||||||
|
.pln(30.30)
|
||||||
|
.build();
|
||||||
|
userService.create(first);
|
||||||
|
assertThatThrownBy(() -> userService.create(second))
|
||||||
|
.isInstanceOf(UserAlreadyExistsException.class)
|
||||||
|
.hasMessage("User with PESEL %s already exists".formatted(EntityCreator.Constants.PESEL));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Try to create too young user")
|
||||||
|
void youngUserTest() {
|
||||||
|
var request = EntityCreator.userRequest().pesel("08280959342").build();
|
||||||
|
assertThatThrownBy(() -> userService.create(request))
|
||||||
|
.isInstanceOf(ValidationFailedException.class)
|
||||||
|
.hasMessageContaining(Adult.MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Transactional
|
@Transactional
|
||||||
|
@DisplayName("Get existing user")
|
||||||
void getExistingUserTest() {
|
void getExistingUserTest() {
|
||||||
var entity = EntityCreator.user().build();
|
var entity = EntityCreator.user().build();
|
||||||
userRepository.save(entity);
|
userRepository.save(entity);
|
||||||
var userOptional = userService.get(EntityCreator.Constants.PESEL);
|
var userOptional = userService.get(EntityCreator.Constants.PESEL);
|
||||||
var expected = UserMapper.entityToPojo(entity);
|
var expected = UserMapper.entityToPojo(entity);
|
||||||
assertThat(userOptional)
|
assertThat(userOptional)
|
||||||
.isNotEmpty()
|
.isNotEmpty()
|
||||||
.get()
|
.get()
|
||||||
.usingRecursiveComparison()
|
.usingRecursiveComparison()
|
||||||
.isEqualTo(expected);
|
.isEqualTo(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@DisplayName("Try get non-existing user")
|
||||||
void getNonExistingUserTest() {
|
void getNonExistingUserTest() {
|
||||||
var userOptional = userService.get(EntityCreator.Constants.PESEL);
|
var userOptional = userService.get(EntityCreator.Constants.PESEL);
|
||||||
assertThat(userOptional).isEmpty();
|
assertThat(userOptional).isEmpty();
|
||||||
|
|
|
@ -15,23 +15,23 @@ class UserMapperTest {
|
||||||
void entityToPojoTest() {
|
void entityToPojoTest() {
|
||||||
var entity = EntityCreator.user().build();
|
var entity = EntityCreator.user().build();
|
||||||
var expected = new User(
|
var expected = new User(
|
||||||
EntityCreator.Constants.NAME,
|
EntityCreator.Constants.NAME,
|
||||||
EntityCreator.Constants.SURNAME,
|
EntityCreator.Constants.SURNAME,
|
||||||
EntityCreator.Constants.PESEL,
|
EntityCreator.Constants.PESEL,
|
||||||
Map.of("PLN", new Currency("PLN", EntityCreator.Constants.PLN))
|
Map.of("PLN", new Currency("PLN", EntityCreator.Constants.PLN))
|
||||||
);
|
);
|
||||||
assertThat(UserMapper.entityToPojo(entity))
|
assertThat(UserMapper.entityToPojo(entity))
|
||||||
.usingRecursiveComparison()
|
.usingRecursiveComparison()
|
||||||
.isEqualTo(expected);
|
.isEqualTo(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void requestToEntityTest() {
|
void requestToEntityTest() {
|
||||||
var request = EntityCreator.userRequest();
|
var request = EntityCreator.userRequest().build();
|
||||||
var expected = EntityCreator.user().build();
|
var expected = EntityCreator.user().build();
|
||||||
assertThat(UserMapper.requestToEntity(request))
|
assertThat(UserMapper.requestToEntity(request))
|
||||||
.usingRecursiveComparison()
|
.usingRecursiveComparison()
|
||||||
.isEqualTo(expected);
|
.isEqualTo(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue