feat: add domain exceptions and global error handler
This commit is contained in:
1 parent
49b1e28855
commit
fcbef8490f
9 files changed
+150
No files matched your search
@@ -0,0 +1,8 @@
|
|||||||
|
package com.slotline.common;
|
||||||
|
|
||||||
|
public class AvailabilityViolationException extends RuntimeException {
|
||||||
|
|
||||||
|
public AvailabilityViolationException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.slotline.common;
|
||||||
|
|
||||||
|
public class ForbiddenOperationException extends RuntimeException {
|
||||||
|
|
||||||
|
public ForbiddenOperationException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package com.slotline.common;
|
||||||
|
|
||||||
|
import com.slotline.recurrence.RecurrenceException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.springframework.dao.ConcurrencyFailureException;
|
||||||
|
import org.springframework.dao.DataIntegrityViolationException;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ProblemDetail;
|
||||||
|
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 GlobalExceptionHandler {
|
||||||
|
|
||||||
|
@ExceptionHandler(NotFoundException.class)
|
||||||
|
public ResponseEntity<ProblemDetail> handleNotFound(NotFoundException exception, HttpServletRequest request) {
|
||||||
|
return problem(HttpStatus.NOT_FOUND, exception.getMessage(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(InvalidCredentialsException.class)
|
||||||
|
public ResponseEntity<ProblemDetail> handleInvalidCredentials(InvalidCredentialsException exception,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return problem(HttpStatus.UNAUTHORIZED, exception.getMessage(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(ForbiddenOperationException.class)
|
||||||
|
public ResponseEntity<ProblemDetail> handleForbidden(ForbiddenOperationException exception,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return problem(HttpStatus.FORBIDDEN, exception.getMessage(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler({InvalidReservationException.class, RecurrenceException.class})
|
||||||
|
public ResponseEntity<ProblemDetail> handleInvalidReservation(RuntimeException exception,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return problem(HttpStatus.BAD_REQUEST, exception.getMessage(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(AvailabilityViolationException.class)
|
||||||
|
public ResponseEntity<ProblemDetail> handleAvailability(AvailabilityViolationException exception,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return problem(HttpStatus.UNPROCESSABLE_ENTITY, exception.getMessage(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler({SlotConflictException.class, UsernameTakenException.class, SlugTakenException.class})
|
||||||
|
public ResponseEntity<ProblemDetail> handleConflict(RuntimeException exception, HttpServletRequest request) {
|
||||||
|
return problem(HttpStatus.CONFLICT, exception.getMessage(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler({ConcurrencyFailureException.class, DataIntegrityViolationException.class})
|
||||||
|
public ResponseEntity<ProblemDetail> handleConcurrentBooking(RuntimeException exception,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return problem(HttpStatus.CONFLICT, "The slot was booked by a concurrent request", request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||||
|
public ResponseEntity<ProblemDetail> handleUnreadable(HttpMessageNotReadableException exception,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return problem(HttpStatus.BAD_REQUEST, "Malformed request body", request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
public ResponseEntity<ProblemDetail> handleValidation(MethodArgumentNotValidException exception,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
List<FieldViolation> violations = exception.getBindingResult().getFieldErrors().stream()
|
||||||
|
.map(error -> new FieldViolation(error.getField(), error.getDefaultMessage()))
|
||||||
|
.toList();
|
||||||
|
ResponseEntity<ProblemDetail> response = problem(HttpStatus.BAD_REQUEST, "Request validation failed", request);
|
||||||
|
response.getBody().setProperty("violations", violations);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseEntity<ProblemDetail> problem(HttpStatus status, String detail, HttpServletRequest request) {
|
||||||
|
ProblemDetail body = ProblemDetail.forStatusAndDetail(status, detail);
|
||||||
|
body.setTitle(status.getReasonPhrase());
|
||||||
|
body.setProperty("path", request.getRequestURI());
|
||||||
|
return ResponseEntity.status(status).body(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record FieldViolation(String field, String message) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.slotline.common;
|
||||||
|
|
||||||
|
public class InvalidCredentialsException extends RuntimeException {
|
||||||
|
|
||||||
|
public InvalidCredentialsException() {
|
||||||
|
super("Invalid username or password");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.slotline.common;
|
||||||
|
|
||||||
|
public class InvalidReservationException extends RuntimeException {
|
||||||
|
|
||||||
|
public InvalidReservationException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.slotline.common;
|
||||||
|
|
||||||
|
public class NotFoundException extends RuntimeException {
|
||||||
|
|
||||||
|
public NotFoundException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.slotline.common;
|
||||||
|
|
||||||
|
public class SlotConflictException extends RuntimeException {
|
||||||
|
|
||||||
|
public SlotConflictException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.slotline.common;
|
||||||
|
|
||||||
|
public class SlugTakenException extends RuntimeException {
|
||||||
|
|
||||||
|
public SlugTakenException(String slug) {
|
||||||
|
super("Organization slug '" + slug + "' is already taken");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.slotline.common;
|
||||||
|
|
||||||
|
public class UsernameTakenException extends RuntimeException {
|
||||||
|
|
||||||
|
public UsernameTakenException(String username) {
|
||||||
|
super("Username '" + username + "' is already taken");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user