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/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..e9026b3 --- /dev/null +++ b/src/main/java/ru/team58/profileservice/controller/advice/ErrorDetails.java @@ -0,0 +1,11 @@ +package ru.team58.profileservice.controller.advice; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@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 { +} 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 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