Compare commits
4 Commits
8ca3f37817
...
13e0801b30
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13e0801b30 | ||
|
|
4c3aa159c5 | ||
|
|
eb51155b4e | ||
|
|
43f474fc5b |
@@ -29,8 +29,6 @@ public interface TranscriptionBlockRepository extends JpaRepository<Transcriptio
|
||||
|
||||
Optional<TranscriptionBlock> findByAnnotationId(UUID annotationId);
|
||||
|
||||
List<TranscriptionBlock> findByMentionedPersons_PersonId(UUID personId);
|
||||
|
||||
@Query("""
|
||||
SELECT DISTINCT b FROM TranscriptionBlock b
|
||||
JOIN FETCH b.mentionedPersons
|
||||
|
||||
@@ -45,19 +45,12 @@ public class PersonMentionPropagationListener {
|
||||
|
||||
String oldNeedle = "@" + event.oldDisplayName();
|
||||
String newNeedle = "@" + event.newDisplayName();
|
||||
// Match @OldName only at a token boundary: not followed by a letter/digit/hyphen
|
||||
// (catches @Hans-Peter when renaming Hans) AND not followed by " <Uppercase>"
|
||||
// (catches @Hans Müller when renaming the single-name @Hans). False negatives —
|
||||
// e.g. "@Hans Bekam" where Bekam is sentence-initial — are accepted as the
|
||||
// conservative trade-off; the alternative (corruption) is irrecoverable.
|
||||
Pattern boundary = Pattern.compile(
|
||||
Pattern.quote(oldNeedle) + "(?![\\p{L}0-9\\-]| (?=\\p{Lu}))");
|
||||
String replacement = Matcher.quoteReplacement(newNeedle);
|
||||
|
||||
for (TranscriptionBlock block : blocks) {
|
||||
if (block.getText() != null) {
|
||||
block.setText(boundary.matcher(block.getText()).replaceAll(replacement));
|
||||
}
|
||||
rewriteBlockText(block, boundary, replacement);
|
||||
for (PersonMention mention : block.getMentionedPersons()) {
|
||||
if (mention.getPersonId().equals(event.personId())) {
|
||||
mention.setDisplayName(event.newDisplayName());
|
||||
@@ -70,4 +63,15 @@ public class PersonMentionPropagationListener {
|
||||
log.info("Propagated rename {} → {} across {} block(s) for person {}",
|
||||
event.oldDisplayName(), event.newDisplayName(), blocks.size(), event.personId());
|
||||
}
|
||||
|
||||
// Match @OldName only at a token boundary: not followed by a letter/digit/hyphen
|
||||
// (catches @Hans-Peter when renaming Hans) AND not followed by " <Uppercase>"
|
||||
// (catches @Hans Müller when renaming the single-name @Hans). False negatives —
|
||||
// e.g. "@Hans Bekam" where Bekam is sentence-initial — are accepted as the
|
||||
// conservative trade-off; the alternative (corruption) is irrecoverable.
|
||||
private void rewriteBlockText(TranscriptionBlock block, Pattern boundary, String replacement) {
|
||||
if (block.getText() != null) {
|
||||
block.setText(boundary.matcher(block.getText()).replaceAll(replacement));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,6 +266,19 @@ class TranscriptionBlockControllerTest {
|
||||
.andExpect(jsonPath("$.code").value("VALIDATION_ERROR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void updateBlock_returns400_whenMentionedPersonPersonIdIsNull() throws Exception {
|
||||
when(userService.findByEmail(any())).thenReturn(mockUser());
|
||||
String body = "{\"text\":\"x\",\"mentionedPersons\":[{\"personId\":null,\"displayName\":\"Auguste Raddatz\"}]}";
|
||||
|
||||
mockMvc.perform(put(URL_BLOCK)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(body))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.code").value("VALIDATION_ERROR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void updateBlock_returns404_whenBlockDoesNotExist() throws Exception {
|
||||
|
||||
@@ -165,7 +165,7 @@ class PersonMentionPropagationListenerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void propagatesAcross200Blocks_inUnderTwoSeconds_latencyFloor() {
|
||||
void propagatesAcross200Blocks_inUnderFiveSeconds_latencyFloor() {
|
||||
UUID personId = savedPersonId("Auguste", "Raddatz");
|
||||
List<UUID> blockIds = new ArrayList<>();
|
||||
for (int i = 0; i < 200; i++) {
|
||||
|
||||
Reference in New Issue
Block a user