77.2. Validation (#9)
This commit is contained in:
@ -78,6 +78,11 @@
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
@ -15,6 +15,8 @@ import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
|
||||
@Slf4j
|
||||
@ -56,7 +58,7 @@ public class UserResource {
|
||||
}
|
||||
|
||||
@PostMapping("add")
|
||||
public User addNewUser(UserDto userDto) {
|
||||
public User addNewUser(@Valid UserDto userDto) {
|
||||
log.debug("User DTO: {}", userDto);
|
||||
return userService.addNewUser(userDto);
|
||||
}
|
||||
|
||||
@ -7,15 +7,26 @@ import lombok.NoArgsConstructor;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.Role;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class UserDto {
|
||||
|
||||
@NotEmpty(message = "Should not be empty")
|
||||
private String firstName;
|
||||
@NotEmpty(message = "Should not be empty")
|
||||
private String lastName;
|
||||
@NotEmpty(message = "Should not be empty")
|
||||
private String username;
|
||||
@NotEmpty(message = "Should not be empty")
|
||||
@Email(message = "Must match email format")
|
||||
private String email;
|
||||
@NotNull(message = "Role is mandatory")
|
||||
private Role role;
|
||||
private boolean isNonLocked;
|
||||
private boolean isActive;
|
||||
|
||||
@ -13,6 +13,7 @@ import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.DisabledException;
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
@ -20,6 +21,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import javax.persistence.NoResultException;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.shyshkin.study.fullstack.supportportal.backend.utility.HttpResponseUtility.createHttpResponse;
|
||||
import static org.springframework.http.HttpStatus.*;
|
||||
@ -81,10 +83,19 @@ public class ExceptionHandling {
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<HttpResponse> internalServerErrorException(Exception exception) {
|
||||
log.error(exception.getMessage());
|
||||
log.error(exception.getMessage(), exception);
|
||||
return createHttpResponse(INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR_MSG);
|
||||
}
|
||||
|
||||
@ExceptionHandler({BindException.class})
|
||||
public ResponseEntity<HttpResponse> validationExceptionHandler(BindException exception) {
|
||||
String fieldsWithErrors = exception.getFieldErrors().stream()
|
||||
.map(fe -> fe.getField() + ":" + fe.getDefaultMessage())
|
||||
.collect(Collectors.joining(","));
|
||||
String message = "Error(s) in parameters: [" + fieldsWithErrors + "]";
|
||||
return createHttpResponse(BAD_REQUEST, message);
|
||||
}
|
||||
|
||||
@ExceptionHandler(NoResultException.class)
|
||||
public ResponseEntity<HttpResponse> notFoundException(NoResultException exception) {
|
||||
log.error(exception.getMessage());
|
||||
|
||||
@ -2,8 +2,10 @@ package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.common.BaseUserTest;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.HttpResponse;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.User;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.UserDto;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
@ -14,6 +16,7 @@ import org.springframework.test.context.TestPropertySource;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.http.HttpStatus.BAD_REQUEST;
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
|
||||
@Slf4j
|
||||
@ -26,44 +29,191 @@ class UserResourceUnSecureTest extends BaseUserTest {
|
||||
@Autowired
|
||||
TestRestTemplate restTemplate;
|
||||
|
||||
@Test
|
||||
void addNewUser() {
|
||||
@Nested
|
||||
class AddNewUserTests {
|
||||
|
||||
//given
|
||||
UserDto userDto = createRandomUserDto();
|
||||
Map<String, ?> paramMap = Map.of(
|
||||
"firstName", userDto.getFirstName(),
|
||||
"lastName", userDto.getLastName(),
|
||||
"username", userDto.getUsername(),
|
||||
"email", userDto.getEmail(),
|
||||
"role", userDto.getRole().name(),
|
||||
"isActive", String.valueOf(userDto.isActive()),
|
||||
"isNonLocked", String.valueOf(userDto.isNonLocked())
|
||||
);
|
||||
@Test
|
||||
void addNewUser_correct() {
|
||||
|
||||
//when
|
||||
ResponseEntity<User> responseEntity = restTemplate
|
||||
.postForEntity(
|
||||
"/user/add?username={username}&email={email}" +
|
||||
"&firstName={firstName}&lastName={lastName}" +
|
||||
"&role={role}&active={isActive}&nonLocked={isNonLocked}",
|
||||
null,
|
||||
User.class,
|
||||
paramMap
|
||||
);
|
||||
//given
|
||||
UserDto userDto = createRandomUserDto();
|
||||
Map<String, ?> paramMap = Map.of(
|
||||
"firstName", userDto.getFirstName(),
|
||||
"lastName", userDto.getLastName(),
|
||||
"username", userDto.getUsername(),
|
||||
"email", userDto.getEmail(),
|
||||
"role", userDto.getRole().name(),
|
||||
"isActive", String.valueOf(userDto.isActive()),
|
||||
"isNonLocked", String.valueOf(userDto.isNonLocked())
|
||||
);
|
||||
|
||||
//then
|
||||
log.debug("Response Entity: {}", responseEntity);
|
||||
assertThat(responseEntity.getStatusCode()).isEqualTo(OK);
|
||||
assertThat(responseEntity.getBody())
|
||||
.isNotNull()
|
||||
.hasNoNullFieldsOrPropertiesExcept("lastLoginDate", "lastLoginDateDisplay")
|
||||
.hasFieldOrPropertyWithValue("username", userDto.getUsername())
|
||||
.hasFieldOrPropertyWithValue("email", userDto.getEmail())
|
||||
.hasFieldOrPropertyWithValue("firstName", userDto.getFirstName())
|
||||
.hasFieldOrPropertyWithValue("lastName", userDto.getLastName())
|
||||
.hasFieldOrPropertyWithValue("isActive", true)
|
||||
.hasFieldOrPropertyWithValue("isNotLocked", true)
|
||||
.hasFieldOrPropertyWithValue("role", "ROLE_ADMIN");
|
||||
//when
|
||||
ResponseEntity<User> responseEntity = restTemplate
|
||||
.postForEntity(
|
||||
"/user/add?username={username}&email={email}" +
|
||||
"&firstName={firstName}&lastName={lastName}" +
|
||||
"&role={role}&active={isActive}&nonLocked={isNonLocked}",
|
||||
null,
|
||||
User.class,
|
||||
paramMap
|
||||
);
|
||||
|
||||
//then
|
||||
log.debug("Response Entity: {}", responseEntity);
|
||||
assertThat(responseEntity.getStatusCode()).isEqualTo(OK);
|
||||
assertThat(responseEntity.getBody())
|
||||
.isNotNull()
|
||||
.hasNoNullFieldsOrPropertiesExcept("lastLoginDate", "lastLoginDateDisplay")
|
||||
.hasFieldOrPropertyWithValue("username", userDto.getUsername())
|
||||
.hasFieldOrPropertyWithValue("email", userDto.getEmail())
|
||||
.hasFieldOrPropertyWithValue("firstName", userDto.getFirstName())
|
||||
.hasFieldOrPropertyWithValue("lastName", userDto.getLastName())
|
||||
.hasFieldOrPropertyWithValue("isActive", true)
|
||||
.hasFieldOrPropertyWithValue("isNotLocked", true)
|
||||
.hasFieldOrPropertyWithValue("role", "ROLE_ADMIN");
|
||||
}
|
||||
|
||||
@Test
|
||||
void addNewUser_missedFirstName() {
|
||||
|
||||
//given
|
||||
UserDto userDto = createRandomUserDto();
|
||||
Map<String, ?> paramMap = Map.of(
|
||||
"lastName", userDto.getLastName(),
|
||||
"username", userDto.getUsername(),
|
||||
"email", userDto.getEmail(),
|
||||
"role", userDto.getRole().name(),
|
||||
"isActive", String.valueOf(userDto.isActive()),
|
||||
"isNonLocked", String.valueOf(userDto.isNonLocked())
|
||||
);
|
||||
|
||||
//when
|
||||
var responseEntity = restTemplate
|
||||
.postForEntity(
|
||||
"/user/add?username={username}&email={email}" +
|
||||
"&lastName={lastName}" +
|
||||
"&role={role}&active={isActive}&nonLocked={isNonLocked}",
|
||||
null,
|
||||
HttpResponse.class,
|
||||
paramMap
|
||||
);
|
||||
|
||||
//then
|
||||
log.debug("Response Entity: {}", responseEntity);
|
||||
assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST);
|
||||
assertThat(responseEntity.getBody())
|
||||
.isNotNull()
|
||||
.hasNoNullFieldsOrProperties()
|
||||
.hasFieldOrPropertyWithValue("httpStatus", BAD_REQUEST)
|
||||
.hasFieldOrPropertyWithValue("message", "ERROR(S) IN PARAMETERS: [FIRSTNAME:SHOULD NOT BE EMPTY]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void addNewUser_wrongRole() {
|
||||
|
||||
//given
|
||||
UserDto userDto = createRandomUserDto();
|
||||
Map<String, ?> paramMap = Map.of(
|
||||
"firstName", userDto.getFirstName(),
|
||||
"lastName", userDto.getLastName(),
|
||||
"username", userDto.getUsername(),
|
||||
"email", userDto.getEmail(),
|
||||
"role", "ROLE_FAKE",
|
||||
"isActive", String.valueOf(userDto.isActive()),
|
||||
"isNonLocked", String.valueOf(userDto.isNonLocked())
|
||||
);
|
||||
|
||||
//when
|
||||
var responseEntity = restTemplate
|
||||
.postForEntity(
|
||||
"/user/add?username={username}&email={email}" +
|
||||
"&firstName={firstName}&lastName={lastName}" +
|
||||
"&role={role}&active={isActive}&nonLocked={isNonLocked}",
|
||||
null,
|
||||
HttpResponse.class,
|
||||
paramMap
|
||||
);
|
||||
|
||||
//then
|
||||
log.debug("Response Entity: {}", responseEntity);
|
||||
assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST);
|
||||
assertThat(responseEntity.getBody())
|
||||
.isNotNull()
|
||||
.hasNoNullFieldsOrProperties()
|
||||
.hasFieldOrPropertyWithValue("httpStatus", BAD_REQUEST)
|
||||
.hasFieldOrPropertyWithValue("message", "ERROR(S) IN PARAMETERS: [ROLE:FAILED TO CONVERT PROPERTY VALUE OF TYPE 'JAVA.LANG.STRING' TO REQUIRED TYPE 'NET.SHYSHKIN.STUDY.FULLSTACK.SUPPORTPORTAL.BACKEND.DOMAIN.ROLE' FOR PROPERTY 'ROLE'; NESTED EXCEPTION IS ORG.SPRINGFRAMEWORK.CORE.CONVERT.CONVERSIONFAILEDEXCEPTION: FAILED TO CONVERT FROM TYPE [JAVA.LANG.STRING] TO TYPE [@JAVAX.VALIDATION.CONSTRAINTS.NOTNULL NET.SHYSHKIN.STUDY.FULLSTACK.SUPPORTPORTAL.BACKEND.DOMAIN.ROLE] FOR VALUE 'ROLE_FAKE'; NESTED EXCEPTION IS JAVA.LANG.ILLEGALARGUMENTEXCEPTION: NO ENUM CONSTANT NET.SHYSHKIN.STUDY.FULLSTACK.SUPPORTPORTAL.BACKEND.DOMAIN.ROLE.ROLE_FAKE]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void addNewUser_incorrectEmail() {
|
||||
|
||||
//given
|
||||
UserDto userDto = createRandomUserDto();
|
||||
Map<String, ?> paramMap = Map.of(
|
||||
"firstName", userDto.getFirstName(),
|
||||
"lastName", userDto.getLastName(),
|
||||
"username", userDto.getUsername(),
|
||||
"email", "not_an_email",
|
||||
"role", userDto.getRole().name(),
|
||||
"isActive", String.valueOf(userDto.isActive()),
|
||||
"isNonLocked", String.valueOf(userDto.isNonLocked())
|
||||
);
|
||||
|
||||
//when
|
||||
var responseEntity = restTemplate
|
||||
.postForEntity(
|
||||
"/user/add?username={username}&email={email}" +
|
||||
"&firstName={firstName}&lastName={lastName}" +
|
||||
"&role={role}&active={isActive}&nonLocked={isNonLocked}",
|
||||
null,
|
||||
HttpResponse.class,
|
||||
paramMap
|
||||
);
|
||||
|
||||
//then
|
||||
log.debug("Response Entity: {}", responseEntity);
|
||||
assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST);
|
||||
assertThat(responseEntity.getBody())
|
||||
.isNotNull()
|
||||
.hasNoNullFieldsOrProperties()
|
||||
.hasFieldOrPropertyWithValue("httpStatus", BAD_REQUEST)
|
||||
.hasFieldOrPropertyWithValue("message", "ERROR(S) IN PARAMETERS: [EMAIL:MUST MATCH EMAIL FORMAT]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void addNewUser_incorrectBoolean() {
|
||||
|
||||
//given
|
||||
UserDto userDto = createRandomUserDto();
|
||||
Map<String, ?> paramMap = Map.of(
|
||||
"firstName", userDto.getFirstName(),
|
||||
"lastName", userDto.getLastName(),
|
||||
"username", userDto.getUsername(),
|
||||
"email", userDto.getEmail(),
|
||||
"role", userDto.getRole().name(),
|
||||
"isActive", "yes",
|
||||
"isNonLocked", "not_a_boolean"
|
||||
);
|
||||
|
||||
//when
|
||||
var responseEntity = restTemplate
|
||||
.postForEntity(
|
||||
"/user/add?username={username}&email={email}" +
|
||||
"&firstName={firstName}&lastName={lastName}" +
|
||||
"&role={role}&active={isActive}&nonLocked={isNonLocked}",
|
||||
null,
|
||||
HttpResponse.class,
|
||||
paramMap
|
||||
);
|
||||
|
||||
//then
|
||||
log.debug("Response Entity: {}", responseEntity);
|
||||
assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST);
|
||||
assertThat(responseEntity.getBody())
|
||||
.isNotNull()
|
||||
.hasNoNullFieldsOrProperties()
|
||||
.hasFieldOrPropertyWithValue("httpStatus", BAD_REQUEST)
|
||||
.hasFieldOrPropertyWithValue("message", "ERROR(S) IN PARAMETERS: [NONLOCKED:FAILED TO CONVERT PROPERTY VALUE OF TYPE 'JAVA.LANG.STRING' TO REQUIRED TYPE 'BOOLEAN' FOR PROPERTY 'NONLOCKED'; NESTED EXCEPTION IS JAVA.LANG.ILLEGALARGUMENTEXCEPTION: INVALID BOOLEAN VALUE [NOT_A_BOOLEAN]]");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user