From f27aa66a75d44eb3597c8c210cdd12bbc17a1c18 Mon Sep 17 00:00:00 2001 From: Lauro Correia Silveira Date: Sat, 30 Dec 2023 23:08:10 +0100 Subject: [PATCH] Feature/create test for authentication controller (#93) - Added one more test to CategoryControllerTest complete all cases * Created news test and improved Exceptions --- pom.xml | 8 +- .../controller/CategoryController.java | 6 +- .../controller/ControllerAdvice.java | 44 ++- .../controller/UserController.java | 27 +- .../controller/VideoController.java | 138 +++---- .../aluraflixapi/controller/dto/ErrorDto.java | 11 - .../aluraflixapi/controller/dto/ErrorVO.java | 10 + .../alura/aluraflixapi/domain/user/User.java | 4 +- .../aluraflixapi/domain/user/dto/UserDto.java | 5 +- .../domain/video/dto/VideoDto.java | 2 - .../exception/CategoryServiceException.java | 11 + .../CategoryTransactionException.java | 8 - .../exception/ErrorMessageVO.java | 6 + .../exception/ResourceNotFoundException.java | 7 + .../exception/VideoServiceException.java | 12 + .../infraestructure/mapper/UserMapper.java | 6 +- .../repository/UserRepository.java | 3 +- .../security/SecurityConfigurations.java | 7 - .../security/SecurityFilter.java | 5 +- .../security/TokenService.java | 10 +- .../service/CategoryService.java | 2 +- .../service/CategoryServiceImpl.java | 119 +++--- .../service/UserDetailsServiceImpl.java | 42 ++- .../service/UserServiceImpl.java | 6 +- .../infraestructure/service/VideoService.java | 2 +- .../service/VideoServiceImpl.java | 34 +- .../springdoc/SpringdocConfiguration.java | 2 +- .../AuthenticationControllerTest.java | 4 +- .../controller/CategoryControllerTest.java | 3 +- .../controller/ControllerAdviceTest.java | 112 ++++++ .../controller/UserControllerTest.java | 73 ++++ .../controller/VideoControllerTest.java | 32 +- .../security/TokenServiceTest.java | 86 +++++ .../service/CategoryServiceImplTest.java | 340 +++++++++--------- .../service/UserDetailsServiceImplTest.java | 54 +++ .../service/UserServiceImplTest.java | 106 ++++++ .../service/VideoServiceImplTest.java | 102 +++++- .../json/video/delete_video_response_ok.json | 2 +- 38 files changed, 1004 insertions(+), 447 deletions(-) delete mode 100644 src/main/java/com/alura/aluraflixapi/controller/dto/ErrorDto.java create mode 100644 src/main/java/com/alura/aluraflixapi/controller/dto/ErrorVO.java create mode 100644 src/main/java/com/alura/aluraflixapi/infraestructure/exception/CategoryServiceException.java delete mode 100644 src/main/java/com/alura/aluraflixapi/infraestructure/exception/CategoryTransactionException.java create mode 100644 src/main/java/com/alura/aluraflixapi/infraestructure/exception/ErrorMessageVO.java create mode 100644 src/main/java/com/alura/aluraflixapi/infraestructure/exception/ResourceNotFoundException.java create mode 100644 src/main/java/com/alura/aluraflixapi/infraestructure/exception/VideoServiceException.java create mode 100644 src/test/java/com/alura/aluraflixapi/controller/ControllerAdviceTest.java create mode 100644 src/test/java/com/alura/aluraflixapi/controller/UserControllerTest.java create mode 100644 src/test/java/com/alura/aluraflixapi/infraestructure/security/TokenServiceTest.java create mode 100644 src/test/java/com/alura/aluraflixapi/infraestructure/service/UserDetailsServiceImplTest.java create mode 100644 src/test/java/com/alura/aluraflixapi/infraestructure/service/UserServiceImplTest.java diff --git a/pom.xml b/pom.xml index c42f36c..22a03b8 100644 --- a/pom.xml +++ b/pom.xml @@ -130,7 +130,13 @@ jacoco-maven-plugin 0.8.11 - **/domain/** + + **/springdoc/* + **/domain/** + **/infraestructure/security/SecurityConfigurations.class + **/AluraFlixApiApplication.class + **/infraestructure/security/SecurityFilter.class + diff --git a/src/main/java/com/alura/aluraflixapi/controller/CategoryController.java b/src/main/java/com/alura/aluraflixapi/controller/CategoryController.java index e336784..c635186 100644 --- a/src/main/java/com/alura/aluraflixapi/controller/CategoryController.java +++ b/src/main/java/com/alura/aluraflixapi/controller/CategoryController.java @@ -10,6 +10,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -67,10 +68,11 @@ public ResponseEntity> getVideosByCategory( } @DeleteMapping(value = "/{id}", produces = "application/json") + @PreAuthorize("hasRole('Admin')") public ResponseEntity deleteCategory(@NotBlank @PathVariable final String id) { log.info("{} Request to Delete Category with Id: {}", PREFIX_LOGGING, id); - final var response = this.categoryService.deleteCategory(id); - return ResponseEntity.status(response).build(); + this.categoryService.deleteCategory(id); + return ResponseEntity.ok().build(); } } diff --git a/src/main/java/com/alura/aluraflixapi/controller/ControllerAdvice.java b/src/main/java/com/alura/aluraflixapi/controller/ControllerAdvice.java index 11a17e0..9961e5c 100644 --- a/src/main/java/com/alura/aluraflixapi/controller/ControllerAdvice.java +++ b/src/main/java/com/alura/aluraflixapi/controller/ControllerAdvice.java @@ -1,36 +1,34 @@ package com.alura.aluraflixapi.controller; -import com.alura.aluraflixapi.controller.dto.ErrorDto; -import java.util.List; +import com.alura.aluraflixapi.controller.dto.ErrorVO; +import com.alura.aluraflixapi.infraestructure.exception.ErrorMessageVO; +import com.alura.aluraflixapi.infraestructure.exception.ResourceNotFoundException; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +import java.util.List; + @RestControllerAdvice public class ControllerAdvice { - /** - * Handle Invalid fields - * @return List of ErrorDto with invalid fields - */ - @ExceptionHandler(MethodArgumentNotValidException.class) - public ResponseEntity> handleInvalidFields( - final MethodArgumentNotValidException ex) { - var errors = ex.getFieldErrors(); - return ResponseEntity.badRequest().body(errors.stream().map(ErrorDto::new).toList()); - } + /** + * Handle Invalid fields + * + * @return List of ErrorDto with invalid fields + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity> handleInvalidFields( + final MethodArgumentNotValidException ex) { + var errors = ex.getFieldErrors(); + return ResponseEntity.badRequest().body(errors.stream().map(ErrorVO::new).toList()); + } - /** - * handle invalid credentials whe user atempt to login - * @param ex HttpMessageNotReadableException - * @return ResponseEntity status bad_request - */ - @ExceptionHandler(HttpMessageNotReadableException.class) - public ResponseEntity handleLoginException( - final HttpMessageNotReadableException ex) { - return ResponseEntity.badRequest().body("Invalid Credentials"); - } + @ExceptionHandler(ResourceNotFoundException.class) + public ResponseEntity handlerResourceNotFoundException(final ResourceNotFoundException ex) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ErrorMessageVO(ex.getMessage(), HttpStatus.NOT_FOUND)); + } } diff --git a/src/main/java/com/alura/aluraflixapi/controller/UserController.java b/src/main/java/com/alura/aluraflixapi/controller/UserController.java index aa1168d..3650554 100644 --- a/src/main/java/com/alura/aluraflixapi/controller/UserController.java +++ b/src/main/java/com/alura/aluraflixapi/controller/UserController.java @@ -3,37 +3,36 @@ import com.alura.aluraflixapi.domain.user.dto.UserDto; import com.alura.aluraflixapi.infraestructure.service.UserService; import jakarta.validation.Valid; -import java.util.List; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.security.access.annotation.Secured; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @Slf4j @RestController @RequestMapping("/user") public class UserController { - private static final String PREFIX_LOGGIN = "[UserController]"; + private static final String PREFIX_LOGGING = "[UserController]"; + private final UserService service; - @Autowired - private UserService service; + public UserController(UserService service) { + this.service = service; + } @PostMapping - @Secured("ROLE_ADMIN") + @PreAuthorize("hasRole('Admin')") public ResponseEntity saveUser(@RequestBody @Valid UserDto userDto) { - log.info("{} Saving new User: {}", PREFIX_LOGGIN, userDto.toString()); + log.info("{} Saving new User: {}", PREFIX_LOGGING, userDto.toString()); var newUser = this.service.saveUser(userDto); return ResponseEntity.ok().body(newUser); } @GetMapping + @PreAuthorize("hasRole('Admin')") public ResponseEntity> getUsers() { - log.info("{} Retrieving Users", PREFIX_LOGGIN); + log.info("{} Retrieving Users", PREFIX_LOGGING); var users = this.service.getUsers(); return ResponseEntity.ok(users); } diff --git a/src/main/java/com/alura/aluraflixapi/controller/VideoController.java b/src/main/java/com/alura/aluraflixapi/controller/VideoController.java index 57f7157..6b829ab 100644 --- a/src/main/java/com/alura/aluraflixapi/controller/VideoController.java +++ b/src/main/java/com/alura/aluraflixapi/controller/VideoController.java @@ -7,16 +7,21 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; + import java.util.List; import java.util.Optional; + import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.annotations.ParameterObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.annotation.Secured; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -34,80 +39,75 @@ @SecurityRequirement(name = "bearer-key") public class VideoController { - private static final String LOGGING_PREFIX = "[VideoController]"; + private static final String LOGGING_PREFIX = "[VideoController]"; + + private final VideoService service; + + @Autowired + public VideoController(final VideoService service) { + this.service = service; + } + + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity> getVideos(@ParameterObject Pageable pageable) { + log.info("{} Request to get All videos", LOGGING_PREFIX); + final Page videos = this.service.getVideos(pageable); + log.info("{} Response {}: ", LOGGING_PREFIX, videos.getContent()); + return ResponseEntity.ok().body(new PageImpl<>(videos.getContent(), pageable, pageable.getPageSize())); + } + - private final VideoService service; + @GetMapping("/{id}") + public ResponseEntity getById(@NotBlank @PathVariable final String id) { + log.info("{} Request to get a video by ID: {}", LOGGING_PREFIX, id); + return Optional.ofNullable(service.getById(id)) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); - @Autowired - public VideoController(final VideoService service) { - this.service = service; - } + } - @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity> getVideos(Pageable pageable) { - log.info("{} Request to get All videos", LOGGING_PREFIX); - final Page videos = this.service.getVideos(pageable); - if (videos.hasContent()) { - log.info("{} Response {}: ", LOGGING_PREFIX, videos); - return ResponseEntity.ok().body(videos); - } else { - return ResponseEntity.noContent().build(); + @PostMapping + public ResponseEntity save(@Valid @RequestBody final VideoDto dto, + final UriComponentsBuilder uriBuilder) { + log.info("{} Request to Save a new video: {}", LOGGING_PREFIX, dto); + final VideoDto videoDto = this.service.save(dto); + //good practices to return the Location in the Header to be searched by Id + //return Http code 201 and Location with Id + return ResponseEntity.created(uriBuilder.path("/videos/{id}").buildAndExpand(videoDto.id()) + .toUri()).body(videoDto); } - } - - - @GetMapping("/{id}") - public ResponseEntity getById(@NotBlank @PathVariable final String id) { - log.info("{} Request to get a video by ID: {}", LOGGING_PREFIX, id); - return Optional.ofNullable(service.getById(id)) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); - - } - - @PostMapping - public ResponseEntity save(@Valid @RequestBody final VideoDto dto, - final UriComponentsBuilder uriBuilder) { - log.info("{} Request to Save a new video: {}", LOGGING_PREFIX, dto); - final VideoDto videoDto = this.service.save(dto); - //good practices to return the Location in the Header to be search by Id - //return Http code 201 and Localtion with Id - return ResponseEntity.created(uriBuilder.path("/videos/{id}").buildAndExpand(videoDto.id()) - .toUri()).body(videoDto); - } - - @PutMapping - public ResponseEntity update(@Valid @RequestBody final UpdateVideoDto dto, - final UriComponentsBuilder uriBuilder) { - log.info("{} Request to update a video: {}", LOGGING_PREFIX, dto); - final var videoDto = this.service.updateMovie(dto); - //good practices to return the Location in the Header to be search by Id - //return Http code 201 and Location with Id - return ResponseEntity.created(uriBuilder.path("/videos/{id}") - .buildAndExpand(videoDto.id()) - .toUri()).body(videoDto); - } - - @DeleteMapping("/{id}") - @Secured("ROLE_ADMIN") - public ResponseEntity delete(@NotBlank @PathVariable final String id) { - log.info("{} Request to Delete a video by ID: {}", LOGGING_PREFIX, id); - final Optional dto = this.service.delete(id); - return dto.map(videoDto -> ResponseEntity.status(HttpStatus.NO_CONTENT).body(videoDto)) - .orElseGet(() -> ResponseEntity.noContent().build()); - } - - @GetMapping("/title") - public ResponseEntity> getVideosByTitle( - @NotBlank @RequestParam("title") final String title) { - log.info("{} Request to get a video by title: {}", LOGGING_PREFIX, title); - final var videosByTitle = this.service.getVideosByTitle(title); - if (videosByTitle.isEmpty()) { - return ResponseEntity.noContent().build(); - } else { - return ResponseEntity.status(HttpStatus.FOUND).body(videosByTitle); + + @PutMapping + public ResponseEntity update(@Valid @RequestBody final UpdateVideoDto dto, + final UriComponentsBuilder uriBuilder) { + log.info("{} Request to update a video: {}", LOGGING_PREFIX, dto); + final var videoDto = this.service.updateMovie(dto); + //good practices to return the Location in the Header to be search by Id + //return Http code 201 and Location with Id + return ResponseEntity.created(uriBuilder.path("/videos/{id}") + .buildAndExpand(videoDto.id()) + .toUri()).body(videoDto); } - } + @DeleteMapping("/{id}") + @PreAuthorize("hasRole('Admin')") + public ResponseEntity delete(@NotBlank @PathVariable final String id) { + log.info("{} Request to Delete a video by ID: {}", LOGGING_PREFIX, id); + final var dto = this.service.delete(id); + return ResponseEntity.ok(dto); + } + + @GetMapping("/title") + public ResponseEntity> getVideosByTitle( + @NotBlank @RequestParam("title") final String title) { + log.info("{} Request to get a video by title: {}", LOGGING_PREFIX, title); + final var videosByTitle = this.service.getVideosByTitle(title); + if (videosByTitle.isEmpty()) { + return ResponseEntity.noContent().build(); + } else { + return ResponseEntity.status(HttpStatus.FOUND).body(videosByTitle); + } + + } } diff --git a/src/main/java/com/alura/aluraflixapi/controller/dto/ErrorDto.java b/src/main/java/com/alura/aluraflixapi/controller/dto/ErrorDto.java deleted file mode 100644 index d1d2af4..0000000 --- a/src/main/java/com/alura/aluraflixapi/controller/dto/ErrorDto.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.alura.aluraflixapi.controller.dto; - -import org.springframework.validation.FieldError; - -public record ErrorDto(String field, String message) { - - public ErrorDto(FieldError fieldError) { - this(fieldError.getField(), fieldError.getDefaultMessage()); - } - -} diff --git a/src/main/java/com/alura/aluraflixapi/controller/dto/ErrorVO.java b/src/main/java/com/alura/aluraflixapi/controller/dto/ErrorVO.java new file mode 100644 index 0000000..22f1f22 --- /dev/null +++ b/src/main/java/com/alura/aluraflixapi/controller/dto/ErrorVO.java @@ -0,0 +1,10 @@ +package com.alura.aluraflixapi.controller.dto; + +import org.springframework.validation.FieldError; + +public record ErrorVO(String field, String message) { + + public ErrorVO(FieldError fieldError) { + this(fieldError.getField(), fieldError.getDefaultMessage()); + } +} diff --git a/src/main/java/com/alura/aluraflixapi/domain/user/User.java b/src/main/java/com/alura/aluraflixapi/domain/user/User.java index 0fadb14..7991394 100644 --- a/src/main/java/com/alura/aluraflixapi/domain/user/User.java +++ b/src/main/java/com/alura/aluraflixapi/domain/user/User.java @@ -6,6 +6,8 @@ import java.io.Serializable; import java.util.Collection; import java.util.List; +import java.util.Set; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.EqualsAndHashCode; @@ -39,7 +41,7 @@ public class User implements Serializable, UserDetails { private String password; @DBRef - private List roles; + private Set roles; @Override public Collection getAuthorities() { diff --git a/src/main/java/com/alura/aluraflixapi/domain/user/dto/UserDto.java b/src/main/java/com/alura/aluraflixapi/domain/user/dto/UserDto.java index 162ae12..f011e8c 100644 --- a/src/main/java/com/alura/aluraflixapi/domain/user/dto/UserDto.java +++ b/src/main/java/com/alura/aluraflixapi/domain/user/dto/UserDto.java @@ -2,7 +2,8 @@ import com.alura.aluraflixapi.domain.user.roles.Roles; import jakarta.validation.constraints.NotBlank; -import java.util.List; + +import java.util.Set; public record UserDto( String id, @@ -10,6 +11,6 @@ public record UserDto( String username, @NotBlank String password, - List roles) { + Set roles) { } diff --git a/src/main/java/com/alura/aluraflixapi/domain/video/dto/VideoDto.java b/src/main/java/com/alura/aluraflixapi/domain/video/dto/VideoDto.java index 1144eef..137ad8a 100644 --- a/src/main/java/com/alura/aluraflixapi/domain/video/dto/VideoDto.java +++ b/src/main/java/com/alura/aluraflixapi/domain/video/dto/VideoDto.java @@ -3,10 +3,8 @@ import com.alura.aluraflixapi.domain.category.dto.CategoryDto; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; -import lombok.Builder; import org.hibernate.validator.constraints.URL; -@Builder public record VideoDto( String id, @NotBlank diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/exception/CategoryServiceException.java b/src/main/java/com/alura/aluraflixapi/infraestructure/exception/CategoryServiceException.java new file mode 100644 index 0000000..487be52 --- /dev/null +++ b/src/main/java/com/alura/aluraflixapi/infraestructure/exception/CategoryServiceException.java @@ -0,0 +1,11 @@ +package com.alura.aluraflixapi.infraestructure.exception; + +public class CategoryServiceException extends RuntimeException { + public CategoryServiceException(String message) { + super(message); + } + + public CategoryServiceException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/exception/CategoryTransactionException.java b/src/main/java/com/alura/aluraflixapi/infraestructure/exception/CategoryTransactionException.java deleted file mode 100644 index 65a6772..0000000 --- a/src/main/java/com/alura/aluraflixapi/infraestructure/exception/CategoryTransactionException.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.alura.aluraflixapi.infraestructure.exception; - -public class CategoryTransactionException extends RuntimeException { - - public CategoryTransactionException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/exception/ErrorMessageVO.java b/src/main/java/com/alura/aluraflixapi/infraestructure/exception/ErrorMessageVO.java new file mode 100644 index 0000000..c6015ce --- /dev/null +++ b/src/main/java/com/alura/aluraflixapi/infraestructure/exception/ErrorMessageVO.java @@ -0,0 +1,6 @@ +package com.alura.aluraflixapi.infraestructure.exception; + +import org.springframework.http.HttpStatus; + +public record ErrorMessageVO(String message, HttpStatus httpStatus) { +} diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/exception/ResourceNotFoundException.java b/src/main/java/com/alura/aluraflixapi/infraestructure/exception/ResourceNotFoundException.java new file mode 100644 index 0000000..32ff49a --- /dev/null +++ b/src/main/java/com/alura/aluraflixapi/infraestructure/exception/ResourceNotFoundException.java @@ -0,0 +1,7 @@ +package com.alura.aluraflixapi.infraestructure.exception; + +public class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/exception/VideoServiceException.java b/src/main/java/com/alura/aluraflixapi/infraestructure/exception/VideoServiceException.java new file mode 100644 index 0000000..6513451 --- /dev/null +++ b/src/main/java/com/alura/aluraflixapi/infraestructure/exception/VideoServiceException.java @@ -0,0 +1,12 @@ +package com.alura.aluraflixapi.infraestructure.exception; + +public class VideoServiceException extends RuntimeException { + + public VideoServiceException(String message) { + super(message); + } + + public VideoServiceException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/mapper/UserMapper.java b/src/main/java/com/alura/aluraflixapi/infraestructure/mapper/UserMapper.java index 39a6927..2b3dcf0 100644 --- a/src/main/java/com/alura/aluraflixapi/infraestructure/mapper/UserMapper.java +++ b/src/main/java/com/alura/aluraflixapi/infraestructure/mapper/UserMapper.java @@ -12,11 +12,11 @@ public interface UserMapper { @Mapping(target = "password", qualifiedByName = "encryptPassword") - User mappToEntity(UserDto dto); + User mapToEntity(UserDto dto); - UserDto mappToDto(User newUser); + UserDto mapToDto(User newUser); - List mappToUsersDto(List users); + List mapToUsersDto(List users); @Named("encryptPassword") diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/repository/UserRepository.java b/src/main/java/com/alura/aluraflixapi/infraestructure/repository/UserRepository.java index 0c24b17..0082d5f 100644 --- a/src/main/java/com/alura/aluraflixapi/infraestructure/repository/UserRepository.java +++ b/src/main/java/com/alura/aluraflixapi/infraestructure/repository/UserRepository.java @@ -6,6 +6,5 @@ public interface UserRepository extends MongoRepository { - - UserDetails findByUsername(String username); + UserDetails findByUsername(String username); } diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/security/SecurityConfigurations.java b/src/main/java/com/alura/aluraflixapi/infraestructure/security/SecurityConfigurations.java index 8cb037f..390feed 100644 --- a/src/main/java/com/alura/aluraflixapi/infraestructure/security/SecurityConfigurations.java +++ b/src/main/java/com/alura/aluraflixapi/infraestructure/security/SecurityConfigurations.java @@ -9,18 +9,11 @@ import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; - -import java.util.Arrays; -import java.util.List; /** * Main Spring Security class configuration In Spring 3.0 the security configuration is done by diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/security/SecurityFilter.java b/src/main/java/com/alura/aluraflixapi/infraestructure/security/SecurityFilter.java index d559b86..e82f01b 100644 --- a/src/main/java/com/alura/aluraflixapi/infraestructure/security/SecurityFilter.java +++ b/src/main/java/com/alura/aluraflixapi/infraestructure/security/SecurityFilter.java @@ -23,7 +23,6 @@ public class SecurityFilter extends OncePerRequestFilter { private static final String PREFIX_LOGGING = "[SecurityFilter]"; public static final String AUTHORIZATION = "Authorization"; private final TokenService tokenService; - private final UserRepository userRepository; public SecurityFilter(final TokenService tokenService, final UserRepository userRepository) { @@ -43,8 +42,8 @@ protected void doFilterInternal(final HttpServletRequest request, if (tokenJWT != null) { //Retrieve user from Token JWT - final var subject = tokenService.getSubject(tokenJWT); - var user = userRepository.findByUsername(subject); + final var subject = this.tokenService.getSubject(tokenJWT); + var user = this.userRepository.findByUsername(subject); //after retrieve the user we need to tell to Spring framework to authenticate him in the context //this is done by calling UsernamePasswordAuthenticationToken and SecurityContextHolder methods diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/security/TokenService.java b/src/main/java/com/alura/aluraflixapi/infraestructure/security/TokenService.java index df966cd..7ac0640 100644 --- a/src/main/java/com/alura/aluraflixapi/infraestructure/security/TokenService.java +++ b/src/main/java/com/alura/aluraflixapi/infraestructure/security/TokenService.java @@ -9,6 +9,8 @@ import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; + +import com.auth0.jwt.exceptions.JWTDecodeException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -24,7 +26,7 @@ public class TokenService { public String generateTokenJWT(User user) { try { - log.info("Generating JWt Token..."); + log.info("Generating Token JWT ..."); return JWT.create() .withIssuer(ALURA_FLIX_API) //owner @@ -39,7 +41,7 @@ public String generateTokenJWT(User user) { .withExpiresAt(getExpireDate()) .sign(Algorithm.HMAC256(secret)); - } catch (JWTCreationException exception) { + } catch (NullPointerException exception) { throw new JWTCreationException("Error to create JWT token", exception.getCause()); } } @@ -52,8 +54,8 @@ public String getSubject(String tokenJWT) { .build() .verify(tokenJWT) .getSubject(); - } catch (JWTCreationException ex) { - throw new JWTCreationException("Error verifying JWT Token", ex.getCause()); + } catch (JWTDecodeException ex) { + throw new JWTDecodeException("Error verifying JWT Token", ex.getCause()); } } diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/service/CategoryService.java b/src/main/java/com/alura/aluraflixapi/infraestructure/service/CategoryService.java index ec3a7f9..ee32c34 100644 --- a/src/main/java/com/alura/aluraflixapi/infraestructure/service/CategoryService.java +++ b/src/main/java/com/alura/aluraflixapi/infraestructure/service/CategoryService.java @@ -13,7 +13,7 @@ public interface CategoryService { CategoryDto findCategoryById(String id); - HttpStatus deleteCategory(String id); + void deleteCategory(String id); List getVideosByCategory(String rating); } diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/service/CategoryServiceImpl.java b/src/main/java/com/alura/aluraflixapi/infraestructure/service/CategoryServiceImpl.java index dfe2fae..cf3df29 100644 --- a/src/main/java/com/alura/aluraflixapi/infraestructure/service/CategoryServiceImpl.java +++ b/src/main/java/com/alura/aluraflixapi/infraestructure/service/CategoryServiceImpl.java @@ -2,12 +2,14 @@ import com.alura.aluraflixapi.domain.category.dto.CategoryDto; import com.alura.aluraflixapi.domain.video.dto.VideoDto; -import com.alura.aluraflixapi.infraestructure.exception.CategoryTransactionException; +import com.alura.aluraflixapi.infraestructure.exception.CategoryServiceException; import com.alura.aluraflixapi.infraestructure.mapper.CategoryMapper; import com.alura.aluraflixapi.infraestructure.mapper.VideoMapper; import com.alura.aluraflixapi.infraestructure.repository.CategoryRepository; import com.alura.aluraflixapi.infraestructure.repository.VideoRepository; + import java.util.List; + import lombok.extern.slf4j.Slf4j; import org.springframework.data.mongodb.MongoTransactionException; import org.springframework.http.HttpStatus; @@ -19,71 +21,70 @@ public class CategoryServiceImpl implements CategoryService { - private final CategoryRepository categoryRepository; + private final CategoryRepository categoryRepository; + + private final VideoRepository videoRepository; - private final VideoRepository videoRepository; + private final VideoMapper videoMapper; - private final VideoMapper videoMapper; + private final CategoryMapper categoryMapper; - private final CategoryMapper categoryMapper; + public CategoryServiceImpl(CategoryRepository categoryRepository, VideoRepository videoRepository, + VideoMapper videoMapper, CategoryMapper categoryMapper) { + this.categoryRepository = categoryRepository; + this.videoRepository = videoRepository; + this.videoMapper = videoMapper; + this.categoryMapper = categoryMapper; + } - public CategoryServiceImpl(CategoryRepository categoryRepository, VideoRepository videoRepository, - VideoMapper videoMapper, CategoryMapper categoryMapper) { - this.categoryRepository = categoryRepository; - this.videoRepository = videoRepository; - this.videoMapper = videoMapper; - this.categoryMapper = categoryMapper; - } + @Override + public List categories() { + return categoryRepository.findAll() + .stream() + .map(entity -> new CategoryDto(entity.getId(), entity.getRating(), entity.getTitle(), + entity.getColorHex())) + .toList(); + } - @Override - public List categories() { - return categoryRepository.findAll() - .stream() - .map(entity -> new CategoryDto(entity.getId(), entity.getRating(), entity.getTitle(), - entity.getColorHex())) - .toList(); - } + @Override + @Transactional + public CategoryDto create(CategoryDto categoryDto) { + try { + final var entity = this.categoryMapper.mapperToEntity(categoryDto); + final var categorySaved = categoryRepository.save(entity); + return this.categoryMapper.mapperToCategoryDto(categorySaved); + } catch (MongoTransactionException exception) { + throw new CategoryServiceException("Error to persist new category", + exception.getMostSpecificCause()); + } + } - @Override - @Transactional - public CategoryDto create(CategoryDto categoryDto) { - try { - final var entity = this.categoryMapper.mapperToEntity(categoryDto); - final var categorySaved = categoryRepository.save(entity); - return this.categoryMapper.mapperToCategoryDto(categorySaved); - } catch (MongoTransactionException exception) { - throw new CategoryTransactionException("Error to persist new category", - exception.getMostSpecificCause()); + @Override + public CategoryDto findCategoryById(String id) { + return this.categoryRepository.findById(id) + .map( + document -> new CategoryDto(document.getId(), document.getRating(), document.getTitle(), + document.getColorHex())) + .orElseThrow(() -> new CategoryServiceException("Category not found: " + id)); } - } - - @Override - public CategoryDto findCategoryById(String id) { - return this.categoryRepository.findById(id) - .map( - document -> new CategoryDto(document.getId(), document.getRating(), document.getTitle(), - document.getColorHex())) - .orElse(null); - } - - @Override - @Transactional - public HttpStatus deleteCategory(String id) { - try { - this.categoryRepository.deleteById(id); - return HttpStatus.ACCEPTED; - } catch (MongoTransactionException exception) { - log.error("Error", exception.getCause()); - return HttpStatus.NO_CONTENT; + + @Override + @Transactional + public void deleteCategory(String id) { + + final var isCategoryPresent = this.categoryRepository.findById(id); + isCategoryPresent.ifPresentOrElse(this.categoryRepository::delete, () -> { + throw new CategoryServiceException("Category not found to delete with Id:" + id); + }); + + } + + @Override + public List getVideosByCategory(String rating) { + final var category = categoryRepository.findCategoryByRating(rating); + return videoRepository.findVideosByCategories(category.getId()) + .stream().map(this.videoMapper::mapToVideoDto) + .toList(); } - } - - @Override - public List getVideosByCategory(String rating) { - final var category = categoryRepository.findCategoryByRating(rating); - return videoRepository.findVideosByCategories(category.getId()) - .stream().map(this.videoMapper::mapToVideoDto) - .toList(); - } } diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/service/UserDetailsServiceImpl.java b/src/main/java/com/alura/aluraflixapi/infraestructure/service/UserDetailsServiceImpl.java index 6fc96aa..1561047 100644 --- a/src/main/java/com/alura/aluraflixapi/infraestructure/service/UserDetailsServiceImpl.java +++ b/src/main/java/com/alura/aluraflixapi/infraestructure/service/UserDetailsServiceImpl.java @@ -2,12 +2,15 @@ import com.alura.aluraflixapi.infraestructure.repository.UserRepository; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; +import java.util.Objects; + /** * User Details Service Impl that connect to database and retrieve a user previously registred */ @@ -15,24 +18,29 @@ @Service public class UserDetailsServiceImpl implements UserDetailsService { - private static final String PREFIX_LOGGING = "[UserDetailsServiceImpl]"; + private static final String PREFIX_LOGGING = "[UserDetailsServiceImpl]"; - private final UserRepository repository; + private final UserRepository repository; - @Autowired - public UserDetailsServiceImpl(UserRepository repository) { - this.repository = repository; - } + @Autowired + public UserDetailsServiceImpl(UserRepository repository) { + this.repository = repository; + } - /** - * Search in databse a user by username and return it - * @return UserDestails - */ - @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - log.info("{} Retrieving User by username: {}", PREFIX_LOGGING, username); - final var user = this.repository.findByUsername(username); - log.info("{} Retrieved User: {}", PREFIX_LOGGING, user); - return user; - } + /** + * Search in databse a user by username and return it + * + * @return UserDestails + */ + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + log.info("{} Retrieving User by username: {}", PREFIX_LOGGING, username); + final var user = this.repository.findByUsername(username); + log.info("{} Retrieved User: {}", PREFIX_LOGGING, user); + if (Objects.isNull(user)) { + throw new UsernameNotFoundException(String.format("User not found for: %s", username)); + } else { + return user; + } + } } diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/service/UserServiceImpl.java b/src/main/java/com/alura/aluraflixapi/infraestructure/service/UserServiceImpl.java index d87f2ba..619172d 100644 --- a/src/main/java/com/alura/aluraflixapi/infraestructure/service/UserServiceImpl.java +++ b/src/main/java/com/alura/aluraflixapi/infraestructure/service/UserServiceImpl.java @@ -26,17 +26,17 @@ public UserServiceImpl(final UserRepository repository, final RoleRepository rol @Override public UserDto saveUser(UserDto dto) { - final User user = mapper.mappToEntity(dto); + final User user = mapper.mapToEntity(dto); //first save Document Roles this.roleRepository.saveAll(dto.roles()); //After save Document User final User newUser = repository.save(user); - return mapper.mappToDto(newUser); + return mapper.mapToDto(newUser); } @Override public List getUsers() { List users = repository.findAll(); - return mapper.mappToUsersDto(users); + return mapper.mapToUsersDto(users); } } diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/service/VideoService.java b/src/main/java/com/alura/aluraflixapi/infraestructure/service/VideoService.java index bebed39..f80370e 100644 --- a/src/main/java/com/alura/aluraflixapi/infraestructure/service/VideoService.java +++ b/src/main/java/com/alura/aluraflixapi/infraestructure/service/VideoService.java @@ -17,7 +17,7 @@ public interface VideoService { UpdateVideoDto updateMovie(UpdateVideoDto dto); - Optional delete(String id); + VideoDto delete(String id); VideoDto getById(String id); diff --git a/src/main/java/com/alura/aluraflixapi/infraestructure/service/VideoServiceImpl.java b/src/main/java/com/alura/aluraflixapi/infraestructure/service/VideoServiceImpl.java index 646649d..fe98a4e 100644 --- a/src/main/java/com/alura/aluraflixapi/infraestructure/service/VideoServiceImpl.java +++ b/src/main/java/com/alura/aluraflixapi/infraestructure/service/VideoServiceImpl.java @@ -6,6 +6,8 @@ import com.alura.aluraflixapi.domain.video.Video; import com.alura.aluraflixapi.domain.video.dto.UpdateVideoDto; import com.alura.aluraflixapi.domain.video.dto.VideoDto; +import com.alura.aluraflixapi.infraestructure.exception.ResourceNotFoundException; +import com.alura.aluraflixapi.infraestructure.exception.VideoServiceException; import com.alura.aluraflixapi.infraestructure.mapper.VideoMapper; import com.alura.aluraflixapi.infraestructure.repository.CategoryRepository; import com.alura.aluraflixapi.infraestructure.repository.VideoRepository; @@ -19,6 +21,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @Service @@ -46,10 +49,10 @@ public Page getVideos(Pageable pageable) { .forEach(video -> { if (Objects.isNull(video.getCategory())) { video.setCategory(Category.builder() - .id(null) + .id("") .rating(Rating.FREE.name()) - .title(null) - .colorHex(null) + .title("") + .colorHex("") .build()); } }); @@ -66,7 +69,7 @@ public VideoDto save(VideoDto dto) { log.info("{} New Video saved", LOGGING_PREFIX); return videoMapper.mapToVideoDto(entityPersisted); } catch (Exception e) { - throw new RuntimeException("Error to persist entity", e.getCause()); + throw new VideoServiceException("Error to persist entity", e.getCause()); } } @@ -81,30 +84,39 @@ public UpdateVideoDto updateMovie(UpdateVideoDto dto) { videoRepository.save(entity); return videoMapper.mapToUpdateVideoDto(entity); } catch (Exception e) { - throw new RuntimeException("Error to update movie", e.getCause()); + throw new VideoServiceException("Error to update movie", e.getCause()); } } @Override - public Optional delete(String id) { - Optional