From 2287cca95a1371d38951941ffc86c7a2b561bdc0 Mon Sep 17 00:00:00 2001 From: Jhonata Demuner Date: Thu, 13 Feb 2025 00:16:13 -0300 Subject: [PATCH 1/3] fix: change assemblers empty list return --- src/main/java/com/studai/domain/quiz/Quiz.java | 4 +++- src/main/java/com/studai/domain/quiz/dto/QuizDTO.java | 3 ++- .../java/com/studai/utils/assembler/QuestionAssembler.java | 6 ++++-- src/main/java/com/studai/utils/assembler/QuizAssembler.java | 1 + .../com/studai/utils/assembler/QuizAttemptAssembler.java | 5 +++-- src/test/java/com/studai/service/quiz/QuizServiceTest.java | 6 ++---- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/studai/domain/quiz/Quiz.java b/src/main/java/com/studai/domain/quiz/Quiz.java index 0039fdb..f5298e8 100644 --- a/src/main/java/com/studai/domain/quiz/Quiz.java +++ b/src/main/java/com/studai/domain/quiz/Quiz.java @@ -5,6 +5,8 @@ import jakarta.persistence.*; import lombok.*; import com.studai.domain.question.Question; + +import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -44,6 +46,6 @@ public class Quiz { @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "quiz_id") - private List attempts; + private List attempts = new ArrayList<>(); } diff --git a/src/main/java/com/studai/domain/quiz/dto/QuizDTO.java b/src/main/java/com/studai/domain/quiz/dto/QuizDTO.java index a2c4c1f..7b9dd96 100644 --- a/src/main/java/com/studai/domain/quiz/dto/QuizDTO.java +++ b/src/main/java/com/studai/domain/quiz/dto/QuizDTO.java @@ -7,6 +7,7 @@ import jakarta.persistence.Column; import lombok.*; +import java.util.ArrayList; import java.util.List; @Getter @@ -24,6 +25,6 @@ public class QuizDTO { private QuizSourceType sourceType; private String sourceUri; private String userId; - private List attempts; + private List attempts = new ArrayList<>(); } diff --git a/src/main/java/com/studai/utils/assembler/QuestionAssembler.java b/src/main/java/com/studai/utils/assembler/QuestionAssembler.java index fbaa7b6..ba6271f 100644 --- a/src/main/java/com/studai/utils/assembler/QuestionAssembler.java +++ b/src/main/java/com/studai/utils/assembler/QuestionAssembler.java @@ -3,7 +3,9 @@ import com.studai.domain.question.Question; import com.studai.domain.question.dto.QuestionDTO; import com.studai.domain.quiz.Quiz; +import com.studai.domain.quiz.attempt.QuizAttempt; +import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @@ -38,14 +40,14 @@ public static QuestionDTO toDTO(Question question) { } public static List toEntityList(List dtoList, Quiz quiz) { - if(dtoList == null) return null; + if(dtoList == null) return new ArrayList<>(); return dtoList.stream() .map(dto -> toEntity(dto, quiz)) .collect(Collectors.toList()); } public static List toDTOList(List questions) { - if(questions == null) return null; + if(questions == null) return new ArrayList<>(); return questions.stream() .map(QuestionAssembler::toDTO) .collect(Collectors.toList()); diff --git a/src/main/java/com/studai/utils/assembler/QuizAssembler.java b/src/main/java/com/studai/utils/assembler/QuizAssembler.java index 70085ad..9b674df 100644 --- a/src/main/java/com/studai/utils/assembler/QuizAssembler.java +++ b/src/main/java/com/studai/utils/assembler/QuizAssembler.java @@ -28,6 +28,7 @@ public static Quiz toEntity(QuizDTO dto, User user){ quiz.setQuestions(questions); List attempts = QuizAttemptAssembler.toEntityList(dto.getAttempts(), quiz, user); + quiz.setAttempts(attempts); return quiz; } diff --git a/src/main/java/com/studai/utils/assembler/QuizAttemptAssembler.java b/src/main/java/com/studai/utils/assembler/QuizAttemptAssembler.java index 16f1f21..0031141 100644 --- a/src/main/java/com/studai/utils/assembler/QuizAttemptAssembler.java +++ b/src/main/java/com/studai/utils/assembler/QuizAttemptAssembler.java @@ -5,6 +5,7 @@ import com.studai.domain.quiz.Quiz; import com.studai.domain.user.User; +import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @@ -34,14 +35,14 @@ public static QuizAttemptDTO toDTO(QuizAttempt quizAttempt) { } public static List toEntityList(List dtoList, Quiz quiz, User user) { - if(dtoList == null || dtoList.isEmpty()) return null; + if(dtoList == null || dtoList.isEmpty()) return new ArrayList<>(); return dtoList.stream() .map(dto -> toEntity(dto, quiz, user)) .collect(Collectors.toList()); } public static List toDTOList(List questions) { - if(questions == null || questions.isEmpty()) return null; + if(questions == null || questions.isEmpty()) return new ArrayList<>(); return questions.stream() .map(QuizAttemptAssembler::toDTO) .collect(Collectors.toList()); diff --git a/src/test/java/com/studai/service/quiz/QuizServiceTest.java b/src/test/java/com/studai/service/quiz/QuizServiceTest.java index c4b1e12..b738209 100644 --- a/src/test/java/com/studai/service/quiz/QuizServiceTest.java +++ b/src/test/java/com/studai/service/quiz/QuizServiceTest.java @@ -21,9 +21,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; -import java.util.UUID; +import java.util.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @@ -100,7 +98,7 @@ void testFindAll_Empty() { when(userService.getCurrentUser()).thenReturn(user); when(quizRepository.findByUser(user)).thenReturn(List.of()); - assertThrows(ResourceNotFoundException.class, () -> quizService.findAll()); + assertEquals(Collections.emptyList(), quizService.findAll()); } @Test From a444e013c88b10b4990c61bc2b7161bc3faddb7a Mon Sep 17 00:00:00 2001 From: Jhonata Demuner Date: Thu, 13 Feb 2025 00:17:22 -0300 Subject: [PATCH 2/3] feat: add meaningful exceptions --- .../studai/service/question/QuestionService.java | 8 ++++---- .../java/com/studai/service/quiz/QuizService.java | 13 +++++++------ .../utils/exception/ResourceNotFoundException.java | 11 +++++++++++ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/studai/service/question/QuestionService.java b/src/main/java/com/studai/service/question/QuestionService.java index 819ad1b..13d3b01 100644 --- a/src/main/java/com/studai/service/question/QuestionService.java +++ b/src/main/java/com/studai/service/question/QuestionService.java @@ -22,18 +22,18 @@ public class QuestionService { private QuizRepository quizRepository; public QuestionDTO create(QuestionDTO dto){ - Quiz quiz = quizRepository.findById(UUID.fromString(dto.getQuizId())).orElseThrow(ResourceNotFoundException::new); + Quiz quiz = quizRepository.findById(UUID.fromString(dto.getQuizId())).orElseThrow(() -> new ResourceNotFoundException("Quiz not found with ID: " + dto.getQuizId())); Question entity = questionRepository.save(QuestionAssembler.toEntity(dto, quiz)); return QuestionAssembler.toDTO(entity); } public QuestionDTO findById(String id){ - Question entity = questionRepository.findById(UUID.fromString(id)).orElseThrow(ResourceNotFoundException::new); + Question entity = questionRepository.findById(UUID.fromString(id)).orElseThrow(() -> new ResourceNotFoundException("Question not found with ID: " + id)); return QuestionAssembler.toDTO(entity); } public QuestionDTO update(QuestionDTO questionDTO) { - Question existingQuestion = questionRepository.findById(UUID.fromString(questionDTO.getId())).orElseThrow(ResourceNotFoundException::new); + Question existingQuestion = questionRepository.findById(UUID.fromString(questionDTO.getId())).orElseThrow(() -> new ResourceNotFoundException("Question not found with ID: " + questionDTO.getId())); Question updatedQuestion = QuestionAssembler.toEntity(questionDTO, existingQuestion.getQuiz()); updatedQuestion.setId(existingQuestion.getId()); @@ -41,7 +41,7 @@ public QuestionDTO update(QuestionDTO questionDTO) { } public QuestionDTO delete(String id) { - Question entity = questionRepository.findById(UUID.fromString(id)).orElseThrow(ResourceNotFoundException::new); + Question entity = questionRepository.findById(UUID.fromString(id)).orElseThrow(() -> new ResourceNotFoundException("Question not found with ID: " + id)); questionRepository.delete(entity); return QuestionAssembler.toDTO(entity); } diff --git a/src/main/java/com/studai/service/quiz/QuizService.java b/src/main/java/com/studai/service/quiz/QuizService.java index 2e45185..b98b5ca 100644 --- a/src/main/java/com/studai/service/quiz/QuizService.java +++ b/src/main/java/com/studai/service/quiz/QuizService.java @@ -59,14 +59,15 @@ public QuizDTO create(String videoId, int questionsNumber, String language){ return QuizAssembler.toDTO(entity); } - public QuizDTO findById(String id){ - Quiz quiz = quizRepository.findById(UUID.fromString(id)).orElseThrow(ResourceNotFoundException::new); + public QuizDTO findById(String id) { + Quiz quiz = quizRepository.findById(UUID.fromString(id)) + .orElseThrow(() -> new ResourceNotFoundException("Quiz not found with ID: " + id)); return QuizAssembler.toDTO(quiz); } + public List findAll() { List quizzes = quizRepository.findByUser(userService.getCurrentUser()); - if(quizzes.isEmpty()) throw new ResourceNotFoundException(); List quizDtos = new ArrayList<>(); for(Quiz quiz : quizzes){ quizDtos.add(QuizAssembler.toDTO(quiz)); @@ -76,7 +77,7 @@ public List findAll() { public QuizDTO update(QuizDTO quizDTO) { Quiz quiz = quizRepository.findById(UUID.fromString(quizDTO.getId())) - .orElseThrow(ResourceNotFoundException::new); + .orElseThrow(() -> new ResourceNotFoundException("Quiz not found with ID: " + quizDTO.getId())); quiz.setTitle(quizDTO.getTitle()); quiz.setDescription(quizDTO.getDescription()); @@ -112,14 +113,14 @@ public QuizDTO update(QuizDTO quizDTO) { public QuizDTO delete(String id) { - Quiz quiz = quizRepository.findById(UUID.fromString(id)).orElseThrow(ResourceNotFoundException::new); + Quiz quiz = quizRepository.findById(UUID.fromString(id)).orElseThrow(() -> new ResourceNotFoundException("Quiz not found with ID: " + id)); quizRepository.delete(quiz); return QuizAssembler.toDTO(quiz); } public QuizAttemptDTO submitAttempt(String quizId, Double score, Long timeSpent){ QuizAttempt attempt = QuizAttempt.builder() - .quiz(quizRepository.findById(UUID.fromString(quizId)).orElseThrow(ResourceNotFoundException::new)) + .quiz(quizRepository.findById(UUID.fromString(quizId)).orElseThrow(() -> new ResourceNotFoundException("Quiz not found with ID: " + quizId))) .user(userService.getCurrentUser()) .score(score) .timeSpent(timeSpent) diff --git a/src/main/java/com/studai/utils/exception/ResourceNotFoundException.java b/src/main/java/com/studai/utils/exception/ResourceNotFoundException.java index e4bdece..570a8fe 100644 --- a/src/main/java/com/studai/utils/exception/ResourceNotFoundException.java +++ b/src/main/java/com/studai/utils/exception/ResourceNotFoundException.java @@ -1,4 +1,15 @@ package com.studai.utils.exception; public class ResourceNotFoundException extends RuntimeException{ + + public ResourceNotFoundException(){}; + + public ResourceNotFoundException(String message) { + super(message); + } + + public ResourceNotFoundException(String message, Throwable cause) { + super(message, cause); + } + } From 73803fe5fb6b00fa4033890b18a7adea4beae64e Mon Sep 17 00:00:00 2001 From: Jhonata Demuner Date: Thu, 13 Feb 2025 00:25:53 -0300 Subject: [PATCH 3/3] feat: map bad credentials exception --- src/main/java/com/studai/config/CorsConfig.java | 2 +- .../utils/exception/GlobalExceptionHandler.java | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/studai/config/CorsConfig.java b/src/main/java/com/studai/config/CorsConfig.java index 56e1030..698ba3f 100644 --- a/src/main/java/com/studai/config/CorsConfig.java +++ b/src/main/java/com/studai/config/CorsConfig.java @@ -12,7 +12,7 @@ public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") - .allowedHeaders("*") + .allowedHeaders("Authorization", "Content-Type") .allowCredentials(true) .maxAge(3600); } diff --git a/src/main/java/com/studai/utils/exception/GlobalExceptionHandler.java b/src/main/java/com/studai/utils/exception/GlobalExceptionHandler.java index ab26da1..2ba7e18 100644 --- a/src/main/java/com/studai/utils/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/studai/utils/exception/GlobalExceptionHandler.java @@ -5,6 +5,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.BadCredentialsException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.ServletWebRequest; @@ -34,6 +35,22 @@ public ResponseEntity handleResourceNotFoundException(ResourceNotFoundEx return new ResponseEntity<>(errorResponse, new HttpHeaders(), HttpStatus.NOT_FOUND); } + @ExceptionHandler(BadCredentialsException.class) + public ResponseEntity handleBadCredentialsException(RuntimeException ex, WebRequest request) { + String path = ((ServletWebRequest) request).getRequest().getRequestURI(); + logger.error("Bad credentials exception at {}: {}", path, ex.getMessage(), ex); + + ErrorResponse errorResponse = ErrorResponse.builder() + .timestamp(new Date()) + .status(HttpStatus.UNAUTHORIZED.value()) + .error(HttpStatus.UNAUTHORIZED.getReasonPhrase()) + .message(ex.getMessage()) + .path(path) + .build(); + + return new ResponseEntity<>(errorResponse, new HttpHeaders(), HttpStatus.UNAUTHORIZED); + } + @ExceptionHandler(RuntimeException.class) public ResponseEntity handleRuntimeException(RuntimeException ex, WebRequest request) { String path = ((ServletWebRequest) request).getRequest().getRequestURI();