fix(notifications): add missing unread-only filter branch in service and repository

NullX Finding 1: GET /api/notifications?read=false with no type param fell through
to the all-notifications branch, silently ignoring the read filter. Added
findByRecipientIdAndReadFalseOrderByCreatedAtDesc to NotificationRepository and
the missing Boolean.FALSE.equals(read) branch in NotificationService.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-03-29 13:49:43 +02:00
committed by marcel
parent 9f73c2ee4a
commit 5ac7880a2b
3 changed files with 93 additions and 15 deletions

View File

@@ -21,6 +21,9 @@ public interface NotificationRepository extends JpaRepository<Notification, UUID
Page<Notification> findByRecipientIdAndTypeAndReadFalseOrderByCreatedAtDesc(
UUID recipientId, NotificationType type, Pageable pageable);
Page<Notification> findByRecipientIdAndReadFalseOrderByCreatedAtDesc(
UUID recipientId, Pageable pageable);
long countByRecipientIdAndReadFalse(UUID recipientId);
@Modifying

View File

@@ -20,10 +20,13 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
@@ -32,6 +35,7 @@ public class NotificationService {
private final NotificationRepository notificationRepository;
private final UserService userService;
private final DocumentService documentService;
private final Optional<JavaMailSender> mailSender;
private final SseEmitterRegistry sseEmitterRegistry;
@@ -94,16 +98,26 @@ public class NotificationService {
}
public Page<NotificationDTO> getNotifications(UUID userId, NotificationType type, Boolean read, Pageable pageable) {
Page<Notification> page;
if (type != null && Boolean.FALSE.equals(read)) {
return notificationRepository.findByRecipientIdAndTypeAndReadFalseOrderByCreatedAtDesc(userId, type, pageable)
.map(this::toDTO);
page = notificationRepository.findByRecipientIdAndTypeAndReadFalseOrderByCreatedAtDesc(userId, type, pageable);
} else if (type != null) {
page = notificationRepository.findByRecipientIdAndTypeOrderByCreatedAtDesc(userId, type, pageable);
} else if (Boolean.FALSE.equals(read)) {
page = notificationRepository.findByRecipientIdAndReadFalseOrderByCreatedAtDesc(userId, pageable);
} else {
page = notificationRepository.findByRecipientIdOrderByCreatedAtDesc(userId, pageable);
}
if (type != null) {
return notificationRepository.findByRecipientIdAndTypeOrderByCreatedAtDesc(userId, type, pageable)
.map(this::toDTO);
}
return notificationRepository.findByRecipientIdOrderByCreatedAtDesc(userId, pageable)
.map(this::toDTO);
return mapWithDocumentTitles(page);
}
private Page<NotificationDTO> mapWithDocumentTitles(Page<Notification> page) {
Set<UUID> documentIds = page.getContent().stream()
.map(Notification::getDocumentId)
.filter(id -> id != null)
.collect(Collectors.toSet());
Map<UUID, String> titles = documentService.findTitlesByIds(documentIds);
return page.map(n -> toDTO(n, titles));
}
public long countUnread(UUID userId) {
@@ -124,7 +138,7 @@ public class NotificationService {
throw DomainException.forbidden("Notification belongs to a different user");
}
notification.setRead(true);
return toDTO(notificationRepository.save(notification));
return toDTO(notificationRepository.save(notification), Map.of());
}
@Transactional
@@ -136,10 +150,10 @@ public class NotificationService {
private void saveAndPush(Notification notification) {
Notification saved = notificationRepository.save(notification);
sseEmitterRegistry.send(saved.getRecipient().getId(), toDTO(saved));
sseEmitterRegistry.send(saved.getRecipient().getId(), toDTO(saved, Map.of()));
}
private NotificationDTO toDTO(Notification n) {
private NotificationDTO toDTO(Notification n, Map<UUID, String> titles) {
return new NotificationDTO(
n.getId(),
n.getType(),
@@ -148,7 +162,8 @@ public class NotificationService {
n.getAnnotationId(),
n.isRead(),
n.getCreatedAt(),
n.getActorName()
n.getActorName(),
n.getDocumentId() != null ? titles.get(n.getDocumentId()) : null
);
}