Merge branch 'RED-9290' into 'master'

RED-9290 - Component management for dossier template clone/import/export

Closes RED-9290

See merge request redactmanager/persistence-service!529
This commit is contained in:
Andrei Isvoran 2024-06-12 08:12:23 +02:00
commit a586ec0eeb
12 changed files with 140 additions and 13 deletions

View File

@ -17,6 +17,7 @@ import org.springframework.util.FileSystemUtils;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.ColorsEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.FileAttributesGeneralConfigurationEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.ComponentDefinitionEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierAttributeConfigEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileAttributeConfigEntity;
@ -24,6 +25,7 @@ import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.
import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMapping;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ComponentDefinitionPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierStatusPersistenceService;
@ -36,6 +38,7 @@ import com.iqser.red.service.persistence.management.v1.processor.service.persist
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierTemplateRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.WatermarkModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentDefinitionAddRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.CloneDossierTemplateRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplateStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.CreateOrUpdateDossierStatusRequest;
@ -71,6 +74,7 @@ public class DossierTemplateCloneService {
WatermarkService watermarkService;
FileManagementStorageService fileManagementStorageService;
ComponentMappingService componentMappingService;
ComponentDefinitionPersistenceService componentDefinitionPersistenceService;
public DossierTemplateEntity cloneDossierTemplate(String dossierTemplateId, CloneDossierTemplateRequest cloneDossierTemplateRequest) {
@ -139,10 +143,29 @@ public class DossierTemplateCloneService {
cloneComponentMappings(dossierTemplateId, clonedDossierTemplate.getId());
// set components
cloneComponents(dossierTemplateId, clonedDossierTemplate.getId());
return clonedDossierTemplate;
}
private void cloneComponents(String dossierTemplate, String clonedDossierTemplateId) {
List<ComponentDefinitionEntity> componentDefinitionEntities = componentDefinitionPersistenceService.findComponentsByDossierTemplateId(dossierTemplate);
for (ComponentDefinitionEntity componentDefinitionEntity : componentDefinitionEntities) {
ComponentDefinitionAddRequest componentDefinitionAddRequest = ComponentDefinitionAddRequest.builder()
.description(componentDefinitionEntity.getDescription())
.technicalName(componentDefinitionEntity.getTechnicalName())
.displayName(componentDefinitionEntity.getDisplayName())
.dossierTemplateId(clonedDossierTemplateId)
.build();
componentDefinitionPersistenceService.insert(componentDefinitionAddRequest, componentDefinitionEntity.getRank());
}
}
private void cloneRules(String dossierTemplateId, String clonedDossierTemplateId) {
for (RuleFileType ruleFileType : RuleFileType.values()) {

View File

@ -43,12 +43,14 @@ import com.iqser.red.service.persistence.management.v1.processor.entity.configur
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.FileAttributesGeneralConfigurationEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.WatermarkEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.ComponentDefinitionEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierAttributeConfigEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierStatusEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileAttributeConfigEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ComponentDefinitionPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierStatusPersistenceService;
@ -63,6 +65,8 @@ import com.iqser.red.service.persistence.management.v1.processor.settings.FileMa
import com.iqser.red.service.persistence.management.v1.processor.utils.FileUtils;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.WatermarkModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentDefinition;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentDefinitionAddRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierAttributeConfig;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplate;
@ -83,6 +87,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemp
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.Type;
import com.iqser.red.service.redaction.v1.model.DroolsValidation;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import com.knecon.fforesight.tenantcommons.TenantContext;
import lombok.RequiredArgsConstructor;
@ -116,6 +121,7 @@ public class DossierTemplateImportService {
private final ObjectMapper objectMapper = new ObjectMapper();
private final FileManagementServiceSettings settings;
private final ComponentMappingService componentMappingService;
private final ComponentDefinitionPersistenceService componentDefinitionPersistenceService;
public String importDossierTemplate(ImportDossierTemplateRequest request) {
@ -291,6 +297,9 @@ public class DossierTemplateImportService {
mappingDataMap.put(fileName, bytes);
}
} else if (ze.getName().contains(ExportFilename.COMPONENTS.getFilename())){
ComponentDefinition componentDefinition = objectMapper.readValue(bytes, ComponentDefinition.class);
importTemplateResult.getComponentDefinitions().add(componentDefinition);
} else {
reportTemplateBytesMap.put(ze.getName(), bos);
}
@ -427,6 +436,13 @@ public class DossierTemplateImportService {
this.deleteTypes(currentTypes, new HashSet<>());
}
if (CollectionUtils.isNotEmpty(request.getComponentDefinitions())) {
this.updateComponents(request, dossierTemplateId);
} else { // no components to add, but remove existing ones
List<ComponentDefinitionEntity> currentComponents = componentDefinitionPersistenceService.findComponentsByDossierTemplateId(dossierTemplateId);
this.deleteComponents(currentComponents);
}
//set report templates
var existingReports = reportTemplatePersistenceService.findByDossierTemplateId(dossierTemplateId);
@ -527,6 +543,19 @@ public class DossierTemplateImportService {
}
}
// set components
if (CollectionUtils.isNotEmpty(request.getComponentDefinitions())) {
for (ComponentDefinition componentDefinition : request.getComponentDefinitions()) {
ComponentDefinitionAddRequest componentDefinitionAddRequest = ComponentDefinitionAddRequest.builder()
.dossierTemplateId(dossierTemplateId)
.displayName(componentDefinition.getDisplayName())
.description(componentDefinition.getDescription())
.technicalName(componentDefinition.getTechnicalName())
.build();
componentDefinitionPersistenceService.insert(componentDefinitionAddRequest, componentDefinition.getRank());
}
}
//set report templates
if (CollectionUtils.isNotEmpty(request.getReportTemplateUploadRequests())) {
request.getReportTemplateUploadRequests()
@ -807,6 +836,21 @@ public class DossierTemplateImportService {
}
private void updateComponents(ImportTemplateResult request, String dossierTemplateId) {
request.getComponentDefinitions().forEach(componentDefinition -> {
componentDefinition.setDossierTemplateId(dossierTemplateId);
componentDefinitionPersistenceService.insert(MagicConverter.convert(componentDefinition, ComponentDefinitionEntity.class));
});
}
private void deleteComponents(List<ComponentDefinitionEntity> componentDefinitionEntities) {
componentDefinitionEntities.forEach(componentDefinition -> {
componentDefinitionPersistenceService.delete(componentDefinition.getId());
});
}
private void validateDossierTemplateName(DossierTemplate dossierTemplateMeta) {
int nameSuffix = 0;

View File

@ -25,6 +25,7 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.iqser.red.service.persistence.management.v1.processor.configuration.MessagingConfiguration;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.BaseDictionaryEntry;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.ComponentDefinitionEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.download.DownloadStatusEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
@ -34,6 +35,7 @@ import com.iqser.red.service.persistence.management.v1.processor.service.ColorsS
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentMappingService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.WatermarkService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ComponentDefinitionPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierStatusPersistenceService;
@ -90,10 +92,9 @@ public class DossierTemplateExportService {
private final ColorsService colorsService;
private final EntryPersistenceService entryPersistenceService;
private final ObjectMapper objectMapper = new ObjectMapper();
private final WatermarkService watermarkService;
private final RabbitTemplate rabbitTemplate;
private final ComponentDefinitionPersistenceService componentDefinitionPersistenceService;
public JSONPrimitive<String> prepareExportDownload(ExportDownloadRequest request) {
@ -263,6 +264,14 @@ public class DossierTemplateExportService {
FileSystemUtils.deleteRecursively(mappingDir);
// components
List<ComponentDefinitionEntity> componentDefinitions = componentDefinitionPersistenceService.findByDossierTemplateIdAndNotSoftDeleted(dossierTemplateId);
for (ComponentDefinitionEntity componentDefinition : componentDefinitions) {
fileSystemBackedArchiver.addEntries(new FileSystemBackedArchiver.ArchiveModel(componentDefinition.getTechnicalName(),
getFilename(ExportFilename.COMPONENTS, JSON_EXT),
objectMapper.writeValueAsBytes(componentDefinition)));
}
storeZipFile(downloadStatus.getStorageId(), fileSystemBackedArchiver);
downloadStatusPersistenceService.updateStatus(downloadStatus, DownloadStatusValue.READY, fileSystemBackedArchiver.getContentLength());

View File

@ -26,17 +26,34 @@ public class ComponentDefinitionPersistenceService {
public ComponentDefinitionEntity insert(ComponentDefinitionAddRequest component) {
return insert(component, countByDossierTemplateId(component.getDossierTemplateId()) + 1);
}
public ComponentDefinitionEntity insert(ComponentDefinitionEntity componentDefinition) {
return componentDefinitionRepository.saveAndFlush(componentDefinition);
}
public ComponentDefinitionEntity insert(ComponentDefinitionAddRequest component, int rank) {
ComponentDefinitionEntity componentDefinitionEntity = new ComponentDefinitionEntity();
componentDefinitionEntity.setId(UUID.randomUUID().toString());
componentDefinitionEntity.setDossierTemplateId(component.getDossierTemplateId());
componentDefinitionEntity.setTechnicalName(SnakeCaseUtils.toSnakeCase(component.getTechnicalName()));
componentDefinitionEntity.setDisplayName(component.getDisplayName());
componentDefinitionEntity.setDescription(component.getDescription());
componentDefinitionEntity.setRank(countByDossierTemplateId(component.getDossierTemplateId()) + 1);
componentDefinitionEntity.setRank(rank);
return componentDefinitionRepository.saveAndFlush(componentDefinitionEntity);
}
public void delete(String id) {
componentDefinitionRepository.deleteById(id);
}
public int countByDossierTemplateId(String dossierTemplateId) {
return componentDefinitionRepository.countByDossierTemplateId(dossierTemplateId);

View File

@ -13,7 +13,7 @@ import org.springframework.stereotype.Repository;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.ComponentDefinitionEntity;
@Repository
public interface ComponentDefinitionRepository extends JpaRepository<ComponentDefinitionEntity, Long> {
public interface ComponentDefinitionRepository extends JpaRepository<ComponentDefinitionEntity, String> {
List<ComponentDefinitionEntity> findByDossierTemplateId(String dossierTemplateId);

View File

@ -33,6 +33,7 @@ import com.iqser.red.service.persistence.management.v1.processor.service.Dossier
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.WatermarkService;
import com.iqser.red.service.persistence.management.v1.processor.service.export.DossierTemplateExportService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ComponentDefinitionPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierStatusPersistenceService;
@ -92,6 +93,8 @@ public class DossierTemplateCloneAndExportWithDuplicateRanksTest {
@MockBean
private DossierTemplateImportService dossierTemplateImportService;
@MockBean
private ComponentDefinitionPersistenceService componentDefinitionPersistenceService;
private DossierTemplatePersistenceService dossierTemplatePersistenceService;
private DossierTemplateCloneService dossierTemplateCloneService;
@ -129,7 +132,8 @@ public class DossierTemplateCloneAndExportWithDuplicateRanksTest {
dossierStatusPersistenceService,
watermarkService,
fileManagementStorageService,
componentMappingService);
componentMappingService,
componentDefinitionPersistenceService);
dossierTemplateExportService = new DossierTemplateExportService(dossierTemplatePersistenceService,
downloadStatusPersistenceService,
dossierAttributeConfigPersistenceService,
@ -144,7 +148,8 @@ public class DossierTemplateCloneAndExportWithDuplicateRanksTest {
colorsService,
entryPersistenceService,
watermarkService,
rabbitTemplate);
rabbitTemplate,
componentDefinitionPersistenceService);
dossierTemplateManagementService = new DossierTemplateManagementService(dossierTemplateExportService,
dossierTemplateImportService,
dossierTemplatePersistenceService,

View File

@ -32,6 +32,7 @@ import com.iqser.red.service.persistence.management.v1.processor.service.Compone
import com.iqser.red.service.persistence.management.v1.processor.service.DossierTemplateCloneService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.WatermarkService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ComponentDefinitionPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierStatusPersistenceService;
@ -95,6 +96,9 @@ public class DossierTemplateCloneServiceTest {
@MockBean
private ComponentMappingService componentMappingService;
@MockBean
private ComponentDefinitionPersistenceService componentDefinitionPersistenceService;
private DossierTemplateCloneService dossierTemplateCloneService;
private DossierTemplatePersistenceService dossierTemplatePersistenceService;
@ -128,7 +132,8 @@ public class DossierTemplateCloneServiceTest {
dossierStatusPersistenceService,
watermarkService,
fileManagementStorageService,
componentMappingService);
componentMappingService,
componentDefinitionPersistenceService);
dummyTemplate = new DossierTemplateEntity();
setNonDefaultValues(dummyTemplate);
@ -255,6 +260,7 @@ public class DossierTemplateCloneServiceTest {
when(watermarkService.getWatermarksForDossierTemplateId(anyString())).thenReturn(Collections.emptyList());
when(dossierStatusPersistenceService.getAllDossierStatusForTemplate(anyString())).thenReturn(Collections.emptyList());
when(colorsService.getColors(anyString())).thenReturn(new ColorsEntity());
when(componentDefinitionPersistenceService.findComponentsByDossierTemplateId(anyString())).thenReturn(Collections.emptyList());
DossierTemplateEntity clonedTemplate = dossierTemplateCloneService.cloneDossierTemplate("dummy-id", request);

View File

@ -9,6 +9,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
@ -67,17 +68,24 @@ public class DossierTemplateImportExportTest extends AbstractPersistenceServerSe
((FileSystemBackedStorageService) storageService).clearStorage();
}
@Test
@SneakyThrows
// @DirtiesContext
public void testImportExportRoundtrip() {
public void testImportExportReoundtrip() {
Path dossierTemplate = new ClassPathResource("files/dossiertemplates/DossierTemplate.zip").getFile().toPath();
Path floraTemplate = new ClassPathResource("files/dossiertemplates/Flora.zip").getFile().toPath();
testImportExport(dossierTemplate);
testImportExport(floraTemplate);
}
@SneakyThrows
public void testImportExport(Path dossierTemplateExportArchive) {
TenantContext.setTenantId(TENANT_1);
Path outDir = Files.createTempDirectory(IMPORTED_TEMPLATE_NAME);
Path dossierTemplateExportArchive = new ClassPathResource("files/dossiertemplates/DossierTemplate.zip").getFile().toPath();
String importedDossierTemplateId = dossierTemplateImportService.importDossierTemplate(ImportDossierTemplateRequest.builder()
.archive(Files.readAllBytes(dossierTemplateExportArchive))
.userId(USER_ID)

View File

@ -88,6 +88,16 @@ public class DossierTemplateImportTest extends AbstractPersistenceServerServiceT
}
@SneakyThrows
@Test
public void testDocumineDossierTemplateImport() {
TenantContext.setTenantId("redaction");
var archive = loadFileFromClasspath("Flora.zip");
testDossierTemplateImport(archive);
}
@SneakyThrows
@Test

View File

@ -21,7 +21,8 @@ public enum ExportFilename {
FALSE_POSITIVES("falsePositives"),
FALSE_RECOMMENDATION("falseRecommendations"),
COMPONENT_MAPPINGS_FOLDER("mappings"),
COMPONENT_MAPPINGS_FILE("componentMappingsList");
COMPONENT_MAPPINGS_FILE("componentMappingsList"),
COMPONENTS("components");
@Getter
private final String filename;

View File

@ -6,6 +6,7 @@ import java.util.List;
import java.util.Map;
import com.iqser.red.service.persistence.service.v1.api.shared.model.WatermarkModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentDefinition;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierAttributeConfig;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplate;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.FileAttributesGeneralConfiguration;
@ -66,6 +67,9 @@ public class ImportTemplateResult {
@Builder.Default
public List<Type> types = new ArrayList<>();
@Builder.Default
public List<ComponentDefinition> componentDefinitions = new ArrayList<>();
@Builder.Default
public Map<String, List<String>> entries = new HashMap<>();