Merge branch 'master' into 'feature/RED-9472'

# Conflicts:
#   buildSrc/src/main/kotlin/com.iqser.red.service.java-conventions.gradle.kts
This commit is contained in:
Yannik Hampe 2024-11-22 10:12:38 +01:00
commit b3c4e670a8
31 changed files with 3265 additions and 85 deletions

View File

@ -7,23 +7,17 @@ import java.io.BufferedInputStream;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@ -161,7 +155,7 @@ public class DownloadController implements DownloadResource {
} // otherwise consider the files from dossier
var validFilesAndNotProcessed = validFiles.stream()
.filter(f -> !(f.getAnalysisVersion() > 0 && f.getNumberOfAnalyses() > 0))
.filter(f -> !(f.getAnalysisVersion() > 0 && f.getNumberOfAnalyses() > 0 && !f.isSoftOrHardDeleted()))
.collect(Collectors.toList());
if (!validFilesAndNotProcessed.isEmpty()) {
throw new BadRequestException("At least a file is in its initial analysis process");

View File

@ -226,7 +226,6 @@ public interface DictionaryResource {
@RequestParam(value = "addToDictionary") boolean addToDictionary);
@ResponseStatus(HttpStatus.ACCEPTED)
@PostMapping(value = DICTIONARY_REST_PATH + DIFFERENCE + DOSSIER_TEMPLATE_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Returns the difference between the dictionaries of the dossier template and all the dossiers inside the template for a list of given types.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully returned DictionaryDifferenceResponse."), @ApiResponse(responseCode = "400", description = "The request is not valid.")})

View File

@ -0,0 +1,148 @@
package com.iqser.red.service.persistence.management.v1.processor.migration;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.LegalBasisEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.LegalBasisMappingPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogLegalBasis;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class LegalBasisMappingService {
private final HashFunction hashFunction;
private final LegalBasisMappingPersistenceService legalBasisMappingPersistenceService;
public LegalBasisMappingService(LegalBasisMappingPersistenceService legalBasisMappingPersistenceService) {
this.hashFunction = Hashing.murmur3_128();
this.legalBasisMappingPersistenceService = legalBasisMappingPersistenceService;
}
public TechnicalNameResult processLegalBasisAndEntityLogLegalBasisList(String currentLegalBasis,
Map<String, String> reasonToTechnicalNameMap,
List<EntityLogLegalBasis> legalBasisList) {
TechnicalNameResult result = computeTechnicalName(currentLegalBasis, reasonToTechnicalNameMap);
if (result == null) {
return null;
}
addToLegalBasisList(result.technicalName(), currentLegalBasis, legalBasisList);
return result;
}
public TechnicalNameResult processLegalBasis(String currentLegalBasis, Map<String, String> reasonToTechnicalNameMap) {
return computeTechnicalName(currentLegalBasis, reasonToTechnicalNameMap);
}
private TechnicalNameResult computeTechnicalName(String currentLegalBasis, Map<String, String> reasonToTechnicalNameMap) {
if (currentLegalBasis == null || currentLegalBasis.isEmpty()) {
return null;
}
if (reasonToTechnicalNameMap.containsValue(currentLegalBasis)) {
return new TechnicalNameResult(currentLegalBasis, false);
}
if (reasonToTechnicalNameMap.containsKey(currentLegalBasis)) {
String technicalName = reasonToTechnicalNameMap.get(currentLegalBasis);
return new TechnicalNameResult(technicalName, false);
}
String technicalName = hashFunction.hashString(currentLegalBasis, StandardCharsets.UTF_8).toString();
return new TechnicalNameResult(technicalName, true);
}
public String processTextAndEntityLogLegalBasisList(String text, Map<String, String> reasonToTechnicalNameMap, List<EntityLogLegalBasis> legalBasisList) {
if (text == null || text.isEmpty()) {
return text;
}
String updatedValue = text;
boolean updated = false;
for (Map.Entry<String, String> entry : reasonToTechnicalNameMap.entrySet()) {
String reason = entry.getKey();
String technicalName = entry.getValue();
if (updatedValue.contains(reason)) {
updatedValue = updatedValue.replace(reason, technicalName);
addToLegalBasisList(technicalName, reason, legalBasisList);
updated = true;
}
}
if (!updated) {
String technicalName = hashFunction.hashString(text, StandardCharsets.UTF_8).toString();
updatedValue = technicalName;
addToLegalBasisList(technicalName, text, legalBasisList);
}
return updatedValue;
}
private void addToLegalBasisList(String technicalName, String reason, List<EntityLogLegalBasis> legalBasisList) {
boolean exists = legalBasisList.stream()
.anyMatch(lb -> lb.getTechnicalName().equals(technicalName));
if (!exists) {
EntityLogLegalBasis newLegalBasis = createEntityLogLegalBasis(technicalName, reason);
legalBasisList.add(newLegalBasis);
}
}
private EntityLogLegalBasis createEntityLogLegalBasis(String technicalName, String reason) {
EntityLogLegalBasis newLegalBasis = new EntityLogLegalBasis();
newLegalBasis.setTechnicalName(technicalName);
newLegalBasis.setReason(reason);
newLegalBasis.setName("Generated Legal Basis");
newLegalBasis.setDescription("");
return newLegalBasis;
}
public Map<String, String> getReasonToTechnicalNameMap(String dossierTemplateId) {
List<LegalBasisEntity> legalBasisMappings = legalBasisMappingPersistenceService.getLegalBasisMapping(dossierTemplateId);
if (legalBasisMappings == null) {
legalBasisMappings = new ArrayList<>();
}
Map<String, String> reasonToTechnicalNameMap = new HashMap<>();
for (LegalBasisEntity lbEntity : legalBasisMappings) {
reasonToTechnicalNameMap.put(lbEntity.getReason(), lbEntity.getTechnicalName());
}
return reasonToTechnicalNameMap;
}
public record TechnicalNameResult(String technicalName, boolean hashed) {
}
}

View File

@ -0,0 +1,259 @@
package com.iqser.red.service.persistence.management.v1.processor.migration.liquibase;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import liquibase.change.custom.CustomTaskChange;
import liquibase.database.Database;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.CustomChangeException;
import liquibase.exception.ValidationErrors;
import liquibase.resource.ResourceAccessor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SuppressWarnings("PMD.CloseResource") // we do not want to close the underlying database connection ;)
public class TechnicalNameChange implements CustomTaskChange {
// there is a bug in our liquibase version which causes custom task changes to be run twice
// that bug got fixed in liquibase 4.25.1
// when we upgrade the dependency this can then be removed
@Setter
private static boolean skipExecution;
@Override
public void execute(Database database) throws CustomChangeException {
if (skipExecution) {
return;
}
Connection connection;
try {
connection = ((JdbcConnection) database.getConnection()).getUnderlyingConnection();
String[] tables = {"manual_legal_basis_change", "manual_force_redaction", "manual_recategorization", "manual_redaction"};
HashFunction hashFunction = Hashing.murmur3_128();
Set<String> allFileIds = collectAllFileIds(connection, tables);
if (allFileIds.isEmpty()) {
log.info("No rows updated because no files with manual redactions are present.");
return;
}
Map<String, String> fileIdToDossierTemplateIdMap = buildFileIdToDossierTemplateIdMap(connection, allFileIds);
Map<String, String> reasonDossierTemplateIdToTechnicalNameMap = buildReasonDossierTemplateIdToTechnicalNameMap(connection);
int totalRowsUpdatedWithTechnicalName = 0;
int totalRowsHashed = 0;
for (String tableName : tables) {
int[] counts = updateLegalBasisForTable(connection, tableName, hashFunction, fileIdToDossierTemplateIdMap, reasonDossierTemplateIdToTechnicalNameMap);
int rowsUpdatedWithTechnicalName = counts[0];
int rowsHashed = counts[1];
log.info("Table '{}': {} rows updated with technical_name, {} rows hashed.", tableName, rowsUpdatedWithTechnicalName, rowsHashed);
totalRowsUpdatedWithTechnicalName += rowsUpdatedWithTechnicalName;
totalRowsHashed += rowsHashed;
}
log.info("Total rows updated with technical_name: {}", totalRowsUpdatedWithTechnicalName);
log.info("Total rows hashed: {}", totalRowsHashed);
} catch (Exception e) {
throw new CustomChangeException("Error applying technical name change", e);
}
skipExecution = true;
}
private Set<String> collectAllFileIds(Connection connection, String[] tables) throws SQLException {
Set<String> fileIds = new HashSet<>();
for (String tableName : tables) {
String selectSql = "SELECT DISTINCT file_id FROM " + tableName + " WHERE file_id IS NOT NULL";
try (PreparedStatement select = connection.prepareStatement(selectSql); ResultSet resultSet = select.executeQuery()) {
while (resultSet.next()) {
String fileId = resultSet.getString("file_id");
fileIds.add(fileId);
}
}
}
return fileIds;
}
private Map<String, String> buildFileIdToDossierTemplateIdMap(Connection connection, Set<String> fileIds) throws SQLException {
Map<String, String> map = new HashMap<>();
if (!fileIds.isEmpty()) {
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("SELECT f.id AS file_id, d.dossier_template_id ");
sqlBuilder.append("FROM file f ");
sqlBuilder.append("JOIN dossier d ON f.dossier_id = d.id ");
sqlBuilder.append("WHERE f.id IN (");
String placeholders = String.join(",", Collections.nCopies(fileIds.size(), "?"));
sqlBuilder.append(placeholders);
sqlBuilder.append(")");
String selectSql = sqlBuilder.toString();
try (PreparedStatement select = connection.prepareStatement(selectSql)) {
int index = 1;
for (String fileId : fileIds) {
select.setString(index++, fileId);
}
try (ResultSet rs = select.executeQuery()) {
while (rs.next()) {
String fileId = rs.getString("file_id");
String dossierTemplateId = rs.getString("dossier_template_id");
map.put(fileId, dossierTemplateId);
}
}
}
}
return map;
}
private Map<String, String> buildReasonDossierTemplateIdToTechnicalNameMap(Connection connection) throws SQLException {
Map<String, String> map = new HashMap<>();
String selectSql = "SELECT reason, legal_basis_mapping_entity_dossier_template_id, technical_name FROM legal_basis_mapping_entity_legal_basis";
try (PreparedStatement select = connection.prepareStatement(selectSql); ResultSet resultSet = select.executeQuery()) {
while (resultSet.next()) {
String reason = resultSet.getString("reason");
String dossierTemplateId = resultSet.getString("legal_basis_mapping_entity_dossier_template_id");
String technicalName = resultSet.getString("technical_name");
if (reason != null && dossierTemplateId != null && technicalName != null) {
String key = getKey(reason, dossierTemplateId);
map.put(key, technicalName);
}
}
}
return map;
}
private int[] updateLegalBasisForTable(Connection connection,
String tableName,
HashFunction hashFunction,
Map<String, String> fileIdToDossierTemplateIdMap,
Map<String, String> reasonDossierTemplateIdToTechnicalNameMap) throws SQLException {
PreparedStatement select = null;
PreparedStatement update = null;
ResultSet resultSet = null;
int rowsUpdatedWithTechnicalName = 0;
int rowsHashed = 0;
try {
String selectSql = "SELECT annotation_id, file_id, legal_basis FROM " + tableName + " WHERE legal_basis IS NOT NULL AND TRIM(legal_basis) <> '';";
select = connection.prepareStatement(selectSql);
resultSet = select.executeQuery();
String updateSql = "UPDATE " + tableName + " SET legal_basis = ? WHERE annotation_id = ? AND file_id = ?";
update = connection.prepareStatement(updateSql);
while (resultSet.next()) {
String annotationId = resultSet.getString("annotation_id");
String fileId = resultSet.getString("file_id");
String originalValue = resultSet.getString("legal_basis");
if (originalValue == null) {
continue;
}
String dossierTemplateId = fileIdToDossierTemplateIdMap.get(fileId);
String newValue = null;
if (dossierTemplateId != null) {
String key = getKey(originalValue, dossierTemplateId);
newValue = reasonDossierTemplateIdToTechnicalNameMap.get(key);
}
if (newValue == null || newValue.trim().isEmpty()) {
newValue = hashFunction.hashString(originalValue, StandardCharsets.UTF_8).toString();
rowsHashed++;
} else {
rowsUpdatedWithTechnicalName++;
}
update.setString(1, newValue);
update.setString(2, annotationId);
update.setString(3, fileId);
update.executeUpdate();
}
} finally {
if (resultSet != null) {
resultSet.close();
}
if (select != null) {
select.close();
}
if (update != null) {
update.close();
}
}
return new int[]{rowsUpdatedWithTechnicalName, rowsHashed};
}
private static String getKey(String originalValue, String dossierTemplateId) {
return originalValue + "|" + dossierTemplateId;
}
@Override
public String getConfirmationMessage() {
return "Technical name change applied to legalbasis fields in specified tables.";
}
@Override
public void setUp() {}
@Override
public void setFileOpener(ResourceAccessor resourceAccessor) {}
@Override
public ValidationErrors validate(Database database) {
return new ValidationErrors();
}
}

View File

@ -48,7 +48,7 @@ public class V17MigrateImportedRedactionsFiles extends Migration {
if (fileManagementStorageService.objectExists(file.getDossierId(), file.getId(), FileType.IMPORTED_REDACTIONS)) {
com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.imported.ImportedRedactions oldImportedRedactions = fileManagementStorageService.getImportedRedactions(
com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.imported.ImportedRedactions oldImportedRedactions = fileManagementStorageService.getOldImportedRedactions(
file.getDossierId(),
file.getId());

View File

@ -0,0 +1,151 @@
package com.iqser.red.service.persistence.management.v1.processor.migration.migrations;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.migration.LegalBasisMappingService;
import com.iqser.red.service.persistence.management.v1.processor.migration.LegalBasisMappingService.TechnicalNameResult;
import com.iqser.red.service.persistence.management.v1.processor.migration.Migration;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileRepository;
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.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.EntityLogLegalBasis;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ManualChange;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.service.EntityLogMongoService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class V29TechnicalNameEntityLogMigration extends Migration {
private final DossierRepository dossierRepository;
private final FileRepository fileRepository;
private final EntityLogMongoService entityLogMongoService;
private final LegalBasisMappingService legalBasisMappingService;
private static final String NAME = "Migration for technical name update in entity logs";
private static final long VERSION = 29;
public V29TechnicalNameEntityLogMigration(DossierRepository dossierRepository,
FileRepository fileRepository,
EntityLogMongoService entityLogMongoService,
LegalBasisMappingService legalBasisMappingService) {
super(NAME, VERSION);
this.dossierRepository = dossierRepository;
this.fileRepository = fileRepository;
this.entityLogMongoService = entityLogMongoService;
this.legalBasisMappingService = legalBasisMappingService;
}
@Override
protected void migrate() {
AtomicInteger totalEntriesProcessed = new AtomicInteger(0);
AtomicInteger totalValuesHashed = new AtomicInteger(0);
AtomicInteger totalValuesUpdatedNormally = new AtomicInteger(0);
List<DossierEntity> dossiers = dossierRepository.findAll();
for (DossierEntity dossier : dossiers) {
String dossierId = dossier.getId();
String dossierTemplateId = dossier.getDossierTemplateId();
Map<String, String> reasonToTechnicalNameMap = legalBasisMappingService.getReasonToTechnicalNameMap(dossierTemplateId);
List<FileEntity> files = fileRepository.findByDossierId(dossierId);
for (FileEntity file : files) {
String fileId = file.getId();
Optional<EntityLog> optionalEntityLog = entityLogMongoService.findEntityLogByDossierIdAndFileId(dossierId, fileId);
if (optionalEntityLog.isPresent()) {
EntityLog entityLog = optionalEntityLog.get();
List<EntityLogLegalBasis> legalBasisList = entityLog.getLegalBasis();
if (legalBasisList == null) {
legalBasisList = new java.util.ArrayList<>();
entityLog.setLegalBasis(legalBasisList);
}
boolean updated = false;
for (EntityLogEntry entry : entityLog.getEntityLogEntry()) {
totalEntriesProcessed.getAndIncrement();
String currentLegalBasis = entry.getLegalBasis();
if (currentLegalBasis != null && !currentLegalBasis.isEmpty()) {
TechnicalNameResult result = legalBasisMappingService.processLegalBasisAndEntityLogLegalBasisList(currentLegalBasis,
reasonToTechnicalNameMap,
legalBasisList);
if (result != null && !currentLegalBasis.equals(result.technicalName())) {
entry.setLegalBasis(result.technicalName());
updated = true;
if (result.hashed()) {
totalValuesHashed.getAndIncrement();
} else {
totalValuesUpdatedNormally.getAndIncrement();
}
}
}
if (entry.getChanges() != null) {
for (Change change : entry.getChanges()) {
updated |= updatePropertyChanges(change.getPropertyChanges(), reasonToTechnicalNameMap, legalBasisList);
}
}
if (entry.getManualChanges() != null) {
for (ManualChange manualChange : entry.getManualChanges()) {
updated |= updatePropertyChanges(manualChange.getPropertyChanges(), reasonToTechnicalNameMap, legalBasisList);
}
}
}
if (updated) {
entityLogMongoService.saveEntityLog(dossierId, fileId, entityLog);
log.info("Updated EntityLog for dossierId: {}, fileId: {}", dossierId, fileId);
}
}
}
}
log.info("Migration completed.");
log.info("Total entries processed: {}", totalEntriesProcessed.get());
log.info("Total values updated normally: {}", totalValuesUpdatedNormally.get());
log.info("Total values hashed: {}", totalValuesHashed.get());
}
private boolean updatePropertyChanges(Map<String, String> propertyChanges, Map<String, String> reasonToTechnicalNameMap, List<EntityLogLegalBasis> legalBasisList) {
boolean updated = false;
if (propertyChanges != null && propertyChanges.containsKey("legalBasis")) {
String legalBasisValue = propertyChanges.get("legalBasis");
String updatedLegalBasisValue = legalBasisMappingService.processTextAndEntityLogLegalBasisList(legalBasisValue, reasonToTechnicalNameMap, legalBasisList);
if (!legalBasisValue.equals(updatedLegalBasisValue)) {
propertyChanges.put("legalBasis", updatedLegalBasisValue);
updated = true;
}
}
return updated;
}
}

View File

@ -0,0 +1,118 @@
package com.iqser.red.service.persistence.management.v1.processor.migration.migrations;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.LegalBasisEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.RuleSetEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
import com.iqser.red.service.persistence.management.v1.processor.migration.Migration;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.LegalBasisMappingPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.RulesPersistenceService;
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.knecon.fforesight.tenantcommons.TenantContext;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@Slf4j
@Setter
@Service
public class V30TechnicalNameRuleFileMigration extends Migration {
private final DossierTemplateRepository dossierTemplateRepository;
private final RulesPersistenceService rulesPersistenceService;
private final LegalBasisMappingPersistenceService legalBasisMappingPersistenceService;
private static final String NAME = "Migration for replacing legal basis reasons with technical names in rule files";
private static final long VERSION = 30;
public V30TechnicalNameRuleFileMigration(DossierTemplateRepository dossierTemplateRepository,
RulesPersistenceService rulesPersistenceService,
LegalBasisMappingPersistenceService legalBasisMappingPersistenceService) {
super(NAME, VERSION);
this.dossierTemplateRepository = dossierTemplateRepository;
this.rulesPersistenceService = rulesPersistenceService;
this.legalBasisMappingPersistenceService = legalBasisMappingPersistenceService;
}
@Override
protected void migrate() {
log.info("Migration: Updating rule files by replacing legal basis reasons with technical names");
updateRuleFiles();
}
private void updateRuleFiles() {
List<DossierTemplateEntity> dossierTemplates = dossierTemplateRepository.findAll();
String tenantId = TenantContext.getTenantId();
dossierTemplates.parallelStream()
.forEach(dossierTemplate -> {
TenantContext.setTenantId(tenantId);
String dossierTemplateId = dossierTemplate.getId();
List<LegalBasisEntity> legalBasisMappings = legalBasisMappingPersistenceService.getLegalBasisMapping(dossierTemplateId);
if (legalBasisMappings == null || legalBasisMappings.isEmpty()) {
log.warn("No legal basis mappings found for dossierTemplateId: {}", dossierTemplateId);
return;
}
Map<String, String> reasonToTechnicalNameMap = legalBasisMappings.stream()
.filter(lb -> StringUtils.isNotBlank(lb.getReason()) && StringUtils.isNotBlank(lb.getTechnicalName()) && !lb.getReason().equals(lb.getTechnicalName()))
.collect(Collectors.toMap(LegalBasisEntity::getReason, LegalBasisEntity::getTechnicalName, (existing, replacement) -> replacement));
if (reasonToTechnicalNameMap.isEmpty()) {
log.warn("No valid mappings to replace for dossierTemplateId: {}", dossierTemplateId);
return;
}
Optional<RuleSetEntity> optionalRuleSet = rulesPersistenceService.getRules(dossierTemplateId, RuleFileType.ENTITY);
if (optionalRuleSet.isPresent()) {
String originalRulesContent = optionalRuleSet.get().getValue();
String updatedRulesContent = replaceReasonsWithTechnicalNames(originalRulesContent, reasonToTechnicalNameMap);
if (!updatedRulesContent.equals(originalRulesContent)) {
rulesPersistenceService.setRules(updatedRulesContent, dossierTemplateId, RuleFileType.ENTITY);
log.info("Updated rule file for dossierTemplateId: {}", dossierTemplateId);
} else {
log.info("No replacements made for dossierTemplateId: {}", dossierTemplateId);
}
} else {
log.warn("No rule set found for dossierTemplateId: {}", dossierTemplateId);
}
});
}
private String replaceReasonsWithTechnicalNames(String rulesContent, Map<String, String> reasonToTechnicalNameMap) {
String rulesContentResult = rulesContent;
for (Map.Entry<String, String> entry : reasonToTechnicalNameMap.entrySet()) {
String reason = entry.getKey();
String technicalName = entry.getValue();
rulesContentResult = StringUtils.replace(rulesContentResult, quoteString(reason), quoteString(technicalName));
}
return rulesContentResult;
}
private static String quoteString(String string) {
return "\"" + string + "\"";
}
}

View File

@ -0,0 +1,128 @@
package com.iqser.red.service.persistence.management.v1.processor.migration.migrations;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.migration.LegalBasisMappingService;
import com.iqser.red.service.persistence.management.v1.processor.migration.LegalBasisMappingService.TechnicalNameResult;
import com.iqser.red.service.persistence.management.v1.processor.migration.Migration;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.imported.ImportedLegalBases;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.imported.ImportedLegalBasis;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.imported.ImportedRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.imported.ImportedRedactionsPerPage;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class V31TechnicalNameImportedFilesMigration extends Migration {
private static final String NAME = "Migration for replacing legal basis reasons with technical names in imported redactions and legal bases files";
private static final long VERSION = 31;
private final DossierRepository dossierRepository;
private final FileRepository fileRepository;
private final FileManagementStorageService fileManagementStorageService;
private final LegalBasisMappingService legalBasisMappingService;
public V31TechnicalNameImportedFilesMigration(DossierRepository dossierRepository,
FileRepository fileRepository,
FileManagementStorageService fileManagementStorageService,
LegalBasisMappingService legalBasisMappingService) {
super(NAME, VERSION);
this.dossierRepository = dossierRepository;
this.fileRepository = fileRepository;
this.fileManagementStorageService = fileManagementStorageService;
this.legalBasisMappingService = legalBasisMappingService;
}
@Override
protected void migrate() {
log.info("Migration: Updating imported redactions and legal bases files by replacing legal basis reasons with technical names");
List<DossierEntity> dossiers = dossierRepository.findAll();
for (DossierEntity dossier : dossiers) {
String dossierId = dossier.getId();
String dossierTemplateId = dossier.getDossierTemplateId();
Map<String, String> reasonToTechnicalNameMap = legalBasisMappingService.getReasonToTechnicalNameMap(dossierTemplateId);
List<FileEntity> files = fileRepository.findByDossierId(dossierId);
for (FileEntity file : files) {
String fileId = file.getId();
if (fileManagementStorageService.objectExists(dossierId, fileId, FileType.IMPORTED_REDACTIONS)) {
ImportedRedactionsPerPage importedRedactions = fileManagementStorageService.getImportedRedactions(dossierId, fileId);
boolean updated = false;
for (ImportedRedaction redaction : importedRedactions.getImportedRedactions().values().stream().flatMap(Collection::stream).toList()) {
String currentLegalBasis = redaction.getLegalBasis();
if (currentLegalBasis != null && !currentLegalBasis.isEmpty()) {
TechnicalNameResult result = legalBasisMappingService.processLegalBasis(currentLegalBasis, reasonToTechnicalNameMap);
if (result != null && !currentLegalBasis.equals(result.technicalName())) {
redaction.setLegalBasis(result.technicalName());
updated = true;
}
}
}
if (updated) {
fileManagementStorageService.storeJSONObject(dossierId, fileId, FileType.IMPORTED_REDACTIONS, importedRedactions);
log.info("Updated imported redactions for dossierId: {}, fileId: {}", dossierId, fileId);
}
}
if (fileManagementStorageService.objectExists(dossierId, fileId, FileType.IMPORTED_LEGAL_BASES)) {
ImportedLegalBases importedLegalBases = fileManagementStorageService.getImportedLegalBases(dossierId, fileId);
boolean updated = false;
List<ImportedLegalBasis> legalBases = importedLegalBases.getImportedLegalBases();
for (ImportedLegalBasis importedLegalBasis : legalBases) {
String currentReason = importedLegalBasis.getReason();
String currentTechnicalName = importedLegalBasis.getTechnicalName();
if (currentTechnicalName == null || currentTechnicalName.isEmpty()) {
if (currentReason != null && !currentReason.isEmpty()) {
TechnicalNameResult result = legalBasisMappingService.processLegalBasis(currentReason, reasonToTechnicalNameMap);
if (result != null) {
importedLegalBasis.setTechnicalName(result.technicalName());
updated = true;
}
}
}
}
if (updated) {
fileManagementStorageService.storeJSONObject(dossierId, fileId, FileType.IMPORTED_LEGAL_BASES, importedLegalBases);
log.info("Updated imported legal bases for dossierId: {}, fileId: {}", dossierId, fileId);
}
}
}
}
log.info("Migration completed.");
}
}

View File

@ -51,7 +51,7 @@ public class ApprovalVerificationService {
addWarning(entry, WarningType.LEGAL_BASIS_MISSING, approveResponse);
} else {
var legalBasisEntity = legalBasisMappings.stream()
.filter(mapping -> mapping.getReason().equals(entry.getLegalBasis()))
.filter(mapping -> mapping.getTechnicalName().equals(entry.getLegalBasis()))
.findFirst();
if (legalBasisEntity.isEmpty() || StringUtils.isEmpty(legalBasisEntity.get().getTechnicalName())) {
addWarning(entry, WarningType.UNMAPPED_JUSTIFICATION, approveResponse);

View File

@ -239,10 +239,11 @@ public class DictionaryManagementService {
@Transactional
public void addEntries(String typeId, List<String> entries, boolean removeCurrent, boolean ignoreInvalidEntries, DictionaryEntryType dictionaryEntryType) {
addEntries(typeId, entries, removeCurrent, ignoreInvalidEntries, dictionaryEntryType, false);
}
@Transactional
public void addEntries(String typeId, List<String> entries, boolean removeCurrent, boolean ignoreInvalidEntries, DictionaryEntryType dictionaryEntryType, boolean isImport) {
@ -293,7 +294,10 @@ public class DictionaryManagementService {
// check for the existence of dossier type and create in case it does not exist
if (isDossierTypeId(typeId)) {
try {
dictionaryPersistenceService.getType(typeId);
TypeEntity type = dictionaryPersistenceService.getType(typeId, true);
if (type.isDeleted()) {
dictionaryPersistenceService.undeleteType(typeId);
}
} catch (NotFoundException e) {
// type not found check first dossier is matching the specified dossier template
var dossierId = getDossierIdFromTypeId(typeId);
@ -397,9 +401,13 @@ public class DictionaryManagementService {
dictionaryPersistenceService.deleteType(typeId);
entryPersistenceService.deleteAllEntriesForTypeId(typeId, currentVersion + 1, DictionaryEntryType.ENTRY);
entryPersistenceService.deleteAllEntriesForTypeId(typeId, currentVersion + 1, DictionaryEntryType.FALSE_POSITIVE);
entryPersistenceService.deleteAllEntriesForTypeId(typeId, currentVersion + 1, DictionaryEntryType.FALSE_RECOMMENDATION);
if (isDossierTypeId(typeId)) {
entryPersistenceService.hardDeleteAllEntriesForTypeId(typeId);
} else {
entryPersistenceService.deleteAllEntriesForTypeId(typeId, currentVersion + 1, DictionaryEntryType.ENTRY);
entryPersistenceService.deleteAllEntriesForTypeId(typeId, currentVersion + 1, DictionaryEntryType.FALSE_POSITIVE);
entryPersistenceService.deleteAllEntriesForTypeId(typeId, currentVersion + 1, DictionaryEntryType.FALSE_RECOMMENDATION);
}
dictionaryPersistenceService.incrementVersion(typeId);
}

View File

@ -268,7 +268,7 @@ public class DictionaryService {
@PreAuthorize("hasAuthority('" + DELETE_DOSSIER_DICTIONARY_TYPE + "')")
public void deleteDossierType(String type, String dossierTemplateId, String dossierId) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyUserIsMemberOrApprover(dossierId);
deleteType(toTypeId(type, dossierTemplateId, dossierId));
}

View File

@ -13,17 +13,17 @@ import java.util.List;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.management.v1.processor.exception.InternalServerErrorException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.utils.StorageIdUtils;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
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.imported.ImportedLegalBases;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.imported.ImportedRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.imported.ImportedRedactionsPerPage;
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.model.redactionlog.imported.ImportedRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.section.SectionGrid;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.service.ComponentLogMongoService;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.service.EntityLogMongoService;
import com.iqser.red.storage.commons.exception.StorageException;
import com.iqser.red.storage.commons.exception.StorageObjectDoesNotExist;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructureWrapper;
@ -170,6 +170,7 @@ public class FileManagementStorageService {
return entityLogMongoService.entityLogDocumentExists(dossierId, fileId);
}
public boolean componentLogExists(String dossierId, String fileId) {
return componentLogMongoService.componentLogDocumentExists(dossierId, fileId);
@ -189,12 +190,12 @@ public class FileManagementStorageService {
}
public ImportedRedactions getImportedRedactions(String dossierId, String fileId) {
public ImportedRedactionsPerPage getImportedRedactions(String dossierId, String fileId) {
try {
return storageService.readJSONObject(TenantContext.getTenantId(),
StorageIdUtils.getStorageId(dossierId, fileId, FileType.IMPORTED_REDACTIONS),
ImportedRedactions.class);
ImportedRedactionsPerPage.class);
} catch (StorageObjectDoesNotExist e) {
throw new NotFoundException("ImportedRedactions does not exist");
} catch (Exception e) {
@ -202,11 +203,41 @@ public class FileManagementStorageService {
}
}
public ImportedLegalBases getImportedLegalBases(String dossierId, String fileId) {
try {
return storageService.readJSONObject(TenantContext.getTenantId(),
StorageIdUtils.getStorageId(dossierId, fileId, FileType.IMPORTED_LEGAL_BASES),
ImportedLegalBases.class);
} catch (StorageObjectDoesNotExist e) {
throw new NotFoundException("ImportedLegalBases does not exist");
} catch (Exception e) {
throw new RuntimeException("Could not convert ImportedLegalBases", e);
}
}
public com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.imported.ImportedRedactions getOldImportedRedactions(String dossierId, String fileId) {
try {
return storageService.readJSONObject(TenantContext.getTenantId(),
StorageIdUtils.getStorageId(dossierId, fileId, FileType.IMPORTED_REDACTIONS),
com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.imported.ImportedRedactions.class);
} catch (StorageObjectDoesNotExist e) {
throw new NotFoundException("ImportedRedactions does not exist");
} catch (Exception e) {
throw new RuntimeException("Could not convert ImportedRedactions", e);
}
}
public boolean objectExists(String dossierId, String fileId, String fileName, String fileExtension) {
return storageService.objectExists(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, fileName, fileExtension));
}
public boolean objectExists(String dossierId, String fileId, FileType origin) {
return storageService.objectExists(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, origin));
@ -224,11 +255,13 @@ public class FileManagementStorageService {
storageService.deleteObject(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, fileType));
}
public void deleteObject(String dossierId, String fileId, String fileName, String fileExtension) {
storageService.deleteObject(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, fileName, fileExtension));
}
public void deleteDocumentAndNerObjects(String dossierId, String fileId) {
deleteObject(dossierId, fileId, FileType.DOCUMENT_STRUCTURE);
@ -249,6 +282,7 @@ public class FileManagementStorageService {
}
public void deleteAllObjects(String dossierId, String fileId) {
deleteObject(dossierId, fileId, FileType.VIEWER_DOCUMENT);
@ -270,6 +304,7 @@ public class FileManagementStorageService {
entityLogMongoService.deleteEntityLog(dossierId, fileId);
}
public void deleteComponentLog(String dossierId, String fileId) {
componentLogMongoService.deleteComponentLog(dossierId, fileId);

View File

@ -264,7 +264,7 @@ public class FileStatusService {
var fileEntity = fileStatusPersistenceService.getStatus(fileId);
if (!fileManagementStorageService.objectExists(dossierId, fileId, FileType.ORIGIN)) {
addToPreprocessingQueue(dossierId, fileId, fileEntity.getFilename());
addToPreprocessingQueue(dossier.getDossierTemplateId(), dossierId, fileId, fileEntity.getFilename());
sendReadOnlyAnalysisEvent(dossierId, fileId, fileEntity);
return;
}
@ -487,9 +487,10 @@ public class FileStatusService {
@SneakyThrows
public void addToPreprocessingQueue(String dossierId, String fileId, String filename) {
public void addToPreprocessingQueue(String dossierTemplateId, String dossierId, String fileId, String filename) {
var processUntouchedDocumentRequest = ProcessUntouchedDocumentRequest.builder()
.dossierTemplateId(dossierTemplateId)
.highlightExtractionEnabled(currentApplicationTypeProvider.isRedactManager())
.dossierId(dossierId)
.fileId(fileId)

View File

@ -10,6 +10,7 @@ import com.iqser.red.service.persistence.management.v1.processor.exception.Confl
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ReanalysisSettings;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.imported.ImportedRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.DeleteImportedRedactionsRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
@ -109,7 +110,11 @@ public class ReanalysisService {
private void analyseFiles(boolean repeatStructureAnalysis, List<FileModel> filesToReanalyse, boolean runOcr) {
filesToReanalyse.forEach(file -> fileStatusService.setStatusFullReprocess(file.getDossierId(), file.getId(), filesToReanalyse.size() == 1, repeatStructureAnalysis, runOcr));
filesToReanalyse.forEach(file -> fileStatusService.setStatusFullReprocess(file.getDossierId(),
file.getId(),
filesToReanalyse.size() == 1,
repeatStructureAnalysis,
runOcr));
}
@ -281,7 +286,11 @@ public class ReanalysisService {
List<FileModel> rejectedFiles = filterInvalidFiles(files);
files.forEach(file -> fileStatusService.setStatusFullReprocess(file.getDossierId(), file.getId(), false, reanalysisSettings.repeatStructureAnalysis(), reanalysisSettings.runOcr()));
files.forEach(file -> fileStatusService.setStatusFullReprocess(file.getDossierId(),
file.getId(),
false,
reanalysisSettings.repeatStructureAnalysis(),
reanalysisSettings.runOcr()));
return rejectedFiles;
}

View File

@ -184,6 +184,13 @@ public class EntryPersistenceService {
}
@Transactional
public void hardDeleteAllEntriesForTypeId(String typeId) {
entryRepository.hardDeleteAllEntriesForTypeId(typeId);
}
public void cloneEntries(String originalTypeId, String newTypeId) {
entryRepository.cloneEntries(originalTypeId, newTypeId);

View File

@ -86,7 +86,7 @@ public interface TypeRepository extends JpaRepository<TypeEntity, String> {
void softDeleteTypeById(@Param("typeId") String typeId);
@Modifying
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("Update TypeEntity t set t.softDeletedTime = null where t.id = :typeId and t.softDeletedTime is not null")
int unSoftDeleteTypeById(@Param("typeId") String typeId);

View File

@ -29,6 +29,11 @@ public interface EntryRepository extends EntryRepositoryCustom, JpaRepository<Di
void deleteAllEntriesForTypeId(@Param("typeId") String typeId, @Param("version") long version);
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("delete from DictionaryEntryEntity e where e.typeId = :typeId")
void hardDeleteAllEntriesForTypeId(@Param("typeId") String typeId);
@Modifying(flushAutomatically = true, clearAutomatically = true)
@Transactional
@Query(value = "insert into dictionary_entry (value, version, deleted, type_id) "

View File

@ -247,3 +247,5 @@ databaseChangeLog:
file: db/changelog/tenant/151-add-component-mapping-indexes.yaml
- include:
file: db/changelog/tenant/152-add-ai-fields-to-entity.yaml
- include:
file: db/changelog/tenant/153-custom-technical-name-change.yaml

View File

@ -0,0 +1,30 @@
databaseChangeLog:
- changeSet:
id: technical-name-change-constraint
author: maverick
changes:
- addUniqueConstraint:
constraintName: uq_technical_name
tableName: legal_basis_mapping_entity_legal_basis
columnNames: technical_name, legal_basis_mapping_entity_dossier_template_id
- changeSet:
id: technical-name-change-index
author: maverick
preConditions:
- not:
indexExists:
indexName: idx_legal_basis_mapping_entity_dossier_template_id
tableName: legal_basis_mapping_entity_legal_basis
changes:
- createIndex:
indexName: idx_legal_basis_mapping_entity_dossier_template_id
tableName: legal_basis_mapping_entity_legal_basis
columns:
- column:
name: legal_basis_mapping_entity_dossier_template_id
- changeSet:
id: custom-technical-name-change
author: maverick
changes:
- customChange:
class: com.iqser.red.service.persistence.management.v1.processor.migration.liquibase.TechnicalNameChange

View File

@ -0,0 +1,373 @@
package com.iqser.red.service.persistence.management.v1.processor.migration;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import com.iqser.red.service.persistence.management.v1.processor.migration.liquibase.TechnicalNameChange;
import liquibase.database.Database;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.CustomChangeException;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
public class TechnicalNameChangeTest {
@Mock
private Database mockDatabase;
@Mock
private JdbcConnection mockJdbcConnection;
@Mock
private Connection mockConnection;
@InjectMocks
private TechnicalNameChange technicalNameChange;
@Mock
private PreparedStatement mockSelectFileIdsPs;
@Mock
private PreparedStatement mockDossierPs;
@Mock
private PreparedStatement mockReasonPs;
@Mock
private ResultSet mockSelectFileIdsRs;
@Mock
private ResultSet mockDossierRs;
@Mock
private ResultSet mockReasonRs;
@Mock
private PreparedStatement mockSelectUpdatePs1;
@Mock
private PreparedStatement mockSelectUpdatePs2;
@Mock
private PreparedStatement mockSelectUpdatePs3;
@Mock
private PreparedStatement mockSelectUpdatePs4;
@Mock
private ResultSet mockSelectUpdateRs1;
@Mock
private ResultSet mockSelectUpdateRs2;
@Mock
private ResultSet mockSelectUpdateRs3;
@Mock
private ResultSet mockSelectUpdateRs4;
@Mock
private PreparedStatement mockUpdatePs;
@BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
when(mockDatabase.getConnection()).thenReturn(mockJdbcConnection);
when(mockJdbcConnection.getUnderlyingConnection()).thenReturn(mockConnection);
TechnicalNameChange.setSkipExecution(false);
}
@Test
public void testExecute_successfulUpdate() throws Exception {
String[] tables = {"manual_legal_basis_change", "manual_force_redaction", "manual_recategorization", "manual_redaction"};
try (PreparedStatement ps = mockSelectFileIdsPs) {
when(mockConnection.prepareStatement(startsWith("SELECT DISTINCT file_id FROM"))).thenReturn(ps);
when(ps.executeQuery()).thenReturn(mockSelectFileIdsRs);
when(mockSelectFileIdsRs.next()).thenReturn(true, true, false);
when(mockSelectFileIdsRs.getString("file_id")).thenReturn("file1", "file2");
}
try (PreparedStatement ps = mockDossierPs) {
when(mockConnection.prepareStatement(startsWith("SELECT f.id AS file_id, d.dossier_template_id"))).thenReturn(ps);
when(ps.executeQuery()).thenReturn(mockDossierRs);
when(mockDossierRs.next()).thenReturn(true, true, false);
when(mockDossierRs.getString("file_id")).thenReturn("file1", "file2");
when(mockDossierRs.getString("dossier_template_id")).thenReturn("template1", "template2");
}
try (PreparedStatement ps = mockReasonPs) {
when(mockConnection.prepareStatement(startsWith("SELECT reason, legal_basis_mapping_entity_dossier_template_id, technical_name"))).thenReturn(ps);
when(ps.executeQuery()).thenReturn(mockReasonRs);
when(mockReasonRs.next()).thenReturn(true, true, false);
when(mockReasonRs.getString("reason")).thenReturn("reason1", "reason2");
when(mockReasonRs.getString("legal_basis_mapping_entity_dossier_template_id")).thenReturn("template1", "template2");
when(mockReasonRs.getString("technical_name")).thenReturn("techName1", (String) null);
}
setupTableMocks(mockSelectUpdatePs1, mockSelectUpdateRs1, "manual_legal_basis_change", "ann1", "ann2", "file1", "file2", "reason1", "reason2");
setupTableMocks(mockSelectUpdatePs2, mockSelectUpdateRs2, "manual_force_redaction", "ann3", "ann4", "file3", "file4", "reason3", "reason4");
setupTableMocks(mockSelectUpdatePs3, mockSelectUpdateRs3, "manual_recategorization", "ann5", "ann6", "file5", "file6", "reason5", "reason6");
setupTableMocks(mockSelectUpdatePs4, mockSelectUpdateRs4, "manual_redaction", "ann7", "ann8", "file7", "file8", "reason7", "reason8");
try (PreparedStatement ps = mockUpdatePs) {
when(mockConnection.prepareStatement(startsWith("UPDATE "))).thenReturn(ps);
when(ps.executeUpdate()).thenReturn(1);
}
technicalNameChange.execute(mockDatabase);
verify(mockConnection, times(4)).prepareStatement(startsWith("SELECT DISTINCT file_id FROM"));
verify(mockConnection, times(1)).prepareStatement(startsWith("SELECT f.id AS file_id, d.dossier_template_id"));
verify(mockConnection, times(1)).prepareStatement(startsWith("SELECT reason, legal_basis_mapping_entity_dossier_template_id, technical_name"));
verify(mockConnection, times(tables.length)).prepareStatement(startsWith("SELECT annotation_id, file_id, legal_basis FROM"));
verify(mockConnection, times(tables.length)).prepareStatement(startsWith("UPDATE "));
verify(mockUpdatePs, times(tables.length * 2)).executeUpdate();
ArgumentCaptor<String> updatedValueCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> annotationIdCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> fileIdCaptor = ArgumentCaptor.forClass(String.class);
verify(mockUpdatePs, times(tables.length * 2)).setString(eq(1), updatedValueCaptor.capture());
verify(mockUpdatePs, times(tables.length * 2)).setString(eq(2), annotationIdCaptor.capture());
verify(mockUpdatePs, times(tables.length * 2)).setString(eq(3), fileIdCaptor.capture());
List<String> updatedValues = updatedValueCaptor.getAllValues();
List<String> annotationIds = annotationIdCaptor.getAllValues();
HashFunction hashFunction = Hashing.murmur3_128();
Map<String, String> expectedUpdates = createExpectedUpdates(hashFunction);
for (int i = 0; i < annotationIds.size(); i++) {
String annotationId = annotationIds.get(i);
String expectedValue = expectedUpdates.get(annotationId);
assertEquals(expectedValue, updatedValues.get(i), "Mismatch for annotation_id: " + annotationId);
}
}
@Test
public void testExecute_withException() {
when(mockDatabase.getConnection()).thenThrow(new RuntimeException("Connection error"));
CustomChangeException exception = assertThrows(CustomChangeException.class, () -> {
technicalNameChange.execute(mockDatabase);
});
assertTrue(exception.getMessage().contains("Error applying technical name change"));
assertEquals("Connection error", exception.getCause().getMessage());
}
@Test
public void testExecute_noFileIds() throws Exception {
try (PreparedStatement ps = mockSelectFileIdsPs) {
when(mockConnection.prepareStatement(startsWith("SELECT DISTINCT file_id FROM"))).thenReturn(ps);
when(ps.executeQuery()).thenReturn(mockSelectFileIdsRs);
when(mockSelectFileIdsRs.next()).thenReturn(false);
}
technicalNameChange.execute(mockDatabase);
verify(mockConnection, never()).prepareStatement(startsWith("SELECT f.id AS file_id, d.dossier_template_id"));
verify(mockConnection, never()).prepareStatement(startsWith("SELECT reason, legal_basis_mapping_entity_dossier_template_id, technical_name"));
verify(mockConnection, never()).prepareStatement(startsWith("SELECT annotation_id, file_id, legal_basis FROM"));
verify(mockConnection, never()).prepareStatement(startsWith("UPDATE "));
verify(mockUpdatePs, never()).executeUpdate();
}
@Test
public void testExecute_noDossierMappings() throws Exception {
String[] tables = {"manual_legal_basis_change", "manual_force_redaction", "manual_recategorization", "manual_redaction"};
try (PreparedStatement ps = mockSelectFileIdsPs) {
when(mockConnection.prepareStatement(startsWith("SELECT DISTINCT file_id FROM"))).thenReturn(ps);
when(ps.executeQuery()).thenReturn(mockSelectFileIdsRs);
when(mockSelectFileIdsRs.next()).thenReturn(true, false);
when(mockSelectFileIdsRs.getString("file_id")).thenReturn("file1");
}
try (PreparedStatement ps = mockDossierPs) {
when(mockConnection.prepareStatement(startsWith("SELECT f.id AS file_id, d.dossier_template_id"))).thenReturn(ps);
when(ps.executeQuery()).thenReturn(mockDossierRs);
when(mockDossierRs.next()).thenReturn(false);
}
try (PreparedStatement ps = mockReasonPs) {
when(mockConnection.prepareStatement(startsWith("SELECT reason, legal_basis_mapping_entity_dossier_template_id, technical_name"))).thenReturn(ps);
when(ps.executeQuery()).thenReturn(mockReasonRs);
when(mockReasonRs.next()).thenReturn(false);
}
for (String table : tables) {
try (PreparedStatement mockSelectUpdatePs = mock(PreparedStatement.class); ResultSet mockSelectUpdateRs = mock(ResultSet.class); PreparedStatement mockUpdate = mock(
PreparedStatement.class)) {
when(mockConnection.prepareStatement(startsWith("SELECT annotation_id, file_id, legal_basis FROM " + table))).thenReturn(mockSelectUpdatePs);
when(mockSelectUpdatePs.executeQuery()).thenReturn(mockSelectUpdateRs);
when(mockSelectUpdateRs.next()).thenReturn(true, false);
when(mockSelectUpdateRs.getString("annotation_id")).thenReturn("ann1");
when(mockSelectUpdateRs.getString("file_id")).thenReturn("file1");
when(mockSelectUpdateRs.getString("legal_basis")).thenReturn("reason1");
when(mockConnection.prepareStatement(startsWith("UPDATE " + table))).thenReturn(mockUpdate);
when(mockUpdate.executeUpdate()).thenReturn(1);
}
}
technicalNameChange.execute(mockDatabase);
verify(mockUpdatePs, never()).executeUpdate();
}
@Test
public void testExecute_allTechnicalNamesPresent() throws Exception {
String[] tables = {"manual_legal_basis_change", "manual_force_redaction", "manual_recategorization", "manual_redaction"};
try (PreparedStatement ps = mockSelectFileIdsPs) {
when(mockConnection.prepareStatement(startsWith("SELECT DISTINCT file_id FROM"))).thenReturn(ps);
when(ps.executeQuery()).thenReturn(mockSelectFileIdsRs);
when(mockSelectFileIdsRs.next()).thenReturn(true, false);
when(mockSelectFileIdsRs.getString("file_id")).thenReturn("file1");
}
try (PreparedStatement ps = mockDossierPs) {
when(mockConnection.prepareStatement(startsWith("SELECT f.id AS file_id, d.dossier_template_id"))).thenReturn(ps);
when(ps.executeQuery()).thenReturn(mockDossierRs);
when(mockDossierRs.next()).thenReturn(true, false);
when(mockDossierRs.getString("file_id")).thenReturn("file1");
when(mockDossierRs.getString("dossier_template_id")).thenReturn("template1");
}
try (PreparedStatement ps = mockReasonPs) {
when(mockConnection.prepareStatement(startsWith("SELECT reason, legal_basis_mapping_entity_dossier_template_id, technical_name"))).thenReturn(ps);
when(ps.executeQuery()).thenReturn(mockReasonRs);
when(mockReasonRs.next()).thenReturn(true, false);
when(mockReasonRs.getString("reason")).thenReturn("reason1");
when(mockReasonRs.getString("legal_basis_mapping_entity_dossier_template_id")).thenReturn("template1");
when(mockReasonRs.getString("technical_name")).thenReturn("techName1");
}
for (String table : tables) {
try (PreparedStatement mockSelectUpdatePs = mock(PreparedStatement.class); ResultSet mockSelectUpdateRs = mock(ResultSet.class); PreparedStatement mockUpdate = mock(
PreparedStatement.class)) {
when(mockConnection.prepareStatement(startsWith("SELECT annotation_id, file_id, legal_basis FROM " + table))).thenReturn(mockSelectUpdatePs);
when(mockSelectUpdatePs.executeQuery()).thenReturn(mockSelectUpdateRs);
when(mockSelectUpdateRs.next()).thenReturn(true, false);
when(mockSelectUpdateRs.getString("annotation_id")).thenReturn("ann1");
when(mockSelectUpdateRs.getString("file_id")).thenReturn("file1");
when(mockSelectUpdateRs.getString("legal_basis")).thenReturn("reason1");
when(mockConnection.prepareStatement(startsWith("UPDATE " + table))).thenReturn(mockUpdate);
when(mockUpdate.executeUpdate()).thenReturn(1);
}
}
technicalNameChange.execute(mockDatabase);
verify(mockUpdatePs, never()).executeUpdate();
}
@Test
public void testExecute_sqlExceptionDuringUpdate() throws Exception {
try (PreparedStatement ps = mockSelectFileIdsPs) {
when(mockConnection.prepareStatement(startsWith("SELECT DISTINCT file_id FROM"))).thenReturn(ps);
when(ps.executeQuery()).thenReturn(mockSelectFileIdsRs);
when(mockSelectFileIdsRs.next()).thenReturn(true, false);
when(mockSelectFileIdsRs.getString("file_id")).thenReturn("file1");
}
try (PreparedStatement ps = mockDossierPs) {
when(mockConnection.prepareStatement(startsWith("SELECT f.id AS file_id, d.dossier_template_id"))).thenReturn(ps);
when(ps.executeQuery()).thenReturn(mockDossierRs);
when(mockDossierRs.next()).thenReturn(true, false);
when(mockDossierRs.getString("file_id")).thenReturn("file1");
when(mockDossierRs.getString("dossier_template_id")).thenReturn("template1");
}
try (PreparedStatement ps = mockReasonPs) {
when(mockConnection.prepareStatement(startsWith("SELECT reason, legal_basis_mapping_entity_dossier_template_id, technical_name"))).thenReturn(ps);
when(ps.executeQuery()).thenReturn(mockReasonRs);
when(mockReasonRs.next()).thenReturn(true, false);
when(mockReasonRs.getString("reason")).thenReturn("reason1");
when(mockReasonRs.getString("legal_basis_mapping_entity_dossier_template_id")).thenReturn("template1");
when(mockReasonRs.getString("technical_name")).thenReturn("techName1");
}
try (PreparedStatement mockSelectUpdatePs = mock(PreparedStatement.class); ResultSet mockSelectUpdateRs = mock(ResultSet.class)) {
when(mockConnection.prepareStatement(startsWith("SELECT annotation_id, file_id, legal_basis FROM manual_legal_basis_change"))).thenReturn(mockSelectUpdatePs);
when(mockSelectUpdatePs.executeQuery()).thenReturn(mockSelectUpdateRs);
when(mockSelectUpdateRs.next()).thenReturn(true, false);
when(mockSelectUpdateRs.getString("annotation_id")).thenReturn("ann1");
when(mockSelectUpdateRs.getString("file_id")).thenReturn("file1");
when(mockSelectUpdateRs.getString("legal_basis")).thenReturn("reason1");
}
try (PreparedStatement mockUpdate = mock(PreparedStatement.class)) {
when(mockConnection.prepareStatement(startsWith("UPDATE manual_legal_basis_change"))).thenReturn(mockUpdate);
when(mockUpdate.executeUpdate()).thenThrow(new SQLException("Update failed"));
}
CustomChangeException exception = assertThrows(CustomChangeException.class, () -> {
technicalNameChange.execute(mockDatabase);
});
assertTrue(exception.getMessage().contains("Error applying technical name change"));
assertInstanceOf(SQLException.class, exception.getCause());
assertEquals("Update failed", exception.getCause().getMessage());
}
private void setupTableMocks(PreparedStatement ps,
ResultSet rs,
String tableName,
String annotationId1,
String annotationId2,
String fileId1,
String fileId2,
String legalBasis1,
String legalBasis2) throws SQLException {
when(mockConnection.prepareStatement(startsWith("SELECT annotation_id, file_id, legal_basis FROM " + tableName))).thenReturn(ps);
when(ps.executeQuery()).thenReturn(rs);
when(rs.next()).thenReturn(true, true, false);
when(rs.getString("annotation_id")).thenReturn(annotationId1, annotationId2);
when(rs.getString("file_id")).thenReturn(fileId1, fileId2);
when(rs.getString("legal_basis")).thenReturn(legalBasis1, legalBasis2);
}
private Map<String, String> createExpectedUpdates(HashFunction hashFunction) {
Map<String, String> expectedUpdates = new HashMap<>();
expectedUpdates.put("ann1", "techName1");
expectedUpdates.put("ann2", hashFunction.hashString("reason2", StandardCharsets.UTF_8).toString());
expectedUpdates.put("ann3", hashFunction.hashString("reason3", StandardCharsets.UTF_8).toString());
expectedUpdates.put("ann4", hashFunction.hashString("reason4", StandardCharsets.UTF_8).toString());
expectedUpdates.put("ann5", hashFunction.hashString("reason5", StandardCharsets.UTF_8).toString());
expectedUpdates.put("ann6", hashFunction.hashString("reason6", StandardCharsets.UTF_8).toString());
expectedUpdates.put("ann7", hashFunction.hashString("reason7", StandardCharsets.UTF_8).toString());
expectedUpdates.put("ann8", hashFunction.hashString("reason8", StandardCharsets.UTF_8).toString());
return expectedUpdates;
}
}

View File

@ -0,0 +1,636 @@
package com.iqser.red.service.persistence.management.v1.processor.migration;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
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.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.LegalBasisEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.migration.migrations.V29TechnicalNameEntityLogMigration;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.LegalBasisMappingPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.MigrationPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileRepository;
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.EntityLogLegalBasis;
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.mongo.service.EntityLogMongoService;
public class TechnicalNameEntityLogMigrationTest {
@Mock
private DossierRepository dossierRepository;
@Mock
private FileRepository fileRepository;
@Mock
private EntityLogMongoService entityLogMongoService;
@Mock
private LegalBasisMappingPersistenceService legalBasisMappingPersistenceService;
@Mock
private MigrationPersistenceService migrationPersistenceService;
private V29TechnicalNameEntityLogMigration migration;
private final HashFunction hashFunction = Hashing.murmur3_128();
@BeforeEach
public void setup() {
MockitoAnnotations.openMocks(this);
when(migrationPersistenceService.getLatestProcessedVersion()).thenReturn(0L);
when(migrationPersistenceService.isProcessed(anyLong(), anyLong())).thenReturn(false);
LegalBasisMappingService legalBasisMappingService = new LegalBasisMappingService(legalBasisMappingPersistenceService);
migration = new V29TechnicalNameEntityLogMigration(dossierRepository, fileRepository, entityLogMongoService, legalBasisMappingService);
migration.setMigrationPersistenceService(migrationPersistenceService);
}
@Test
public void testMigrationUpdatesMappedLegalBasisTechnicalNames() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(List.of(dossier));
LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_TechName").reason("LB1_Reason").build();
LegalBasisEntity lb2 = LegalBasisEntity.builder().technicalName("LB2_TechName").reason("LB2_Reason").build();
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Arrays.asList(lb1, lb2));
FileEntity file = createFile("file1", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
EntityLogEntry entry1 = EntityLogEntry.builder().legalBasis("LB1_Reason").build();
EntityLogEntry entry2 = EntityLogEntry.builder().legalBasis("LB2_Reason").build();
EntityLog entityLog = createEntityLog(Arrays.asList(entry1, entry2));
entityLog.setLegalBasis(List.of(EntityLogLegalBasis.builder().technicalName("LB1_TechName").reason("LB1_Reason").build(),
EntityLogLegalBasis.builder().technicalName("LB2_TechName").reason("LB2_Reason").build()));
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));
migration.run();
assertAll(() -> assertEquals("LB1_TechName", entry1.getLegalBasis()), () -> assertEquals("LB2_TechName", entry2.getLegalBasis()));
verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
}
@Test
public void testMigrationHashesUnmappedLegalBasis() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(List.of(dossier));
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.emptyList());
FileEntity file = createFile("file1", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
EntityLogEntry entry1 = EntityLogEntry.builder().legalBasis("Unmapped_LB1").build();
EntityLogEntry entry2 = EntityLogEntry.builder().legalBasis("Unmapped_LB2").build();
EntityLog entityLog = createEntityLog(Arrays.asList(entry1, entry2));
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));
migration.run();
String hashedLB1 = hashFunction.hashString("Unmapped_LB1", StandardCharsets.UTF_8).toString();
String hashedLB2 = hashFunction.hashString("Unmapped_LB2", StandardCharsets.UTF_8).toString();
assertAll(() -> assertEquals(hashedLB1, entry1.getLegalBasis()), () -> assertEquals(hashedLB2, entry2.getLegalBasis()));
verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
}
@Test
public void testMigrationSkipsEntriesWithoutLegalBasis() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(List.of(dossier));
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.emptyList());
FileEntity file = createFile("file1", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
EntityLogEntry entry1 = EntityLogEntry.builder().legalBasis(null).build();
EntityLogEntry entry2 = EntityLogEntry.builder().legalBasis("").build();
EntityLog entityLog = createEntityLog(Arrays.asList(entry1, entry2));
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));
migration.run();
assertAll(() -> assertNull(entry1.getLegalBasis()), () -> assertEquals("", entry2.getLegalBasis()));
verify(entityLogMongoService, never()).saveEntityLog(anyString(), anyString(), any(EntityLog.class));
}
@Test
public void testMigrationSkipsFilesWithoutEntityLog() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(List.of(dossier));
FileEntity file = createFile("file1", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.empty());
migration.run();
verify(entityLogMongoService, never()).saveEntityLog(anyString(), anyString(), any(EntityLog.class));
}
@Test
public void testMigrationWithNoDossiers() {
when(dossierRepository.findAll()).thenReturn(Collections.emptyList());
migration.run();
verify(fileRepository, never()).findByDossierId(anyString());
verify(entityLogMongoService, never()).findEntityLogByDossierIdAndFileId(anyString(), anyString());
}
@Test
public void testMigrationWithMixedEntries() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(List.of(dossier));
LegalBasisEntity mappedLB = LegalBasisEntity.builder().technicalName("Mapped_TechName").reason("Mapped_Reason").build();
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(List.of(mappedLB));
FileEntity file = createFile("file1", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
EntityLogEntry mappedEntry = EntityLogEntry.builder().legalBasis("Mapped_Reason").build();
EntityLogEntry unmappedEntry = EntityLogEntry.builder().legalBasis("Unmapped_Reason").build();
EntityLogEntry nullEntry = EntityLogEntry.builder().legalBasis(null).build();
EntityLog entityLog = createEntityLog(Arrays.asList(mappedEntry, unmappedEntry, nullEntry));
entityLog.setLegalBasis(new ArrayList<>());
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));
migration.run();
String hashedUnmapped = hashFunction.hashString("Unmapped_Reason", StandardCharsets.UTF_8).toString();
assertAll(() -> assertEquals("Mapped_TechName", mappedEntry.getLegalBasis()),
() -> assertEquals(hashedUnmapped, unmappedEntry.getLegalBasis()),
() -> assertNull(nullEntry.getLegalBasis()));
assertEquals(2, entityLog.getLegalBasis().size());
assertTrue(entityLog.getLegalBasis()
.stream()
.anyMatch(lb -> lb.getTechnicalName().equals("Mapped_TechName") && lb.getReason().equals("Mapped_Reason")));
assertTrue(entityLog.getLegalBasis()
.stream()
.anyMatch(lb -> lb.getTechnicalName().equals(hashedUnmapped) && lb.getReason().equals("Unmapped_Reason")));
verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
}
@Test
public void testMigrationDoesNotUpdateWhenNoChanges() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(List.of(dossier));
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.singletonList(LegalBasisEntity.builder()
.technicalName("LB1_Tech")
.reason("LB1_Reason")
.build()));
FileEntity file = createFile("file1", "dossier1");
EntityLogEntry entry = EntityLogEntry.builder().legalBasis("LB1_Tech").build();
EntityLog entityLog = createEntityLog(List.of(entry));
entityLog.setLegalBasis(new ArrayList<>(List.of(EntityLogLegalBasis.builder().technicalName("LB1_Tech").reason("LB1_Reason").build())));
when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));
migration.run();
verify(entityLogMongoService, never()).saveEntityLog(anyString(), anyString(), any(EntityLog.class));
}
@Test
public void testMigrationProcessesMultipleDossiers() {
DossierEntity dossier1 = createDossier("dossier1", "template1");
DossierEntity dossier2 = createDossier("dossier2", "template2");
when(dossierRepository.findAll()).thenReturn(List.of(dossier1, dossier2));
LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_Tech").reason("LB1_Reason").build();
LegalBasisEntity lb2 = LegalBasisEntity.builder().technicalName("LB2_Tech").reason("LB2_Reason").build();
LegalBasisEntity lb3 = LegalBasisEntity.builder().technicalName("LB3_Tech").reason("LB3_Reason").build();
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(List.of(lb1, lb2));
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template2")).thenReturn(List.of(lb3));
FileEntity file1 = createFile("file1", "dossier1");
FileEntity file2 = createFile("file2", "dossier2");
when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file1));
when(fileRepository.findByDossierId("dossier2")).thenReturn(List.of(file2));
EntityLogEntry entry1 = EntityLogEntry.builder().legalBasis("LB1_Reason").build();
EntityLog entityLog1 = createEntityLog(List.of(entry1));
entityLog1.setLegalBasis(List.of(EntityLogLegalBasis.builder().technicalName("LB1_Tech").reason("LB1_Reason").build(),
EntityLogLegalBasis.builder().technicalName("LB2_Tech").reason("LB2_Reason").build()));
EntityLogEntry entry2 = EntityLogEntry.builder().legalBasis("LB3_Reason").build();
EntityLog entityLog2 = createEntityLog(List.of(entry2));
entityLog2.setLegalBasis(List.of(EntityLogLegalBasis.builder().technicalName("LB3_Tech").reason("LB3_Reason").build()));
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog1));
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier2", "file2")).thenReturn(Optional.of(entityLog2));
migration.run();
assertAll(() -> assertEquals("LB1_Tech", entry1.getLegalBasis()), () -> assertEquals("LB3_Tech", entry2.getLegalBasis()));
verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog1);
verify(entityLogMongoService).saveEntityLog("dossier2", "file2", entityLog2);
}
@Test
public void testMigrationProcessesMultipleFilesPerDossier() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(List.of(dossier));
LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_Tech").reason("LB1_Reason").build();
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(List.of(lb1));
FileEntity file1 = createFile("file1", "dossier1");
FileEntity file2 = createFile("file2", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file1, file2));
EntityLogEntry entry1 = EntityLogEntry.builder().legalBasis("LB1_Reason").build();
EntityLog entityLog1 = createEntityLog(List.of(entry1));
entityLog1.setLegalBasis(List.of(EntityLogLegalBasis.builder().technicalName("LB1_Tech").reason("LB1_Reason").build()));
EntityLogEntry entry2 = EntityLogEntry.builder().legalBasis("Unmapped_LB").build();
EntityLog entityLog2 = createEntityLog(List.of(entry2));
entityLog2.setLegalBasis(new ArrayList<>());
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog1));
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file2")).thenReturn(Optional.of(entityLog2));
migration.run();
String expectedHash = hashFunction.hashString("Unmapped_LB", StandardCharsets.UTF_8).toString();
assertAll(() -> assertEquals("LB1_Tech", entry1.getLegalBasis()), () -> assertEquals(expectedHash, entry2.getLegalBasis()));
verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog1);
verify(entityLogMongoService).saveEntityLog("dossier1", "file2", entityLog2);
}
@Test
public void testMigrationDoesNotAddDuplicateLegalBasis() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(List.of(dossier));
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.emptyList());
FileEntity file = createFile("file1", "dossier1");
String hashedLB = hashFunction.hashString("Unmapped_LB", StandardCharsets.UTF_8).toString();
EntityLogLegalBasis existingLB = createEntityLogLegalBasisForUnmappedLegalBasis(hashedLB);
EntityLogEntry entry = EntityLogEntry.builder().legalBasis("Unmapped_LB").build();
EntityLog entityLog = createEntityLog(List.of(entry));
entityLog.setLegalBasis(new ArrayList<>(List.of(existingLB)));
when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));
migration.run();
assertEquals(hashedLB, entry.getLegalBasis());
assertEquals(1, entityLog.getLegalBasis().size());
verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
}
@Test
public void testMigrationInitializesLegalBasisListWhenNull() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(List.of(dossier));
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.emptyList());
FileEntity file = createFile("file1", "dossier1");
EntityLogEntry entry = EntityLogEntry.builder().legalBasis("Unmapped_LB").build();
EntityLog entityLog = createEntityLog(List.of(entry));
entityLog.setLegalBasis(null);
when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));
migration.run();
assertNotNull(entityLog.getLegalBasis());
assertEquals(1, entityLog.getLegalBasis().size());
String expectedHash = hashFunction.hashString("Unmapped_LB", StandardCharsets.UTF_8).toString();
assertEquals(expectedHash, entry.getLegalBasis());
EntityLogLegalBasis newLB = entityLog.getLegalBasis()
.get(0);
assertEquals(expectedHash, newLB.getTechnicalName());
assertEquals("Unmapped_LB", newLB.getReason());
assertEquals("Generated Legal Basis", newLB.getName());
assertEquals("", newLB.getDescription());
verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
}
@Test
public void testMigrationUpdates() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(List.of(dossier));
LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_Tech").reason("LB1_Reason").build();
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(List.of(lb1));
FileEntity file = createFile("file1", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
EntityLogEntry entry1 = EntityLogEntry.builder().legalBasis("LB1_Reason").build();
EntityLogEntry entry2 = EntityLogEntry.builder().legalBasis("Unmapped_LB").build();
EntityLogEntry entry3 = EntityLogEntry.builder().legalBasis("Another_Unmapped_LB").build();
EntityLog entityLog = createEntityLog(Arrays.asList(entry1, entry2, entry3));
entityLog.setLegalBasis(new ArrayList<>());
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));
migration.run();
String hashedUnmappedLB = hashFunction.hashString("Unmapped_LB", StandardCharsets.UTF_8).toString();
String hashedAnotherUnmappedLB = hashFunction.hashString("Another_Unmapped_LB", StandardCharsets.UTF_8).toString();
assertAll(() -> assertEquals("LB1_Tech", entry1.getLegalBasis()),
() -> assertEquals(hashedUnmappedLB, entry2.getLegalBasis()),
() -> assertEquals(hashedAnotherUnmappedLB, entry3.getLegalBasis()));
assertEquals(3, entityLog.getLegalBasis().size());
verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
}
@Test
public void testMigrationHandlesDuplicateEntityLogEntries() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(List.of(dossier));
LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_Tech").reason("LB1_Reason").build();
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(List.of(lb1));
FileEntity file = createFile("file1", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
EntityLogEntry entry1 = EntityLogEntry.builder().legalBasis("LB1_Reason").build();
EntityLogEntry entry2 = EntityLogEntry.builder().legalBasis("LB1_Reason").build();
EntityLog entityLog = createEntityLog(List.of(entry1, entry2));
entityLog.setLegalBasis(new ArrayList<>());
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));
migration.run();
assertAll(() -> assertEquals("LB1_Tech", entry1.getLegalBasis()),
() -> assertEquals("LB1_Tech", entry2.getLegalBasis()),
() -> assertEquals(1, entityLog.getLegalBasis().size()));
verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
}
@Test
public void testMigrationHandlesExistingHashedTechnicalName() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(List.of(dossier));
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.emptyList());
FileEntity file = createFile("file1", "dossier1");
String hashedLB = hashFunction.hashString("Unmapped_LB", StandardCharsets.UTF_8).toString();
EntityLogLegalBasis existingLB = createEntityLogLegalBasisForUnmappedLegalBasis(hashedLB);
EntityLogEntry entry = EntityLogEntry.builder().legalBasis("Unmapped_LB").build();
EntityLog entityLog = createEntityLog(List.of(entry));
entityLog.setLegalBasis(new ArrayList<>(List.of(existingLB)));
when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));
migration.run();
assertEquals(hashedLB, entry.getLegalBasis());
assertEquals(1, entityLog.getLegalBasis().size());
verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
}
@Test
public void testMigrationSkipsSavingWhenNoEntriesUpdated() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(List.of(dossier));
LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_Tech").reason("LB1_Reason").build();
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(List.of(lb1));
FileEntity file = createFile("file1", "dossier1");
EntityLogEntry entry = EntityLogEntry.builder().legalBasis("LB1_Tech").build();
EntityLogLegalBasis lb = EntityLogLegalBasis.builder()
.technicalName("LB1_Tech")
.reason("LB1_Reason")
.name("Generated Legal Basis")
.description("")
.build();
EntityLog entityLog = createEntityLog(List.of(entry));
entityLog.setLegalBasis(new ArrayList<>(List.of(lb)));
when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));
migration.run();
verify(entityLogMongoService, never()).saveEntityLog(anyString(), anyString(), any(EntityLog.class));
}
@Test
public void testMigrationUpdatesPropertyChangesLegalBasis() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(List.of(dossier));
LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("personal_data_geolocation_article_39e3").reason("Article 39(e)(3) of Regulation (EC) No 178/2002").build();
LegalBasisEntity lb2 = LegalBasisEntity.builder()
.technicalName("article_63_2_a_regulation_1107_2009")
.reason("Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)")
.build();
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(List.of(lb1, lb2));
FileEntity file = createFile("file1", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
Change change1 = new Change();
change1.setAnalysisNumber(18);
change1.setType(ChangeType.CHANGED);
change1.setDateTime(OffsetDateTime.now());
Map<String, String> propertyChanges1 = new HashMap<>();
propertyChanges1.put("legalBasis", "personal_data_geolocation_article_39e3 -> Article 39(e)(3) of Regulation (EC) No 178/2002");
change1.setPropertyChanges(propertyChanges1);
Change change2 = new Change();
change2.setAnalysisNumber(19);
change2.setType(ChangeType.CHANGED);
change2.setDateTime(OffsetDateTime.now());
Map<String, String> propertyChanges2 = new HashMap<>();
propertyChanges2.put("legalBasis", "Article 39(e)(3) of Regulation (EC) No 178/2002 -> personal_data_geolocation_article_39e3");
change2.setPropertyChanges(propertyChanges2);
ManualChange manualChange = new ManualChange();
manualChange.setManualRedactionType(ManualRedactionType.RECATEGORIZE);
manualChange.setProcessedDate(OffsetDateTime.now());
manualChange.setRequestedDate(OffsetDateTime.now());
manualChange.setUserId("user123");
Map<String, String> manualPropertyChanges = new HashMap<>();
manualPropertyChanges.put("legalBasis", "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)");
manualChange.setPropertyChanges(manualPropertyChanges);
EntityLogEntry entry = EntityLogEntry.builder()
.legalBasis("personal_data_geolocation_article_39e3")
.changes(List.of(change1, change2))
.manualChanges(List.of(manualChange))
.build();
EntityLog entityLog = createEntityLog(List.of(entry));
entityLog.setLegalBasis(new ArrayList<>());
when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));
migration.run();
String expectedLegalBasis1 = "personal_data_geolocation_article_39e3 -> personal_data_geolocation_article_39e3";
String expectedLegalBasis2 = "personal_data_geolocation_article_39e3 -> personal_data_geolocation_article_39e3";
String expectedManualLegalBasis = "article_63_2_a_regulation_1107_2009";
assertEquals(expectedLegalBasis1,
change1.getPropertyChanges()
.get("legalBasis"));
assertEquals(expectedLegalBasis2,
change2.getPropertyChanges()
.get("legalBasis"));
assertEquals(expectedManualLegalBasis,
manualChange.getPropertyChanges()
.get("legalBasis"));
verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
assertEquals(2, entityLog.getLegalBasis().size());
assertTrue(entityLog.getLegalBasis()
.stream()
.anyMatch(lb -> lb.getTechnicalName().equals("personal_data_geolocation_article_39e3")));
assertTrue(entityLog.getLegalBasis()
.stream()
.anyMatch(lb -> lb.getTechnicalName().equals("article_63_2_a_regulation_1107_2009")));
}
private DossierEntity createDossier(String id, String templateId) {
DossierEntity dossier = new DossierEntity();
dossier.setId(id);
dossier.setDossierTemplateId(templateId);
return dossier;
}
private FileEntity createFile(String id, String dossierId) {
FileEntity file = new FileEntity();
file.setId(id);
file.setDossierId(dossierId);
return file;
}
private EntityLog createEntityLog(List<EntityLogEntry> entries) {
EntityLog entityLog = new EntityLog();
entityLog.setEntityLogEntry(entries);
return entityLog;
}
private EntityLogLegalBasis createEntityLogLegalBasisForUnmappedLegalBasis(String technicalName) {
return EntityLogLegalBasis.builder()
.technicalName(technicalName)
.reason("Unmapped_LB")
.name("Generated Legal Basis")
.description("")
.build();
}
}

View File

@ -0,0 +1,366 @@
package com.iqser.red.service.persistence.management.v1.processor.migration;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.LegalBasisEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.migration.migrations.V31TechnicalNameImportedFilesMigration;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.LegalBasisMappingPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.MigrationPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.imported.ImportedLegalBases;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.imported.ImportedLegalBasis;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.imported.ImportedRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.imported.ImportedRedactionsPerPage;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
public class TechnicalNameImportedFilesMigrationTest {
@Mock
private DossierRepository dossierRepository;
@Mock
private FileRepository fileRepository;
@Mock
private FileManagementStorageService fileManagementStorageService;
@Mock
private LegalBasisMappingPersistenceService legalBasisMappingPersistenceService;
@Mock
private MigrationPersistenceService migrationPersistenceService;
private LegalBasisMappingService legalBasisMappingService;
private V31TechnicalNameImportedFilesMigration migration;
@BeforeEach
public void setup() {
MockitoAnnotations.openMocks(this);
when(migrationPersistenceService.getLatestProcessedVersion()).thenReturn(0L);
when(migrationPersistenceService.isProcessed(anyLong(), anyLong())).thenReturn(false);
legalBasisMappingService = new LegalBasisMappingService(legalBasisMappingPersistenceService);
migration = new V31TechnicalNameImportedFilesMigration(dossierRepository, fileRepository, fileManagementStorageService, legalBasisMappingService);
migration.setMigrationPersistenceService(migrationPersistenceService);
}
@Test
public void testMigrationUpdatesMappedLegalBasisTechnicalNamesInImportedRedactions() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(Collections.singletonList(dossier));
LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_TechName").reason("LB1_Reason").build();
LegalBasisEntity lb2 = LegalBasisEntity.builder().technicalName("LB2_TechName").reason("LB2_Reason").build();
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Arrays.asList(lb1, lb2));
FileEntity file = createFile("file1", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(Collections.singletonList(file));
when(fileManagementStorageService.objectExists("dossier1", "file1", FileType.IMPORTED_REDACTIONS)).thenReturn(true);
ImportedRedaction redaction1 = new ImportedRedaction();
redaction1.setLegalBasis("LB1_Reason");
ImportedRedaction redaction2 = new ImportedRedaction();
redaction2.setLegalBasis("LB2_Reason");
ImportedRedactionsPerPage importedRedactions = new ImportedRedactionsPerPage();
importedRedactions.setImportedRedactions(Map.of(1, Arrays.asList(redaction1, redaction2)));
when(fileManagementStorageService.getImportedRedactions("dossier1", "file1")).thenReturn(importedRedactions);
migration.run();
assertEquals("LB1_TechName", redaction1.getLegalBasis());
assertEquals("LB2_TechName", redaction2.getLegalBasis());
verify(fileManagementStorageService).storeJSONObject("dossier1", "file1", FileType.IMPORTED_REDACTIONS, importedRedactions);
}
@Test
public void testMigrationHashesUnmappedLegalBasisInImportedRedactions() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(Collections.singletonList(dossier));
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.emptyList());
FileEntity file = createFile("file1", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(Collections.singletonList(file));
when(fileManagementStorageService.objectExists("dossier1", "file1", FileType.IMPORTED_REDACTIONS)).thenReturn(true);
ImportedRedaction redaction1 = new ImportedRedaction();
redaction1.setLegalBasis("Unmapped_LB1");
ImportedRedaction redaction2 = new ImportedRedaction();
redaction2.setLegalBasis("Unmapped_LB2");
ImportedRedactionsPerPage importedRedactions = new ImportedRedactionsPerPage();
importedRedactions.setImportedRedactions(Map.of(1, Arrays.asList(redaction1, redaction2)));
when(fileManagementStorageService.getImportedRedactions("dossier1", "file1")).thenReturn(importedRedactions);
migration.run();
String hashedLB1 = legalBasisMappingService.processLegalBasis("Unmapped_LB1", new HashMap<>()).technicalName();
String hashedLB2 = legalBasisMappingService.processLegalBasis("Unmapped_LB2", new HashMap<>()).technicalName();
assertEquals(hashedLB1, redaction1.getLegalBasis());
assertEquals(hashedLB2, redaction2.getLegalBasis());
verify(fileManagementStorageService).storeJSONObject("dossier1", "file1", FileType.IMPORTED_REDACTIONS, importedRedactions);
}
@Test
public void testMigrationUpdatesTechnicalNamesInImportedLegalBases() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(Collections.singletonList(dossier));
LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_TechName").reason("LB1_Reason").build();
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.singletonList(lb1));
FileEntity file = createFile("file1", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(Collections.singletonList(file));
when(fileManagementStorageService.objectExists("dossier1", "file1", FileType.IMPORTED_LEGAL_BASES)).thenReturn(true);
ImportedLegalBasis importedLegalBasis = new ImportedLegalBasis();
importedLegalBasis.setReason("LB1_Reason");
importedLegalBasis.setTechnicalName(null);
ImportedLegalBases importedLegalBases = new ImportedLegalBases();
importedLegalBases.setImportedLegalBases(Collections.singletonList(importedLegalBasis));
when(fileManagementStorageService.getImportedLegalBases("dossier1", "file1")).thenReturn(importedLegalBases);
migration.run();
assertEquals("LB1_TechName", importedLegalBasis.getTechnicalName());
verify(fileManagementStorageService).storeJSONObject("dossier1", "file1", FileType.IMPORTED_LEGAL_BASES, importedLegalBases);
}
@Test
public void testMigrationHandlesFilesWithoutImportedRedactionsOrLegalBases() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(Collections.singletonList(dossier));
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.emptyList());
FileEntity file = createFile("file1", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(Collections.singletonList(file));
when(fileManagementStorageService.objectExists("dossier1", "file1", FileType.IMPORTED_REDACTIONS)).thenReturn(false);
when(fileManagementStorageService.objectExists("dossier1", "file1", FileType.IMPORTED_LEGAL_BASES)).thenReturn(false);
migration.run();
verify(fileManagementStorageService, never()).getImportedRedactions(anyString(), anyString());
verify(fileManagementStorageService, never()).getImportedLegalBases(anyString(), anyString());
verify(fileManagementStorageService, never()).storeJSONObject(anyString(), anyString(), any(FileType.class), any());
}
@Test
public void testMigrationSkipsDossiersWithNoFiles() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(Collections.singletonList(dossier));
when(fileRepository.findByDossierId("dossier1")).thenReturn(Collections.emptyList());
migration.run();
verify(fileManagementStorageService, never()).objectExists(anyString(), anyString(), any(FileType.class));
}
@Test
public void testMigrationProcessesMultipleDossiersAndFiles() {
DossierEntity dossier1 = createDossier("dossier1", "template1");
DossierEntity dossier2 = createDossier("dossier2", "template2");
when(dossierRepository.findAll()).thenReturn(Arrays.asList(dossier1, dossier2));
LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_TechName").reason("LB1_Reason").build();
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.singletonList(lb1));
LegalBasisEntity lb2 = LegalBasisEntity.builder().technicalName("LB2_TechName").reason("LB2_Reason").build();
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template2")).thenReturn(Collections.singletonList(lb2));
FileEntity file1 = createFile("file1", "dossier1");
FileEntity file2 = createFile("file2", "dossier2");
when(fileRepository.findByDossierId("dossier1")).thenReturn(Collections.singletonList(file1));
when(fileRepository.findByDossierId("dossier2")).thenReturn(Collections.singletonList(file2));
when(fileManagementStorageService.objectExists("dossier1", "file1", FileType.IMPORTED_REDACTIONS)).thenReturn(true);
when(fileManagementStorageService.objectExists("dossier2", "file2", FileType.IMPORTED_REDACTIONS)).thenReturn(true);
ImportedRedaction redaction1 = new ImportedRedaction();
redaction1.setLegalBasis("LB1_Reason");
ImportedRedactionsPerPage importedRedactions1 = new ImportedRedactionsPerPage();
importedRedactions1.setImportedRedactions(Map.of(1, Collections.singletonList(redaction1)));
when(fileManagementStorageService.getImportedRedactions("dossier1", "file1")).thenReturn(importedRedactions1);
ImportedRedaction redaction2 = new ImportedRedaction();
redaction2.setLegalBasis("LB2_Reason");
ImportedRedactionsPerPage importedRedactions2 = new ImportedRedactionsPerPage();
importedRedactions2.setImportedRedactions(Map.of(1, Collections.singletonList(redaction2)));
when(fileManagementStorageService.getImportedRedactions("dossier2", "file2")).thenReturn(importedRedactions2);
migration.run();
assertEquals("LB1_TechName", redaction1.getLegalBasis());
assertEquals("LB2_TechName", redaction2.getLegalBasis());
verify(fileManagementStorageService).storeJSONObject("dossier1", "file1", FileType.IMPORTED_REDACTIONS, importedRedactions1);
verify(fileManagementStorageService).storeJSONObject("dossier2", "file2", FileType.IMPORTED_REDACTIONS, importedRedactions2);
}
@Test
public void testMigrationDoesNotUpdateWhenNoChangesNeeded() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(Collections.singletonList(dossier));
LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_TechName").reason("LB1_Reason").build();
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.singletonList(lb1));
FileEntity file = createFile("file1", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(Collections.singletonList(file));
when(fileManagementStorageService.objectExists("dossier1", "file1", FileType.IMPORTED_REDACTIONS)).thenReturn(true);
ImportedRedaction redaction = new ImportedRedaction();
redaction.setLegalBasis("LB1_TechName");
ImportedRedactionsPerPage importedRedactions = new ImportedRedactionsPerPage();
importedRedactions.setImportedRedactions(Map.of(1,Collections.singletonList(redaction)));
when(fileManagementStorageService.getImportedRedactions("dossier1", "file1")).thenReturn(importedRedactions);
migration.run();
assertEquals("LB1_TechName", redaction.getLegalBasis());
verify(fileManagementStorageService, never()).storeJSONObject(anyString(), anyString(), any(FileType.class), any());
}
@Test
public void testMigrationHandlesImportedLegalBasesWithExistingTechnicalNames() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(Collections.singletonList(dossier));
LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_TechName").reason("LB1_Reason").build();
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.singletonList(lb1));
FileEntity file = createFile("file1", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(Collections.singletonList(file));
when(fileManagementStorageService.objectExists("dossier1", "file1", FileType.IMPORTED_LEGAL_BASES)).thenReturn(true);
ImportedLegalBasis importedLegalBasis = new ImportedLegalBasis();
importedLegalBasis.setReason("LB1_Reason");
importedLegalBasis.setTechnicalName("LB1_TechName");
ImportedLegalBases importedLegalBases = new ImportedLegalBases();
importedLegalBases.setImportedLegalBases(Collections.singletonList(importedLegalBasis));
when(fileManagementStorageService.getImportedLegalBases("dossier1", "file1")).thenReturn(importedLegalBases);
migration.run();
assertEquals("LB1_TechName", importedLegalBasis.getTechnicalName());
verify(fileManagementStorageService, never()).storeJSONObject(anyString(), anyString(), any(FileType.class), any());
}
@Test
public void testMigrationHashesUnmappedReasonsInImportedLegalBases() {
DossierEntity dossier = createDossier("dossier1", "template1");
when(dossierRepository.findAll()).thenReturn(Collections.singletonList(dossier));
when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.emptyList());
FileEntity file = createFile("file1", "dossier1");
when(fileRepository.findByDossierId("dossier1")).thenReturn(Collections.singletonList(file));
when(fileManagementStorageService.objectExists("dossier1", "file1", FileType.IMPORTED_LEGAL_BASES)).thenReturn(true);
ImportedLegalBasis importedLegalBasis = new ImportedLegalBasis();
importedLegalBasis.setReason("Unmapped_Reason");
importedLegalBasis.setTechnicalName(null);
ImportedLegalBases importedLegalBases = new ImportedLegalBases();
importedLegalBases.setImportedLegalBases(Collections.singletonList(importedLegalBasis));
when(fileManagementStorageService.getImportedLegalBases("dossier1", "file1")).thenReturn(importedLegalBases);
migration.run();
String hashedTechnicalName = legalBasisMappingService.processLegalBasis("Unmapped_Reason", new HashMap<>()).technicalName();
assertEquals(hashedTechnicalName, importedLegalBasis.getTechnicalName());
verify(fileManagementStorageService).storeJSONObject("dossier1", "file1", FileType.IMPORTED_LEGAL_BASES, importedLegalBases);
}
private DossierEntity createDossier(String id, String templateId) {
DossierEntity dossier = new DossierEntity();
dossier.setId(id);
dossier.setDossierTemplateId(templateId);
return dossier;
}
private FileEntity createFile(String id, String dossierId) {
FileEntity file = new FileEntity();
file.setId(id);
file.setDossierId(dossierId);
return file;
}
}

View File

@ -0,0 +1,672 @@
package com.iqser.red.service.persistence.management.v1.processor.migration;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.*;
import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.LegalBasisEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.RuleSetEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
import com.iqser.red.service.persistence.management.v1.processor.migration.migrations.V30TechnicalNameRuleFileMigration;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.LegalBasisMappingPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.MigrationPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.RulesPersistenceService;
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;
public class TechnicalNameRuleFileMigrationTest {
private static final String TEMPLATE_ID_1 = "template1";
private static final String TEMPLATE_ID_2 = "template2";
private static final String RULES_WITH_REASONS = "rule \"CBI.0.4: Redact CBI Authors (vertebrate Study)\"\n"
+ " when\n"
+ " FileAttribute(label == \"Vertebrate Study\", value soundslike \"Yes\" || value.toLowerCase() == \"y\")\n"
+ " $entity: TextEntity(type() == \"CBI_author\", dictionaryEntry)\n"
+ " then\n"
+ " $entity.redact(\"CBI.0.4\", \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n"
+ " end\n"
+ "\n"
+ "rule \"CBI.0.3: Redact CBI Authors (non vertebrate Study)\"\n"
+ " when\n"
+ " not FileAttribute(label == \"Vertebrate Study\", value soundslike \"Yes\" || value.toLowerCase() == \"y\")\n"
+ " $entity: TextEntity(type() == \"CBI_author\", dictionaryEntry)\n"
+ " then\n"
+ " $entity.redact(\"CBI.0.3\", \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n"
+ " end";
private static final String RULES_WITH_SPECIAL_CHARACTERS = "rule \"Special Characters Rule\"\n"
+ " when\n"
+ " // conditions\n"
+ " then\n"
+ " // actions\n"
+ " $entity.redact(\"RuleCode\", \"Description\", \"Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)\");\n"
+ " end";
private static final String RULES_WITH_NO_REASONS = "rule \"Some Rule\"\n" + " when\n" + " // conditions\n" + " then\n" + " // actions\n" + " end";
private static final String RULES_WITH_PARTIAL_MATCH = "rule \"Partial Match Rule\"\n"
+ " when\n"
+ " // conditions\n"
+ " then\n"
+ " // actions\n"
+ " $entity.redact(\"RuleCode\", \"Description\", \"Some Article 39(e)(2) of Regulation (EC) No 178/2002 Additional Text\");\n"
+ " end";
private static final String RULES_WITH_IDENTICAL_REASON_AND_TECHNICAL_NAME = "rule \"Identical Reason and Technical Name Rule\"\n"
+ " when\n"
+ " // conditions\n"
+ " then\n"
+ " // actions\n"
+ " $entity.redact(\"RuleCode\", \"Description\", \"same_name\");\n"
+ " end";
private static final String RULES_WITH_MULTIPLE_TEMPLATES = "rule \"Composition Rule\"\n"
+ " when\n"
+ " // conditions\n"
+ " then\n"
+ " // actions\n"
+ " $entity.redact(\"RuleCode\", \"Description\", \"Article 63(2)(d) of Regulation (EC) No 1107/2009\");\n"
+ " end";
private static final String RULES_WITH_NULL_VALUES = "rule \"Null Values Rule\"\n"
+ " when\n"
+ " // conditions\n"
+ " then\n"
+ " // actions\n"
+ " $entity.redact(\"RuleCode\", \"Description\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n"
+ " end";
private static final String RULES_WITH_PARTIAL_VALID_MAPPINGS = "rule \"Partial Valid Mappings Rule\"\n"
+ " when\n"
+ " // conditions\n"
+ " then\n"
+ " // actions\n"
+ " $entity.redact(\"RuleCode\", \"Description\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n"
+ " $entity.redact(\"RuleCode\", \"Description\", \"Some Other Reason\");\n"
+ " end";
@Mock
private DossierTemplateRepository dossierTemplateRepository;
@Mock
private RulesPersistenceService rulesPersistenceService;
@Mock
private LegalBasisMappingPersistenceService legalBasisMappingPersistenceService;
@Mock
private MigrationPersistenceService migrationPersistenceService;
@InjectMocks
private V30TechnicalNameRuleFileMigration migration;
@BeforeEach
public void setup() {
MockitoAnnotations.openMocks(this);
migration.setMigrationPersistenceService(migrationPersistenceService);
when(migrationPersistenceService.getLatestProcessedVersion()).thenReturn(0L);
when(migrationPersistenceService.isProcessed(anyLong(), anyLong())).thenReturn(false);
}
private DossierTemplateEntity createDossierTemplate(String id) {
DossierTemplateEntity dossierTemplate = new DossierTemplateEntity();
dossierTemplate.setId(id);
return dossierTemplate;
}
private LegalBasisEntity createLegalBasis(String reason, String technicalName) {
return LegalBasisEntity.builder().reason(reason).technicalName(technicalName).build();
}
private RuleSetEntity createRuleSet(String content) {
RuleSetEntity ruleSet = new RuleSetEntity();
ruleSet.setValue(content);
return ruleSet;
}
@Test
public void testMigrationReplacesLegalBasisReasonsWithTechnicalNames() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
List<LegalBasisEntity> mappings = Arrays.asList(createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002",
"vertebrate_study_personal_data_geolocation_article_39e2"),
createLegalBasis("Article 39(e)(3) of Regulation (EC) No 178/2002", "personal_data_geolocation_article_39e3"));
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(mappings);
RuleSetEntity ruleSet = createRuleSet(RULES_WITH_REASONS);
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet));
migration.run();
ArgumentCaptor<String> rulesCaptor = ArgumentCaptor.forClass(String.class);
verify(rulesPersistenceService).setRules(rulesCaptor.capture(), eq(TEMPLATE_ID_1), eq(RuleFileType.ENTITY));
String updatedRules = rulesCaptor.getValue();
assertAll(() -> assertTrue(updatedRules.contains("vertebrate_study_personal_data_geolocation_article_39e2")),
() -> assertTrue(updatedRules.contains("personal_data_geolocation_article_39e3")),
() -> assertFalse(updatedRules.contains("Article 39(e)(2) of Regulation (EC) No 178/2002")),
() -> assertFalse(updatedRules.contains("Article 39(e)(3) of Regulation (EC) No 178/2002")));
}
@Test
public void testMigrationWithNoLegalBasisMappings() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Collections.emptyList());
migration.run();
verifyNoInteractions(rulesPersistenceService);
}
@Test
public void testMigrationWithNoRuleSet() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002", "vertebrate_study_personal_data_geolocation_article_39e2");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Collections.singletonList(lb1));
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.empty());
migration.run();
verify(rulesPersistenceService, never()).setRules(anyString(), anyString(), any(RuleFileType.class));
}
@Test
public void testMigrationWithNoReplacementsNeeded() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis("Nonexistent Reason", "nonexistent_technical_name");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Collections.singletonList(lb1));
RuleSetEntity ruleSet = createRuleSet(RULES_WITH_NO_REASONS);
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet));
migration.run();
verify(rulesPersistenceService, never()).setRules(anyString(), anyString(), any(RuleFileType.class));
}
@Test
public void testMigrationHandlesSpecialCharactersInReasons() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis("Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)",
"manufacturing_production_process");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Collections.singletonList(lb1));
RuleSetEntity ruleSet = createRuleSet(RULES_WITH_SPECIAL_CHARACTERS);
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet));
migration.run();
ArgumentCaptor<String> rulesCaptor = ArgumentCaptor.forClass(String.class);
verify(rulesPersistenceService).setRules(rulesCaptor.capture(), eq(TEMPLATE_ID_1), eq(RuleFileType.ENTITY));
String updatedRules = rulesCaptor.getValue();
assertAll(() -> assertTrue(updatedRules.contains("manufacturing_production_process")),
() -> assertFalse(updatedRules.contains("Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)")));
}
@Test
public void testMigrationDoesNotReplacePartialMatches() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002", "vertebrate_study_personal_data_geolocation_article_39e2");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Collections.singletonList(lb1));
RuleSetEntity ruleSet = createRuleSet(RULES_WITH_PARTIAL_MATCH);
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet));
migration.run();
verify(rulesPersistenceService, never()).setRules(anyString(), anyString(), any(RuleFileType.class));
}
@Test
public void testMigrationSkipsIdenticalReasonAndTechnicalName() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis("same_name", "same_name");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Collections.singletonList(lb1));
RuleSetEntity ruleSet = createRuleSet(RULES_WITH_IDENTICAL_REASON_AND_TECHNICAL_NAME);
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet));
migration.run();
verify(rulesPersistenceService, never()).setRules(anyString(), anyString(), any(RuleFileType.class));
}
@Test
public void testMigrationProcessesMultipleDossierTemplates() {
DossierTemplateEntity dossierTemplate1 = createDossierTemplate(TEMPLATE_ID_1);
DossierTemplateEntity dossierTemplate2 = createDossierTemplate(TEMPLATE_ID_2);
when(dossierTemplateRepository.findAll()).thenReturn(Arrays.asList(dossierTemplate1, dossierTemplate2));
LegalBasisEntity lb1 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002", "vertebrate_study_personal_data_geolocation_article_39e2");
LegalBasisEntity lb2 = createLegalBasis("Article 63(2)(d) of Regulation (EC) No 1107/2009", "composition_plant_protection_product");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Collections.singletonList(lb1));
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_2)).thenReturn(Collections.singletonList(lb2));
RuleSetEntity ruleSet1 = createRuleSet(RULES_WITH_REASONS);
RuleSetEntity ruleSet2 = createRuleSet(RULES_WITH_MULTIPLE_TEMPLATES);
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet1));
when(rulesPersistenceService.getRules(TEMPLATE_ID_2, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet2));
migration.run();
ArgumentCaptor<String> rulesCaptor1 = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> rulesCaptor2 = ArgumentCaptor.forClass(String.class);
verify(rulesPersistenceService, times(2)).setRules(any(), any(), eq(RuleFileType.ENTITY));
verify(rulesPersistenceService).setRules(rulesCaptor1.capture(), eq(TEMPLATE_ID_1), eq(RuleFileType.ENTITY));
verify(rulesPersistenceService).setRules(rulesCaptor2.capture(), eq(TEMPLATE_ID_2), eq(RuleFileType.ENTITY));
String updatedRules1 = rulesCaptor1.getValue();
String updatedRules2 = rulesCaptor2.getValue();
assertAll(() -> {
assertTrue(updatedRules1.contains("vertebrate_study_personal_data_geolocation_article_39e2"));
assertFalse(updatedRules1.contains("Article 39(e)(2) of Regulation (EC) No 178/2002"));
}, () -> {
assertTrue(updatedRules2.contains("composition_plant_protection_product"));
assertFalse(updatedRules2.contains("Article 63(2)(d) of Regulation (EC) No 1107/2009"));
});
}
@Test
public void testMigrationHandlesNullValuesGracefully() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis(null, "personal_data_geolocation_article_39e3");
LegalBasisEntity lb2 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002", null);
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Arrays.asList(lb1, lb2));
RuleSetEntity ruleSet = createRuleSet(RULES_WITH_NULL_VALUES);
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet));
migration.run();
verify(rulesPersistenceService, never()).setRules(anyString(), anyString(), any(RuleFileType.class));
}
@Test
public void testMigrationUpdatesRuleSetWhenOnlySomeMappingsAreValid() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002", "vertebrate_study_personal_data_geolocation_article_39e2");
LegalBasisEntity lb2 = createLegalBasis(null, "some_technical_name");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Arrays.asList(lb1, lb2));
RuleSetEntity ruleSet = createRuleSet(RULES_WITH_PARTIAL_VALID_MAPPINGS);
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet));
migration.run();
ArgumentCaptor<String> rulesCaptor = ArgumentCaptor.forClass(String.class);
verify(rulesPersistenceService).setRules(rulesCaptor.capture(), eq(TEMPLATE_ID_1), eq(RuleFileType.ENTITY));
String updatedRules = rulesCaptor.getValue();
assertAll(() -> assertTrue(updatedRules.contains("vertebrate_study_personal_data_geolocation_article_39e2")),
() -> assertFalse(updatedRules.contains("Article 39(e)(2) of Regulation (EC) No 178/2002")),
() -> assertTrue(updatedRules.contains("Some Other Reason")));
}
@Test
public void testMigrationWithEmptyRulesContent() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002", "vertebrate_study_personal_data_geolocation_article_39e2");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Collections.singletonList(lb1));
RuleSetEntity ruleSet = createRuleSet("");
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet));
migration.run();
verify(rulesPersistenceService, never()).setRules(anyString(), anyString(), any(RuleFileType.class));
}
@Test
public void testMigrationReplacesMultipleOccurrencesOfSameReason() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002", "tech_name_39e2");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Collections.singletonList(lb1));
String duplicatedRules = RULES_WITH_REASONS + "\n" + RULES_WITH_REASONS;
RuleSetEntity ruleSet = createRuleSet(duplicatedRules);
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet));
migration.run();
ArgumentCaptor<String> rulesCaptor = ArgumentCaptor.forClass(String.class);
verify(rulesPersistenceService).setRules(rulesCaptor.capture(), eq(TEMPLATE_ID_1), eq(RuleFileType.ENTITY));
String updatedRules = rulesCaptor.getValue();
assertAll(() -> assertEquals(2, StringUtils.countMatches(updatedRules, "tech_name_39e2")),
() -> assertFalse(updatedRules.contains("Article 39(e)(2) of Regulation (EC) No 178/2002")));
}
@Test
public void testMigrationDoesNotReplaceOverlappingReasonStrings() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002", "tech_name_39e2");
LegalBasisEntity lb2 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002 Extended", "tech_name_extended");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Arrays.asList(lb1, lb2));
String overlappingRules = "rule \"Test Rule\"\n"
+ " when\n"
+ " $entity: TextEntity(type() == \"CBI_author\", dictionaryEntry)\n"
+ " then\n"
+ " $entity.redact(\"RuleCode\", \"Description\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n"
+ " $entity.redact(\"RuleCode\", \"Description\", \"Article 39(e)(2) of Regulation (EC) No 178/2002 Extended\");\n"
+ " end";
RuleSetEntity ruleSet = createRuleSet(overlappingRules);
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet));
migration.run();
ArgumentCaptor<String> rulesCaptor = ArgumentCaptor.forClass(String.class);
verify(rulesPersistenceService).setRules(rulesCaptor.capture(), eq(TEMPLATE_ID_1), eq(RuleFileType.ENTITY));
String updatedRules = rulesCaptor.getValue();
assertAll(() -> assertTrue(updatedRules.contains("tech_name_39e2")),
() -> assertTrue(updatedRules.contains("tech_name_extended")),
() -> assertFalse(updatedRules.contains("Article 39(e)(2) of Regulation (EC) No 178/2002")),
() -> assertFalse(updatedRules.contains("Article 39(e)(2) of Regulation (EC) No 178/2002 Extended")));
}
@Test
public void testMigrationWithDuplicateLegalBasisMappings() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002", "tech_name_39e2");
LegalBasisEntity lb2 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002", "tech_name_39e2_duplicate");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Arrays.asList(lb1, lb2));
RuleSetEntity ruleSet = createRuleSet(RULES_WITH_REASONS);
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet));
migration.run();
ArgumentCaptor<String> rulesCaptor = ArgumentCaptor.forClass(String.class);
verify(rulesPersistenceService).setRules(rulesCaptor.capture(), eq(TEMPLATE_ID_1), eq(RuleFileType.ENTITY));
String updatedRules = rulesCaptor.getValue();
assertAll(() -> assertTrue(updatedRules.contains("tech_name_39e2_duplicate")),
() -> assertFalse(updatedRules.contains("\"tech_name_39e2\"")),
() -> assertFalse(updatedRules.contains("Article 39(e)(2) of Regulation (EC) No 178/2002")));
}
@Test
public void testMigrationWithTechnicalNamesContainingQuotes() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002", "tech_name_with_\"quotes\"");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Collections.singletonList(lb1));
RuleSetEntity ruleSet = createRuleSet(RULES_WITH_REASONS);
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet));
migration.run();
ArgumentCaptor<String> rulesCaptor = ArgumentCaptor.forClass(String.class);
verify(rulesPersistenceService).setRules(rulesCaptor.capture(), eq(TEMPLATE_ID_1), eq(RuleFileType.ENTITY));
String updatedRules = rulesCaptor.getValue();
assertAll(() -> assertTrue(updatedRules.contains("tech_name_with_\"quotes\"")),
() -> assertFalse(updatedRules.contains("Article 39(e)(2) of Regulation (EC) No 178/2002")));
}
@Test
public void testMigrationDoesNotAlterOtherRuleFileTypes() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002", "tech_name_39e2");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Collections.singletonList(lb1));
RuleSetEntity entityRuleSet = createRuleSet(RULES_WITH_REASONS);
RuleSetEntity componentRuleSet = createRuleSet("component rules content");
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(entityRuleSet));
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.COMPONENT)).thenReturn(Optional.of(componentRuleSet));
migration.run();
ArgumentCaptor<String> entityRulesCaptor = ArgumentCaptor.forClass(String.class);
verify(rulesPersistenceService).setRules(entityRulesCaptor.capture(), eq(TEMPLATE_ID_1), eq(RuleFileType.ENTITY));
String updatedEntityRules = entityRulesCaptor.getValue();
assertAll(() -> assertTrue(updatedEntityRules.contains("tech_name_39e2")),
() -> assertFalse(updatedEntityRules.contains("Article 39(e)(2) of Regulation (EC) No 178/2002")));
verify(rulesPersistenceService, never()).setRules(eq("component rules content"), eq(TEMPLATE_ID_1), eq(RuleFileType.COMPONENT));
}
@Test
public void testMigrationDoesNotReprocessAlreadyMigratedRuleSets() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
when(migrationPersistenceService.getLatestProcessedVersion()).thenReturn(28L);
migration.run();
verifyNoInteractions(rulesPersistenceService);
}
@Test
public void testMigrationWithLargeRuleSetsAndMappings() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
List<LegalBasisEntity> largeMappings = IntStream.range(0, 1000).mapToObj(i -> createLegalBasis("Article " + i + "(e)(2) of Regulation (EC) No 178/2002", "tech_name_" + i))
.collect(Collectors.toList());
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(largeMappings);
String largeRules = IntStream.range(0, 1000)
.mapToObj(i -> "rule \"Rule "
+ i
+ "\"\n"
+ " when\n"
+ " $entity: TextEntity(type() == \"CBI_author\", dictionaryEntry)\n"
+ " then\n"
+ " $entity.redact(\"RuleCode\", \"Description\", \"Article "
+ i
+ "(e)(2) of Regulation (EC) No 178/2002\");\n"
+ " end\n\n")
.collect(Collectors.joining());
RuleSetEntity ruleSet = createRuleSet(largeRules);
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet));
migration.run();
ArgumentCaptor<String> rulesCaptor = ArgumentCaptor.forClass(String.class);
verify(rulesPersistenceService).setRules(rulesCaptor.capture(), eq(TEMPLATE_ID_1), eq(RuleFileType.ENTITY));
String updatedRules = rulesCaptor.getValue();
assertAll(() -> assertTrue(updatedRules.contains("tech_name_0")),
() -> assertTrue(updatedRules.contains("tech_name_500")),
() -> assertTrue(updatedRules.contains("tech_name_999")),
() -> {
assertFalse(updatedRules.contains("Article 0(e)(2) of Regulation (EC) No 178/2002"));
assertFalse(updatedRules.contains("Article 500(e)(2) of Regulation (EC) No 178/2002"));
assertFalse(updatedRules.contains("Article 999(e)(2) of Regulation (EC) No 178/2002"));
});
}
@Test
public void testMigrationSkipsMappingsWithMissingTechnicalNames() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002", null);
LegalBasisEntity lb2 = createLegalBasis("Article 39(e)(3) of Regulation (EC) No 178/2002", "tech_name_39e3");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Arrays.asList(lb1, lb2));
RuleSetEntity ruleSet = createRuleSet(RULES_WITH_REASONS);
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet));
migration.run();
ArgumentCaptor<String> rulesCaptor = ArgumentCaptor.forClass(String.class);
verify(rulesPersistenceService).setRules(rulesCaptor.capture(), eq(TEMPLATE_ID_1), eq(RuleFileType.ENTITY));
String updatedRules = rulesCaptor.getValue();
assertAll(() -> assertTrue(updatedRules.contains("tech_name_39e3")),
() -> assertFalse(updatedRules.contains("Article 39(e)(3) of Regulation (EC) No 178/2002")),
() -> assertTrue(updatedRules.contains("Article 39(e)(2) of Regulation (EC) No 178/2002")));
}
@Test
public void testMigrationWithEmptyLegalBasisMappingsList() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Collections.emptyList());
migration.run();
verifyNoInteractions(rulesPersistenceService);
}
@Test
public void testMigrationProcessesOnlySpecifiedRuleFileType() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002", "tech_name_39e2");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Collections.singletonList(lb1));
RuleSetEntity entityRuleSet = createRuleSet(RULES_WITH_REASONS);
RuleSetEntity componentRuleSet = createRuleSet("component rules content");
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(entityRuleSet));
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.COMPONENT)).thenReturn(Optional.of(componentRuleSet));
migration.run();
ArgumentCaptor<String> entityRulesCaptor = ArgumentCaptor.forClass(String.class);
verify(rulesPersistenceService).setRules(entityRulesCaptor.capture(), eq(TEMPLATE_ID_1), eq(RuleFileType.ENTITY));
String updatedEntityRules = entityRulesCaptor.getValue();
assertAll(() -> assertTrue(updatedEntityRules.contains("tech_name_39e2")),
() -> assertFalse(updatedEntityRules.contains("Article 39(e)(2) of Regulation (EC) No 178/2002")));
verify(rulesPersistenceService, never()).setRules(eq("component rules content"), eq(TEMPLATE_ID_1), eq(RuleFileType.COMPONENT));
}
@Test
public void testMigrationWithInvalidRuleFileContent() {
DossierTemplateEntity dossierTemplate = createDossierTemplate(TEMPLATE_ID_1);
when(dossierTemplateRepository.findAll()).thenReturn(Collections.singletonList(dossierTemplate));
LegalBasisEntity lb1 = createLegalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002", "tech_name_39e2");
when(legalBasisMappingPersistenceService.getLegalBasisMapping(TEMPLATE_ID_1)).thenReturn(Collections.singletonList(lb1));
String malformedRules = "rule \"Malformed Rule\"\n"
+ " when\n"
+ " $entity: TextEntity(type() == \"CBI_author\", dictionaryEntry)\n"
+ " then\n"
+ " $entity.redact(RuleCode, Description, Article 39(e)(2) of Regulation (EC) No 178/2002);\n"
+ " end";
RuleSetEntity ruleSet = createRuleSet(malformedRules);
when(rulesPersistenceService.getRules(TEMPLATE_ID_1, RuleFileType.ENTITY)).thenReturn(Optional.of(ruleSet));
migration.run();
verify(rulesPersistenceService, never()).setRules(anyString(), anyString(), any(RuleFileType.class));
}
@Test
public void testMigrationWithNoDossierTemplates() {
when(dossierTemplateRepository.findAll()).thenReturn(Collections.emptyList());
migration.run();
verifyNoInteractions(rulesPersistenceService, legalBasisMappingPersistenceService);
}
}

View File

@ -14,6 +14,7 @@ import com.iqser.red.service.peristence.v1.server.integration.client.DictionaryC
import com.iqser.red.service.peristence.v1.server.integration.client.DossierTemplateClient;
import com.iqser.red.service.peristence.v1.server.integration.client.LegalBasisClient;
import com.iqser.red.service.peristence.v1.server.integration.client.RulesClient;
import com.iqser.red.service.persistence.management.v1.processor.service.CurrentApplicationTypeProvider;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType;
@ -36,6 +37,9 @@ public class DossierTemplateTesterAndProvider {
@Autowired
private DictionaryClient dictionaryClient;
@Autowired
private CurrentApplicationTypeProvider currentApplicationTypeProvider;
public Colors provideDefaultColors(String dossierTemplateId) {
@ -82,6 +86,9 @@ public class DossierTemplateTesterAndProvider {
rulesClient.upload(new RulesUploadRequestModel("ABCD", loadedTemplate.getDossierTemplateId(), RuleFileType.ENTITY));
legalBasisClient.setLegalBasisMapping(List.of(new LegalBasis("name", "description", "reason","technicalName")), loadedTemplate.getDossierTemplateId());
if (currentApplicationTypeProvider.isDocuMine()) {
rulesClient.upload(new RulesUploadRequestModel("ABCD", loadedTemplate.getDossierTemplateId(), RuleFileType.COMPONENT));
}
loadedTemplate = dossierTemplateClient.getDossierTemplate(result.getDossierTemplateId());
return loadedTemplate;

View File

@ -70,7 +70,7 @@ public class ApprovalTest extends AbstractPersistenceServerServiceTest {
.id("id1")
.positions(List.of(new Position(1, 1, 1, 1, 1)))
.state(EntryState.APPLIED)
.legalBasis("legalBasis")
.legalBasis("legal_basis")
.entryType(EntryType.ENTITY)
.value("value")
.build()));

View File

@ -64,8 +64,6 @@ public class ComponentOverrideTest extends AbstractPersistenceServerServiceTest
@Test
@Disabled
// todo: fix me
public void testOverrides() throws IOException {
var dossier = dossierTesterAndProvider.provideTestDossier();
@ -187,8 +185,6 @@ public class ComponentOverrideTest extends AbstractPersistenceServerServiceTest
@Test
@SneakyThrows
@Disabled
//todo: fix me
public void testDeletedFileOverrides() throws IOException {
var dossier = dossierTesterAndProvider.provideTestDossier();
@ -257,7 +253,7 @@ public class ComponentOverrideTest extends AbstractPersistenceServerServiceTest
assertTrue(overrides.getComponentOverrides().isEmpty());
// case 2: re-upload file that was deleted before overrides should also not be returned anymore
fileTesterAndProvider.testAndProvideFile(dossier, "filename3");
fileTesterAndProvider.testAndProvideFile(dossier, "filename2");
var e = assertThrows(FeignException.class, () -> componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId()));
assertEquals(e.status(), 404);

View File

@ -20,13 +20,18 @@ import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.iqser.red.service.peristence.v1.server.integration.client.DictionaryClient;
import com.iqser.red.service.peristence.v1.server.integration.client.DossierClient;
import com.iqser.red.service.peristence.v1.server.integration.client.DossierTemplateClient;
import com.iqser.red.service.peristence.v1.server.integration.service.DossierTemplateTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.service.DossierTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.service.TypeProvider;
import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.DictionaryEntryEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.DictionaryManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.DictionaryService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.TypeRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.dictionaryentry.EntryRepository;
import com.iqser.red.service.persistence.service.v1.api.external.model.UpdateEntries;
import com.iqser.red.service.persistence.service.v1.api.shared.model.CreateTypeValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.TypeValue;
@ -60,6 +65,15 @@ public class DictionaryTest extends AbstractPersistenceServerServiceTest {
@Autowired
private DossierTemplateClient dossierTemplateClient;
@Autowired
private DossierClient dossierClient;
@Autowired
private TypeRepository typeRepository;
@Autowired
private EntryRepository entryRepository;
private final List<String> testList1 = List.of("William c. Spare", "Charalampos", "Carina Wilson", "PATRICIA A. SHEEHY", "William C. Spare", "carlsen", "Patricia A. Sheehy");
private final List<String> testList2 = List.of("William C. Spare", "Charalampos", "Carina Wilson", "Patricia A. Sheehy", "William c. Spare", "carlsen", "PATRICIA A. SHEEHY");
@ -409,19 +423,19 @@ public class DictionaryTest extends AbstractPersistenceServerServiceTest {
.build());
assertThat(returnedtype1.isDossierDictionaryOnly()).isTrue();
assertThat(dictionaryClient.getAllTypes(dossier.getDossierTemplateId(), null, false)
.getTypes()
.stream().filter(t -> !t.isSystemManaged()).collect(Collectors.toList())
.size()).isEqualTo(1);
assertThat(dictionaryClient.getAllTypes(dossier.getDossierTemplateId(), dossier.getId(), false)
.getTypes()
.stream().filter(t -> !t.isSystemManaged()).collect(Collectors.toList())
.size()).isEqualTo(2);
assertThat(dictionaryClient.getAllTypes(dossier.getDossierTemplateId(), null, false).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.collect(Collectors.toList()).size()).isEqualTo(1);
assertThat(dictionaryClient.getAllTypes(dossier.getDossierTemplateId(), dossier.getId(), false).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.collect(Collectors.toList()).size()).isEqualTo(2);
var dictionary = dictionaryClient.getDictionaryForType(returnedtype1.getType(), dossier.getDossierTemplateId(), dossier2.getId());
assertThat(dictionaryClient.getAllTypes(dossier.getDossierTemplateId(), dossier2.getId(), false)
.getTypes()
.stream().filter(t -> !t.isSystemManaged()).collect(Collectors.toList())
.size()).isEqualTo(2);
assertThat(dictionaryClient.getAllTypes(dossier.getDossierTemplateId(), dossier2.getId(), false).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.collect(Collectors.toList()).size()).isEqualTo(2);
}
@ -461,9 +475,10 @@ public class DictionaryTest extends AbstractPersistenceServerServiceTest {
assertThat(actualEntries).usingComparator(new ListContentWithoutOrderAndWithoutDuplicatesComparator<>()).isEqualTo(entries);
dictionaryClient.deleteType(createdType.getType(), createdType.getDossierTemplateId());
assertThat(dictionaryClient.getAllTypes(createdType.getDossierTemplateId(), null, false)
.getTypes()
.stream().filter(t -> !t.isSystemManaged()).collect(Collectors.toList()).size()).isEqualTo(0);
assertThat(dictionaryClient.getAllTypes(createdType.getDossierTemplateId(), null, false).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.collect(Collectors.toList()).size()).isEqualTo(0);
dictionaryClient.addType(type);
@ -1019,20 +1034,20 @@ public class DictionaryTest extends AbstractPersistenceServerServiceTest {
.build());
assertThat(dtType.getRank()).isEqualTo(100);
assertThat(dtType.isDossierDictionaryOnly()).isFalse();
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), null, true)
.getTypes()
.stream().filter(t -> !t.isSystemManaged()).collect(Collectors.toList())
.size()).isEqualTo(1);
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), null, true).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.collect(Collectors.toList()).size()).isEqualTo(1);
dictionaryClient.deleteType(dtType.getType(), dtType.getDossierTemplateId());
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), null, false)
.getTypes()
.stream().filter(t -> !t.isSystemManaged()).collect(Collectors.toList())
.size()).isEqualTo(0);
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), null, true)
.getTypes()
.stream().filter(t -> !t.isSystemManaged()).collect(Collectors.toList())
.size()).isEqualTo(1);
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), null, false).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.collect(Collectors.toList()).size()).isEqualTo(0);
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), null, true).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.collect(Collectors.toList()).size()).isEqualTo(1);
var dtType2 = dictionaryClient.addType(CreateTypeValue.builder()
.type("test2")
@ -1046,24 +1061,24 @@ public class DictionaryTest extends AbstractPersistenceServerServiceTest {
.dossierTemplateId(dossierTemplate.getId())
.dossierDictionaryOnly(false)
.build());
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), null, false)
.getTypes()
.stream().filter(t -> !t.isSystemManaged()).collect(Collectors.toList())
.size()).isEqualTo(1);
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), null, true)
.getTypes()
.stream().filter(t -> !t.isSystemManaged()).collect(Collectors.toList())
.size()).isEqualTo(2);
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), null, false).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.collect(Collectors.toList()).size()).isEqualTo(1);
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), null, true).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.collect(Collectors.toList()).size()).isEqualTo(2);
assertThat(dtType2.getRank()).isEqualTo(dtType.getRank());
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), dossier.getId(), false)
.getTypes()
.stream().filter(t -> !t.isSystemManaged()).collect(Collectors.toList())
.size()).isEqualTo(2);
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), dossier.getId(), true)
.getTypes()
.stream().filter(t -> !t.isSystemManaged()).collect(Collectors.toList())
.size()).isEqualTo(3);
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), dossier.getId(), false).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.collect(Collectors.toList()).size()).isEqualTo(2);
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), dossier.getId(), true).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.collect(Collectors.toList()).size()).isEqualTo(3);
}
@ -1096,7 +1111,10 @@ public class DictionaryTest extends AbstractPersistenceServerServiceTest {
.dossierDictionaryOnly(false)
.build());
List<TypeValue> types = dictionaryClient.getAllTypes(dossierTemplate.getId(), null, true).getTypes().stream().filter(t -> !t.isSystemManaged()).collect(Collectors.toList());
List<TypeValue> types = dictionaryClient.getAllTypes(dossierTemplate.getId(), null, true).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.collect(Collectors.toList());
assertThat(types.size()).isEqualTo(1);
String dictionaryEntry = "entry1";
@ -1129,10 +1147,10 @@ public class DictionaryTest extends AbstractPersistenceServerServiceTest {
.build());
assertThat(dtType.getRank()).isEqualTo(100);
assertThat(dtType.isDossierDictionaryOnly()).isFalse();
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), null, true)
.getTypes()
.stream().filter(t -> !t.isSystemManaged()).collect(Collectors.toList())
.size()).isEqualTo(1);
assertThat(dictionaryClient.getAllTypes(dossierTemplate.getId(), null, true).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.collect(Collectors.toList()).size()).isEqualTo(1);
assertThatThrownBy(() -> dictionaryManagementService.addEntries(dtType.getTypeId(),
List.of("entry1", "entry2"),
@ -1143,6 +1161,137 @@ public class DictionaryTest extends AbstractPersistenceServerServiceTest {
}
@Test
void testDeleteDossierThenDeleteType() {
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate, "TestDossier");
CreateTypeValue createTypeValue = CreateTypeValue.builder()
.type("test_type")
.label("Test Type")
.hexColor("#fcba03")
.rank(100)
.hint(false)
.caseInsensitive(false)
.recommendation(false)
.addToDictionaryAction(true)
.dossierTemplateId(dossierTemplate.getId())
.dossierDictionaryOnly(false)
.build();
var type = dictionaryClient.addType(createTypeValue);
List<TypeValue> customTypes = dictionaryClient.getAllTypes(dossierTemplate.getId(), dossier.getId(), false).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.toList();
assertEquals(2, customTypes.size());
dossierClient.deleteDossier(dossier.getId());
dictionaryClient.deleteType(type.getType(), type.getDossierTemplateId());
var types = dictionaryClient.getAllTypes(dossierTemplate.getId(), dossier.getId(), false).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.toList();
assertThat(types).isEmpty();
dictionaryClient.addType(createTypeValue);
customTypes = dictionaryClient.getAllTypes(dossierTemplate.getId(), dossier.getId(), false).getTypes()
.stream()
.filter(t -> !t.isSystemManaged())
.toList();
assertEquals(2, customTypes.size());
}
@Test
void testRecreateTypeAndCheckMergedDictionary() {
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate, "TestDossier");
var type = typeProvider.testAndProvideType(dossierTemplate, null, "type1", false);
List<String> templateEntries = List.of("aaa", "bbb");
dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), templateEntries, false, null, DictionaryEntryType.ENTRY);
var templateDictionary = dictionaryClient.getDictionaryForType(type.getType(), dossierTemplate.getId(), null);
assertThat(templateDictionary.getEntries()).containsExactlyInAnyOrderElementsOf(templateEntries);
List<String> dossierEntries = List.of("ccc");
dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), dossierEntries, false, dossier.getId(), DictionaryEntryType.ENTRY);
var dossierDictionary = dictionaryClient.getDictionaryForType(type.getType(), dossierTemplate.getId(), dossier.getId());
assertThat(dossierDictionary.getEntries()).containsExactlyInAnyOrderElementsOf(dossierEntries);
Dictionary mergedDictionary = dictionaryClient.getMergedDictionaries(type.getType(), type.getDossierTemplateId(), dossier.getId());
assertThat(mergedDictionary).isNotNull();
List<String> allEntries = new ArrayList<>(templateEntries);
allEntries.addAll(dossierEntries);
assertThat(mergedDictionary.getEntries()).containsExactlyInAnyOrderElementsOf(allEntries);
List<String> newDossierEntries = List.of("entry d");
UpdateEntries updateEntries = new UpdateEntries(newDossierEntries, allEntries);
dictionaryClient.updateEntries(type.getType(), type.getDossierTemplateId(), updateEntries, dossier.getId(), DictionaryEntryType.ENTRY);
List<DictionaryEntryEntity> deleted = entryRepository.findAll().stream().filter(DictionaryEntryEntity::isDeleted).toList();
assertEquals(3, deleted.size());
var updatedDossierDictionary = dictionaryClient.getDictionaryForType(type.getType(), dossierTemplate.getId(), dossier.getId());
assertThat(updatedDossierDictionary.getEntries()).containsExactlyInAnyOrderElementsOf(newDossierEntries);
// deletion
dictionaryClient.deleteType(type.getType(), type.getDossierTemplateId());
Assertions.assertThrows(FeignException.NotFound.class, () -> dictionaryClient.getDictionaryForType(type.getType(), dossierTemplate.getId(), null));
List<TypeEntity> deletedType1Entities = typeRepository.findAllTypesByDossierTemplateIdOrDossierId(dossierTemplate.getId(), dossier.getId())
.stream()
.filter(typeEntity -> typeEntity.getType().equals("type1") && typeEntity.isDeleted())
.toList();
assertEquals(2, deletedType1Entities.size());
deleted = entryRepository.findAll().stream().filter(DictionaryEntryEntity::isDeleted).toList();
assertEquals(2, deleted.size());
// recreation
var type2 = typeProvider.testAndProvideType(dossierTemplate, null, "type1", false);
dictionaryClient.addEntry(type2.getType(), type2.getDossierTemplateId(), templateEntries, false, null, DictionaryEntryType.ENTRY);
deleted = entryRepository.findAll().stream().filter(DictionaryEntryEntity::isDeleted).toList();
assertEquals(0, deleted.size());
deletedType1Entities = typeRepository.findAllTypesByDossierTemplateIdOrDossierId(dossierTemplate.getId(), dossier.getId())
.stream()
.filter(typeEntity -> typeEntity.getType().equals("type1") && typeEntity.isDeleted())
.toList();
assertEquals(1, deletedType1Entities.size());
// problematic call because it creates/update entities on a get
dictionaryClient.getAllTypes(dossierTemplate.getId(), dossier.getId(), false);
deletedType1Entities = typeRepository.findAllTypesByDossierTemplateIdOrDossierId(dossierTemplate.getId(), dossier.getId())
.stream()
.filter(typeEntity -> typeEntity.getType().equals("type1") && typeEntity.isDeleted())
.toList();
assertEquals(0, deletedType1Entities.size());
Dictionary mergedDictionary2 = dictionaryClient.getMergedDictionaries(type2.getType(), type2.getDossierTemplateId(), dossier.getId());
assertThat(mergedDictionary2).isNotNull();
assertThat(mergedDictionary2.getEntries()).containsExactlyInAnyOrderElementsOf(templateEntries);
assertThat(mergedDictionary2.getEntries()).doesNotContain("entry d");
}
private static final class ListContentWithoutOrderAndWithoutDuplicatesComparator<T> implements Comparator<List<? extends T>> {
@SuppressWarnings("SuspiciousMethodCalls")

View File

@ -19,12 +19,10 @@ import static org.mockito.Mockito.when;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
@ -32,6 +30,7 @@ import org.junit.jupiter.api.Test;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.annotation.Autowired;
import com.iqser.red.service.dictionarymerge.commons.DictionaryEntry;
import com.iqser.red.service.peristence.v1.server.integration.client.DictionaryClient;
import com.iqser.red.service.peristence.v1.server.integration.client.FileClient;
import com.iqser.red.service.peristence.v1.server.integration.client.FileProcessingClient;
@ -59,7 +58,6 @@ import com.iqser.red.service.persistence.management.v1.processor.service.persist
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.annotations.RecategorizationPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.queue.SearchTermOccurrencesResponseReceiver;
import com.iqser.red.service.persistence.management.v1.processor.service.redactionlog.RedactionRequest;
import com.iqser.red.service.persistence.management.v1.processor.utils.DossierMapper;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AnalyzeResult;
import com.iqser.red.service.persistence.service.v1.api.shared.model.BulkLocalResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel;
@ -80,10 +78,12 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactionResponse;
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.ManualRedactionEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dictionary.Dictionary;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
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.model.dossiertemplate.type.DictionaryEntryType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddCommentRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionBulkLocalRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ForceRedactionRequestModel;
@ -4175,6 +4175,90 @@ public class ManualRedactionTest extends AbstractPersistenceServerServiceTest {
}
}
@Test
public void testAddRedactionWithCommentToDossierAndTemplateDictionary() {
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
var file = fileTesterAndProvider.testAndProvideFile(dossier);
// Create a type that allows adding to dictionary and not to template dictionary
var type = typeProvider.testAndProvideType(dossierTemplate, dossier, "typeWithComment", true, 50);
assertThat(type.isDossierDictionaryOnly()).isTrue();
// Verify initial state of dictionaries
var initialDossierDictionary = internalDictionaryClient.getDictionaryForType(
toTypeId(type.getType(), dossierTemplate.getId(), dossier.getId()), null);
assertThat(initialDossierDictionary.getEntries()).isEmpty();
var initialTemplateDictionary = internalDictionaryClient.getDictionaryForType(
toTypeId(type.getType(), dossierTemplate.getId()), null);
assertThat(initialTemplateDictionary.getEntries()).isEmpty();
// Add a manual redaction with a comment to the dossier dictionary
String comment = "This is a test comment for the redaction.";
String redactionValue = "Leia Organa";
ManualRedactionResponse addRedactionResponse = manualRedactionClient.addRedactionBulk(
dossier.getId(),
file.getId(),
Set.of(AddRedactionRequestModel.builder()
.positions(List.of(Rectangle.builder()
.topLeftX(100)
.topLeftY(200)
.width(50)
.height(20)
.page(1)
.build()))
.section("Confidential Section")
.addToDictionary(true)
.addToAllDossiers(false)
.type(type.getType())
.reason("Confidential Information")
.value(redactionValue)
.legalBasis("GDPR")
.comment(new AddCommentRequestModel(comment))
.build())
);
// Extract the annotation ID from the response for verification
String annotationId = addRedactionResponse.getManualAnnotationResponses()
.stream()
.findFirst()
.map(ManualAnnotationResponse::getAnnotationId)
.orElseThrow(() -> new AssertionError("No annotation ID returned"));
// Verify the redaction is present
var manualRedactions = manualRedactionClient.getManualRedactions(
dossier.getId(), file.getId(), false, true);
assertThat(manualRedactions.getEntriesToAdd()).hasSize(1);
ManualRedactionEntry addedRedaction = manualRedactions.getEntriesToAdd()
.stream()
.filter(entry -> entry.getAnnotationId().equals(annotationId))
.findFirst()
.orElseThrow(() -> new AssertionError("Redaction not found in manual redactions"));
assertThat(addedRedaction.getValue()).isEqualTo(redactionValue);
// Verify the comment was created
List<CommentEntity> comments = commentRepository.findAll();
assertFalse(comments.isEmpty());
assertEquals(comments.get(0).getText(), comment);
assertEquals(comments.get(0).getAnnotationId(), annotationId);
// Verify the redaction is added to the dossier dictionary
var updatedDossierDictionary = internalDictionaryClient.getDictionaryForType(
toTypeId(type.getType(), dossierTemplate.getId(), dossier.getId()), null);
assertThat(updatedDossierDictionary.getEntries().stream().map(DictionaryEntry::getValue)).containsExactly(redactionValue);
// Verify the redaction was not added to the template dictionary
var updatedTemplateDictionary = internalDictionaryClient.getDictionaryForType(
toTypeId(type.getType(), dossierTemplate.getId()), null);
assertThat(updatedTemplateDictionary.getEntries()).isEmpty();
}
@Test
public void testSingleForceAndBulkForceOnDictEntryAutomaticAnalysisOff() {

View File

@ -1 +1 @@
hub.image.name.prefix=docker-dev.knecon.com/tests/
hub.image.name.prefix=docker-dev.knecon.com/tests/

View File

@ -1,12 +1,14 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class EntityLogLegalBasis {
private String name;

View File

@ -11,6 +11,7 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
public class ImportedLegalBasis {
private String technicalName;
private String name;
private String description;
private String reason;