Compare commits
6 Commits
7906373053
...
8ca3f37817
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ca3f37817 | ||
|
|
1dc812bd47 | ||
|
|
7a647b5633 | ||
|
|
5f76d4a1ac | ||
|
|
c7958681f5 | ||
|
|
1f3f879f9c |
@@ -31,6 +31,15 @@ public interface TranscriptionBlockRepository extends JpaRepository<Transcriptio
|
||||
|
||||
List<TranscriptionBlock> findByMentionedPersons_PersonId(UUID personId);
|
||||
|
||||
@Query("""
|
||||
SELECT DISTINCT b FROM TranscriptionBlock b
|
||||
JOIN FETCH b.mentionedPersons
|
||||
WHERE b.id IN (
|
||||
SELECT bb.id FROM TranscriptionBlock bb JOIN bb.mentionedPersons m WHERE m.personId = :personId
|
||||
)
|
||||
""")
|
||||
List<TranscriptionBlock> findByPersonIdWithMentionsFetched(@Param("personId") UUID personId);
|
||||
|
||||
void deleteByAnnotationId(UUID annotationId);
|
||||
|
||||
int countByDocumentId(UUID documentId);
|
||||
|
||||
@@ -35,10 +35,10 @@ public class PersonMentionPropagationListener {
|
||||
private final TranscriptionBlockRepository blockRepository;
|
||||
|
||||
@EventListener
|
||||
@Transactional
|
||||
@Transactional // Joins publisher's transaction — async switch requires @TransactionalEventListener(AFTER_COMMIT)
|
||||
public void onPersonDisplayNameChanged(PersonDisplayNameChangedEvent event) {
|
||||
List<TranscriptionBlock> blocks =
|
||||
blockRepository.findByMentionedPersons_PersonId(event.personId());
|
||||
blockRepository.findByPersonIdWithMentionsFetched(event.personId());
|
||||
if (blocks.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -333,6 +333,21 @@ class PersonControllerTest {
|
||||
.andExpect(jsonPath("$.lastName").value("Müller"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void updatePerson_returns409_whenRenameConflict() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
when(personService.updatePerson(eq(id), any()))
|
||||
.thenThrow(DomainException.conflict(ErrorCode.PERSON_RENAME_CONFLICT,
|
||||
"Concurrent block edit during rename"));
|
||||
|
||||
mockMvc.perform(put("/api/persons/{id}", id)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"firstName\":\"Augusta\",\"lastName\":\"Raddatz\",\"personType\":\"PERSON\"}"))
|
||||
.andExpect(status().isConflict())
|
||||
.andExpect(jsonPath("$.code").value("PERSON_RENAME_CONFLICT"));
|
||||
}
|
||||
|
||||
// ─── POST /api/persons/{id}/merge ─────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
|
||||
@@ -92,4 +92,36 @@ class TranscriptionBlockMentionsRepositoryTest {
|
||||
TranscriptionBlock reloaded = blockRepository.findById(saved.getId()).orElseThrow();
|
||||
assertThat(reloaded.getMentionedPersons()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByPersonIdWithMentionsFetched_returnsOnlyBlocksReferencingPerson_withMentionsLoaded() {
|
||||
UUID augusteId = UUID.randomUUID();
|
||||
UUID hermannId = UUID.randomUUID();
|
||||
|
||||
blockRepository.saveAndFlush(TranscriptionBlock.builder()
|
||||
.annotationId(annotationId).documentId(documentId)
|
||||
.text("Brief von @Auguste Raddatz an @Hermann Müller.")
|
||||
.sortOrder(0)
|
||||
.mentionedPersons(List.of(
|
||||
new PersonMention(augusteId, "Auguste Raddatz"),
|
||||
new PersonMention(hermannId, "Hermann Müller")))
|
||||
.build());
|
||||
blockRepository.saveAndFlush(TranscriptionBlock.builder()
|
||||
.annotationId(annotationId).documentId(documentId)
|
||||
.text("Unrelated block without Auguste.")
|
||||
.sortOrder(1)
|
||||
.mentionedPersons(List.of(new PersonMention(hermannId, "Hermann Müller")))
|
||||
.build());
|
||||
em.clear();
|
||||
|
||||
List<TranscriptionBlock> result =
|
||||
blockRepository.findByPersonIdWithMentionsFetched(augusteId);
|
||||
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(result.get(0).getMentionedPersons())
|
||||
.extracting(PersonMention::getPersonId, PersonMention::getDisplayName)
|
||||
.containsExactlyInAnyOrder(
|
||||
org.assertj.core.groups.Tuple.tuple(augusteId, "Auguste Raddatz"),
|
||||
org.assertj.core.groups.Tuple.tuple(hermannId, "Hermann Müller"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,8 +187,8 @@ class PersonMentionPropagationListenerTest {
|
||||
long elapsedMs = (System.nanoTime() - start) / 1_000_000;
|
||||
|
||||
assertThat(elapsedMs)
|
||||
.as("Propagation across 200 blocks must stay under 2s — merge-blocking regression floor")
|
||||
.isLessThan(2000L);
|
||||
.as("Propagation across 200 blocks must stay under 5s — merge-blocking regression floor")
|
||||
.isLessThan(5000L);
|
||||
|
||||
em.clear();
|
||||
TranscriptionBlock first = blockRepository.findById(blockIds.get(0)).orElseThrow();
|
||||
|
||||
@@ -284,7 +284,7 @@ class PersonServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void updatePerson_doesNotPublishEvent_whenOnlyAliasChanges() {
|
||||
void updatePerson_doesNotPublishEvent_whenDisplayNameFieldsUnchanged() {
|
||||
UUID id = UUID.randomUUID();
|
||||
Person existing = Person.builder()
|
||||
.id(id).firstName("Auguste").lastName("Raddatz")
|
||||
@@ -321,7 +321,7 @@ class PersonServiceTest {
|
||||
.build();
|
||||
|
||||
TranscriptionBlockRepository blockRepo = mock(TranscriptionBlockRepository.class);
|
||||
when(blockRepo.findByMentionedPersons_PersonId(id))
|
||||
when(blockRepo.findByPersonIdWithMentionsFetched(id))
|
||||
.thenReturn(List.of(referencingBlock));
|
||||
when(blockRepo.saveAllAndFlush(any()))
|
||||
.thenThrow(new ObjectOptimisticLockingFailureException(
|
||||
|
||||
Reference in New Issue
Block a user