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);
|
Optional<TranscriptionBlock> findByAnnotationId(UUID annotationId);
|
||||||
|
|
||||||
List<TranscriptionBlock> findByMentionedPersons_PersonId(UUID personId);
|
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT DISTINCT b FROM TranscriptionBlock b
|
SELECT DISTINCT b FROM TranscriptionBlock b
|
||||||
JOIN FETCH b.mentionedPersons
|
JOIN FETCH b.mentionedPersons
|
||||||
|
|||||||
@@ -45,19 +45,12 @@ public class PersonMentionPropagationListener {
|
|||||||
|
|
||||||
String oldNeedle = "@" + event.oldDisplayName();
|
String oldNeedle = "@" + event.oldDisplayName();
|
||||||
String newNeedle = "@" + event.newDisplayName();
|
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 boundary = Pattern.compile(
|
||||||
Pattern.quote(oldNeedle) + "(?![\\p{L}0-9\\-]| (?=\\p{Lu}))");
|
Pattern.quote(oldNeedle) + "(?![\\p{L}0-9\\-]| (?=\\p{Lu}))");
|
||||||
String replacement = Matcher.quoteReplacement(newNeedle);
|
String replacement = Matcher.quoteReplacement(newNeedle);
|
||||||
|
|
||||||
for (TranscriptionBlock block : blocks) {
|
for (TranscriptionBlock block : blocks) {
|
||||||
if (block.getText() != null) {
|
rewriteBlockText(block, boundary, replacement);
|
||||||
block.setText(boundary.matcher(block.getText()).replaceAll(replacement));
|
|
||||||
}
|
|
||||||
for (PersonMention mention : block.getMentionedPersons()) {
|
for (PersonMention mention : block.getMentionedPersons()) {
|
||||||
if (mention.getPersonId().equals(event.personId())) {
|
if (mention.getPersonId().equals(event.personId())) {
|
||||||
mention.setDisplayName(event.newDisplayName());
|
mention.setDisplayName(event.newDisplayName());
|
||||||
@@ -70,4 +63,15 @@ public class PersonMentionPropagationListener {
|
|||||||
log.info("Propagated rename {} → {} across {} block(s) for person {}",
|
log.info("Propagated rename {} → {} across {} block(s) for person {}",
|
||||||
event.oldDisplayName(), event.newDisplayName(), blocks.size(), event.personId());
|
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"));
|
.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
|
@Test
|
||||||
@WithMockUser(authorities = "WRITE_ALL")
|
@WithMockUser(authorities = "WRITE_ALL")
|
||||||
void updateBlock_returns404_whenBlockDoesNotExist() throws Exception {
|
void updateBlock_returns404_whenBlockDoesNotExist() throws Exception {
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ class PersonMentionPropagationListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void propagatesAcross200Blocks_inUnderTwoSeconds_latencyFloor() {
|
void propagatesAcross200Blocks_inUnderFiveSeconds_latencyFloor() {
|
||||||
UUID personId = savedPersonId("Auguste", "Raddatz");
|
UUID personId = savedPersonId("Auguste", "Raddatz");
|
||||||
List<UUID> blockIds = new ArrayList<>();
|
List<UUID> blockIds = new ArrayList<>();
|
||||||
for (int i = 0; i < 200; i++) {
|
for (int i = 0; i < 200; i++) {
|
||||||
|
|||||||
Reference in New Issue
Block a user