diff --git a/src/main/java/com/faf223/expensetrackerfaf/controller/ExpenseController.java b/src/main/java/com/faf223/expensetrackerfaf/controller/ExpenseController.java index 54a7e38..152b166 100644 --- a/src/main/java/com/faf223/expensetrackerfaf/controller/ExpenseController.java +++ b/src/main/java/com/faf223/expensetrackerfaf/controller/ExpenseController.java @@ -9,6 +9,10 @@ import com.faf223.expensetrackerfaf.model.User; import com.faf223.expensetrackerfaf.service.ExpenseCategoryService; import com.faf223.expensetrackerfaf.service.ExpenseService; import com.faf223.expensetrackerfaf.service.UserService; +import com.faf223.expensetrackerfaf.util.errors.ErrorResponse; +import com.faf223.expensetrackerfaf.util.exceptions.TransactionNotCreatedException; +import com.faf223.expensetrackerfaf.util.exceptions.TransactionNotUpdatedException; +import com.faf223.expensetrackerfaf.util.exceptions.TransactionsNotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -41,7 +45,7 @@ public class ExpenseController { public ResponseEntity> getAllExpenses() { List expenses = expenseService.getTransactions().stream().map(expenseMapper::toDto).collect(Collectors.toList()); if (!expenses.isEmpty()) return ResponseEntity.ok(expenses); - else return ResponseEntity.notFound().build(); + else throw new TransactionsNotFoundException("Transactions not found"); } @PostMapping() @@ -49,6 +53,9 @@ public class ExpenseController { BindingResult bindingResult) { Expense expense = expenseMapper.toExpense(expenseDTO); + if(bindingResult.hasErrors()) + throw new TransactionNotCreatedException(ErrorResponse.from(bindingResult).getMessage()); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.getPrincipal() instanceof UserDetails userDetails) { @@ -61,7 +68,7 @@ public class ExpenseController { return ResponseEntity.status(HttpStatus.CREATED).build(); } - return ResponseEntity.notFound().build(); + throw new TransactionNotCreatedException("Could not create new expense"); } @@ -70,6 +77,9 @@ public class ExpenseController { public ResponseEntity updateExpense(@PathVariable long id, @RequestBody ExpenseCreationDTO expenseDTO, BindingResult bindingResult) { Expense expense = expenseService.getTransactionById(id); + + if(expense == null) throw new TransactionsNotFoundException("The expense has not been found"); + ExpenseCategory category = expenseCategoryService.getCategoryById(expenseDTO.getExpenseCategory()); expense.setCategory(category); expense.setAmount(expenseDTO.getAmount()); @@ -78,7 +88,7 @@ public class ExpenseController { expenseService.createOrUpdate(expense); return ResponseEntity.status(HttpStatus.CREATED).build(); } else { - return ResponseEntity.notFound().build(); + throw new TransactionNotUpdatedException(ErrorResponse.from(bindingResult).getMessage()); } } @@ -104,14 +114,14 @@ public class ExpenseController { } } - return ResponseEntity.notFound().build(); + throw new TransactionsNotFoundException("The expenses have not been found"); } @GetMapping("/categories") public ResponseEntity> getAllCategories() { List categories = expenseCategoryService.getAllCategories(); if (!categories.isEmpty()) return ResponseEntity.ok(categories); - else return ResponseEntity.notFound().build(); + else throw new TransactionsNotFoundException("The expenses have not been found"); } @DeleteMapping("/delete/{id}") diff --git a/src/main/java/com/faf223/expensetrackerfaf/controller/GlobalExceptionHandler.java b/src/main/java/com/faf223/expensetrackerfaf/controller/GlobalExceptionHandler.java new file mode 100644 index 0000000..e903c63 --- /dev/null +++ b/src/main/java/com/faf223/expensetrackerfaf/controller/GlobalExceptionHandler.java @@ -0,0 +1,56 @@ +package com.faf223.expensetrackerfaf.controller; + +import com.faf223.expensetrackerfaf.util.errors.ErrorResponse; +import com.faf223.expensetrackerfaf.util.exceptions.TransactionDoesNotBelongToTheUserException; +import com.faf223.expensetrackerfaf.util.exceptions.TransactionNotCreatedException; +import com.faf223.expensetrackerfaf.util.exceptions.TransactionsNotFoundException; +import com.faf223.expensetrackerfaf.util.exceptions.UserNotFoundException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler + private ResponseEntity handleDoesNotBelongException(TransactionDoesNotBelongToTheUserException e) { + ErrorResponse response = new ErrorResponse( + e.getMessage(), + System.currentTimeMillis() + ); + + return new ResponseEntity<>(response, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler + private ResponseEntity handleTransactionNotCreatedException(TransactionNotCreatedException e) { + ErrorResponse response = new ErrorResponse( + e.getMessage(), + System.currentTimeMillis() + ); + + return new ResponseEntity<>(response, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler + private ResponseEntity handleTransactionsNotFoundException(TransactionsNotFoundException e) { + ErrorResponse response = new ErrorResponse( + e.getMessage(), + System.currentTimeMillis() + ); + + return new ResponseEntity<>(response, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler + private ResponseEntity handleUserNotFoundException(UserNotFoundException e) { + ErrorResponse response = new ErrorResponse( + e.getMessage(), + System.currentTimeMillis() + ); + + return new ResponseEntity<>(response, HttpStatus.NOT_FOUND); + } + +} diff --git a/src/main/java/com/faf223/expensetrackerfaf/controller/IncomeController.java b/src/main/java/com/faf223/expensetrackerfaf/controller/IncomeController.java index b6bd9cc..3b9bdd8 100644 --- a/src/main/java/com/faf223/expensetrackerfaf/controller/IncomeController.java +++ b/src/main/java/com/faf223/expensetrackerfaf/controller/IncomeController.java @@ -1,13 +1,18 @@ package com.faf223.expensetrackerfaf.controller; -import com.faf223.expensetrackerfaf.dto.ExpenseDTO; import com.faf223.expensetrackerfaf.dto.IncomeCreationDTO; import com.faf223.expensetrackerfaf.dto.IncomeDTO; import com.faf223.expensetrackerfaf.dto.mappers.IncomeMapper; -import com.faf223.expensetrackerfaf.model.*; +import com.faf223.expensetrackerfaf.model.Income; +import com.faf223.expensetrackerfaf.model.IncomeCategory; +import com.faf223.expensetrackerfaf.model.User; import com.faf223.expensetrackerfaf.service.IncomeCategoryService; import com.faf223.expensetrackerfaf.service.IncomeService; import com.faf223.expensetrackerfaf.service.UserService; +import com.faf223.expensetrackerfaf.util.errors.ErrorResponse; +import com.faf223.expensetrackerfaf.util.exceptions.TransactionNotCreatedException; +import com.faf223.expensetrackerfaf.util.exceptions.TransactionNotUpdatedException; +import com.faf223.expensetrackerfaf.util.exceptions.TransactionsNotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -40,13 +45,17 @@ public class IncomeController { public ResponseEntity> getAllIncomes() { List incomes = incomeService.getTransactions().stream().map(incomeMapper::toDto).collect(Collectors.toList()); if (!incomes.isEmpty()) return ResponseEntity.ok(incomes); - else return ResponseEntity.notFound().build(); + else throw new TransactionsNotFoundException("Transactions not found"); } @PostMapping() public ResponseEntity createNewIncome(@RequestBody IncomeCreationDTO incomeDTO, BindingResult bindingResult) { Income income = incomeMapper.toIncome(incomeDTO); + + if(bindingResult.hasErrors()) + throw new TransactionNotCreatedException(ErrorResponse.from(bindingResult).getMessage()); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.getPrincipal() instanceof UserDetails userDetails) { @@ -55,12 +64,11 @@ public class IncomeController { User user = userService.getUserByEmail(email); income.setUser(user); - System.out.println(income); incomeService.createOrUpdate(income); return ResponseEntity.status(HttpStatus.CREATED).build(); } - return ResponseEntity.notFound().build(); + throw new TransactionNotCreatedException("Could not create new expense"); } // TODO: check if the income belongs to the user, extract logic into service @@ -68,6 +76,9 @@ public class IncomeController { public ResponseEntity updateIncome(@PathVariable long id, @RequestBody IncomeCreationDTO incomeDTO, BindingResult bindingResult) { Income income = incomeService.getTransactionById(id); + + if(income == null) throw new TransactionsNotFoundException("The income has not been found"); + IncomeCategory category = incomeCategoryService.getCategoryById(incomeDTO.getIncomeCategory()); income.setCategory(category); income.setAmount(incomeDTO.getAmount()); @@ -76,30 +87,10 @@ public class IncomeController { incomeService.createOrUpdate(income); return ResponseEntity.status(HttpStatus.CREATED).build(); } else { - return ResponseEntity.notFound().build(); + throw new TransactionNotUpdatedException(ErrorResponse.from(bindingResult).getMessage()); } } - @GetMapping("/personal-incomes") - public ResponseEntity> getIncomesByUser() { - - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - if (authentication != null && authentication.getPrincipal() instanceof UserDetails userDetails) { - - String email = userDetails.getUsername(); - List incomes = incomeService.getTransactionsByEmail(email).stream().map(incomeMapper::toDto).collect(Collectors.toList()); - - if (!incomes.isEmpty()) { - return ResponseEntity.ok(incomes); - } else { - return ResponseEntity.ok(Collections.emptyList()); - } - } - - return ResponseEntity.notFound().build(); - } - @GetMapping("/personal-incomes") public ResponseEntity> getExpensesByUser(@RequestParam Optional date, @RequestParam Optional month) { @@ -122,14 +113,14 @@ public class IncomeController { } } - return ResponseEntity.notFound().build(); + throw new TransactionsNotFoundException("The expenses have not been found"); } @GetMapping("/categories") public ResponseEntity> getAllCategories() { List categories = incomeCategoryService.getAllCategories(); if (!categories.isEmpty()) return ResponseEntity.ok(categories); - else return ResponseEntity.notFound().build(); + else throw new TransactionsNotFoundException("The expenses have not been found"); } @DeleteMapping("/delete/{id}") diff --git a/src/main/java/com/faf223/expensetrackerfaf/controller/UserController.java b/src/main/java/com/faf223/expensetrackerfaf/controller/UserController.java index 407f99c..c961523 100644 --- a/src/main/java/com/faf223/expensetrackerfaf/controller/UserController.java +++ b/src/main/java/com/faf223/expensetrackerfaf/controller/UserController.java @@ -5,6 +5,7 @@ import com.faf223.expensetrackerfaf.dto.UserDTO; import com.faf223.expensetrackerfaf.dto.mappers.UserMapper; import com.faf223.expensetrackerfaf.model.User; import com.faf223.expensetrackerfaf.service.UserService; +import com.faf223.expensetrackerfaf.util.exceptions.UserNotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -32,7 +33,7 @@ public class UserController { userService.updateUser(user); return ResponseEntity.ok(userMapper.toDto(user)); } else { - return ResponseEntity.notFound().build(); + throw new UserNotFoundException("The user has not been found"); } } @@ -43,9 +44,8 @@ public class UserController { if (authentication != null && authentication.getPrincipal() instanceof UserDetails userDetails) { User user = userService.getUserByEmail(userDetails.getUsername()); if (user != null) return ResponseEntity.ok(userMapper.toDto(user)); - else return ResponseEntity.notFound().build(); } - return ResponseEntity.notFound().build(); + throw new UserNotFoundException("The user has not been found"); } @GetMapping() diff --git a/src/main/java/com/faf223/expensetrackerfaf/util/errors/ErrorResponse.java b/src/main/java/com/faf223/expensetrackerfaf/util/errors/ErrorResponse.java new file mode 100644 index 0000000..e2f3dd7 --- /dev/null +++ b/src/main/java/com/faf223/expensetrackerfaf/util/errors/ErrorResponse.java @@ -0,0 +1,40 @@ +package com.faf223.expensetrackerfaf.util.errors; + +import lombok.Data; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; + +import java.util.List; + +@Data +public class ErrorResponse { + private String message; + private long timestamp; + + public ErrorResponse(String message, long timestamp) { + this.message = message; + this.timestamp = timestamp; + } + + public ErrorResponse(String message) { + this.message = message; + this.timestamp = System.currentTimeMillis(); + } + + public static ErrorResponse from(BindingResult bindingResult) { + if(bindingResult.hasErrors()) { + StringBuilder errorMessage = new StringBuilder(); + + List errors = bindingResult.getFieldErrors(); + for(FieldError fieldError : errors) + errorMessage.append(fieldError.getField()) + .append(" - ") + .append(fieldError.getDefaultMessage()) + .append(";"); + + return new ErrorResponse(errorMessage.toString(), System.currentTimeMillis()); + } + + return new ErrorResponse("No error message was provided", System.currentTimeMillis()); + } +} diff --git a/src/main/java/com/faf223/expensetrackerfaf/util/exceptions/TransactionDoesNotBelongToTheUserException.java b/src/main/java/com/faf223/expensetrackerfaf/util/exceptions/TransactionDoesNotBelongToTheUserException.java new file mode 100644 index 0000000..6cf8e46 --- /dev/null +++ b/src/main/java/com/faf223/expensetrackerfaf/util/exceptions/TransactionDoesNotBelongToTheUserException.java @@ -0,0 +1,7 @@ +package com.faf223.expensetrackerfaf.util.exceptions; + +public class TransactionDoesNotBelongToTheUserException extends RuntimeException { + public TransactionDoesNotBelongToTheUserException(String message) { + super(message); + } +} diff --git a/src/main/java/com/faf223/expensetrackerfaf/util/exceptions/TransactionNotCreatedException.java b/src/main/java/com/faf223/expensetrackerfaf/util/exceptions/TransactionNotCreatedException.java new file mode 100644 index 0000000..f035f3a --- /dev/null +++ b/src/main/java/com/faf223/expensetrackerfaf/util/exceptions/TransactionNotCreatedException.java @@ -0,0 +1,7 @@ +package com.faf223.expensetrackerfaf.util.exceptions; + +public class TransactionNotCreatedException extends RuntimeException { + public TransactionNotCreatedException(String message) { + super(message); + } +} diff --git a/src/main/java/com/faf223/expensetrackerfaf/util/exceptions/TransactionNotUpdatedException.java b/src/main/java/com/faf223/expensetrackerfaf/util/exceptions/TransactionNotUpdatedException.java new file mode 100644 index 0000000..0ffeff9 --- /dev/null +++ b/src/main/java/com/faf223/expensetrackerfaf/util/exceptions/TransactionNotUpdatedException.java @@ -0,0 +1,7 @@ +package com.faf223.expensetrackerfaf.util.exceptions; + +public class TransactionNotUpdatedException extends RuntimeException { + public TransactionNotUpdatedException(String message) { + super(message); + } +} diff --git a/src/main/java/com/faf223/expensetrackerfaf/util/exceptions/TransactionsNotFoundException.java b/src/main/java/com/faf223/expensetrackerfaf/util/exceptions/TransactionsNotFoundException.java new file mode 100644 index 0000000..e001cc7 --- /dev/null +++ b/src/main/java/com/faf223/expensetrackerfaf/util/exceptions/TransactionsNotFoundException.java @@ -0,0 +1,7 @@ +package com.faf223.expensetrackerfaf.util.exceptions; + +public class TransactionsNotFoundException extends RuntimeException { + public TransactionsNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/com/faf223/expensetrackerfaf/util/exceptions/UserNotFoundException.java b/src/main/java/com/faf223/expensetrackerfaf/util/exceptions/UserNotFoundException.java new file mode 100644 index 0000000..808c0eb --- /dev/null +++ b/src/main/java/com/faf223/expensetrackerfaf/util/exceptions/UserNotFoundException.java @@ -0,0 +1,7 @@ +package com.faf223.expensetrackerfaf.util.exceptions; + +public class UserNotFoundException extends RuntimeException { + public UserNotFoundException(String message) { + super(message); + } +}