From 6fcd2bdd85c2aa354f4c7e589d3bc8860ce9f1a4 Mon Sep 17 00:00:00 2001 From: andwati Date: Fri, 12 Jun 2026 16:44:18 +0300 Subject: [PATCH] completed exception handlers --- .../exception/GlobalExceptionHandler.java | 200 ++++++++++++++++++ .../exception/InsufficientStockException.java | 28 +++ .../exception/InventoryNotFoundException.java | 10 + .../OrderNotCancellableException.java | 10 + .../exception/OrderNotFoundException.java | 10 + .../exception/ProductNotFoundException.java | 10 + 6 files changed, 268 insertions(+) create mode 100644 orders-backend/src/main/java/com/andwati/orders/exception/GlobalExceptionHandler.java create mode 100644 orders-backend/src/main/java/com/andwati/orders/exception/InsufficientStockException.java create mode 100644 orders-backend/src/main/java/com/andwati/orders/exception/InventoryNotFoundException.java create mode 100644 orders-backend/src/main/java/com/andwati/orders/exception/OrderNotCancellableException.java create mode 100644 orders-backend/src/main/java/com/andwati/orders/exception/OrderNotFoundException.java create mode 100644 orders-backend/src/main/java/com/andwati/orders/exception/ProductNotFoundException.java diff --git a/orders-backend/src/main/java/com/andwati/orders/exception/GlobalExceptionHandler.java b/orders-backend/src/main/java/com/andwati/orders/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..3e007b0 --- /dev/null +++ b/orders-backend/src/main/java/com/andwati/orders/exception/GlobalExceptionHandler.java @@ -0,0 +1,200 @@ +package com.andwati.orders.exception; + +import com.andwati.orders.dto.response.ErrorResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.ConstraintViolationException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.HandlerMethodValidationException; + +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleMethodArgumentNotValid( + MethodArgumentNotValidException exception, + HttpServletRequest request + ) { + List> details = exception.getBindingResult() + .getFieldErrors() + .stream() + .map(this::fieldErrorToMap) + .toList(); + + return build( + HttpStatus.BAD_REQUEST, + "VALIDATION_ERROR", + "Request validation failed", + details, + request + ); + } + + @ExceptionHandler(ConstraintViolationException.class) + public ResponseEntity handleConstraintViolation( + ConstraintViolationException exception, + HttpServletRequest request + ) { + List> details = exception.getConstraintViolations() + .stream() + .map(violation -> Map.of( + "field", violation.getPropertyPath().toString(), + "message", violation.getMessage() + )) + .toList(); + + return build( + HttpStatus.BAD_REQUEST, + "VALIDATION_ERROR", + "Request validation failed", + details, + request + ); + } + + @ExceptionHandler(HandlerMethodValidationException.class) + public ResponseEntity handleHandlerMethodValidation( + HandlerMethodValidationException exception, + HttpServletRequest request + ) { + return build( + HttpStatus.BAD_REQUEST, + "VALIDATION_ERROR", + "Request validation failed", + exception.getMessage(), + request + ); + } + + @ExceptionHandler(ProductNotFoundException.class) + public ResponseEntity handleProductNotFound( + ProductNotFoundException exception, + HttpServletRequest request + ) { + return build( + HttpStatus.NOT_FOUND, + "PRODUCT_NOT_FOUND", + exception.getMessage(), + null, + request + ); + } + + @ExceptionHandler(OrderNotFoundException.class) + public ResponseEntity handleOrderNotFound( + OrderNotFoundException exception, + HttpServletRequest request + ) { + return build( + HttpStatus.NOT_FOUND, + "ORDER_NOT_FOUND", + exception.getMessage(), + null, + request + ); + } + + @ExceptionHandler(InventoryNotFoundException.class) + public ResponseEntity handleInventoryNotFound( + InventoryNotFoundException exception, + HttpServletRequest request + ) { + return build( + HttpStatus.NOT_FOUND, + "INVENTORY_NOT_FOUND", + exception.getMessage(), + null, + request + ); + } + + @ExceptionHandler(InsufficientStockException.class) + public ResponseEntity handleInsufficientStock( + InsufficientStockException exception, + HttpServletRequest request + ) { + return build( + HttpStatus.CONFLICT, + "INSUFFICIENT_STOCK", + exception.getMessage(), + exception.getDetails(), + request + ); + } + + @ExceptionHandler(OrderNotCancellableException.class) + public ResponseEntity handleOrderNotCancellable( + OrderNotCancellableException exception, + HttpServletRequest request + ) { + return build( + HttpStatus.CONFLICT, + "ORDER_NOT_CANCELLABLE", + exception.getMessage(), + null, + request + ); + } + + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity handleIllegalArgument( + IllegalArgumentException exception, + HttpServletRequest request + ) { + return build( + HttpStatus.BAD_REQUEST, + "VALIDATION_ERROR", + exception.getMessage(), + null, + request + ); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleUnexpected( + Exception exception, + HttpServletRequest request + ) { + return build( + HttpStatus.INTERNAL_SERVER_ERROR, + "INTERNAL_ERROR", + "An unexpected error occurred", + null, + request + ); + } + + private Map fieldErrorToMap(FieldError fieldError) { + Map map = new HashMap<>(); + map.put("field", fieldError.getField()); + map.put("message", fieldError.getDefaultMessage()); + return map; + } + + private ResponseEntity build( + HttpStatus status, + String error, + String message, + Object details, + HttpServletRequest request + ) { + ErrorResponse response = new ErrorResponse( + error, + message, + details, + Instant.now(), + request.getRequestURI() + ); + + return ResponseEntity.status(status).body(response); + } +} \ No newline at end of file diff --git a/orders-backend/src/main/java/com/andwati/orders/exception/InsufficientStockException.java b/orders-backend/src/main/java/com/andwati/orders/exception/InsufficientStockException.java new file mode 100644 index 0000000..de864be --- /dev/null +++ b/orders-backend/src/main/java/com/andwati/orders/exception/InsufficientStockException.java @@ -0,0 +1,28 @@ +package com.andwati.orders.exception; + +import java.util.Map; +import java.util.UUID; + +public class InsufficientStockException extends RuntimeException { + + private final Map details; + + public InsufficientStockException( + UUID productId, + String productName, + int requestedQuantity, + int availableQuantity + ) { + super("Insufficient stock for " + productName); + this.details = Map.of( + "productId", productId, + "productName", productName, + "requestedQuantity", requestedQuantity, + "availableQuantity", availableQuantity + ); + } + + public Map getDetails() { + return details; + } +} \ No newline at end of file diff --git a/orders-backend/src/main/java/com/andwati/orders/exception/InventoryNotFoundException.java b/orders-backend/src/main/java/com/andwati/orders/exception/InventoryNotFoundException.java new file mode 100644 index 0000000..eef4620 --- /dev/null +++ b/orders-backend/src/main/java/com/andwati/orders/exception/InventoryNotFoundException.java @@ -0,0 +1,10 @@ +package com.andwati.orders.exception; + +import java.util.UUID; + +public class InventoryNotFoundException extends RuntimeException { + + public InventoryNotFoundException(UUID productId) { + super("Inventory record for product " + productId + " does not exist"); + } +} \ No newline at end of file diff --git a/orders-backend/src/main/java/com/andwati/orders/exception/OrderNotCancellableException.java b/orders-backend/src/main/java/com/andwati/orders/exception/OrderNotCancellableException.java new file mode 100644 index 0000000..d904db3 --- /dev/null +++ b/orders-backend/src/main/java/com/andwati/orders/exception/OrderNotCancellableException.java @@ -0,0 +1,10 @@ +package com.andwati.orders.exception; + +import java.util.UUID; + +public class OrderNotCancellableException extends RuntimeException { + + public OrderNotCancellableException(UUID orderId) { + super("Order " + orderId + " cannot be cancelled because it is not in PLACED status"); + } +} \ No newline at end of file diff --git a/orders-backend/src/main/java/com/andwati/orders/exception/OrderNotFoundException.java b/orders-backend/src/main/java/com/andwati/orders/exception/OrderNotFoundException.java new file mode 100644 index 0000000..672acc1 --- /dev/null +++ b/orders-backend/src/main/java/com/andwati/orders/exception/OrderNotFoundException.java @@ -0,0 +1,10 @@ +package com.andwati.orders.exception; + +import java.util.UUID; + +public class OrderNotFoundException extends RuntimeException { + + public OrderNotFoundException(UUID orderId) { + super("Order " + orderId + " does not exist"); + } +} \ No newline at end of file diff --git a/orders-backend/src/main/java/com/andwati/orders/exception/ProductNotFoundException.java b/orders-backend/src/main/java/com/andwati/orders/exception/ProductNotFoundException.java new file mode 100644 index 0000000..c9d7b4c --- /dev/null +++ b/orders-backend/src/main/java/com/andwati/orders/exception/ProductNotFoundException.java @@ -0,0 +1,10 @@ +package com.andwati.orders.exception; + +import java.util.UUID; + +public class ProductNotFoundException extends RuntimeException { + + public ProductNotFoundException(UUID productId) { + super("Product " + productId + " does not exist"); + } +} \ No newline at end of file