diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/EntityLogController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/EntityLogController.java index 72965545b..aaea93cdf 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/EntityLogController.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/EntityLogController.java @@ -27,9 +27,10 @@ public class EntityLogController implements EntityLogResource { @PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')") public EntityLog getEntityLog(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, - @RequestParam(value = "excludedType", required = false) List excludedTypes) { + @RequestParam(value = "excludedType", required = false) List excludedTypes, + @RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed) { - return entityLogService.getEntityLog(dossierId, fileId, excludedTypes); + return entityLogService.getEntityLog(dossierId, fileId, excludedTypes, includeUnprocessed); } diff --git a/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/resource/EntityLogResource.java b/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/resource/EntityLogResource.java index 289537a36..7fcf79d52 100644 --- a/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/resource/EntityLogResource.java +++ b/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/resource/EntityLogResource.java @@ -29,13 +29,16 @@ public interface EntityLogResource { String DOSSIER_ID = "dossierId"; String DOSSIER_ID_PATH_VARIABLE = "/{" + DOSSIER_ID + "}"; + String FALSE = "false"; @GetMapping(value = ENTITY_LOG_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE) - @Operation(summary = "Gets the entity log for a fileId", description = "None") + @Operation(summary = "Gets the entity log for a fileId", description = "Gets the entity log for a given file. The flag includeUnprocessed will merge into the entity log all the unprocessed changes if it's set to true." + + "Default value for the flag is false.") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The entity log is not found.")}) EntityLog getEntityLog(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, - @RequestParam(value = "excludedType", required = false) List excludedTypes); + @RequestParam(value = "excludedType", required = false) List excludedTypes, + @RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed); @PostMapping(value = ENTITY_LOG_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE + "/filtered", produces = MediaType.APPLICATION_JSON_VALUE) diff --git a/persistence-service-v1/persistence-service-internal-api-impl-v1/src/main/java/com/iqser/red/service/persistence/v1/internal/api/controller/EntityLogInternalController.java b/persistence-service-v1/persistence-service-internal-api-impl-v1/src/main/java/com/iqser/red/service/persistence/v1/internal/api/controller/EntityLogInternalController.java index 5ec246486..0dd4d4fbb 100644 --- a/persistence-service-v1/persistence-service-internal-api-impl-v1/src/main/java/com/iqser/red/service/persistence/v1/internal/api/controller/EntityLogInternalController.java +++ b/persistence-service-v1/persistence-service-internal-api-impl-v1/src/main/java/com/iqser/red/service/persistence/v1/internal/api/controller/EntityLogInternalController.java @@ -21,9 +21,10 @@ public class EntityLogInternalController implements EntityLogResource { public EntityLog getEntityLog(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, - @RequestParam(value = "excludedType", required = false) List excludedTypes) { + @RequestParam(value = "excludedType", required = false) List excludedTypes, + @RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed) { - return entityLogService.getEntityLog(dossierId, fileId, excludedTypes); + return entityLogService.getEntityLog(dossierId, fileId, excludedTypes, includeUnprocessed); } diff --git a/persistence-service-v1/persistence-service-internal-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/internal/resources/EntityLogResource.java b/persistence-service-v1/persistence-service-internal-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/internal/resources/EntityLogResource.java index 95cd6c908..1a7b7c23b 100644 --- a/persistence-service-v1/persistence-service-internal-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/internal/resources/EntityLogResource.java +++ b/persistence-service-v1/persistence-service-internal-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/internal/resources/EntityLogResource.java @@ -26,11 +26,14 @@ public interface EntityLogResource { String DOSSIER_ID = "dossierId"; String DOSSIER_ID_PATH_VARIABLE = "/{" + DOSSIER_ID + "}"; + String FALSE = "false"; + @GetMapping(value = ENTITY_LOG_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Gets the entity log for a fileId", description = "None") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The entity log is not found.")}) EntityLog getEntityLog(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, - @RequestParam(value = "excludedType", required = false) List excludedTypes); + @RequestParam(value = "excludedType", required = false) List excludedTypes, + @RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/client/redactionservice/UnprocessedManualEntityClient.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/client/redactionservice/UnprocessedManualEntityClient.java new file mode 100644 index 000000000..4378485c4 --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/client/redactionservice/UnprocessedManualEntityClient.java @@ -0,0 +1,10 @@ +package com.iqser.red.service.persistence.management.v1.processor.client.redactionservice; + +import org.springframework.cloud.openfeign.FeignClient; + +import com.iqser.red.service.redaction.v1.resources.UnprocessedManualEntityResource; + +@FeignClient(name = "UnprocessedManualEntityClient", url = "${redaction-service.url}") +public interface UnprocessedManualEntityClient extends UnprocessedManualEntityResource { + +} diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/EntityLogService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/EntityLogService.java index 88bc4ebbd..06b909f56 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/EntityLogService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/EntityLogService.java @@ -1,39 +1,70 @@ package com.iqser.red.service.persistence.management.v1.processor.service; +import static com.iqser.red.service.persistence.management.v1.processor.utils.TypeIdUtils.toTypeId; + import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import org.springframework.stereotype.Service; +import com.iqser.red.service.persistence.management.v1.processor.client.redactionservice.UnprocessedManualEntityClient; +import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity; +import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException; +import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionProviderService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService; +import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Change; +import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ChangeType; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLog; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogEntry; +import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryState; +import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.FilteredEntityLogRequest; +import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ManualChange; +import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ManualRedactionType; +import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Position; +import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions; +import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval; +import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualForceRedaction; +import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualLegalBasisChange; +import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRecategorization; +import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRedactionEntry; +import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualResizeRedaction; +import com.iqser.red.service.redaction.v1.model.UnprocessedManualEntity; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; @Service @RequiredArgsConstructor @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +@Slf4j public class EntityLogService { FileManagementStorageService fileManagementStorageService; FileStatusService fileStatusService; + ManualRedactionProviderService manualRedactionProviderService; + UnprocessedManualEntityClient unprocessedManualEntityClient; + DossierService dossierService; CommentService commentService; + DictionaryPersistenceService dictionaryPersistenceService; public EntityLog getEntityLog(String dossierId, String fileId) { - return getEntityLog(dossierId, fileId, Collections.emptyList()); + return getEntityLog(dossierId, fileId, Collections.emptyList(), false); } - public EntityLog getEntityLog(String dossierId, String fileId, List excludedTypes) { + public EntityLog getEntityLog(String dossierId, String fileId, List excludedTypes, boolean includeUnprocessed) { var fileStatus = fileStatusService.getStatus(fileId); @@ -49,6 +80,13 @@ public class EntityLogService { entityLog.getEntityLogEntry().removeIf(entry -> excludedTypes.contains(entry.getType())); } + if (includeUnprocessed) { + DossierEntity dossier = dossierService.getDossierById(dossierId); + ManualRedactions manualRedactions = manualRedactionProviderService.getManualRedactions(fileId, true); + List unprocessedManualEntities = getUnprocessedManualEntities(dossierId, fileId, dossier.getDossierTemplateId(), manualRedactions); + mergeEntityLog(manualRedactions, unprocessedManualEntities, entityLog, dossier.getDossierTemplateId()); + } + Map commentCountPerAnnotationId = commentService.getCommentCounts(fileId); entityLog.getEntityLogEntry().forEach(entityLogEntry -> entityLogEntry.setNumberOfComments(commentCountPerAnnotationId.getOrDefault(entityLogEntry.getId(), 0))); @@ -62,7 +100,7 @@ public class EntityLogService { filteredEntityLogRequest.setSpecifiedDate(OffsetDateTime.MIN); } - var entityLog = getEntityLog(dossierId, fileId, filteredEntityLogRequest.getExcludedTypes()); + var entityLog = getEntityLog(dossierId, fileId, filteredEntityLogRequest.getExcludedTypes(), false); var entityLogEntry = entityLog.getEntityLogEntry(); Iterator it = entityLogEntry.iterator(); @@ -92,4 +130,194 @@ public class EntityLogService { return entityLog; } + private List getUnprocessedManualEntities(String dossierId, String fileId, String dossierTemplateId, ManualRedactions manualRedactions) { + + return unprocessedManualEntityClient.mergeUnprocessedManualEntities(fileId, dossierId, dossierTemplateId, manualRedactions); + + } + + public void mergeEntityLog(ManualRedactions manualRedactions, List unprocessedManualEntities, EntityLog entityLog, String dossierTemplateId) { + + log.info("Merging EntityLog"); + mergeManualRedactionEntries(manualRedactions.getEntriesToAdd(), unprocessedManualEntities, entityLog, dossierTemplateId); + mergeIdsToRemove(manualRedactions.getIdsToRemove(), entityLog); + mergeResizeRedactions(manualRedactions.getResizeRedactions(), entityLog); + mergeLegalBasisChanges(manualRedactions.getLegalBasisChanges(), entityLog); + mergeRecategorizations(manualRedactions.getRecategorizations(), entityLog); + mergeForceRedactions(manualRedactions.getForceRedactions(), entityLog); + log.info("EntityLog merged successfully!"); + } + + + private void mergeManualRedactionEntries(Set manualRedactionEntries, List unprocessedManualEntities, EntityLog entityLog, String dossierTemplateId) { + + manualRedactionEntries.forEach(manualRedactionEntry -> { + UnprocessedManualEntity unprocessedManualEntity = unprocessedManualEntities.stream() + .filter(manualEntity -> manualEntity.getAnnotationId().equals(manualRedactionEntry.getAnnotationId())) + .findFirst().orElseThrow(() -> new NotFoundException("Entry with annotationId " + manualRedactionEntry.getAnnotationId() + " not found")); + List changes = new ArrayList<>(); + changes.add(Change.builder() + .analysisNumber(entityLog.getAnalysisNumber()) + .dateTime(OffsetDateTime.now()) + .type(ChangeType.ADDED) + .build()); + entityLog.getEntityLogEntry().add(EntityLogEntry.builder() + .id(manualRedactionEntry.getAnnotationId()) + .type(manualRedactionEntry.getType()) + .value(manualRedactionEntry.getValue()) + .legalBasis(manualRedactionEntry.getLegalBasis()) + .reason(manualRedactionEntry.getReason()) + .entryType(isHint(manualRedactionEntry.getType(), dossierTemplateId) ? EntryType.HINT : EntryType.ENTITY) + .imported(false) + .matchedRule("") + .section(manualRedactionEntry.getSection()) + .color(unprocessedManualEntity.getColor()) + .positions(unprocessedManualEntity.getPositions()) + .textAfter(unprocessedManualEntity.getTextAfter()) + .textBefore(unprocessedManualEntity.getTextBefore()) + .startOffset(unprocessedManualEntity.getStartOffset()) + .endOffset(unprocessedManualEntity.getEndOffset()) + .containingNodeId(unprocessedManualEntity.getContainingNodeId()) + .closestHeadline(unprocessedManualEntity.getClosestHeadline()) + .imageHasTransparency(false) + .dictionaryEntry(manualRedactionEntry.isAddToDictionary()) + .dossierDictionaryEntry(manualRedactionEntry.isAddToDossierDictionary()) + .excluded(false) + .changes(changes) + .manualChanges(List.of(ManualChange.builder() + .manualRedactionType(ManualRedactionType.ADD_LOCALLY) + .requestedDate(manualRedactionEntry.getRequestDate()) + .processedDate(null) + .userId(manualRedactionEntry.getUser()) + .propertyChanges(Map.of("value", manualRedactionEntry.getValue())) + .build())) + .engines(new HashSet<>()) + .reference(new HashSet<>()) + .importedRedactionIntersections(new HashSet<>()) + .build()); + }); + } + + + private void mergeIdsToRemove(Set idRemovals, EntityLog entityLog) { + + idRemovals.forEach(idRemoval -> { + var entity = entityLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(idRemoval.getAnnotationId())).findAny(); + if (entity.isPresent()) { + entity.get().setState(EntryState.IGNORED); + addChanges(entity.get().getChanges(), ChangeType.REMOVED, entityLog.getAnalysisNumber()); + entity.get().getManualChanges().add(ManualChange.builder() + .manualRedactionType(ManualRedactionType.REMOVE_LOCALLY) + .requestedDate(idRemoval.getRequestDate()) + .processedDate(null) + .userId(idRemoval.getUser()).build()); + } + }); + } + + private void mergeResizeRedactions(Set manualResizeRedactions, EntityLog entityLog) { + + manualResizeRedactions.forEach(manualResizeRedaction -> { + var entity = entityLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(manualResizeRedaction.getAnnotationId())).findAny(); + if (entity.isPresent()) { + var newPosition = manualResizeRedaction.getPositions().get(0); + entity.get().setPositions(List.of(new Position( + newPosition.getTopLeftX(), + newPosition.getTopLeftY(), + newPosition.getWidth(), + newPosition.getHeight(), + newPosition.getPage()))); + addChanges(entity.get().getChanges(), ChangeType.CHANGED, entityLog.getAnalysisNumber()); + entity.get().getManualChanges().add(ManualChange.builder() + .manualRedactionType(ManualRedactionType.RESIZE) + .requestedDate(manualResizeRedaction.getRequestDate()) + .processedDate(null) + .propertyChanges(Map.of("value", manualResizeRedaction.getValue())) + .userId(manualResizeRedaction.getUser()).build()); + } + }); + } + + private void mergeLegalBasisChanges(Set manualLegalBasisChanges, EntityLog entityLog) { + + manualLegalBasisChanges.forEach(manualLegalBasisChange -> { + var entity = entityLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(manualLegalBasisChange.getAnnotationId())).findAny(); + if (entity.isPresent()) { + entity.get().setLegalBasis(manualLegalBasisChange.getLegalBasis()); + entity.get().setSection(manualLegalBasisChange.getSection()); + entity.get().setValue(manualLegalBasisChange.getValue()); + addChanges(entity.get().getChanges(), ChangeType.CHANGED, entityLog.getAnalysisNumber()); + entity.get().getManualChanges().add(ManualChange.builder() + .manualRedactionType(ManualRedactionType.LEGAL_BASIS_CHANGE) + .requestedDate(manualLegalBasisChange.getRequestDate()) + .processedDate(null) + .propertyChanges(Map.of("value", manualLegalBasisChange.getValue(), + "section", manualLegalBasisChange.getSection(), + "legalBasis", manualLegalBasisChange.getLegalBasis())) + .userId(manualLegalBasisChange.getUser()) + .build()); + } + }); + } + + private void mergeRecategorizations(Set recategorizations, EntityLog entityLog) { + + recategorizations.forEach(recategorization -> { + var entity = entityLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(recategorization.getAnnotationId())).findAny(); + if (entity.isPresent()) { + entity.get().setType(recategorization.getType()); + addChanges(entity.get().getChanges(), ChangeType.CHANGED, entityLog.getAnalysisNumber()); + entity.get().getManualChanges().add(ManualChange.builder() + .manualRedactionType(ManualRedactionType.RECATEGORIZE) + .requestedDate(recategorization.getRequestDate()) + .processedDate(recategorization.getProcessedDate()) + .userId(recategorization.getUser()) + .propertyChanges(Map.of("type", recategorization.getType())) + .build()); + } + }); + } + + private void mergeForceRedactions(Set forceRedactions, EntityLog entityLog) { + + forceRedactions.forEach(forceRedaction -> { + var entity = entityLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(forceRedaction.getAnnotationId())).findAny(); + if (entity.isPresent()) { + entity.get().setLegalBasis(forceRedaction.getLegalBasis()); + entity.get().setState(EntryState.APPLIED); + addChanges(entity.get().getChanges(), ChangeType.CHANGED, entityLog.getAnalysisNumber()); + entity.get().getManualChanges().add(ManualChange.builder() + .manualRedactionType(ManualRedactionType.FORCE_REDACT) + .requestedDate(forceRedaction.getRequestDate()) + .processedDate(forceRedaction.getProcessedDate()) + .userId(forceRedaction.getUser()) + .propertyChanges(Map.of("legalBasis", forceRedaction.getLegalBasis())) + .build()); + } + }); + } + + private void addChanges(List changes, ChangeType changeType, int analysisNumber) { + + if (!changes.isEmpty()) { + changes.add(Change.builder() + .analysisNumber(analysisNumber + 1) + .dateTime(OffsetDateTime.now()) + .type(changeType) + .build()); + } else { + changes.add(Change.builder().analysisNumber(analysisNumber).dateTime(OffsetDateTime.now()).type(changeType).build()); + } + } + + private boolean isHint(String type, String dossierTemplateId) { + + String typeId = toTypeId(type, dossierTemplateId); + TypeEntity typeEntity = dictionaryPersistenceService.getType(typeId); + if (typeEntity == null) { + throw new NotFoundException("TypeEntity could not be found for typeId: " + typeId); + } + return typeEntity.isHint(); + } + } diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/EntityLogTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/EntityLogTest.java new file mode 100644 index 000000000..d0899c48c --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/EntityLogTest.java @@ -0,0 +1,280 @@ +package com.iqser.red.service.peristence.v1.server.integration.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.testcontainers.shaded.com.google.common.collect.Lists; + +import com.iqser.red.service.persistence.management.v1.processor.client.redactionservice.UnprocessedManualEntityClient; +import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity; +import com.iqser.red.service.persistence.management.v1.processor.service.CommentService; +import com.iqser.red.service.persistence.management.v1.processor.service.DossierService; +import com.iqser.red.service.persistence.management.v1.processor.service.EntityLogService; +import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService; +import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService; +import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionProviderService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService; +import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ChangeType; +import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLog; +import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogEntry; +import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryState; +import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType; +import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ManualRedactionType; +import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Position; +import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions; +import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle; +import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval; +import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualForceRedaction; +import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualLegalBasisChange; +import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRedactionEntry; +import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualResizeRedaction; +import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel; +import com.iqser.red.service.redaction.v1.model.UnprocessedManualEntity; + +@ExtendWith(SpringExtension.class) +public class EntityLogTest { + + @MockBean + private FileStatusService fileStatusService; + + @MockBean + private FileManagementStorageService fileManagementStorageService; + + @MockBean + private DossierService dossierService; + + @MockBean + private CommentService commentService; + + @MockBean + private ManualRedactionProviderService manualRedactionProviderService; + + @MockBean + private UnprocessedManualEntityClient unprocessedManualEntityClient; + + @MockBean + private DictionaryPersistenceService dictionaryPersistenceService; + + private EntityLogService entityLogService; + + @BeforeEach + public void setUp() { + + entityLogService = new EntityLogService(fileManagementStorageService, fileStatusService, manualRedactionProviderService, unprocessedManualEntityClient, dossierService, commentService, dictionaryPersistenceService); + } + + + @Test + public void testGetEntityLogWithUnprocessedRedactions() { + + String fileId = "fileId"; + String dossierId = "dossierId"; + String dossierTemplateId = "dossierTemplateId"; + + String entryToAddId = 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 = provideManualRedactions(entryToAddId, entryToRemoveId, entryToResizeId, entryLegalBasisId, forceRedactionId, fileId); + UnprocessedManualEntity unprocessedManualEntity = UnprocessedManualEntity.builder() + .textBefore("textBefore") + .textAfter("textAfter") + .containingNodeId(List.of(0, 0)) + .endOffset(10) + .startOffset(1) + .annotationId(entryToAddId) + .closestHeadline("closestHeadline") + .color(new float[]{0,0,0}) + .build(); + + var entityLog = provideEntityLog(entryToRemoveId, entryToResizeId, entryLegalBasisId, forceRedactionId); + + when(manualRedactionProviderService.getManualRedactions(fileId, true)).thenReturn(manualRedactions); + when(unprocessedManualEntityClient.mergeUnprocessedManualEntities(any(), any(), any(), any())).thenReturn(List.of(unprocessedManualEntity)); + 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()); + + EntityLog response = entityLogService.getEntityLog(dossierId, fileId, null, true); + + assertNotNull(response); + assertFalse(response.getEntityLogEntry().isEmpty()); + + var optionalEntityLogEntry = response.getEntityLogEntry().stream().filter(entityLogEntry1 -> entityLogEntry1.getId().equals(entryToAddId)).findFirst(); + assertTrue(optionalEntityLogEntry.isPresent()); + var entityLogEntry = optionalEntityLogEntry.get(); + assertEquals(entityLogEntry.getType(), "manual"); + assertEquals(entityLogEntry.getEntryType(), EntryType.ENTITY); + assertNull(entityLogEntry.getState()); + assertEquals(entityLogEntry.getValue(), "Test"); + assertEquals(entityLogEntry.getReason(), "Reason"); + assertEquals(entityLogEntry.getTextAfter(), "textAfter"); + assertEquals(entityLogEntry.getTextBefore(), "textBefore"); + assertEquals(entityLogEntry.getStartOffset(), 1); + assertEquals(entityLogEntry.getEndOffset(), 10); + assertEquals(entityLogEntry.getManualChanges().get(0).getManualRedactionType(), ManualRedactionType.ADD_LOCALLY); + + var optionalRemoveEntryLogEntry = response.getEntityLogEntry().stream().filter(entityLogEntry1 -> entityLogEntry1.getId().equals(entryToRemoveId)).findFirst(); + assertTrue(optionalRemoveEntryLogEntry.isPresent()); + var removeEntryLogEntry = optionalRemoveEntryLogEntry.get(); + assertEquals(removeEntryLogEntry.getEntryType(), EntryType.ENTITY); + assertEquals(removeEntryLogEntry.getState(), EntryState.IGNORED); + assertEquals(removeEntryLogEntry.getManualChanges().get(0).getManualRedactionType(), ManualRedactionType.REMOVE_LOCALLY); + assertEquals(removeEntryLogEntry.getChanges().get(0).getType(), ChangeType.REMOVED); + + var optionalResizeEntryLogEntry = response.getEntityLogEntry().stream().filter(entityLogEntry1 -> entityLogEntry1.getId().equals(entryToResizeId)).findFirst(); + assertTrue(optionalResizeEntryLogEntry.isPresent()); + var resizeEntryLogEntry = optionalResizeEntryLogEntry.get(); + assertEquals(resizeEntryLogEntry.getPositions().get(0).getRectangle()[0], 2); + assertEquals(resizeEntryLogEntry.getPositions().get(0).getRectangle()[1], 2); + assertEquals(resizeEntryLogEntry.getPositions().get(0).getRectangle()[2], 2); + assertEquals(resizeEntryLogEntry.getPositions().get(0).getRectangle()[3], 2); + assertEquals(resizeEntryLogEntry.getPositions().get(0).getPageNumber(), 1); + assertEquals(resizeEntryLogEntry.getEntryType(), EntryType.ENTITY); + assertEquals(resizeEntryLogEntry.getState(), EntryState.APPLIED); + assertEquals(resizeEntryLogEntry.getManualChanges().get(0).getManualRedactionType(), ManualRedactionType.RESIZE); + assertEquals(resizeEntryLogEntry.getChanges().get(0).getType(), ChangeType.CHANGED); + + var optionalLegalBasisEntryLogEntry = response.getEntityLogEntry().stream().filter(entityLogEntry1 -> entityLogEntry1.getId().equals(entryLegalBasisId)).findFirst(); + assertTrue(optionalLegalBasisEntryLogEntry.isPresent()); + var legalBasisEntryLogEntry = optionalLegalBasisEntryLogEntry.get(); + assertEquals(legalBasisEntryLogEntry.getLegalBasis(), "New legal basis"); + assertEquals(legalBasisEntryLogEntry.getEntryType(), EntryType.ENTITY); + assertEquals(legalBasisEntryLogEntry.getState(), EntryState.APPLIED); + assertEquals(legalBasisEntryLogEntry.getManualChanges().get(0).getManualRedactionType(), ManualRedactionType.LEGAL_BASIS_CHANGE); + assertEquals(legalBasisEntryLogEntry.getChanges().get(0).getType(), ChangeType.CHANGED); + + var optionalForceRedactionEntryLogEntry = response.getEntityLogEntry().stream().filter(entityLogEntry1 -> entityLogEntry1.getId().equals(forceRedactionId)).findFirst(); + assertTrue(optionalForceRedactionEntryLogEntry.isPresent()); + var forceRedactionEntryLogEntry = optionalForceRedactionEntryLogEntry.get(); + assertEquals(forceRedactionEntryLogEntry.getLegalBasis(), "Force"); + assertEquals(forceRedactionEntryLogEntry.getEntryType(), EntryType.ENTITY); + assertEquals(forceRedactionEntryLogEntry.getState(), EntryState.APPLIED); + assertEquals(forceRedactionEntryLogEntry.getManualChanges().get(0).getManualRedactionType(), ManualRedactionType.FORCE_REDACT); + assertEquals(forceRedactionEntryLogEntry.getChanges().get(0).getType(), ChangeType.CHANGED); + + } + + private EntityLog provideEntityLog(String entryToRemoveId, String entryToResizeId, String entryLegalBasisId, String forceRedactionId) { + + List positions = new ArrayList<>(); + positions.add(new Position(1, 1, 1, 1, 1)); + return new EntityLog(1, + 1, + Lists.newArrayList(EntityLogEntry.builder() + .id(entryToRemoveId) + .type("manual") + .value("Luke Skywalker") + .entryType(EntryType.ENTITY) + .state(EntryState.APPLIED) + .dictionaryEntry(true) + .build(), + EntityLogEntry.builder() + .id(entryToResizeId) + .type("manual") + .value("Darth Vader") + .entryType(EntryType.ENTITY) + .state(EntryState.APPLIED) + .dictionaryEntry(true) + .positions(positions) + .build(), + EntityLogEntry.builder() + .id(entryLegalBasisId) + .type("manual") + .value("Darth Luke") + .entryType(EntryType.ENTITY) + .state(EntryState.APPLIED) + .dictionaryEntry(true) + .positions(positions) + .build(), + EntityLogEntry.builder() + .id(forceRedactionId) + .type("manual") + .value("Darth Luke") + .entryType(EntryType.ENTITY) + .state(EntryState.APPLIED) + .dictionaryEntry(true) + .positions(positions) + .build()), + null, + 0, + 0, + 0, + 0); + } + + private ManualRedactions provideManualRedactions(String entryToAddId, String entryToRemoveId, String entryToResizeId, String entryLegalBasisId, String forceRedactionId, String fileId) { + + List positions = new ArrayList<>(); + positions.add(new Rectangle(2, 2, 2, 2, 1)); + return ManualRedactions.builder() + .entriesToAdd(Set.of( + ManualRedactionEntry.builder() + .positions(List.of(new Rectangle(1f, 2f, 3f, 4f, 1))) + .annotationId(entryToAddId) + .value("Test") + .reason("Reason") + .addToDictionary(false) + .addToDossierDictionary(false) + .fileId(fileId) + .requestDate(OffsetDateTime.now()) + .type("manual") + .build())) + .idsToRemove(Set.of( + IdRemoval.builder() + .annotationId(entryToRemoveId) + .build() + )) + .resizeRedactions(Set.of( + ManualResizeRedaction.builder() + .fileId(fileId) + .value("Random") + .annotationId(entryToResizeId) + .positions(positions) + .build() + )) + .legalBasisChanges(Set.of( + ManualLegalBasisChange.builder() + .annotationId(entryLegalBasisId) + .value("Random") + .legalBasis("New legal basis") + .section("Section") + .build() + )) + .forceRedactions(Set.of( + ManualForceRedaction.builder() + .annotationId(forceRedactionId) + .fileId(fileId) + .legalBasis("Force") + .build() + )) + .build(); + } +} diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/MessageType.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/MessageType.java index 9e75196e7..09dc18164 100644 --- a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/MessageType.java +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/MessageType.java @@ -3,6 +3,5 @@ package com.iqser.red.service.persistence.service.v1.api.shared.model; public enum MessageType { ANALYSE, - REANALYSE, - SURROUNDING_TEXT + REANALYSE } diff --git a/persistence-service-v1/pom.xml b/persistence-service-v1/pom.xml index e0282b76c..51a38b5ce 100755 --- a/persistence-service-v1/pom.xml +++ b/persistence-service-v1/pom.xml @@ -31,7 +31,7 @@ - 4.126.0 + 4.157.0 2.71.0 4.29.0 4.13.0