From faf9e0f7af7ec0a9ad6f3504c3118adeb3798e94 Mon Sep 17 00:00:00 2001 From: KamilM1205 Date: Sun, 29 Jun 2025 21:51:35 +0400 Subject: [PATCH 1/4] Added: migrations for not null columns and delete password field. --- src/main/resources/db/migration/V1_0_1__AlterUser.sql | 5 +++++ src/main/resources/db/migration/V1_0_2__DeletePassword.sql | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 src/main/resources/db/migration/V1_0_1__AlterUser.sql create mode 100644 src/main/resources/db/migration/V1_0_2__DeletePassword.sql diff --git a/src/main/resources/db/migration/V1_0_1__AlterUser.sql b/src/main/resources/db/migration/V1_0_1__AlterUser.sql new file mode 100644 index 0000000..f0e12d7 --- /dev/null +++ b/src/main/resources/db/migration/V1_0_1__AlterUser.sql @@ -0,0 +1,5 @@ +ALTER TABLE "users" + ALTER COLUMN username SET NOT NULL, + ALTER COLUMN "firstName" SET NOT NULL, + ALTER COLUMN "lastName" SET NOT NULL, + ALTER COLUMN "email" SET NOT NULL; \ No newline at end of file diff --git a/src/main/resources/db/migration/V1_0_2__DeletePassword.sql b/src/main/resources/db/migration/V1_0_2__DeletePassword.sql new file mode 100644 index 0000000..cb6935c --- /dev/null +++ b/src/main/resources/db/migration/V1_0_2__DeletePassword.sql @@ -0,0 +1,2 @@ +ALTER TABLE "users" + DROP COLUMN "hashed_password"; \ No newline at end of file From e86ab4f2091ce9db1f97b1bbeb31dd00935721ef Mon Sep 17 00:00:00 2001 From: KamilM1205 Date: Sun, 29 Jun 2025 22:28:03 +0400 Subject: [PATCH 2/4] Fix: Mapper set null fields for DTO and Entity. --- build.gradle.kts | 4 ++-- .../controller/schemes/CreateUserRequest.java | 7 +++++++ .../controller/schemes/GetUserResponse.java | 7 +++++++ .../controller/schemes/UpdateUserRequest.java | 7 +++++++ .../controller/schemes/UserResponse.java | 7 +++++++ .../ru/team58/profileservice/mapper/UserMapper.java | 4 +++- .../profileservice/persistence/entity/UserEntity.java | 11 +++++------ src/main/resources/application.properties | 2 +- 8 files changed, 39 insertions(+), 10 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 5a31b41..39bf11b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,16 +43,16 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.flywaydb:flyway-core") implementation("org.flywaydb:flyway-database-postgresql:11.10.0") - implementation("org.mapstruct:mapstruct:1.6.3") implementation("org.postgresql:postgresql") implementation("org.apache.logging.log4j:log4j-api:2.22.1") implementation("org.apache.logging.log4j:log4j-core:2.22.1") implementation("org.slf4j:slf4j-api:2.0.13") implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.6") runtimeOnly("org.apache.logging.log4j:log4j-slf4j-impl:2.22.1") - annotationProcessor("org.mapstruct:mapstruct-processor:1.6.3") compileOnly("org.projectlombok:lombok") annotationProcessor("org.projectlombok:lombok") + annotationProcessor("org.mapstruct:mapstruct-processor:1.6.3") + implementation("org.mapstruct:mapstruct:1.6.3") testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.springframework.security:spring-security-test") testRuntimeOnly("org.junit.platform:junit-platform-launcher") 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 9205b1d..6344ece 100644 --- a/src/main/java/ru/team58/profileservice/controller/schemes/CreateUserRequest.java +++ b/src/main/java/ru/team58/profileservice/controller/schemes/CreateUserRequest.java @@ -1,5 +1,12 @@ package ru.team58.profileservice.controller.schemes; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder public class CreateUserRequest { String username; String firstName; diff --git a/src/main/java/ru/team58/profileservice/controller/schemes/GetUserResponse.java b/src/main/java/ru/team58/profileservice/controller/schemes/GetUserResponse.java index 3b52b02..d73ddd6 100644 --- a/src/main/java/ru/team58/profileservice/controller/schemes/GetUserResponse.java +++ b/src/main/java/ru/team58/profileservice/controller/schemes/GetUserResponse.java @@ -1,5 +1,12 @@ package ru.team58.profileservice.controller.schemes; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder public class GetUserResponse { String username; String firstName; 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 f97b284..6548494 100644 --- a/src/main/java/ru/team58/profileservice/controller/schemes/UpdateUserRequest.java +++ b/src/main/java/ru/team58/profileservice/controller/schemes/UpdateUserRequest.java @@ -1,7 +1,14 @@ package ru.team58.profileservice.controller.schemes; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + import java.util.UUID; +@Getter +@Setter +@Builder public class UpdateUserRequest { UUID id; String username; diff --git a/src/main/java/ru/team58/profileservice/controller/schemes/UserResponse.java b/src/main/java/ru/team58/profileservice/controller/schemes/UserResponse.java index 2e0ef09..4433079 100644 --- a/src/main/java/ru/team58/profileservice/controller/schemes/UserResponse.java +++ b/src/main/java/ru/team58/profileservice/controller/schemes/UserResponse.java @@ -1,7 +1,14 @@ package ru.team58.profileservice.controller.schemes; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + import java.util.UUID; +@Getter +@Setter +@Builder public class UserResponse { UUID id; String username; diff --git a/src/main/java/ru/team58/profileservice/mapper/UserMapper.java b/src/main/java/ru/team58/profileservice/mapper/UserMapper.java index aeced74..857bcae 100644 --- a/src/main/java/ru/team58/profileservice/mapper/UserMapper.java +++ b/src/main/java/ru/team58/profileservice/mapper/UserMapper.java @@ -1,6 +1,7 @@ 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; @@ -9,12 +10,13 @@ import ru.team58.profileservice.controller.schemes.UserResponse; import ru.team58.profileservice.persistence.entity.UserEntity; import ru.team58.profileservice.service.UserDTO; -@Mapper +@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); 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 62c3236..b509abe 100644 --- a/src/main/java/ru/team58/profileservice/persistence/entity/UserEntity.java +++ b/src/main/java/ru/team58/profileservice/persistence/entity/UserEntity.java @@ -2,7 +2,6 @@ package ru.team58.profileservice.persistence.entity; import jakarta.persistence.*; import lombok.*; -import ru.team58.profileservice.service.UserDTO; import java.util.UUID; @@ -17,17 +16,17 @@ public class UserEntity { @Id @Column(name = "id", nullable = false) @GeneratedValue(strategy = GenerationType.UUID) - UUID id; + private UUID id; @Column(name = "username", nullable = false) - String username; + private String username; @Column(name = "firstName", nullable = false) - String firstName; + private String firstName; @Column(name = "lastName", nullable = false) - String lastName; + private String lastName; @Column(name = "email", nullable = false) - String email; + private String email; } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 294cc22..ef4016b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -5,7 +5,7 @@ spring.datasource.url=jdbc:postgresql://localhost:5432/userdb spring.datasource.username=userdb spring.datasource.password=1205 -spring.jpa.hibernate.ddl-auto=update +spring.jpa.hibernate.ddl-auto=validate ## Flyway spring.flyway.enabled=true From a4bdcda16508213abb6062c0bc80ad032ac6121c Mon Sep 17 00:00:00 2001 From: KamilM1205 Date: Sun, 29 Jun 2025 23:52:52 +0400 Subject: [PATCH 3/4] Add: Response exceptions. Handling exceptions. --- .../controller/UserController.java | 69 +++++++++++++++++-- .../controller/advice/ErrorDetails.java | 13 ++++ .../UserControllerExceptionHandler.java | 40 +++++++++++ .../exceptions/BadRequestException.java | 4 ++ .../exceptions/ForbiddenException.java | 4 ++ .../exceptions/UnauthorizedException.java | 4 ++ .../UserAlreadyExistsException.java | 4 ++ .../exceptions/UserNotFoundException.java | 4 ++ 8 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 src/main/java/ru/team58/profileservice/controller/advice/ErrorDetails.java create mode 100644 src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java create mode 100644 src/main/java/ru/team58/profileservice/controller/exceptions/BadRequestException.java create mode 100644 src/main/java/ru/team58/profileservice/controller/exceptions/ForbiddenException.java create mode 100644 src/main/java/ru/team58/profileservice/controller/exceptions/UnauthorizedException.java create mode 100644 src/main/java/ru/team58/profileservice/controller/exceptions/UserAlreadyExistsException.java create mode 100644 src/main/java/ru/team58/profileservice/controller/exceptions/UserNotFoundException.java diff --git a/src/main/java/ru/team58/profileservice/controller/UserController.java b/src/main/java/ru/team58/profileservice/controller/UserController.java index 70378ed..2c18b1b 100644 --- a/src/main/java/ru/team58/profileservice/controller/UserController.java +++ b/src/main/java/ru/team58/profileservice/controller/UserController.java @@ -2,6 +2,9 @@ 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.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; @@ -9,6 +12,7 @@ import ru.team58.profileservice.controller.schemes.UserResponse; 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 java.security.Principal; import java.util.UUID; @@ -20,33 +24,88 @@ public class UserController { @GetMapping("/") public UserResponse getMe(Principal principal) { + if (principal == null) { + throw new UnauthorizedException(); + } + + try { + userService.getByUsername(principal.getName()); + } catch (Exception ex) { + throw new UserNotFoundException(); + } + return UserMapper.INSTANCE.toUserResponse(userService.getByUsername(principal.getName())); } @PutMapping("/") public void updateMe(Principal principal, @RequestBody UpdateUserRequest request) { + if (principal == null) { + throw new UnauthorizedException(); + } + + try { + userService.getByUsername(principal.getName()); + } catch (Exception ex) { + throw new UserNotFoundException(); + } + userService.updateUser(UserMapper.INSTANCE.toUserDTO(request)); } @DeleteMapping("/") public void deleteMe(Principal principal) { - UserDTO user = userService.getByUsername(principal.getName()); + if (principal == null) { + throw new UnauthorizedException(); + } + + UserDTO user; + + try { + user = userService.getByUsername(principal.getName()); + } catch (Exception ex) { + throw new UserNotFoundException(); + } + userService.deleteUser(user.getId()); } @PostMapping("/") public void createMe(@RequestBody CreateUserRequest request) { - userService.createUser(UserMapper.INSTANCE.toUserDTO(request)); + UserDTO user = UserMapper.INSTANCE.toUserDTO(request); + + try { + userService.createUser(user); + } catch (UserAlreadyExistsException ex) { + throw new ru.team58.profileservice.controller.exceptions.UserAlreadyExistsException(); + } catch (IllegalArgumentException ex) { + throw new BadRequestException(); + } } @GetMapping("/{id}") public GetUserResponse getUser(@PathVariable UUID id) { - UserDTO user = userService.getById(id); + UserDTO user; + + try { + user = userService.getById(id); + } catch (ru.team58.profileservice.service.exceptions.UserNotFoundException ex) { + throw new UserNotFoundException(); + } + return UserMapper.INSTANCE.toGetUserResponse(user); } @DeleteMapping("/{id}") - public void deleteUser(@PathVariable UUID id) { - userService.deleteUser(id); + public void deleteUser(Principal principal, @PathVariable UUID id) { + // TODO: Add checking role + if (principal == null) { + throw new UnauthorizedException(); + } + + try { + userService.deleteUser(id); + } catch (ru.team58.profileservice.service.exceptions.UserNotFoundException ex) { + throw new UserNotFoundException(); + } } } diff --git a/src/main/java/ru/team58/profileservice/controller/advice/ErrorDetails.java b/src/main/java/ru/team58/profileservice/controller/advice/ErrorDetails.java new file mode 100644 index 0000000..43ffcbb --- /dev/null +++ b/src/main/java/ru/team58/profileservice/controller/advice/ErrorDetails.java @@ -0,0 +1,13 @@ +package ru.team58.profileservice.controller.advice; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@AllArgsConstructor +public class ErrorDetails { + private String message; + private int status; +} diff --git a/src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java b/src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java new file mode 100644 index 0000000..bd0d990 --- /dev/null +++ b/src/main/java/ru/team58/profileservice/controller/advice/UserControllerExceptionHandler.java @@ -0,0 +1,40 @@ +package ru.team58.profileservice.controller.advice; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import ru.team58.profileservice.controller.exceptions.*; + +@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); + } + + @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); + } + + @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); + } + + @ExceptionHandler(ForbiddenException.class) + public ResponseEntity handleForbiddenException(ForbiddenException ex) { + ErrorDetails errorDetails = new ErrorDetails("Forbidden", HttpStatus.FORBIDDEN.value()); + return new ResponseEntity<>(errorDetails, HttpStatus.FORBIDDEN); + } + + @ExceptionHandler(UnauthorizedException.class) + public ResponseEntity handleUnauthorizedException(UnauthorizedException ex) { + ErrorDetails errorDetails = new ErrorDetails("Unauthorized", HttpStatus.UNAUTHORIZED.value()); + return new ResponseEntity<>(errorDetails, HttpStatus.UNAUTHORIZED); + } +} diff --git a/src/main/java/ru/team58/profileservice/controller/exceptions/BadRequestException.java b/src/main/java/ru/team58/profileservice/controller/exceptions/BadRequestException.java new file mode 100644 index 0000000..48fd739 --- /dev/null +++ b/src/main/java/ru/team58/profileservice/controller/exceptions/BadRequestException.java @@ -0,0 +1,4 @@ +package ru.team58.profileservice.controller.exceptions; + +public class BadRequestException extends RuntimeException { +} diff --git a/src/main/java/ru/team58/profileservice/controller/exceptions/ForbiddenException.java b/src/main/java/ru/team58/profileservice/controller/exceptions/ForbiddenException.java new file mode 100644 index 0000000..4461ccc --- /dev/null +++ b/src/main/java/ru/team58/profileservice/controller/exceptions/ForbiddenException.java @@ -0,0 +1,4 @@ +package ru.team58.profileservice.controller.exceptions; + +public class ForbiddenException extends RuntimeException { +} diff --git a/src/main/java/ru/team58/profileservice/controller/exceptions/UnauthorizedException.java b/src/main/java/ru/team58/profileservice/controller/exceptions/UnauthorizedException.java new file mode 100644 index 0000000..c787bb2 --- /dev/null +++ b/src/main/java/ru/team58/profileservice/controller/exceptions/UnauthorizedException.java @@ -0,0 +1,4 @@ +package ru.team58.profileservice.controller.exceptions; + +public class UnauthorizedException extends RuntimeException { +} diff --git a/src/main/java/ru/team58/profileservice/controller/exceptions/UserAlreadyExistsException.java b/src/main/java/ru/team58/profileservice/controller/exceptions/UserAlreadyExistsException.java new file mode 100644 index 0000000..9108862 --- /dev/null +++ b/src/main/java/ru/team58/profileservice/controller/exceptions/UserAlreadyExistsException.java @@ -0,0 +1,4 @@ +package ru.team58.profileservice.controller.exceptions; + +public class UserAlreadyExistsException extends BadRequestException{ +} diff --git a/src/main/java/ru/team58/profileservice/controller/exceptions/UserNotFoundException.java b/src/main/java/ru/team58/profileservice/controller/exceptions/UserNotFoundException.java new file mode 100644 index 0000000..0e95904 --- /dev/null +++ b/src/main/java/ru/team58/profileservice/controller/exceptions/UserNotFoundException.java @@ -0,0 +1,4 @@ +package ru.team58.profileservice.controller.exceptions; + +public class UserNotFoundException extends ForbiddenException { +} From 94a6f234c58af14d17ce0d1b9e6dbf63b303f4f2 Mon Sep 17 00:00:00 2001 From: KamilM1205 Date: Sun, 29 Jun 2025 23:53:56 +0400 Subject: [PATCH 4/4] Fix: removed unused imports. --- .../team58/profileservice/controller/advice/ErrorDetails.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/ru/team58/profileservice/controller/advice/ErrorDetails.java b/src/main/java/ru/team58/profileservice/controller/advice/ErrorDetails.java index 43ffcbb..e9026b3 100644 --- a/src/main/java/ru/team58/profileservice/controller/advice/ErrorDetails.java +++ b/src/main/java/ru/team58/profileservice/controller/advice/ErrorDetails.java @@ -2,8 +2,6 @@ package ru.team58.profileservice.controller.advice; import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; @Getter @AllArgsConstructor