feat(dashboard): enrich activity feed DTO with commentId + annotationId

ActivityFeedItemDTO gains nullable commentId and annotationId fields.
DashboardService.getActivity forwards commentId from the projection
and batch-resolves annotationId via the new
CommentService.findAnnotationIdsByIds lookup. Both remain null for
non-comment kinds, so the bulk lookup is skipped entirely when the
feed has no comment rows.

Refs #300.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-21 17:11:03 +02:00
parent 40260be07a
commit f50a746619
3 changed files with 98 additions and 3 deletions

View File

@@ -10,6 +10,7 @@ import org.raddatz.familienarchiv.audit.AuditLogQueryService;
import org.raddatz.familienarchiv.model.AppUser;
import org.raddatz.familienarchiv.model.Document;
import org.raddatz.familienarchiv.model.TranscriptionBlock;
import org.raddatz.familienarchiv.service.CommentService;
import org.raddatz.familienarchiv.service.DocumentService;
import org.raddatz.familienarchiv.service.TranscriptionService;
import org.raddatz.familienarchiv.service.UserService;
@@ -17,6 +18,7 @@ import org.raddatz.familienarchiv.service.UserService;
import java.time.Instant;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@@ -33,6 +35,7 @@ class DashboardServiceTest {
@Mock DocumentService documentService;
@Mock TranscriptionService transcriptionService;
@Mock UserService userService;
@Mock CommentService commentService;
@InjectMocks DashboardService dashboardService;
@@ -94,7 +97,72 @@ class DashboardServiceTest {
verify(documentService, never()).getDocumentById(docId);
}
// ─── getActivity comment/annotation enrichment ────────────────────────────
@Test
void getActivity_populatesCommentId_forCommentEvents() {
UUID userId = UUID.randomUUID();
UUID docId = UUID.randomUUID();
UUID commentId = UUID.randomUUID();
ActivityFeedRow row = mockFeedRow(docId, "COMMENT_ADDED", commentId);
when(auditLogQueryService.findActivityFeed(userId, 5)).thenReturn(List.of(row));
when(documentService.getDocumentsByIds(List.of(docId))).thenReturn(List.of(
Document.builder().id(docId).title("B").originalFilename("b.pdf").receivers(new HashSet<>()).build()
));
when(commentService.findAnnotationIdsByIds(List.of(commentId))).thenReturn(Map.of());
List<ActivityFeedItemDTO> items = dashboardService.getActivity(userId, 5);
assertThat(items).hasSize(1);
assertThat(items.get(0).commentId()).isEqualTo(commentId);
}
@Test
void getActivity_populatesAnnotationId_viaCommentService() {
UUID userId = UUID.randomUUID();
UUID docId = UUID.randomUUID();
UUID commentId = UUID.randomUUID();
UUID annotationId = UUID.randomUUID();
ActivityFeedRow row = mockFeedRow(docId, "COMMENT_ADDED", commentId);
when(auditLogQueryService.findActivityFeed(userId, 5)).thenReturn(List.of(row));
when(documentService.getDocumentsByIds(List.of(docId))).thenReturn(List.of(
Document.builder().id(docId).title("B").originalFilename("b.pdf").receivers(new HashSet<>()).build()
));
when(commentService.findAnnotationIdsByIds(List.of(commentId)))
.thenReturn(Map.of(commentId, annotationId));
List<ActivityFeedItemDTO> items = dashboardService.getActivity(userId, 5);
assertThat(items).hasSize(1);
assertThat(items.get(0).annotationId()).isEqualTo(annotationId);
}
@Test
void getActivity_leavesBothNull_forNonCommentKinds() {
UUID userId = UUID.randomUUID();
UUID docId = UUID.randomUUID();
ActivityFeedRow row = mockFeedRow(docId, "TEXT_SAVED", null);
when(auditLogQueryService.findActivityFeed(userId, 5)).thenReturn(List.of(row));
when(documentService.getDocumentsByIds(List.of(docId))).thenReturn(List.of(
Document.builder().id(docId).title("B").originalFilename("b.pdf").receivers(new HashSet<>()).build()
));
List<ActivityFeedItemDTO> items = dashboardService.getActivity(userId, 5);
assertThat(items).hasSize(1);
assertThat(items.get(0).commentId()).isNull();
assertThat(items.get(0).annotationId()).isNull();
verify(commentService, never()).findAnnotationIdsByIds(anyList());
}
private ActivityFeedRow mockFeedRow(UUID docId, String kind) {
return mockFeedRow(docId, kind, null);
}
private ActivityFeedRow mockFeedRow(UUID docId, String kind, UUID commentId) {
return new ActivityFeedRow() {
public String getKind() { return kind; }
public UUID getActorId() { return null; }
@@ -107,7 +175,7 @@ class DashboardServiceTest {
public boolean isYouParticipated() { return false; }
public int getCount() { return 1; }
public Instant getHappenedAtUntil() { return null; }
public UUID getCommentId() { return null; }
public UUID getCommentId() { return commentId; }
};
}
}