reorder-log: re-order the pending entries, such that they appear directly...

This commit is contained in:
Kilian Schüttler 2024-03-21 08:44:15 +01:00
parent 7490402bb7
commit ba8cf68779
3 changed files with 227 additions and 169 deletions

View File

@ -8,11 +8,14 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
@ -70,82 +73,113 @@ public class EntityLogMergeService {
@Observed(name = "EntityLogMergeService", contextualName = "merge-entity-log")
public EntityLog mergeEntityLog(ManualRedactions manualRedactions, EntityLog entityLog, DossierEntity dossier) {
public EntityLog mergeEntityLog(ManualRedactions unprocessedManualRedactions, EntityLog entityLog, DossierEntity dossier) {
log.debug("Merging EntityLog");
List<BaseAnnotation> allManualChanges = manualRedactions.buildAll();
List<String> manualChangesIds = allManualChanges.stream()
.map(BaseAnnotation::getAnnotationId)
.toList();
List<EntityLogEntry> matchingEntities = entityLog.getEntityLogEntry()
.stream()
.filter(entityLogEntry -> manualChangesIds.contains(entityLogEntry.getId()))
.collect(Collectors.toList());
long start = System.currentTimeMillis();
final int analysisNumber = entityLog.getAnalysisNumber();
// Sort manual changes by date, so we process them in order of when they were requested
List<BaseAnnotation> allLocalManualChanges = allManualChanges.stream()
.filter(BaseAnnotation::isLocal)
.sorted(Comparator.comparing(BaseAnnotation::getRequestDate))
.toList();
Map<String, List<BaseAnnotation>> allManualChanges = unprocessedManualRedactions.buildAll()
.stream()
.collect(Collectors.groupingBy(BaseAnnotation::getAnnotationId));
List<EntityLogEntry> entityLogEntries = new LinkedList<>(entityLog.getEntityLogEntry());
buildUnprocessedLocalManualRedactions(unprocessedManualRedactions, entityLog, dossier).forEach(entityLogEntries::add);
buildPendingDictionaryChanges(unprocessedManualRedactions).forEach(entityLogEntries::add);
int numberOfAddedPendingEntries = 0; // since list is dynamically growing we need to keep track of the number of added pending entries to ignore them in the loop
for (int i = 0; i + numberOfAddedPendingEntries < entityLogEntries.size(); i++) {
EntityLogEntry entityLogEntry = entityLogEntries.get(i + numberOfAddedPendingEntries);
if (allManualChanges.containsKey(entityLogEntry.getId())) {
mergeLocalManualChanges(dossier, allManualChanges, entityLogEntry, analysisNumber);
List<EntityLogEntry> pendingDictionaryEntries = buildPendingDictionaryEntries(allManualChanges, entityLogEntry);
// insert pending entries directly after the associated entry to enable performant linking in UI
for (EntityLogEntry pendingDictionaryEntry : pendingDictionaryEntries) {
numberOfAddedPendingEntries++;
entityLogEntries.add(i + numberOfAddedPendingEntries, pendingDictionaryEntry);
}
allLocalManualChanges.forEach(manualChange -> {
// this is ugly and should be replaced with switch pattern matching https://openjdk.org/jeps/406 -> requires Java 17 (preview) or higher
if (manualChange instanceof ManualRedactionEntry manualRedactionEntry) {
var entityLogEntry = mergeManualRedactionEntries(manualRedactionEntry, entityLog, dossier);
entityLogEntry.ifPresent(matchingEntities::add);
} else if (manualChange instanceof IdRemoval idRemoval) {
mergeIdsToRemove(idRemoval, matchingEntities, analysisNumber);
} else if (manualChange instanceof ManualResizeRedaction manualResizeRedaction) {
mergeResizeRedactions(manualResizeRedaction, matchingEntities, analysisNumber);
} else if (manualChange instanceof ManualLegalBasisChange manualLegalBasisChange) {
mergeLegalBasisChanges(manualLegalBasisChange, matchingEntities, analysisNumber);
} else if (manualChange instanceof ManualRecategorization manualRecategorization) {
mergeRecategorizations(manualRecategorization, matchingEntities, dossier, analysisNumber);
} else if (manualChange instanceof ManualForceRedaction manualForceRedaction) {
mergeForceRedactions(manualForceRedaction, matchingEntities, analysisNumber);
}
});
// Sort manual changes by date, so we process them in order of when they were requested
List<BaseAnnotation> allDictionaryManualChanges = allManualChanges.stream()
.filter(baseAnnotation -> !baseAnnotation.isLocal())
.sorted(Comparator.comparing(BaseAnnotation::getRequestDate))
.toList();
}
List<EntityLogEntry> pendingEntries = allDictionaryManualChanges.stream()
.map(manualChange -> {
if (manualChange instanceof ManualRedactionEntry manualRedactionEntry) {
return pendingDictionaryEntryFactory.buildAddToDictionaryEntry(manualRedactionEntry);
} else if (manualChange instanceof IdRemoval idRemoval) {
return pendingDictionaryEntryFactory.buildRemoveFromDictionary(idRemoval, findMatchingEntry(idRemoval.getAnnotationId(), matchingEntities));
} else if (manualChange instanceof ManualResizeRedaction manualResizeRedaction) {
return pendingDictionaryEntryFactory.buildResizeWithDictionary(manualResizeRedaction, findMatchingEntry(manualChange.getAnnotationId(), matchingEntities));
} else if (manualChange instanceof ManualRecategorization manualRecategorization) {
return pendingDictionaryEntryFactory.buildRecategorizeWithDictionary(manualRecategorization,
findMatchingEntry(manualChange.getAnnotationId(), matchingEntities));
} else {
throw new IllegalArgumentException(String.format("Manual change of type %s has no defined dictionary action!", manualChange.getClass()));
}
})
.toList();
entityLog.setEntityLogEntry(entityLogEntries);
entityLog.getEntityLogEntry().addAll(pendingEntries);
log.debug("EntityLog merged successfully.");
log.debug("EntityLog merged successfully in {} ms.", System.currentTimeMillis() - start);
return entityLog;
}
private EntityLogEntry findMatchingEntry(String annotationId, List<EntityLogEntry> matchingEntities) {
private Stream<EntityLogEntry> buildPendingDictionaryChanges(ManualRedactions unprocessedManualRedactions) {
return matchingEntities.stream()
.filter(entityLogEntry -> entityLogEntry.getId().equals(annotationId))
.findAny()
.orElseThrow(() -> new NotFoundException("No matching EntityLogEntry found for id " + annotationId));
return unprocessedManualRedactions.getEntriesToAdd()
.stream()
.filter(manualAdd -> !manualAdd.isLocal())
.map(pendingDictionaryEntryFactory::buildAddToDictionaryEntry);
}
private Stream<EntityLogEntry> buildUnprocessedLocalManualRedactions(ManualRedactions unprocessedManualRedactions, EntityLog entityLog, DossierEntity dossier) {
return unprocessedManualRedactions.getEntriesToAdd()
.stream()
.filter(ManualRedactionEntry::isLocal)
.map(manualRedactionEntry -> mergeManualRedactionEntries(manualRedactionEntry, entityLog, dossier))
.filter(Optional::isPresent)
.map(Optional::get);
}
private List<EntityLogEntry> buildPendingDictionaryEntries(Map<String, List<BaseAnnotation>> allManualChanges, EntityLogEntry entityLogEntry) {
return allManualChanges.getOrDefault(entityLogEntry.getId(), Collections.emptyList())
.stream()
.filter(baseAnnotation -> !baseAnnotation.isLocal())
.sorted(Comparator.comparing(BaseAnnotation::getRequestDate))
.map(dictionaryChange -> {
if (dictionaryChange instanceof ManualRedactionEntry) {
return null; // pending dictionaries are inserted before the manual changes loop
} else if (dictionaryChange instanceof IdRemoval idRemoval) {
return pendingDictionaryEntryFactory.buildRemoveFromDictionary(idRemoval, entityLogEntry);
} else if (dictionaryChange instanceof ManualResizeRedaction manualResizeRedaction) {
return pendingDictionaryEntryFactory.buildResizeWithDictionary(manualResizeRedaction, entityLogEntry);
} else if (dictionaryChange instanceof ManualRecategorization manualRecategorization) {
return pendingDictionaryEntryFactory.buildRecategorizeWithDictionary(manualRecategorization, entityLogEntry);
} else {
throw new IllegalArgumentException(String.format("Manual change of type %s has no defined dictionary action!", dictionaryChange.getClass()));
}
})
.filter(Objects::nonNull)
.toList();
}
private void mergeLocalManualChanges(DossierEntity dossier, Map<String, List<BaseAnnotation>> allManualChanges, EntityLogEntry entityLogEntry, int analysisNumber) {
allManualChanges.getOrDefault(entityLogEntry.getId(), Collections.emptyList())
.stream()
.filter(BaseAnnotation::isLocal)
.sorted(Comparator.comparing(BaseAnnotation::getRequestDate))
.forEach(localChange -> {
if (localChange instanceof IdRemoval idRemoval) {
mergeIdsToRemove(idRemoval, entityLogEntry, analysisNumber);
} else if (localChange instanceof ManualResizeRedaction manualResizeRedaction) {
mergeResizeRedactions(manualResizeRedaction, entityLogEntry, analysisNumber);
} else if (localChange instanceof ManualLegalBasisChange manualLegalBasisChange) {
mergeLegalBasisChanges(manualLegalBasisChange, entityLogEntry, analysisNumber);
} else if (localChange instanceof ManualRecategorization manualRecategorization) {
mergeRecategorizations(manualRecategorization, entityLogEntry, dossier, analysisNumber);
} else if (localChange instanceof ManualForceRedaction manualForceRedaction) {
mergeForceRedactions(manualForceRedaction, entityLogEntry, analysisNumber);
}
});
}
@ -234,79 +268,63 @@ public class EntityLogMergeService {
}
private void mergeIdsToRemove(IdRemoval idRemoval, List<EntityLogEntry> entityLogEntries, int analysisNumber) {
private void mergeIdsToRemove(IdRemoval idRemoval, EntityLogEntry entityLogEntry, int analysisNumber) {
var entity = entityLogEntries.stream()
.filter(entityLogEntry -> entityLogEntry.getId().equals(idRemoval.getAnnotationId()))
.findAny();
entity.ifPresent(entityLogEntry -> {
entityLogEntry.setState(EntryState.IGNORED);
entityLogEntry.getEngines().add(Engine.MANUAL);
addChanges(entityLogEntry.getChanges(), ChangeType.REMOVED, analysisNumber, idRemoval.getRequestDate());
entityLogEntry.getManualChanges()
.add(ManualChange.builder()
.manualRedactionType(ManualRedactionType.REMOVE)
.requestedDate(idRemoval.getRequestDate())
.processedDate(null)
.userId(idRemoval.getUser())
.propertyChanges(Collections.emptyMap())
.build());
});
}
private void mergeResizeRedactions(ManualResizeRedaction manualResizeRedaction, List<EntityLogEntry> entityLogEntries, int analysisNumber) {
var entity = entityLogEntries.stream()
.filter(entityLogEntry -> entityLogEntry.getId().equals(manualResizeRedaction.getAnnotationId()))
.findAny();
entity.ifPresent(entityLogEntry -> {
entityLogEntry.setTextAfter(manualResizeRedaction.getTextAfter());
entityLogEntry.setTextBefore(manualResizeRedaction.getTextBefore());
entityLogEntry.setPositions(convertPositions(manualResizeRedaction.getPositions()));
entityLogEntry.setValue(manualResizeRedaction.getValue());
entityLogEntry.getEngines().add(Engine.MANUAL);
addChanges(entityLogEntry.getChanges(), ChangeType.CHANGED, analysisNumber, manualResizeRedaction.getRequestDate());
ManualChange.ManualChangeBuilder manualChange = ManualChange.builder()
.manualRedactionType(ManualRedactionType.RESIZE)
.requestedDate(manualResizeRedaction.getRequestDate())
.processedDate(null)
.userId(manualResizeRedaction.getUser());
if (!Strings.isNullOrEmpty(manualResizeRedaction.getValue())) {
manualChange.propertyChanges(Map.of("value", manualResizeRedaction.getValue()));
}
entityLogEntry.getManualChanges().add(manualChange.build());
});
}
@Deprecated(forRemoval = true)
private void mergeLegalBasisChanges(ManualLegalBasisChange manualLegalBasisChange, List<EntityLogEntry> entityLogEntries, int analysisNumber) {
var entity = entityLogEntries.stream()
.filter(entityLogEntry -> entityLogEntry.getId().equals(manualLegalBasisChange.getAnnotationId()))
.findAny();
entity.ifPresent(entityLogEntry -> {
entityLogEntry.setLegalBasis(manualLegalBasisChange.getLegalBasis());
entityLogEntry.setSection(manualLegalBasisChange.getSection());
entityLogEntry.setValue(manualLegalBasisChange.getValue());
entityLogEntry.getEngines().add(Engine.MANUAL);
addChanges(entityLogEntry.getChanges(), ChangeType.CHANGED, analysisNumber, manualLegalBasisChange.getRequestDate());
Map<String, String> propertyChanges = getPropertyChanges(manualLegalBasisChange);
entityLogEntry.getManualChanges()
.add(ManualChange.builder()
.manualRedactionType(ManualRedactionType.LEGAL_BASIS_CHANGE)
.requestedDate(manualLegalBasisChange.getRequestDate())
.processedDate(null)
.propertyChanges(propertyChanges)
.userId(manualLegalBasisChange.getUser())
.build());
});
entityLogEntry.setState(EntryState.IGNORED);
entityLogEntry.getEngines().add(Engine.MANUAL);
addChanges(entityLogEntry.getChanges(), ChangeType.REMOVED, analysisNumber, idRemoval.getRequestDate());
entityLogEntry.getManualChanges()
.add(ManualChange.builder()
.manualRedactionType(ManualRedactionType.REMOVE)
.requestedDate(idRemoval.getRequestDate())
.processedDate(null)
.userId(idRemoval.getUser())
.propertyChanges(Collections.emptyMap())
.build());
}
private void mergeResizeRedactions(ManualResizeRedaction manualResizeRedaction, EntityLogEntry entityLogEntry, int analysisNumber) {
entityLogEntry.setTextAfter(manualResizeRedaction.getTextAfter());
entityLogEntry.setTextBefore(manualResizeRedaction.getTextBefore());
entityLogEntry.setPositions(convertPositions(manualResizeRedaction.getPositions()));
entityLogEntry.setValue(manualResizeRedaction.getValue());
entityLogEntry.getEngines().add(Engine.MANUAL);
addChanges(entityLogEntry.getChanges(), ChangeType.CHANGED, analysisNumber, manualResizeRedaction.getRequestDate());
ManualChange.ManualChangeBuilder manualChange = ManualChange.builder()
.manualRedactionType(ManualRedactionType.RESIZE)
.requestedDate(manualResizeRedaction.getRequestDate())
.processedDate(null)
.userId(manualResizeRedaction.getUser());
if (!Strings.isNullOrEmpty(manualResizeRedaction.getValue())) {
manualChange.propertyChanges(Map.of("value", manualResizeRedaction.getValue()));
}
entityLogEntry.getManualChanges().add(manualChange.build());
}
private void mergeLegalBasisChanges(ManualLegalBasisChange manualLegalBasisChange, EntityLogEntry entityLogEntry, int analysisNumber) {
entityLogEntry.setLegalBasis(manualLegalBasisChange.getLegalBasis());
entityLogEntry.setSection(manualLegalBasisChange.getSection());
entityLogEntry.setValue(manualLegalBasisChange.getValue());
entityLogEntry.getEngines().add(Engine.MANUAL);
addChanges(entityLogEntry.getChanges(), ChangeType.CHANGED, analysisNumber, manualLegalBasisChange.getRequestDate());
Map<String, String> propertyChanges = getPropertyChanges(manualLegalBasisChange);
entityLogEntry.getManualChanges()
.add(ManualChange.builder()
.manualRedactionType(ManualRedactionType.LEGAL_BASIS_CHANGE)
.requestedDate(manualLegalBasisChange.getRequestDate())
.processedDate(null)
.propertyChanges(propertyChanges)
.userId(manualLegalBasisChange.getUser())
.build());
}
@Deprecated(forRemoval = true)
private Map<String, String> getPropertyChanges(ManualLegalBasisChange manualLegalBasisChange) {
Map<String, String> propertyChanges = new HashMap<>();
@ -323,27 +341,23 @@ public class EntityLogMergeService {
}
private void mergeRecategorizations(ManualRecategorization recategorization, List<EntityLogEntry> entityLogEntries, DossierEntity dossier, int analysisNumber) {
private void mergeRecategorizations(ManualRecategorization recategorization, EntityLogEntry entityLogEntry, DossierEntity dossier, int analysisNumber) {
boolean isHint = isHint(recategorization.getType(), dossier);
var entity = entityLogEntries.stream()
.filter(entityLogEntry -> entityLogEntry.getId().equals(recategorization.getAnnotationId()))
.findAny();
entity.ifPresent(entityLogEntry -> {
entityLogEntry.setType(recategorization.getType());
entityLogEntry.setEntryType(getEntryType(isHint, recategorization.getType()));
entityLogEntry.setState(isHint ? EntryState.SKIPPED : EntryState.APPLIED);
entityLogEntry.getEngines().add(Engine.MANUAL);
addChanges(entityLogEntry.getChanges(), ChangeType.CHANGED, analysisNumber, recategorization.getRequestDate());
entityLogEntry.getManualChanges()
.add(ManualChange.builder()
.manualRedactionType(ManualRedactionType.RECATEGORIZE)
.requestedDate(recategorization.getRequestDate())
.processedDate(recategorization.getProcessedDate())
.userId(recategorization.getUser())
.propertyChanges(getPropertyChanges(recategorization))
.build());
});
entityLogEntry.setType(recategorization.getType());
entityLogEntry.setEntryType(getEntryType(isHint, recategorization.getType()));
entityLogEntry.setState(isHint ? EntryState.SKIPPED : EntryState.APPLIED);
entityLogEntry.getEngines().add(Engine.MANUAL);
addChanges(entityLogEntry.getChanges(), ChangeType.CHANGED, analysisNumber, recategorization.getRequestDate());
entityLogEntry.getManualChanges()
.add(ManualChange.builder()
.manualRedactionType(ManualRedactionType.RECATEGORIZE)
.requestedDate(recategorization.getRequestDate())
.processedDate(recategorization.getProcessedDate())
.userId(recategorization.getUser())
.propertyChanges(getPropertyChanges(recategorization))
.build());
}
@ -363,26 +377,22 @@ public class EntityLogMergeService {
}
private void mergeForceRedactions(ManualForceRedaction forceRedaction, List<EntityLogEntry> entityLogEntries, int analysisNumber) {
private void mergeForceRedactions(ManualForceRedaction forceRedaction, EntityLogEntry entityLogEntry, int analysisNumber) {
entityLogEntry.setLegalBasis(forceRedaction.getLegalBasis());
entityLogEntry.setState(entityLogEntry.getEntryType().equals(EntryType.HINT) ? EntryState.SKIPPED : EntryState.APPLIED);
entityLogEntry.getEngines().add(Engine.MANUAL);
addChanges(entityLogEntry.getChanges(), ChangeType.CHANGED, analysisNumber, forceRedaction.getRequestDate());
var forceRedactManualChange = ManualChange.builder()
.manualRedactionType(ManualRedactionType.FORCE)
.requestedDate(forceRedaction.getRequestDate())
.processedDate(forceRedaction.getProcessedDate())
.userId(forceRedaction.getUser());
if (forceRedaction.getLegalBasis() != null && !forceRedaction.getLegalBasis().isEmpty()) {
forceRedactManualChange.propertyChanges(Map.of("legalBasis", forceRedaction.getLegalBasis()));
}
entityLogEntry.getManualChanges().add(forceRedactManualChange.build());
var entity = entityLogEntries.stream()
.filter(entityLogEntry -> entityLogEntry.getId().equals(forceRedaction.getAnnotationId()))
.findAny();
entity.ifPresent(entityLogEntry -> {
entityLogEntry.setLegalBasis(forceRedaction.getLegalBasis());
entityLogEntry.setState(entityLogEntry.getEntryType().equals(EntryType.HINT) ? EntryState.SKIPPED : EntryState.APPLIED);
entityLogEntry.getEngines().add(Engine.MANUAL);
addChanges(entityLogEntry.getChanges(), ChangeType.CHANGED, analysisNumber, forceRedaction.getRequestDate());
var forceRedactManualChange = ManualChange.builder()
.manualRedactionType(ManualRedactionType.FORCE)
.requestedDate(forceRedaction.getRequestDate())
.processedDate(forceRedaction.getProcessedDate())
.userId(forceRedaction.getUser());
if (forceRedaction.getLegalBasis() != null && !forceRedaction.getLegalBasis().isEmpty()) {
forceRedactManualChange.propertyChanges(Map.of("legalBasis", forceRedaction.getLegalBasis()));
}
entityLogEntry.getManualChanges().add(forceRedactManualChange.build());
});
}

View File

@ -64,8 +64,8 @@ public class EntityLogService {
if (includeUnprocessed) {
DossierEntity dossier = dossierService.getDossierById(dossierId);
ManualRedactions manualRedactions = manualRedactionProviderService.getManualRedactions(fileId, ManualChangesQueryOptions.unprocessedOnly());
entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, dossier);
ManualRedactions unprocessedManualRedactions = manualRedactionProviderService.getManualRedactions(fileId, ManualChangesQueryOptions.unprocessedOnly());
entityLog = entityLogMergeService.mergeEntityLog(unprocessedManualRedactions, entityLog, dossier);
}
if (fileStatus.getExcludedPages() != null && !fileStatus.getExcludedPages().isEmpty()) {

View File

@ -236,6 +236,54 @@ public class EntityLogMergeTest {
}
@Test
public void testUnprocessedDictChangesAreDirectlyAfterOriginEntry() {
String fileId = "fileId";
String dossierId = "dossierId";
String dossierTemplateId = "dossierTemplateId";
String entryToAddId = UUID.randomUUID().toString();
String rectangleToAddId = UUID.randomUUID().toString();
String entryToRemoveId = UUID.randomUUID().toString();
String entryToResizeId = UUID.randomUUID().toString();
String entryLegalBasisId = UUID.randomUUID().toString();
String forceRedactionId = UUID.randomUUID().toString();
ManualRedactions manualRedactions = new ManualRedactions();
List<Rectangle> positions = new ArrayList<>();
positions.add(new Rectangle(2, 2, 2, 2, 1));
manualRedactions.setResizeRedactions(Set.of(ManualResizeRedaction.builder()
.fileId("fileId")
.value("Random")
.annotationId(entryToResizeId)
.positions(positions)
.requestDate(OffsetDateTime.now())
.updateDictionary(true)
.user("User")
.fileId("file")
.build()));
var entityLog = provideEntityLog(entryToRemoveId, entryToResizeId, entryLegalBasisId, forceRedactionId);
when(manualRedactionProviderService.getManualRedactions(any(), any())).thenReturn(manualRedactions);
when(fileStatusService.getStatus(fileId)).thenReturn(FileModel.builder().excluded(false).dossierStatusId(dossierTemplateId).id(fileId).build());
when(fileManagementStorageService.getEntityLog(dossierId, fileId)).thenReturn(entityLog);
when(dossierService.getDossierById(dossierId)).thenReturn(DossierEntity.builder().dossierTemplateId(dossierTemplateId).build());
when(dictionaryPersistenceService.getType(anyString())).thenReturn(TypeEntity.builder().isHint(false).build());
when(fileStatusPersistenceService.getStatus(fileId)).thenReturn(FileEntity.builder().id(fileId).fileAttributes(Collections.emptyList()).build());
when(fileStatusService.convertAttributes(any(), anyString())).thenReturn(Collections.emptyList());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build());
var resizedEntry = response.getEntityLogEntry().stream().filter(entityLogEntry1 -> entityLogEntry1.getId().equals(entryToResizeId)).findFirst().get();
int index = response.getEntityLogEntry().indexOf(resizedEntry);
assertEquals(response.getEntityLogEntry().get(index + 1).getId(), resizedEntry.getId());
assertEquals(response.getEntityLogEntry().get(index + 1).getState(), EntryState.PENDING);
}
private EntityLog provideEntityLog(String entryToRemoveId, String entryToResizeId, String entryLegalBasisId, String forceRedactionId) {
List<Position> positions = new ArrayList<>();