diff --git a/backend/pom.xml b/backend/pom.xml index da863de2..2f36d8d7 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -34,6 +34,10 @@ org.springframework.boot spring-boot-starter-actuator + + org.springframework.boot + spring-boot-starter-validation + org.springframework.boot spring-boot-starter-data-jpa diff --git a/backend/src/main/java/org/raddatz/familienarchiv/controller/GlobalExceptionHandler.java b/backend/src/main/java/org/raddatz/familienarchiv/controller/GlobalExceptionHandler.java index afeb052a..dff7d648 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/controller/GlobalExceptionHandler.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/controller/GlobalExceptionHandler.java @@ -2,6 +2,7 @@ package org.raddatz.familienarchiv.controller; import java.util.stream.Collectors; +import jakarta.validation.ConstraintViolationException; import org.raddatz.familienarchiv.exception.DomainException; import org.raddatz.familienarchiv.exception.ErrorCode; import org.springframework.http.ResponseEntity; @@ -32,6 +33,14 @@ public class GlobalExceptionHandler { return ResponseEntity.badRequest().body(new ErrorResponse(ErrorCode.VALIDATION_ERROR, message)); } + @ExceptionHandler(ConstraintViolationException.class) + public ResponseEntity handleConstraintViolation(ConstraintViolationException ex) { + String message = ex.getConstraintViolations().stream() + .map(v -> v.getPropertyPath() + ": " + v.getMessage()) + .collect(Collectors.joining(", ")); + return ResponseEntity.badRequest().body(new ErrorResponse(ErrorCode.VALIDATION_ERROR, message)); + } + @ExceptionHandler(MethodArgumentTypeMismatchException.class) public ResponseEntity handleTypeMismatch(MethodArgumentTypeMismatchException ex) { String message = "Invalid value '" + ex.getValue() + "' for parameter '" + ex.getName() + "'"; diff --git a/backend/src/main/java/org/raddatz/familienarchiv/controller/NotificationController.java b/backend/src/main/java/org/raddatz/familienarchiv/controller/NotificationController.java index 61bef6c6..ac85ae46 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/controller/NotificationController.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/controller/NotificationController.java @@ -1,6 +1,9 @@ package org.raddatz.familienarchiv.controller; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; import lombok.RequiredArgsConstructor; +import io.swagger.v3.oas.annotations.Parameter; import org.raddatz.familienarchiv.dto.NotificationDTO; import org.raddatz.familienarchiv.dto.NotificationPreferenceDTO; import org.raddatz.familienarchiv.model.AppUser; @@ -16,6 +19,7 @@ import org.springframework.data.domain.Sort; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.core.Authentication; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; @@ -25,6 +29,7 @@ import java.util.UUID; @RestController @RequiredArgsConstructor +@Validated public class NotificationController { private final NotificationService notificationService; @@ -44,9 +49,9 @@ public class NotificationController { @GetMapping("/api/notifications") public Page getNotifications( @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "10") int size, - @RequestParam(required = false) NotificationType type, - @RequestParam(required = false) Boolean read, + @RequestParam(defaultValue = "10") @Min(1) @Max(100) int size, + @Parameter(description = "Filter by notification type") @RequestParam(required = false) NotificationType type, + @Parameter(description = "Filter by read status") @RequestParam(required = false) Boolean read, Authentication authentication) { AppUser user = resolveUser(authentication); PageRequest pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());