RED-9888: DM: Components not restored after file re-upload

This commit is contained in:
Maverick Studer 2024-08-15 15:25:54 +02:00
parent dc8abbba58
commit e4ec187c2f
10 changed files with 205 additions and 24 deletions

View File

@ -172,6 +172,18 @@ public class ComponentLogService {
}
private void hardDeleteAllOverrides(String dossierId, String fileId) {
componentLogMongoService.hardDeleteComponentLogEntries(dossierId, fileId);
}
private void softDeleteAllOverrides(String dossierId, String fileId) {
componentLogMongoService.softDeleteComponentLogEntries(dossierId, fileId);
}
private void insertOverride(String dossierId, String fileId, ComponentLogEntry componentToAdd) {
@ -181,7 +193,7 @@ public class ComponentLogService {
public List<ComponentLogEntry> getOverrides(String dossierId, String fileId) {
return componentLogMongoService.findOverrides(dossierId, fileId);
return componentLogMongoService.findOverrides(dossierId, fileId, false);
}

View File

@ -20,7 +20,7 @@ public class DossierDeletionService {
public void undeleteDossier(String dossierId, List<String> relevantFileIds, OffsetDateTime dossierSoftDeleteTime) {
relevantFileIds.forEach(fileId -> {
fileDeletionService.undeleteFile(fileId, dossierSoftDeleteTime);
fileDeletionService.undeleteFile(dossierId, fileId, dossierSoftDeleteTime);
});
dossierService.undeleteDossier(dossierId);
@ -30,7 +30,7 @@ public class DossierDeletionService {
@Transactional
public void softDeleteDossier(String dossierId, List<String> relevantFileIds, OffsetDateTime softDeleteTime) {
fileDeletionService.softDeleteFiles(relevantFileIds, softDeleteTime);
fileDeletionService.softDeleteFiles(dossierId, relevantFileIds, softDeleteTime);
dossierService.softDeleteDossier(dossierId, softDeleteTime);
}
@ -38,7 +38,7 @@ public class DossierDeletionService {
@Transactional
public void hardDeleteDossier(String dossierId, List<String> relevantFileIds) {
fileDeletionService.hardDeleteFiles(relevantFileIds);
fileDeletionService.hardDeleteFiles(dossierId, relevantFileIds);
dossierService.hardDeleteDossier(dossierId);
}

View File

@ -17,6 +17,7 @@ import com.iqser.red.service.persistence.management.v1.processor.service.persist
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.annotations.RemoveRedactionPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.annotations.ResizeRedactionPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.service.ComponentLogMongoService;
import com.iqser.red.service.search.v1.model.IndexMessageType;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
@ -38,10 +39,11 @@ public class FileDeletionService {
private final FileStatusPersistenceService fileStatusPersistenceService;
private final FileManagementStorageService fileManagementStorageService;
private final IndexingService indexingService;
private final ComponentLogMongoService componentLogMongoService;
@Transactional
public void undeleteFile(String fileId, OffsetDateTime softDeletedTime) {
public void undeleteFile(String dossierId, String fileId, OffsetDateTime softDeletedTime) {
// revert comment deletion
commentPersistenceService.undeleteByFileId(fileId, softDeletedTime);
@ -54,12 +56,15 @@ public class FileDeletionService {
legalBasisChangePersistenceService.undeleteByFileId(fileId, softDeletedTime);
resizeRedactionPersistenceService.undeleteByFileId(fileId, softDeletedTime);
//revert override deletion
componentLogMongoService.undeleteComponentLogEntries(dossierId, fileId);
fileStatusPersistenceService.undelete(fileId);
}
@Transactional
public void softDeleteFiles(List<String> fileIds, OffsetDateTime softDeleteTime) {
public void softDeleteFiles(String dossierId, List<String> fileIds, OffsetDateTime softDeleteTime) {
// delete all annotations
addRedactionPersistenceService.softDeleteByFileIds(fileIds, softDeleteTime);
@ -75,6 +80,9 @@ public class FileDeletionService {
// delete all viewed pages for all files
viewedPagesPersistenceService.deleteForFiles(fileIds);
//delete all overrides
fileIds.forEach(fileId -> componentLogMongoService.softDeleteComponentLogEntries(dossierId, fileId));
fileStatusPersistenceService.softDeleteFiles(fileIds, softDeleteTime);
}
@ -114,14 +122,14 @@ public class FileDeletionService {
@Transactional
public void hardDeleteFile(String fileId) {
public void hardDeleteFile(String dossierId, String fileId) {
this.hardDeleteFiles(List.of(fileId));
this.hardDeleteFiles(dossierId, List.of(fileId));
}
@Transactional
public void hardDeleteFiles(List<String> fileIds) {
public void hardDeleteFiles(String dossierId, List<String> fileIds) {
// delete all annotations
addRedactionPersistenceService.deleteByFileIds(fileIds);
@ -137,6 +145,10 @@ public class FileDeletionService {
// delete all viewed pages for all files
viewedPagesPersistenceService.deleteForFiles(fileIds);
//delete all overrides
fileIds.forEach(fileId -> componentLogMongoService.hardDeleteComponentLogEntries(dossierId, fileId));
fileStatusPersistenceService.hardDeleteFiles(fileIds);
}

View File

@ -92,7 +92,7 @@ public class FileService {
}
OffsetDateTime softDeleteTime = OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS);
fileDeletionService.softDeleteFiles(List.of(fileId), softDeleteTime);
fileDeletionService.softDeleteFiles(dossierId, List.of(fileId), softDeleteTime);
fileDeletionService.reindexDeletedFiles(dossierId, List.of(fileId));
websocketService.sendFileEvent(dossierId, fileId, FileEventType.SOFT_DELETE);
}
@ -106,7 +106,7 @@ public class FileService {
throw new DossierNotFoundException(DOSSIER_NOT_FOUND_MESSAGE);
}
fileDeletionService.hardDeleteFiles(fileIds);
fileDeletionService.hardDeleteFiles(dossierId, fileIds);
fileDeletionService.hardDeleteFileDataAndIndexUpdates(dossierId, fileIds);
fileIds.forEach(fileId -> {
websocketService.sendFileEvent(dossierId, fileId, FileEventType.HARD_DELETE);
@ -124,7 +124,7 @@ public class FileService {
for (String fileId : fileIds) {
FileModel fileStatus = fileStatusService.getStatus(fileId);
OffsetDateTime softDeletedTime = fileStatus.getDeleted();
fileDeletionService.undeleteFile(fileId, softDeletedTime);
fileDeletionService.undeleteFile(dossierId, fileId, softDeletedTime);
}
fileDeletionService.reindexUndeletedFiles(dossier.getDossierTemplateId(), dossierId, fileIds);
fileIds.forEach(fileId -> {

View File

@ -61,7 +61,7 @@ public class DeletedFilesCleanupJob implements Job {
if (file.getHardDeletedTime() == null && file.getDeleted() != null && file.getDeleted()
.isBefore(now.minusHours(applicationConfigurationEntity.getSoftDeleteCleanupTime()))) {
fileDeletionService.hardDeleteFile(file.getId());
fileDeletionService.hardDeleteFile(file.getDossierId(), file.getId());
fileDeletionService.hardDeleteFileDataAndIndexUpdates(dossierEntity.getId(),file.getId());
log.info("Hard deleted file with dossier id {} and file id {}", dossierEntity.getId(), file.getId());
}

View File

@ -21,9 +21,11 @@ import com.iqser.red.service.peristence.v1.server.integration.client.DossierTemp
import com.iqser.red.service.peristence.v1.server.integration.service.DossierTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.service.FileTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest;
import com.iqser.red.service.persistence.management.v1.processor.service.FileService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentsOverrides;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.AddFileRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
import com.iqser.red.service.persistence.service.v2.api.external.model.Component;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentValue;
@ -49,6 +51,9 @@ public class ComponentOverrideTest extends AbstractPersistenceServerServiceTest
@Autowired
private DossierTemplateClient dossierTemplateClient;
@Autowired
private FileService fileService;
@Test
public void testOverrides() throws IOException {
@ -199,7 +204,86 @@ public class ComponentOverrideTest extends AbstractPersistenceServerServiceTest
.findAny()
.get();
assertTrue(studyTitle.isOverridden());
assertTrue(studyTitle.getComponentValues().stream().anyMatch(componentLogEntryValue -> componentLogEntryValue.getValue().equals(value)));
assertTrue(studyTitle.getComponentValues()
.stream()
.anyMatch(componentLogEntryValue -> componentLogEntryValue.getValue().equals(value)));
}
@Test
public void testDeletedFileOverrides() throws IOException {
var dossier = dossierTesterAndProvider.provideTestDossier();
var dossierTemplate = dossierTemplateClient.getDossierTemplate(dossier.getDossierTemplateId());
var file = fileTesterAndProvider.testAndProvideFile(dossier, "filename");
System.out.println("DOSSIER TEMPLATE ID: " + dossierTemplate.getId());
System.out.println("DOSSIER ID: " + dossier.getId());
System.out.println("FILE ID: " + file.getId());
Component componentOverrideModel = Component.builder()
.name("Study_Title")
.componentValues(List.of(ComponentValue.builder()
.value("AAAA Strange Chemical Name And the rest of a title With a dash and some more text")
.originalValue("Strange Chemical Name And the rest of a title With a dash and some more text")
.valueDescription("First found value of type title or else ''")
.componentRuleId("StudyTitle.0.0")
.entityReferences(List.of(EntityReference.builder()
.id("cf7f0d0c4c07918ce7d67b204f5fdb7d")
.type("title")
.entityRuleId("DOC.6.1")
.page(1)
.build()))
.build()))
.build();
var componentLogJson = new ClassPathResource("files/componentlog/exampleComponentLog.json");
fileManagementStorageService.storeObject(dossier.getId(), file.getId(), FileType.COMPONENT_LOG, componentLogJson.getInputStream());
componentClient.addOverride(dossierTemplate.getId(), dossier.getId(), file.getId(), componentOverrideModel);
var overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());
assertFalse(overrides.getComponentOverrides().isEmpty());
assertTrue(overrides.getComponentOverrides()
.get(0).isOverridden());
// case 1: delete file, overrides should not be returned anymore
fileService.deleteFile(dossier.getId(), file.getFileId());
overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());
assertTrue(overrides.getComponentOverrides().isEmpty());
// case 2: re-upload file that was deleted before overrides should also not be returned anymore
fileTesterAndProvider.testAndProvideFile(dossier, "filename");
overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());
assertTrue(overrides.getComponentOverrides().isEmpty());
// delete file again
fileService.deleteFile(dossier.getId(), file.getFileId());
// case 3: when undeleting, the overrides should be there again (restored)
fileService.undeleteFiles(dossier.getId(), Set.of(file.getFileId()));
overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());
assertFalse(overrides.getComponentOverrides().isEmpty());
assertTrue(overrides.getComponentOverrides()
.get(0).isOverridden());
// case 4: on hard delete overrides should also be gone of course
fileService.hardDeleteFiles(dossier.getId(), List.of(file.getFileId()));
overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());
assertTrue(overrides.getComponentOverrides().isEmpty());
}

View File

@ -1,5 +1,6 @@
package com.iqser.red.service.persistence.service.v1.api.shared.mongo.document;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
@ -37,4 +38,6 @@ public class ComponentDocument {
boolean overridden;
OffsetDateTime softDeletedTime;
}

View File

@ -15,12 +15,19 @@ public interface ComponentDocumentRepository extends MongoRepository<ComponentDo
@Query(value = "{ 'componentLogId' : ?0}", delete = true)
void deleteByComponentLogId(String componentLogId);
@Query(value = "{ 'componentLogId': ?0, 'componentName': ?1 }")
Optional<ComponentDocument> findComponentDocumentByName(String componentLogId, String componentName);
@Query(value = "{ 'componentLogId': ?0 }")
List<ComponentDocument> findByComponentLogId(String componentLogId);
@Query(value = "{ 'componentLogId': ?0, 'softDeletedTime': { $eq: null } }")
List<ComponentDocument> findByComponentLogIdExcludeSoftDeleted(String componentLogId);
@Query(value = "{ 'componentLogId': ?0 }", fields = "{ 'overrideValues': 0 }")
List<ComponentDocument> findWithoutOverrideValuesByDossierIdAndFileId(String componentLogId);

View File

@ -0,0 +1,41 @@
package com.iqser.red.service.persistence.service.v1.api.shared.mongo.service;
import java.time.OffsetDateTime;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.document.ComponentDocument;
@Service
public class ComponentDocumentUpdateService {
private final MongoTemplate mongoTemplate;
public ComponentDocumentUpdateService(MongoTemplate mongoTemplate) {this.mongoTemplate = mongoTemplate;}
public void setSoftDeletedTime(String componentLogId) {
Query query = new Query(Criteria.where("componentLogId").is(componentLogId));
Update update = new Update().set("softDeletedTime", OffsetDateTime.now());
mongoTemplate.updateFirst(query, update, ComponentDocument.class);
}
public void unsetSoftDeletedTime(String componentLogId) {
Query query = new Query(Criteria.where("componentLogId").is(componentLogId));
Update update = new Update().unset("softDeletedTime");
mongoTemplate.updateFirst(query, update, ComponentDocument.class);
}
}

View File

@ -17,24 +17,20 @@ import com.iqser.red.service.persistence.service.v1.api.shared.mongo.repository.
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.repository.ComponentLogDocumentRepository;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
@Service
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
@RequiredArgsConstructor
public class ComponentLogMongoService {
ComponentLogDocumentRepository componentLogDocumentRepository;
ComponentDocumentRepository componentDocumentRepository;
ComponentDocumentUpdateService componentDocumentUpdateService;
ComponentLogDocumentMapper mapper = ComponentLogDocumentMapper.INSTANCE;
public ComponentLogMongoService(ComponentLogDocumentRepository componentLogDocumentRepository, ComponentDocumentRepository componentDocumentRepository) {
this.componentLogDocumentRepository = componentLogDocumentRepository;
this.componentDocumentRepository = componentDocumentRepository;
}
public void insertComponentLog(String dossierId, String fileId, ComponentLog componentLog) {
ComponentLogDocument componentLogDocument = componentLogDocumentRepository.insert(mapper.toComponentLogDocument(dossierId, fileId, componentLog));
@ -109,6 +105,30 @@ public class ComponentLogMongoService {
}
public void hardDeleteComponentLogEntries(String dossierId, String fileId) {
String componentLogId = mapper.getComponentLogId(dossierId, fileId);
componentDocumentRepository.deleteByComponentLogId(componentLogId);
}
public void softDeleteComponentLogEntries(String dossierId, String fileId) {
String componentLogId = mapper.getComponentLogId(dossierId, fileId);
componentDocumentUpdateService.setSoftDeletedTime(componentLogId);
}
public void undeleteComponentLogEntries(String dossierId, String fileId) {
String componentLogId = mapper.getComponentLogId(dossierId, fileId);
componentDocumentUpdateService.unsetSoftDeletedTime(componentLogId);
}
private ComponentLogDocument getComponentLogDocument(String componentLogId) {
return componentLogDocumentRepository.findById(componentLogId)
@ -170,12 +190,14 @@ public class ComponentLogMongoService {
}
public List<ComponentLogEntry> findOverrides(String dossierId, String fileId) {
public List<ComponentLogEntry> findOverrides(String dossierId, String fileId, boolean includeSoftDeleted) {
String componentLogId = mapper.getComponentLogId(dossierId, fileId);
return componentDocumentRepository.findByComponentLogId(componentLogId)
.stream()
List<ComponentDocument> componentDocuments = includeSoftDeleted ? //
componentDocumentRepository.findByComponentLogId(componentLogId) : componentDocumentRepository.findByComponentLogIdExcludeSoftDeleted(componentLogId);
return componentDocuments.stream()
.map(mapper::fromComponentDocument)
.peek(componentLogEntry -> componentLogEntry.setOverridden(true))
.toList();