From d047c177009b7763b8bce584a89a36dc95b1440e Mon Sep 17 00:00:00 2001 From: KamilM1205 Date: Mon, 30 Jun 2025 00:28:21 +0400 Subject: [PATCH 1/3] Added: email validation, email validation exception. --- .../profileservice/controller/UserController.java | 5 +++++ .../advice/UserControllerExceptionHandler.java | 6 ++++++ .../exceptions/IncorrectEmailException.java | 4 ++++ .../team58/profileservice/utils/EmailValidator.java | 13 +++++++++++++ 4 files changed, 28 insertions(+) create mode 100644 src/main/java/ru/team58/profileservice/controller/exceptions/IncorrectEmailException.java create mode 100644 src/main/java/ru/team58/profileservice/utils/EmailValidator.java diff --git a/src/main/java/ru/team58/profileservice/controller/UserController.java b/src/main/java/ru/team58/profileservice/controller/UserController.java index 2c18b1b..36738de 100644 --- a/src/main/java/ru/team58/profileservice/controller/UserController.java +++ b/src/main/java/ru/team58/profileservice/controller/UserController.java @@ -3,6 +3,7 @@ package ru.team58.profileservice.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import ru.team58.profileservice.controller.exceptions.BadRequestException; +import ru.team58.profileservice.controller.exceptions.IncorrectEmailException; import ru.team58.profileservice.controller.exceptions.UnauthorizedException; import ru.team58.profileservice.controller.exceptions.UserNotFoundException; import ru.team58.profileservice.controller.schemes.CreateUserRequest; @@ -13,6 +14,7 @@ import ru.team58.profileservice.mapper.UserMapper; import ru.team58.profileservice.service.UserDTO; import ru.team58.profileservice.service.UserService; import ru.team58.profileservice.service.exceptions.UserAlreadyExistsException; +import ru.team58.profileservice.utils.EmailValidator; import java.security.Principal; import java.util.UUID; @@ -73,6 +75,9 @@ public class UserController { public void createMe(@RequestBody CreateUserRequest request) { UserDTO user = UserMapper.INSTANCE.toUserDTO(request); + if (!EmailValidator.validateEmail(user.getEmail())) + throw new IncorrectEmailException(); + try { userService.createUser(user); } catch (UserAlreadyExistsException ex) { diff --git a/src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java b/src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java index bd0d990..a45a5ee 100644 --- a/src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java +++ b/src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java @@ -37,4 +37,10 @@ public class UserControllerExceptionHandler { ErrorDetails errorDetails = new ErrorDetails("Unauthorized", HttpStatus.UNAUTHORIZED.value()); return new ResponseEntity<>(errorDetails, HttpStatus.UNAUTHORIZED); } + + @ExceptionHandler(IncorrectEmailException.class) + public ResponseEntity handleIncorrectEmailException(IncorrectEmailException ex) { + ErrorDetails errorDetails = new ErrorDetails("Email is incorrect", HttpStatus.BAD_REQUEST.value()); + return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST); + } } diff --git a/src/main/java/ru/team58/profileservice/controller/exceptions/IncorrectEmailException.java b/src/main/java/ru/team58/profileservice/controller/exceptions/IncorrectEmailException.java new file mode 100644 index 0000000..7e35e40 --- /dev/null +++ b/src/main/java/ru/team58/profileservice/controller/exceptions/IncorrectEmailException.java @@ -0,0 +1,4 @@ +package ru.team58.profileservice.controller.exceptions; + +public class IncorrectEmailException extends BadRequestException { +} diff --git a/src/main/java/ru/team58/profileservice/utils/EmailValidator.java b/src/main/java/ru/team58/profileservice/utils/EmailValidator.java new file mode 100644 index 0000000..84e007b --- /dev/null +++ b/src/main/java/ru/team58/profileservice/utils/EmailValidator.java @@ -0,0 +1,13 @@ +package ru.team58.profileservice.utils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class EmailValidator { + public static boolean validateEmail(String email) { + String regexPattern = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@" + + "[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$"; + + return Pattern.compile(regexPattern).matcher(email).matches(); + } +} From beb275d7cbabc99724fbdb3bf55ff73cdc9611dd Mon Sep 17 00:00:00 2001 From: KamilM1205 Date: Mon, 30 Jun 2025 22:12:52 +0400 Subject: [PATCH 2/3] Added: Validation for all fields of requests. Refactor: mappers struct. --- build.gradle.kts | 1 + .../controller/UserController.java | 25 +++++------- .../UserControllerExceptionHandler.java | 20 ++++++++-- .../controller/schemes/CreateUserRequest.java | 15 +++---- .../controller/schemes/UpdateUserRequest.java | 15 +++---- .../controller/schemes/UserScheme.java | 40 +++++++++++++++++++ .../mapper/CreateUserMapper.java | 16 ++++++++ .../profileservice/mapper/GetUserMapper.java | 13 ++++++ .../mapper/UpdateUserMapper.java | 13 ++++++ .../profileservice/mapper/UserDTOMapper.java | 13 ++++++ .../mapper/UserEntityMapper.java | 13 ++++++ .../profileservice/mapper/UserMapper.java | 27 ------------- .../mapper/UserResponseMapper.java | 13 ++++++ .../mapper/UserSchemeMapper.java | 15 +++++++ .../persistence/entity/UserEntity.java | 1 + .../profileservice/service/UserDTO.java | 7 +--- .../service/UserServiceImpl.java | 11 ++--- .../profileservice/utils/EmailValidator.java | 13 ------ 18 files changed, 182 insertions(+), 89 deletions(-) create mode 100644 src/main/java/ru/team58/profileservice/controller/schemes/UserScheme.java create mode 100644 src/main/java/ru/team58/profileservice/mapper/CreateUserMapper.java create mode 100644 src/main/java/ru/team58/profileservice/mapper/GetUserMapper.java create mode 100644 src/main/java/ru/team58/profileservice/mapper/UpdateUserMapper.java create mode 100644 src/main/java/ru/team58/profileservice/mapper/UserDTOMapper.java create mode 100644 src/main/java/ru/team58/profileservice/mapper/UserEntityMapper.java delete mode 100644 src/main/java/ru/team58/profileservice/mapper/UserMapper.java create mode 100644 src/main/java/ru/team58/profileservice/mapper/UserResponseMapper.java create mode 100644 src/main/java/ru/team58/profileservice/mapper/UserSchemeMapper.java delete mode 100644 src/main/java/ru/team58/profileservice/utils/EmailValidator.java diff --git a/build.gradle.kts b/build.gradle.kts index 39bf11b..133f628 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,6 +41,7 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-validation") implementation("org.flywaydb:flyway-core") implementation("org.flywaydb:flyway-database-postgresql:11.10.0") implementation("org.postgresql:postgresql") diff --git a/src/main/java/ru/team58/profileservice/controller/UserController.java b/src/main/java/ru/team58/profileservice/controller/UserController.java index 36738de..a8affb8 100644 --- a/src/main/java/ru/team58/profileservice/controller/UserController.java +++ b/src/main/java/ru/team58/profileservice/controller/UserController.java @@ -1,20 +1,20 @@ package ru.team58.profileservice.controller; +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import ru.team58.profileservice.controller.exceptions.BadRequestException; -import ru.team58.profileservice.controller.exceptions.IncorrectEmailException; import ru.team58.profileservice.controller.exceptions.UnauthorizedException; import ru.team58.profileservice.controller.exceptions.UserNotFoundException; import ru.team58.profileservice.controller.schemes.CreateUserRequest; import ru.team58.profileservice.controller.schemes.GetUserResponse; import ru.team58.profileservice.controller.schemes.UpdateUserRequest; import ru.team58.profileservice.controller.schemes.UserResponse; -import ru.team58.profileservice.mapper.UserMapper; +import ru.team58.profileservice.mapper.*; import ru.team58.profileservice.service.UserDTO; import ru.team58.profileservice.service.UserService; import ru.team58.profileservice.service.exceptions.UserAlreadyExistsException; -import ru.team58.profileservice.utils.EmailValidator; import java.security.Principal; import java.util.UUID; @@ -36,11 +36,11 @@ public class UserController { throw new UserNotFoundException(); } - return UserMapper.INSTANCE.toUserResponse(userService.getByUsername(principal.getName())); + return UserResponseMapper.INSTANCE.toUserResponse(userService.getByUsername(principal.getName())); } - @PutMapping("/") - public void updateMe(Principal principal, @RequestBody UpdateUserRequest request) { + @PutMapping(value = "/", consumes = {MediaType.APPLICATION_JSON_VALUE}) + public void updateMe(Principal principal, @Valid @RequestBody UpdateUserRequest request) { if (principal == null) { throw new UnauthorizedException(); } @@ -51,7 +51,7 @@ public class UserController { throw new UserNotFoundException(); } - userService.updateUser(UserMapper.INSTANCE.toUserDTO(request)); + userService.updateUser(UpdateUserMapper.INSTANCE.toUserDTO(request)); } @DeleteMapping("/") @@ -71,12 +71,9 @@ public class UserController { userService.deleteUser(user.getId()); } - @PostMapping("/") - public void createMe(@RequestBody CreateUserRequest request) { - UserDTO user = UserMapper.INSTANCE.toUserDTO(request); - - if (!EmailValidator.validateEmail(user.getEmail())) - throw new IncorrectEmailException(); + @PostMapping(value = "/", consumes = MediaType.APPLICATION_JSON_VALUE) + public void createMe(@Valid @RequestBody CreateUserRequest request) { + UserDTO user = CreateUserMapper.INSTANCE.toUserDTO(request); try { userService.createUser(user); @@ -97,7 +94,7 @@ public class UserController { throw new UserNotFoundException(); } - return UserMapper.INSTANCE.toGetUserResponse(user); + return GetUserMapper.INSTANCE.toGetUserResponse(user); } @DeleteMapping("/{id}") diff --git a/src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java b/src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java index a45a5ee..d72c55f 100644 --- a/src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java +++ b/src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java @@ -2,10 +2,16 @@ package ru.team58.profileservice.controller.advice; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; import ru.team58.profileservice.controller.exceptions.*; +import java.util.HashMap; +import java.util.Map; + @ControllerAdvice public class UserControllerExceptionHandler { @ExceptionHandler(UserNotFoundException.class) @@ -38,9 +44,15 @@ public class UserControllerExceptionHandler { return new ResponseEntity<>(errorDetails, HttpStatus.UNAUTHORIZED); } - @ExceptionHandler(IncorrectEmailException.class) - public ResponseEntity handleIncorrectEmailException(IncorrectEmailException ex) { - ErrorDetails errorDetails = new ErrorDetails("Email is incorrect", HttpStatus.BAD_REQUEST.value()); - return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST); + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity> handleValidationExceptions( + MethodArgumentNotValidException ex) { + Map errors = new HashMap<>(); + ex.getBindingResult().getAllErrors().forEach((error) -> { + String fieldName = ((FieldError) error).getField(); + String errorMessage = error.getDefaultMessage(); + errors.put(fieldName, errorMessage); + }); + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); } } diff --git a/src/main/java/ru/team58/profileservice/controller/schemes/CreateUserRequest.java b/src/main/java/ru/team58/profileservice/controller/schemes/CreateUserRequest.java index 6344ece..428ab9a 100644 --- a/src/main/java/ru/team58/profileservice/controller/schemes/CreateUserRequest.java +++ b/src/main/java/ru/team58/profileservice/controller/schemes/CreateUserRequest.java @@ -1,15 +1,10 @@ package ru.team58.profileservice.controller.schemes; -import lombok.Builder; -import lombok.Getter; -import lombok.Setter; +import lombok.*; +import lombok.experimental.SuperBuilder; @Getter @Setter -@Builder -public class CreateUserRequest { - String username; - String firstName; - String lastName; - String email; -} +@SuperBuilder +@NoArgsConstructor +public class CreateUserRequest extends UserScheme {} diff --git a/src/main/java/ru/team58/profileservice/controller/schemes/UpdateUserRequest.java b/src/main/java/ru/team58/profileservice/controller/schemes/UpdateUserRequest.java index 6548494..51cadcb 100644 --- a/src/main/java/ru/team58/profileservice/controller/schemes/UpdateUserRequest.java +++ b/src/main/java/ru/team58/profileservice/controller/schemes/UpdateUserRequest.java @@ -1,18 +1,13 @@ package ru.team58.profileservice.controller.schemes; -import lombok.Builder; -import lombok.Getter; -import lombok.Setter; +import lombok.*; +import lombok.experimental.SuperBuilder; import java.util.UUID; @Getter @Setter -@Builder -public class UpdateUserRequest { - UUID id; - String username; - String firstName; - String lastName; - String email; +@SuperBuilder +@NoArgsConstructor +public class UpdateUserRequest extends UserScheme { } diff --git a/src/main/java/ru/team58/profileservice/controller/schemes/UserScheme.java b/src/main/java/ru/team58/profileservice/controller/schemes/UserScheme.java new file mode 100644 index 0000000..8965b6e --- /dev/null +++ b/src/main/java/ru/team58/profileservice/controller/schemes/UserScheme.java @@ -0,0 +1,40 @@ +package ru.team58.profileservice.controller.schemes; + +import jakarta.validation.constraints.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +import java.util.UUID; + +@SuperBuilder +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class UserScheme { + private UUID id; + + @NotBlank(message = "Username cannot be empty") + @Pattern(regexp = "^[a-z0-9.]+$", message = "Username can have only characters: a-z and 0-9") + @Size(min = 3, max = 14, message = "Username must be bigger than 3 and less than 14") + private String username; + + @NotBlank(message = "First name cannot be empty") + @Pattern(regexp = "^[a-zA-Zа-яА-Я]*$", message = "First name can contain only letters") + @Size(min = 3, max = 255, message = "First name must be bigger than 3 and less than 255") + private String firstName; + + @NotBlank(message = "Last name cannot be empty") + @Pattern(regexp = "^[a-zA-Zа-яА-Я]*$", message = "Last name can contain only letters") + @Size(min = 3, max = 255, message = "Last name must be bigger than 3 and less than 255") + private String lastName; + + @Email(regexp = "[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,3}", message = """ + Email can contain: a-z, 0-9, '.', '_', '%', '+', '-'. + And have format like: "example@domain.com".""") + @Size(min = 5, max = 255, message = "Email must be bigger than 5 and less than 255") + private String email; +} diff --git a/src/main/java/ru/team58/profileservice/mapper/CreateUserMapper.java b/src/main/java/ru/team58/profileservice/mapper/CreateUserMapper.java new file mode 100644 index 0000000..ca6e501 --- /dev/null +++ b/src/main/java/ru/team58/profileservice/mapper/CreateUserMapper.java @@ -0,0 +1,16 @@ +package ru.team58.profileservice.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; +import ru.team58.profileservice.controller.schemes.CreateUserRequest; +import ru.team58.profileservice.service.UserDTO; + +@Mapper +public interface CreateUserMapper { + CreateUserMapper INSTANCE = Mappers.getMapper(CreateUserMapper.class); + + @Mapping(target = "id", ignore = true) + UserDTO toUserDTO(CreateUserRequest request); + +} diff --git a/src/main/java/ru/team58/profileservice/mapper/GetUserMapper.java b/src/main/java/ru/team58/profileservice/mapper/GetUserMapper.java new file mode 100644 index 0000000..44321f1 --- /dev/null +++ b/src/main/java/ru/team58/profileservice/mapper/GetUserMapper.java @@ -0,0 +1,13 @@ +package ru.team58.profileservice.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; +import ru.team58.profileservice.controller.schemes.GetUserResponse; +import ru.team58.profileservice.service.UserDTO; + +@Mapper +public interface GetUserMapper { + GetUserMapper INSTANCE = Mappers.getMapper(GetUserMapper.class); + + GetUserResponse toGetUserResponse(UserDTO dto); +} diff --git a/src/main/java/ru/team58/profileservice/mapper/UpdateUserMapper.java b/src/main/java/ru/team58/profileservice/mapper/UpdateUserMapper.java new file mode 100644 index 0000000..165dd8f --- /dev/null +++ b/src/main/java/ru/team58/profileservice/mapper/UpdateUserMapper.java @@ -0,0 +1,13 @@ +package ru.team58.profileservice.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; +import ru.team58.profileservice.controller.schemes.UpdateUserRequest; +import ru.team58.profileservice.service.UserDTO; + +@Mapper +public interface UpdateUserMapper { + UpdateUserMapper INSTANCE = Mappers.getMapper(UpdateUserMapper.class); + + UserDTO toUserDTO(UpdateUserRequest request); +} diff --git a/src/main/java/ru/team58/profileservice/mapper/UserDTOMapper.java b/src/main/java/ru/team58/profileservice/mapper/UserDTOMapper.java new file mode 100644 index 0000000..91e8300 --- /dev/null +++ b/src/main/java/ru/team58/profileservice/mapper/UserDTOMapper.java @@ -0,0 +1,13 @@ +package ru.team58.profileservice.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; +import ru.team58.profileservice.persistence.entity.UserEntity; +import ru.team58.profileservice.service.UserDTO; + +@Mapper +public interface UserDTOMapper { + UserDTOMapper INSTANCE = Mappers.getMapper(UserDTOMapper.class); + + UserDTO toUserDTO(UserEntity entity); +} diff --git a/src/main/java/ru/team58/profileservice/mapper/UserEntityMapper.java b/src/main/java/ru/team58/profileservice/mapper/UserEntityMapper.java new file mode 100644 index 0000000..75b85c1 --- /dev/null +++ b/src/main/java/ru/team58/profileservice/mapper/UserEntityMapper.java @@ -0,0 +1,13 @@ +package ru.team58.profileservice.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; +import ru.team58.profileservice.persistence.entity.UserEntity; +import ru.team58.profileservice.service.UserDTO; + +@Mapper +public interface UserEntityMapper { + UserEntityMapper INSTANCE = Mappers.getMapper(UserEntityMapper.class); + + UserEntity toUserEntity(UserDTO dto); +} diff --git a/src/main/java/ru/team58/profileservice/mapper/UserMapper.java b/src/main/java/ru/team58/profileservice/mapper/UserMapper.java deleted file mode 100644 index 857bcae..0000000 --- a/src/main/java/ru/team58/profileservice/mapper/UserMapper.java +++ /dev/null @@ -1,27 +0,0 @@ -package ru.team58.profileservice.mapper; - -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.factory.Mappers; -import ru.team58.profileservice.controller.schemes.CreateUserRequest; -import ru.team58.profileservice.controller.schemes.GetUserResponse; -import ru.team58.profileservice.controller.schemes.UpdateUserRequest; -import ru.team58.profileservice.controller.schemes.UserResponse; -import ru.team58.profileservice.persistence.entity.UserEntity; -import ru.team58.profileservice.service.UserDTO; - -@Mapper(componentModel = "spring") -public interface UserMapper { - UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); - - UserDTO toUserDTO(UserEntity entity); - UserDTO toUserDTO(UpdateUserRequest request); - @Mapping(target = "id", ignore = true) - UserDTO toUserDTO(CreateUserRequest request); - - UserEntity toUserEntity(UserDTO dto); - - UserResponse toUserResponse(UserDTO dto); - - GetUserResponse toGetUserResponse(UserDTO dto); -} diff --git a/src/main/java/ru/team58/profileservice/mapper/UserResponseMapper.java b/src/main/java/ru/team58/profileservice/mapper/UserResponseMapper.java new file mode 100644 index 0000000..d79cf3b --- /dev/null +++ b/src/main/java/ru/team58/profileservice/mapper/UserResponseMapper.java @@ -0,0 +1,13 @@ +package ru.team58.profileservice.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; +import ru.team58.profileservice.controller.schemes.UserResponse; +import ru.team58.profileservice.service.UserDTO; + +@Mapper +public interface UserResponseMapper { + UserResponseMapper INSTANCE = Mappers.getMapper(UserResponseMapper.class); + + UserResponse toUserResponse(UserDTO dto); +} diff --git a/src/main/java/ru/team58/profileservice/mapper/UserSchemeMapper.java b/src/main/java/ru/team58/profileservice/mapper/UserSchemeMapper.java new file mode 100644 index 0000000..de00ed4 --- /dev/null +++ b/src/main/java/ru/team58/profileservice/mapper/UserSchemeMapper.java @@ -0,0 +1,15 @@ +package ru.team58.profileservice.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; +import ru.team58.profileservice.controller.schemes.UserScheme; +import ru.team58.profileservice.service.UserDTO; + +@Mapper(uses = {CreateUserMapper.class, UpdateUserMapper.class}) +public interface UserSchemeMapper { + UserSchemeMapper INSTANCE = Mappers.getMapper(UserSchemeMapper.class); + + @Mapping(target = "id", ignore = true) + UserDTO toUserDTO(UserScheme user); +} diff --git a/src/main/java/ru/team58/profileservice/persistence/entity/UserEntity.java b/src/main/java/ru/team58/profileservice/persistence/entity/UserEntity.java index b509abe..6cb0d41 100644 --- a/src/main/java/ru/team58/profileservice/persistence/entity/UserEntity.java +++ b/src/main/java/ru/team58/profileservice/persistence/entity/UserEntity.java @@ -1,6 +1,7 @@ package ru.team58.profileservice.persistence.entity; import jakarta.persistence.*; +import jakarta.validation.constraints.*; import lombok.*; import java.util.UUID; diff --git a/src/main/java/ru/team58/profileservice/service/UserDTO.java b/src/main/java/ru/team58/profileservice/service/UserDTO.java index d0834f6..d23bd29 100644 --- a/src/main/java/ru/team58/profileservice/service/UserDTO.java +++ b/src/main/java/ru/team58/profileservice/service/UserDTO.java @@ -1,12 +1,7 @@ package ru.team58.profileservice.service; +import jakarta.validation.Valid; import lombok.*; -import ru.team58.profileservice.persistence.entity.UserEntity; - -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Objects; import java.util.UUID; @Getter diff --git a/src/main/java/ru/team58/profileservice/service/UserServiceImpl.java b/src/main/java/ru/team58/profileservice/service/UserServiceImpl.java index 821adcb..69a9ddc 100644 --- a/src/main/java/ru/team58/profileservice/service/UserServiceImpl.java +++ b/src/main/java/ru/team58/profileservice/service/UserServiceImpl.java @@ -2,7 +2,8 @@ package ru.team58.profileservice.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import ru.team58.profileservice.mapper.UserMapper; +import ru.team58.profileservice.mapper.UserDTOMapper; +import ru.team58.profileservice.mapper.UserEntityMapper; import ru.team58.profileservice.persistence.entity.UserEntity; import ru.team58.profileservice.persistence.repo.UserRepository; import ru.team58.profileservice.service.exceptions.UserAlreadyExistsException; @@ -18,13 +19,13 @@ public class UserServiceImpl implements UserService { @Override public UserDTO getById(UUID id) { UserEntity user = userRepository.findById(id).orElseThrow(UserNotFoundException::new); - return UserMapper.INSTANCE.toUserDTO(user); + return UserDTOMapper.INSTANCE.toUserDTO(user); } @Override public UserDTO getByUsername(String username) { UserEntity user = userRepository.findByUsername(username).orElseThrow(UserNotFoundException::new); - return UserMapper.INSTANCE.toUserDTO(user); + return UserDTOMapper.INSTANCE.toUserDTO(user); } @Override @@ -32,7 +33,7 @@ public class UserServiceImpl implements UserService { userRepository.findByUsername(user.username).ifPresent(u -> { throw new UserAlreadyExistsException(); }); - return userRepository.save(UserMapper.INSTANCE.toUserEntity(user)).getId(); + return userRepository.save(UserEntityMapper.INSTANCE.toUserEntity(user)).getId(); } @Override @@ -44,6 +45,6 @@ public class UserServiceImpl implements UserService { @Override public void updateUser(UserDTO user) { userRepository.findById(user.getId()).orElseThrow(UserNotFoundException::new); - userRepository.save(UserMapper.INSTANCE.toUserEntity(user)); + userRepository.save(UserEntityMapper.INSTANCE.toUserEntity(user)); } } diff --git a/src/main/java/ru/team58/profileservice/utils/EmailValidator.java b/src/main/java/ru/team58/profileservice/utils/EmailValidator.java deleted file mode 100644 index 84e007b..0000000 --- a/src/main/java/ru/team58/profileservice/utils/EmailValidator.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.team58.profileservice.utils; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class EmailValidator { - public static boolean validateEmail(String email) { - String regexPattern = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@" - + "[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$"; - - return Pattern.compile(regexPattern).matcher(email).matches(); - } -} From b88414c69983f922115017469c27a5c551553807 Mon Sep 17 00:00:00 2001 From: KamilM1205 Date: Wed, 23 Jul 2025 14:59:50 +0400 Subject: [PATCH 3/3] Added: - Validation for all fields of requests. - ErrorDTO Refactor: mappers struct. --- .../controller/UserController.java | 20 ++++++-- .../controller/advice/ErrorDTO.java | 16 +++++++ .../UserControllerExceptionHandler.java | 48 +++++++++++-------- .../controller/schemes/UserScheme.java | 1 + .../mapper/CreateUserMapper.java | 6 +-- .../profileservice/mapper/GetUserMapper.java | 4 +- .../mapper/UpdateUserMapper.java | 5 +- .../profileservice/mapper/UserDTOMapper.java | 5 +- .../mapper/UserEntityMapper.java | 5 +- .../mapper/UserResponseMapper.java | 5 +- .../mapper/UserSchemeMapper.java | 3 -- .../service/UserServiceImpl.java | 13 +++-- .../profileservice/ProfileServiceTest.java | 12 +++++ 13 files changed, 87 insertions(+), 56 deletions(-) create mode 100644 src/main/java/ru/team58/profileservice/controller/advice/ErrorDTO.java create mode 100644 src/test/java/ru/team58/profileservice/ProfileServiceTest.java diff --git a/src/main/java/ru/team58/profileservice/controller/UserController.java b/src/main/java/ru/team58/profileservice/controller/UserController.java index a8affb8..446b742 100644 --- a/src/main/java/ru/team58/profileservice/controller/UserController.java +++ b/src/main/java/ru/team58/profileservice/controller/UserController.java @@ -24,6 +24,18 @@ public class UserController { @Autowired UserService userService; + @Autowired + UserResponseMapper userResponseMapper; + + @Autowired + GetUserMapper getUserMapper; + + @Autowired + UpdateUserMapper updateUserMapper; + + @Autowired + CreateUserMapper createUserMapper; + @GetMapping("/") public UserResponse getMe(Principal principal) { if (principal == null) { @@ -36,7 +48,7 @@ public class UserController { throw new UserNotFoundException(); } - return UserResponseMapper.INSTANCE.toUserResponse(userService.getByUsername(principal.getName())); + return userResponseMapper.toUserResponse(userService.getByUsername(principal.getName())); } @PutMapping(value = "/", consumes = {MediaType.APPLICATION_JSON_VALUE}) @@ -51,7 +63,7 @@ public class UserController { throw new UserNotFoundException(); } - userService.updateUser(UpdateUserMapper.INSTANCE.toUserDTO(request)); + userService.updateUser(updateUserMapper.toUserDTO(request)); } @DeleteMapping("/") @@ -73,7 +85,7 @@ public class UserController { @PostMapping(value = "/", consumes = MediaType.APPLICATION_JSON_VALUE) public void createMe(@Valid @RequestBody CreateUserRequest request) { - UserDTO user = CreateUserMapper.INSTANCE.toUserDTO(request); + UserDTO user = createUserMapper.toUserDTO(request); try { userService.createUser(user); @@ -94,7 +106,7 @@ public class UserController { throw new UserNotFoundException(); } - return GetUserMapper.INSTANCE.toGetUserResponse(user); + return getUserMapper.toGetUserResponse(user); } @DeleteMapping("/{id}") diff --git a/src/main/java/ru/team58/profileservice/controller/advice/ErrorDTO.java b/src/main/java/ru/team58/profileservice/controller/advice/ErrorDTO.java new file mode 100644 index 0000000..a22df5a --- /dev/null +++ b/src/main/java/ru/team58/profileservice/controller/advice/ErrorDTO.java @@ -0,0 +1,16 @@ +package ru.team58.profileservice.controller.advice; + +import lombok.*; +import org.springframework.http.HttpStatus; + +import java.util.List; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder +public class ErrorDTO { + HttpStatus status; + List errors; +} diff --git a/src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java b/src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java index d72c55f..b9f7546 100644 --- a/src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java +++ b/src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java @@ -6,53 +6,59 @@ import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import ru.team58.profileservice.controller.exceptions.*; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; @ControllerAdvice public class UserControllerExceptionHandler { @ExceptionHandler(UserNotFoundException.class) - public ResponseEntity handleUserNotFoundException(UserNotFoundException ex) { - ErrorDetails errorDetails = new ErrorDetails("User not found", HttpStatus.FORBIDDEN.value()); - return new ResponseEntity<>(errorDetails, HttpStatus.FORBIDDEN); + public ResponseEntity handleUserNotFoundException(UserNotFoundException ex) { + var error = new ErrorDTO(HttpStatus.FORBIDDEN, List.of("User not found")); + return ResponseEntity.status(error.status).body(error); } @ExceptionHandler(UserAlreadyExistsException.class) - public ResponseEntity handleUserAlreadyExistsException(UserAlreadyExistsException ex) { - ErrorDetails errorDetails = new ErrorDetails("User already exists", HttpStatus.BAD_REQUEST.value()); - return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST); + public ResponseEntity handleUserAlreadyExistsException(UserAlreadyExistsException ex) { + var errorDetails = new ErrorDetails("User already exists", HttpStatus.BAD_REQUEST.value()); + var error = new ErrorDTO(HttpStatus.BAD_REQUEST, List.of("User already exists")); + return ResponseEntity.status(error.status).body(error); } @ExceptionHandler(BadRequestException.class) - public ResponseEntity handleBadRequestException(BadRequestException ex) { - ErrorDetails errorDetails = new ErrorDetails("Bad Request", HttpStatus.BAD_REQUEST.value()); - return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST); + public ResponseEntity handleBadRequestException(BadRequestException ex) { + var error = new ErrorDTO(HttpStatus.BAD_REQUEST, List.of("Bad request")); + return ResponseEntity.status(error.status).body(error); } @ExceptionHandler(ForbiddenException.class) - public ResponseEntity handleForbiddenException(ForbiddenException ex) { - ErrorDetails errorDetails = new ErrorDetails("Forbidden", HttpStatus.FORBIDDEN.value()); - return new ResponseEntity<>(errorDetails, HttpStatus.FORBIDDEN); + public ResponseEntity handleForbiddenException(ForbiddenException ex) { + var error = new ErrorDTO(HttpStatus.FORBIDDEN, List.of("Forbidden")); + return ResponseEntity.status(error.status).body(error); } @ExceptionHandler(UnauthorizedException.class) - public ResponseEntity handleUnauthorizedException(UnauthorizedException ex) { - ErrorDetails errorDetails = new ErrorDetails("Unauthorized", HttpStatus.UNAUTHORIZED.value()); - return new ResponseEntity<>(errorDetails, HttpStatus.UNAUTHORIZED); + public ResponseEntity handleUnauthorizedException(UnauthorizedException ex) { + var error = new ErrorDTO(HttpStatus.UNAUTHORIZED, List.of("Unauthorized")); + return ResponseEntity.status(error.status).body(error); } @ExceptionHandler(MethodArgumentNotValidException.class) - public ResponseEntity> handleValidationExceptions( + public ResponseEntity handleValidationExceptions( MethodArgumentNotValidException ex) { - Map errors = new HashMap<>(); + var errors = new ArrayList(); ex.getBindingResult().getAllErrors().forEach((error) -> { - String fieldName = ((FieldError) error).getField(); - String errorMessage = error.getDefaultMessage(); - errors.put(fieldName, errorMessage); + var fieldName = ((FieldError) error).getField(); + var errorMessage = error.getDefaultMessage(); + errors.add(String.format("%s: %s", fieldName, errorMessage)); }); - return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + + var error = new ErrorDTO(HttpStatus.BAD_REQUEST, errors); + return ResponseEntity.status(error.status).body(error); } } diff --git a/src/main/java/ru/team58/profileservice/controller/schemes/UserScheme.java b/src/main/java/ru/team58/profileservice/controller/schemes/UserScheme.java index 8965b6e..49246ba 100644 --- a/src/main/java/ru/team58/profileservice/controller/schemes/UserScheme.java +++ b/src/main/java/ru/team58/profileservice/controller/schemes/UserScheme.java @@ -32,6 +32,7 @@ public class UserScheme { @Size(min = 3, max = 255, message = "Last name must be bigger than 3 and less than 255") private String lastName; + @NotBlank @Email(regexp = "[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,3}", message = """ Email can contain: a-z, 0-9, '.', '_', '%', '+', '-'. And have format like: "example@domain.com".""") diff --git a/src/main/java/ru/team58/profileservice/mapper/CreateUserMapper.java b/src/main/java/ru/team58/profileservice/mapper/CreateUserMapper.java index ca6e501..b1a6df8 100644 --- a/src/main/java/ru/team58/profileservice/mapper/CreateUserMapper.java +++ b/src/main/java/ru/team58/profileservice/mapper/CreateUserMapper.java @@ -2,15 +2,11 @@ package ru.team58.profileservice.mapper; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.factory.Mappers; import ru.team58.profileservice.controller.schemes.CreateUserRequest; import ru.team58.profileservice.service.UserDTO; -@Mapper +@Mapper(componentModel = "spring") public interface CreateUserMapper { - CreateUserMapper INSTANCE = Mappers.getMapper(CreateUserMapper.class); - @Mapping(target = "id", ignore = true) UserDTO toUserDTO(CreateUserRequest request); - } diff --git a/src/main/java/ru/team58/profileservice/mapper/GetUserMapper.java b/src/main/java/ru/team58/profileservice/mapper/GetUserMapper.java index 44321f1..7750f75 100644 --- a/src/main/java/ru/team58/profileservice/mapper/GetUserMapper.java +++ b/src/main/java/ru/team58/profileservice/mapper/GetUserMapper.java @@ -5,9 +5,7 @@ import org.mapstruct.factory.Mappers; import ru.team58.profileservice.controller.schemes.GetUserResponse; import ru.team58.profileservice.service.UserDTO; -@Mapper +@Mapper(componentModel = "spring") public interface GetUserMapper { - GetUserMapper INSTANCE = Mappers.getMapper(GetUserMapper.class); - GetUserResponse toGetUserResponse(UserDTO dto); } diff --git a/src/main/java/ru/team58/profileservice/mapper/UpdateUserMapper.java b/src/main/java/ru/team58/profileservice/mapper/UpdateUserMapper.java index 165dd8f..f8e67ca 100644 --- a/src/main/java/ru/team58/profileservice/mapper/UpdateUserMapper.java +++ b/src/main/java/ru/team58/profileservice/mapper/UpdateUserMapper.java @@ -1,13 +1,10 @@ package ru.team58.profileservice.mapper; import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; import ru.team58.profileservice.controller.schemes.UpdateUserRequest; import ru.team58.profileservice.service.UserDTO; -@Mapper +@Mapper(componentModel = "spring") public interface UpdateUserMapper { - UpdateUserMapper INSTANCE = Mappers.getMapper(UpdateUserMapper.class); - UserDTO toUserDTO(UpdateUserRequest request); } diff --git a/src/main/java/ru/team58/profileservice/mapper/UserDTOMapper.java b/src/main/java/ru/team58/profileservice/mapper/UserDTOMapper.java index 91e8300..9d39f48 100644 --- a/src/main/java/ru/team58/profileservice/mapper/UserDTOMapper.java +++ b/src/main/java/ru/team58/profileservice/mapper/UserDTOMapper.java @@ -1,13 +1,10 @@ package ru.team58.profileservice.mapper; import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; import ru.team58.profileservice.persistence.entity.UserEntity; import ru.team58.profileservice.service.UserDTO; -@Mapper +@Mapper(componentModel = "spring") public interface UserDTOMapper { - UserDTOMapper INSTANCE = Mappers.getMapper(UserDTOMapper.class); - UserDTO toUserDTO(UserEntity entity); } diff --git a/src/main/java/ru/team58/profileservice/mapper/UserEntityMapper.java b/src/main/java/ru/team58/profileservice/mapper/UserEntityMapper.java index 75b85c1..6840427 100644 --- a/src/main/java/ru/team58/profileservice/mapper/UserEntityMapper.java +++ b/src/main/java/ru/team58/profileservice/mapper/UserEntityMapper.java @@ -1,13 +1,10 @@ package ru.team58.profileservice.mapper; import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; import ru.team58.profileservice.persistence.entity.UserEntity; import ru.team58.profileservice.service.UserDTO; -@Mapper +@Mapper(componentModel = "spring") public interface UserEntityMapper { - UserEntityMapper INSTANCE = Mappers.getMapper(UserEntityMapper.class); - UserEntity toUserEntity(UserDTO dto); } diff --git a/src/main/java/ru/team58/profileservice/mapper/UserResponseMapper.java b/src/main/java/ru/team58/profileservice/mapper/UserResponseMapper.java index d79cf3b..c810ba6 100644 --- a/src/main/java/ru/team58/profileservice/mapper/UserResponseMapper.java +++ b/src/main/java/ru/team58/profileservice/mapper/UserResponseMapper.java @@ -1,13 +1,10 @@ package ru.team58.profileservice.mapper; import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; import ru.team58.profileservice.controller.schemes.UserResponse; import ru.team58.profileservice.service.UserDTO; -@Mapper +@Mapper(componentModel = "spring") public interface UserResponseMapper { - UserResponseMapper INSTANCE = Mappers.getMapper(UserResponseMapper.class); - UserResponse toUserResponse(UserDTO dto); } diff --git a/src/main/java/ru/team58/profileservice/mapper/UserSchemeMapper.java b/src/main/java/ru/team58/profileservice/mapper/UserSchemeMapper.java index de00ed4..34a826c 100644 --- a/src/main/java/ru/team58/profileservice/mapper/UserSchemeMapper.java +++ b/src/main/java/ru/team58/profileservice/mapper/UserSchemeMapper.java @@ -2,14 +2,11 @@ package ru.team58.profileservice.mapper; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.factory.Mappers; import ru.team58.profileservice.controller.schemes.UserScheme; import ru.team58.profileservice.service.UserDTO; @Mapper(uses = {CreateUserMapper.class, UpdateUserMapper.class}) public interface UserSchemeMapper { - UserSchemeMapper INSTANCE = Mappers.getMapper(UserSchemeMapper.class); - @Mapping(target = "id", ignore = true) UserDTO toUserDTO(UserScheme user); } diff --git a/src/main/java/ru/team58/profileservice/service/UserServiceImpl.java b/src/main/java/ru/team58/profileservice/service/UserServiceImpl.java index 69a9ddc..1b2fb5e 100644 --- a/src/main/java/ru/team58/profileservice/service/UserServiceImpl.java +++ b/src/main/java/ru/team58/profileservice/service/UserServiceImpl.java @@ -16,16 +16,21 @@ public class UserServiceImpl implements UserService { @Autowired UserRepository userRepository; + @Autowired + UserDTOMapper userDTOMapper; + @Autowired + UserEntityMapper userEntityMapper; + @Override public UserDTO getById(UUID id) { UserEntity user = userRepository.findById(id).orElseThrow(UserNotFoundException::new); - return UserDTOMapper.INSTANCE.toUserDTO(user); + return userDTOMapper.toUserDTO(user); } @Override public UserDTO getByUsername(String username) { UserEntity user = userRepository.findByUsername(username).orElseThrow(UserNotFoundException::new); - return UserDTOMapper.INSTANCE.toUserDTO(user); + return userDTOMapper.toUserDTO(user); } @Override @@ -33,7 +38,7 @@ public class UserServiceImpl implements UserService { userRepository.findByUsername(user.username).ifPresent(u -> { throw new UserAlreadyExistsException(); }); - return userRepository.save(UserEntityMapper.INSTANCE.toUserEntity(user)).getId(); + return userRepository.save(userEntityMapper.toUserEntity(user)).getId(); } @Override @@ -45,6 +50,6 @@ public class UserServiceImpl implements UserService { @Override public void updateUser(UserDTO user) { userRepository.findById(user.getId()).orElseThrow(UserNotFoundException::new); - userRepository.save(UserEntityMapper.INSTANCE.toUserEntity(user)); + userRepository.save(userEntityMapper.toUserEntity(user)); } } diff --git a/src/test/java/ru/team58/profileservice/ProfileServiceTest.java b/src/test/java/ru/team58/profileservice/ProfileServiceTest.java new file mode 100644 index 0000000..afeecdc --- /dev/null +++ b/src/test/java/ru/team58/profileservice/ProfileServiceTest.java @@ -0,0 +1,12 @@ +package ru.team58.profileservice; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +public class ProfileServiceTest { + @Test + public void contextLoads() { + + } +}