77.2. Validation (#9)

This commit is contained in:
Art
2021-09-09 21:24:15 +03:00
parent 6c4b06233d
commit beaab1d298
5 changed files with 217 additions and 38 deletions

View File

@ -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>

View File

@ -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);
}

View File

@ -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;

View File

@ -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());

View File

@ -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,8 +29,11 @@ class UserResourceUnSecureTest extends BaseUserTest {
@Autowired
TestRestTemplate restTemplate;
@Nested
class AddNewUserTests {
@Test
void addNewUser() {
void addNewUser_correct() {
//given
UserDto userDto = createRandomUserDto();
@ -66,4 +72,148 @@ class UserResourceUnSecureTest extends BaseUserTest {
.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]]");
}
}
}