RED-7327 - Add group redactions

This commit is contained in:
Andrei Isvoran 2024-08-19 15:17:48 +02:00
parent 9bcf2d177e
commit 6d1b1ca31b
64 changed files with 3039 additions and 38 deletions

View File

@ -0,0 +1,299 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.group.GroupRedactionService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.GroupRedactionResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.AddAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.AddTextGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.ChangePageRangeAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.ChangeValueAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.RecategorizeGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.ResizeAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.UpdateLegalBasisRequestModel;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import jakarta.validation.Valid;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
@RestController
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class GroupRedactionController implements GroupRedactionResource {
static String GROUP_IDS = "groupIds";
AuditPersistenceService auditPersistenceService;
AccessControlService accessControlService;
GroupRedactionService groupRedactionService;
@Override
public GroupRedactionResponse addTextGroupRedaction(String dossierId, String fileId, @Valid @RequestBody List<AddTextGroupRedactionRequestModel> addRedactionRequests) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsMemberOrApprover(dossierId);
GroupRedactionResponse groupRedactionResponse = groupRedactionService.addTextGroupRedaction(dossierId, fileId, addRedactionRequests);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Group redactions was added")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
GROUP_IDS,
groupRedactionResponse.getGroupRedactions()
.stream()
.map(GroupRedaction::getGroupId)
.toList()))
.build());
return groupRedactionResponse;
}
@Override
public GroupRedactionResponse addAreaGroupRedaction(String dossierId, String fileId, List<AddAreaGroupRedactionRequestModel> addRedactionRequests) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsMemberOrApprover(dossierId);
GroupRedactionResponse groupRedactionResponse = groupRedactionService.addAreaGroupRedaction(dossierId, fileId, addRedactionRequests);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Group redactions was added")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
GROUP_IDS,
groupRedactionResponse.getGroupRedactions()
.stream()
.map(GroupRedaction::getGroupId)
.toList()))
.build());
return groupRedactionResponse;
}
@Override
public GroupRedactionResponse getGroupRedactions(String dossierId, String fileId, boolean includeSoftDeleted) {
return groupRedactionService.getAllGroupRedactions(fileId, includeSoftDeleted);
}
@Override
public GroupRedaction getGroupRedaction(String dossierId, String fileId, String groupId) {
return groupRedactionService.getGroupRedaction(groupId);
}
@Override
public GroupRedactionResponse updateLegalBasis(String dossierId, String fileId, List<UpdateLegalBasisRequestModel> updateLegalBasisRequestModels) {
GroupRedactionResponse groupRedactionResponse = groupRedactionService.updateLegalBasis(dossierId, fileId, updateLegalBasisRequestModels);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Group redactions legal basis was modified.")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
GROUP_IDS,
groupRedactionResponse.getGroupRedactions()
.stream()
.map(GroupRedaction::getGroupId)
.toList(),
"legalBasis",
groupRedactionResponse.getGroupRedactions()
.stream()
.map(GroupRedaction::getLegalBasis)
.toList()))
.build());
return groupRedactionResponse;
}
@Override
public GroupRedactionResponse recategorizeGroupRedaction(String dossierId,
String fileId,
List<RecategorizeGroupRedactionRequestModel> recategorizeGroupRedactionRequestModels) {
GroupRedactionResponse groupRedactionResponse = groupRedactionService.recategorizeGroupRedaction(dossierId, fileId, recategorizeGroupRedactionRequestModels);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Group redactions type was modified.")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
GROUP_IDS,
groupRedactionResponse.getGroupRedactions()
.stream()
.map(GroupRedaction::getGroupId)
.toList(),
"type",
groupRedactionResponse.getGroupRedactions()
.stream()
.map(GroupRedaction::getTypeId)
.toList()))
.build());
return groupRedactionResponse;
}
@Override
public GroupRedactionResponse forceGroupRedaction(String dossierId, String fileId, List<GroupRedactionRequestModel> groupRedactionRequestModels) {
GroupRedactionResponse groupRedactionResponse = groupRedactionService.forceGroupRedaction(dossierId, fileId, groupRedactionRequestModels);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Group redactions forced.")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
GROUP_IDS,
groupRedactionResponse.getGroupRedactions()
.stream()
.map(GroupRedaction::getGroupId)
.toList()))
.build());
return groupRedactionResponse;
}
@Override
public GroupRedactionResponse resizeGroupRedaction(String dossierId, String fileId, List<ResizeAreaGroupRedactionRequestModel> groupRedactionRequestModels) {
GroupRedactionResponse groupRedactionResponse = groupRedactionService.resizeAreaGroupRedaction(dossierId, fileId, groupRedactionRequestModels);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Area Group redactions positions were modified.")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
GROUP_IDS,
groupRedactionResponse.getGroupRedactions()
.stream()
.map(GroupRedaction::getGroupId)
.toList()))
.build());
return groupRedactionResponse;
}
@Override
public GroupRedactionResponse changeValueGroupRedaction(String dossierId, String fileId, List<ChangeValueAreaGroupRedactionRequestModel> groupRedactionRequestModels) {
GroupRedactionResponse groupRedactionResponse = groupRedactionService.changeValueGroupRedaction(dossierId, fileId, groupRedactionRequestModels);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Area Group redactions value was modified.")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
GROUP_IDS,
groupRedactionResponse.getGroupRedactions()
.stream()
.map(GroupRedaction::getGroupId)
.toList()))
.build());
return groupRedactionResponse;
}
@Override
public GroupRedactionResponse changePageRangeGroupRedaction(String dossierId, String fileId, List<ChangePageRangeAreaGroupRedactionRequestModel> groupRedactionRequestModels) {
GroupRedactionResponse groupRedactionResponse = groupRedactionService.changePageRangesGroupRedaction(dossierId, fileId, groupRedactionRequestModels);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Area Group redactions page ranges were modified.")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
GROUP_IDS,
groupRedactionResponse.getGroupRedactions()
.stream()
.map(GroupRedaction::getGroupId)
.toList()))
.build());
return groupRedactionResponse;
}
@Override
public GroupRedactionResponse removeGroupRedaction(String dossierId, String fileId, List<GroupRedactionRequestModel> groupRedactionRequestModels) {
GroupRedactionResponse groupRedactionResponse = groupRedactionService.removeGroupRedaction(dossierId, fileId, groupRedactionRequestModels);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Group redactions were removed.")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
GROUP_IDS,
groupRedactionResponse.getGroupRedactions()
.stream()
.map(GroupRedaction::getGroupId)
.toList()))
.build());
return groupRedactionResponse;
}
}

View File

@ -0,0 +1,174 @@
package com.iqser.red.service.persistence.service.v1.api.external.resource;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.AddAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.AddTextGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.ChangePageRangeAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.ChangeValueAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.RecategorizeGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.ResizeAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.UpdateLegalBasisRequestModel;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
public interface GroupRedactionResource {
String GROUP_REDACTION_REST_PATH = ExternalApi.BASE_PATH + "/group";
String TEXT_GROUP_REDACTION_REST_PATH = GROUP_REDACTION_REST_PATH + "/text";
String AREA_GROUP_REDACTION_REST_PATH = GROUP_REDACTION_REST_PATH + "/area";
String DOSSIER_ID = "dossierId";
String DOSSIER_ID_PATH_PARAM = "/{" + DOSSIER_ID + "}";
String FILE_ID = "fileId";
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
String INCLUDE_SOFT_DELETED_PARAM = "includeSoftDeleted";
String GROUP_ID = "groupId";
String GROUP_ID_PATH_VARIABLE = "/{" + GROUP_ID + "}";
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = TEXT_GROUP_REDACTION_REST_PATH
+ "/add"
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Adds a text group redaction", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
GroupRedactionResponse addTextGroupRedaction(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@Valid @RequestBody List<AddTextGroupRedactionRequestModel> addRedactionRequests);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = AREA_GROUP_REDACTION_REST_PATH
+ "/add"
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Adds an area group redaction", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
GroupRedactionResponse addAreaGroupRedaction(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@Valid @RequestBody List<AddAreaGroupRedactionRequestModel> addRedactionRequests);
@ResponseStatus(value = HttpStatus.OK)
@GetMapping(value = GROUP_REDACTION_REST_PATH + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Returns all the group redactions", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found")})
GroupRedactionResponse getGroupRedactions(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(name = INCLUDE_SOFT_DELETED_PARAM, defaultValue = "false", required = false) boolean includeSoftDeleted);
@ResponseStatus(value = HttpStatus.OK)
@GetMapping(value = GROUP_REDACTION_REST_PATH + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE + GROUP_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Returns a specific the group redactions", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found")})
GroupRedaction getGroupRedaction(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@PathVariable(GROUP_ID) String groupId);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = GROUP_REDACTION_REST_PATH
+ "/updateLegalBasis"
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Update the legal basis of a list of group redactions", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
GroupRedactionResponse updateLegalBasis(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody List<UpdateLegalBasisRequestModel> updateLegalBasisRequestModels);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = GROUP_REDACTION_REST_PATH
+ "/recategorize"
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Update the type of a list of group redactions", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
GroupRedactionResponse recategorizeGroupRedaction(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody List<RecategorizeGroupRedactionRequestModel> recategorizeGroupRedactionRequestModels);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = GROUP_REDACTION_REST_PATH
+ "/force"
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Force of a list of group redactions", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
GroupRedactionResponse forceGroupRedaction(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody List<GroupRedactionRequestModel> groupRedactionRequestModels);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = AREA_GROUP_REDACTION_REST_PATH
+ "/resize"
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Resize a list of area group redactions", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
GroupRedactionResponse resizeGroupRedaction(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody List<ResizeAreaGroupRedactionRequestModel> groupRedactionRequestModels);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = AREA_GROUP_REDACTION_REST_PATH
+ "/change/value"
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Change the value for a list of area group redactions", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
GroupRedactionResponse changeValueGroupRedaction(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody List<ChangeValueAreaGroupRedactionRequestModel> groupRedactionRequestModels);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = AREA_GROUP_REDACTION_REST_PATH
+ "/change/pageRange"
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Change the page range for a list of area group redactions", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
GroupRedactionResponse changePageRangeGroupRedaction(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody List<ChangePageRangeAreaGroupRedactionRequestModel> groupRedactionRequestModels);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = GROUP_REDACTION_REST_PATH
+ "/remove"
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Remove of a list of group redactions", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
GroupRedactionResponse removeGroupRedaction(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody List<GroupRedactionRequestModel> groupRedactionRequestModels);
}

View File

@ -10,12 +10,14 @@ import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.dictionarymerge.commons.DictionaryEntry;
import com.iqser.red.service.persistence.management.v1.processor.service.ColorsService;
import com.iqser.red.service.persistence.management.v1.processor.service.group.GroupRedactionService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.EntryPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.internal.resources.DictionaryResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.Colors;
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.dossiertemplate.type.Type;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -28,6 +30,7 @@ public class DictionaryInternalController implements DictionaryResource {
private final EntryPersistenceService entryPersistenceService;
private final DictionaryPersistenceService dictionaryPersistenceService;
private final ColorsService colorsService;
private final GroupRedactionService groupRedactionService;
@Override
@ -52,22 +55,6 @@ public class DictionaryInternalController implements DictionaryResource {
}
private void setEntriesForTypes(Long fromVersion, List<Type> targets) {
targets.forEach(target -> {
setEntriesForType(target.getTypeId(), target, fromVersion != null ? fromVersion : -1);
});
}
private void setEntriesForType(String typeId, Type target, Long fromVersion) {
target.setEntries(convert(entryPersistenceService.getEntries(typeId, DictionaryEntryType.ENTRY, fromVersion), DictionaryEntry.class));
target.setFalsePositiveEntries(convert(entryPersistenceService.getEntries(typeId, DictionaryEntryType.FALSE_POSITIVE, fromVersion), DictionaryEntry.class));
target.setFalseRecommendationEntries(convert(entryPersistenceService.getEntries(typeId, DictionaryEntryType.FALSE_RECOMMENDATION, fromVersion), DictionaryEntry.class));
}
@Override
public Type getDictionaryForType(@PathVariable(TYPE_PARAMETER_NAME) String typeId, @RequestParam(value = FROM_VERSION_PARAM, required = false) Long fromVersion) {
@ -98,4 +85,28 @@ public class DictionaryInternalController implements DictionaryResource {
return convert(colorsService.getColors(dossierTemplateId), Colors.class);
}
@Override
public GroupRedactionResponse getGroupRedactions(String fileId, boolean includeSoftDeleted) {
log.info("Getting text groups for file through dictionary controller {}", fileId);
return groupRedactionService.getAllGroupRedactions(fileId, includeSoftDeleted);
}
private void setEntriesForTypes(Long fromVersion, List<Type> targets) {
targets.forEach(target -> {
setEntriesForType(target.getTypeId(), target, fromVersion != null ? fromVersion : -1);
});
}
private void setEntriesForType(String typeId, Type target, Long fromVersion) {
target.setEntries(convert(entryPersistenceService.getEntries(typeId, DictionaryEntryType.ENTRY, fromVersion), DictionaryEntry.class));
target.setFalsePositiveEntries(convert(entryPersistenceService.getEntries(typeId, DictionaryEntryType.FALSE_POSITIVE, fromVersion), DictionaryEntry.class));
target.setFalseRecommendationEntries(convert(entryPersistenceService.getEntries(typeId, DictionaryEntryType.FALSE_RECOMMENDATION, fromVersion), DictionaryEntry.class));
}
}

View File

@ -0,0 +1,30 @@
package com.iqser.red.service.persistence.v1.internal.api.controller;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.service.group.GroupRedactionService;
import com.iqser.red.service.persistence.service.v1.api.internal.resources.GroupRedactionResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionInternalResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionResponse;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
@RestController
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@Slf4j
public class GroupRedactionInternalController implements GroupRedactionResource {
GroupRedactionService groupRedactionService;
@Override
public GroupRedactionInternalResponse getGroupRedactions(String dossierId, String fileId, boolean includeSoftDeleted) {
return groupRedactionService.getAllGroupRedactionsInternal(fileId, includeSoftDeleted);
}
}

View File

@ -1,5 +1,7 @@
package com.iqser.red.service.persistence.service.v1.api.internal.resources;
import static com.iqser.red.service.persistence.service.v1.api.internal.resources.GroupRedactionResource.INCLUDE_SOFT_DELETED_PARAM;
import java.util.List;
import org.springframework.http.HttpStatus;
@ -12,6 +14,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.Colors;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.Type;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionResponse;
@ResponseStatus(value = HttpStatus.OK)
public interface DictionaryResource {
@ -35,6 +38,14 @@ public interface DictionaryResource {
String COLOR_PATH = "/color";
String VERSION_PATH = "/version";
String GROUP_REDACTION_REST_PATH = "/group";
String DOSSIER_ID = "dossierId";
String DOSSIER_ID_PATH_PARAM = "/{" + DOSSIER_ID + "}";
String FILE_ID = "fileId";
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
@GetMapping(value = InternalApi.BASE_PATH + TYPE_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
List<Type> getAllTypesForDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@ -64,4 +75,8 @@ public interface DictionaryResource {
@GetMapping(value = InternalApi.BASE_PATH + COLOR_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
Colors getColors(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId);
@GetMapping(value = InternalApi.BASE_PATH + GROUP_REDACTION_REST_PATH + "2" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
GroupRedactionResponse getGroupRedactions(@PathVariable(FILE_ID) String fileId, @RequestParam(name = INCLUDE_SOFT_DELETED_PARAM, defaultValue = "false", required = false) boolean includeSoftDeleted);
}

View File

@ -0,0 +1,32 @@
package com.iqser.red.service.persistence.service.v1.api.internal.resources;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionInternalResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionResponse;
@ResponseStatus(value = HttpStatus.OK)
public interface GroupRedactionResource {
String GROUP_REDACTION_REST_PATH = "/group";
String DOSSIER_ID = "dossierId";
String DOSSIER_ID_PATH_PARAM = "/{" + DOSSIER_ID + "}";
String FILE_ID = "fileId";
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
String INCLUDE_SOFT_DELETED_PARAM = "includeSoftDeleted";
@GetMapping(value = InternalApi.BASE_PATH + GROUP_REDACTION_REST_PATH + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
GroupRedactionInternalResponse getGroupRedactions(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(name = INCLUDE_SOFT_DELETED_PARAM, defaultValue = "false", required = false) boolean includeSoftDeleted);
}

View File

@ -68,6 +68,8 @@ dependencies {
api("org.springframework.cloud:spring-cloud-starter-openfeign:${springCloudVersion}")
api("commons-validator:commons-validator:1.7")
api("com.opencsv:opencsv:5.9")
api("com.vladmihalcea:hibernate-types-60:2.21.1")
api("org.hibernate.orm:hibernate-core:6.5.2.Final")
implementation("org.mapstruct:mapstruct:1.5.5.Final")
annotationProcessor("org.mapstruct:mapstruct-processor:1.5.5.Final")

View File

@ -0,0 +1,34 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.group;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
@Data
@Entity
@DiscriminatorValue("area")
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
public class AreaGroupEntity extends GroupRedactionEntity {
@OneToMany(mappedBy = "areaGroup", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
@JsonManagedReference
private List<PageRangeEntity> pageRanges;
@Column(name = "section")
private String section;
}

View File

@ -0,0 +1,8 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.group;
public enum ChangeType {
REMOVE,
RECATEGORIZE,
LEGAL_BASIS_CHANGE
}

View File

@ -0,0 +1,59 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.group;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupChangeType;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "group_change")
public class GroupChangeEntity {
@Id
@Column(name = "change_id", nullable = false, length = 36)
private String changeId;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "group_id", nullable = false)
@JsonBackReference
private GroupRedactionEntity group;
@Column(name = "user_id", nullable = false)
private String userId;
@Column(name = "change_date", nullable = false)
private OffsetDateTime changeDate;
@Column(name = "change_type", nullable = false)
@Enumerated(EnumType.STRING)
private GroupChangeType changeType;
@Builder.Default
@OneToMany(mappedBy = "groupChange", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
@JsonManagedReference
private List<PropertyChangeEntity> propertyChanges = new ArrayList<>();
}

View File

@ -0,0 +1,80 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.group;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.DiscriminatorColumn;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
@Data
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "group_type")
@Table(name = "group_redaction")
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class GroupRedactionEntity {
@Id
@Column(name = "group_id", nullable = false, length = 36)
private String groupId;
@Column(name = "file_id")
private String fileId;
@Column(name = "type_id")
private String typeId;
@Column(name = "value", length = 4000)
private String value;
@Column(name = "legal_basis")
private String legalBasis;
@Enumerated(EnumType.STRING)
@Column(name = "entry_type")
private EntryType entryType;
@Column(name = "user_id")
private String userId;
@Column(name = "request_date")
private OffsetDateTime requestDate;
@Column(name = "processed_date")
private OffsetDateTime processedDate;
@Column(name = "soft_deleted_time")
private OffsetDateTime softDeletedTime;
@Builder.Default
@OneToMany(mappedBy = "group", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
@JsonManagedReference
private List<GroupChangeEntity> groupChanges = new ArrayList<>();
@OneToOne(mappedBy = "groupRedaction", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
@JsonManagedReference
private PositionOnPageEntity positionsOnPage;
}

View File

@ -0,0 +1,52 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.group;
import java.util.UUID;
import com.fasterxml.jackson.annotation.JsonBackReference;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.PrePersist;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "area_group_page_range")
public class PageRangeEntity {
@Id
@Column(name = "page_range_id", nullable = false, length = 36)
private String pageRangeId;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "group_id", nullable = false)
@JsonBackReference
private AreaGroupEntity areaGroup;
@Column(name = "start_page", nullable = false)
private int startPage;
@Column(name = "end_page", nullable = false)
private int endPage;
@PrePersist
public void initializeUUID() {
if (this.pageRangeId == null) {
this.pageRangeId = UUID.randomUUID().toString();
}
}
}

View File

@ -0,0 +1,62 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.group;
import java.util.UUID;
import com.fasterxml.jackson.annotation.JsonBackReference;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToOne;
import jakarta.persistence.PrePersist;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "area_group_position")
public class PositionOnPageEntity {
@Id
@Column(name = "position_id", nullable = false, length = 36)
private String positionId;
@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "group_id", nullable = false)
@JsonBackReference
private GroupRedactionEntity groupRedaction;
@Column(name = "x", nullable = false)
private float x;
@Column(name = "y", nullable = false)
private float y;
@Column(name = "width", nullable = false)
private float width;
@Column(name = "height", nullable = false)
private float height;
@Column(name = "page_number", nullable = false)
private int pageNumber;
@PrePersist
public void initializeUUID() {
if (this.positionId == null) {
this.positionId = UUID.randomUUID().toString();
}
}
}

View File

@ -0,0 +1,44 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.group;
import com.fasterxml.jackson.annotation.JsonBackReference;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "property_change")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PropertyChangeEntity {
@Id
@Column(name = "property_change_id", nullable = false, length = 36)
private String propertyChangeId;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "change_id", nullable = false)
@JsonBackReference
private GroupChangeEntity groupChange;
@Column(name = "property_name", nullable = false)
private String propertyName;
@Column(name = "property_old_value", nullable = false)
private String propertyOldValue;
@Column(name = "property_new_value", nullable = false)
private String propertyNewValue;
}

View File

@ -0,0 +1,16 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.group;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.SuperBuilder;
@Data
@Entity
@DiscriminatorValue("text")
@AllArgsConstructor
@SuperBuilder
public class TextGroupEntity extends GroupRedactionEntity {
}

View File

@ -24,9 +24,11 @@ import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.GroupRedactionEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.PendingDictionaryEntryFactory;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.PendingGroupRedactionFactory;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Change;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ChangeType;
@ -69,10 +71,11 @@ public class EntityLogMergeService {
DictionaryPersistenceService dictionaryPersistenceService;
PendingDictionaryEntryFactory pendingDictionaryEntryFactory;
EntityLogMongoService entityLogMongoService;
PendingGroupRedactionFactory pendingGroupRedactionFactory;
@Observed(name = "EntityLogMergeService", contextualName = "merge-entity-log")
public EntityLog mergeEntityLog(ManualRedactions unprocessedManualRedactions, EntityLog entityLog, DossierEntity dossier) {
public EntityLog mergeEntityLog(ManualRedactions unprocessedManualRedactions, EntityLog entityLog, DossierEntity dossier, List<GroupRedactionEntity> unprocessedGroupRedactions) {
final int analysisNumber = entityLog.getAnalysisNumber();
@ -80,7 +83,7 @@ public class EntityLogMergeService {
List<EntityLogEntry> entityLogEntries = new LinkedList<>(entityLog.getEntityLogEntry());
merge(unprocessedManualRedactions, entityLog.getEntityLogEntry(), dossier, analysisNumber, entityLogEntries, allManualChanges);
merge(unprocessedManualRedactions, entityLog.getEntityLogEntry(), dossier, analysisNumber, entityLogEntries, allManualChanges, unprocessedGroupRedactions);
entityLog.setEntityLogEntry(entityLogEntries);
return entityLog;
@ -97,7 +100,7 @@ public class EntityLogMergeService {
List<EntityLogEntry> entityLogEntries = entityLogMongoService.findEntityLogEntriesByIds(dossier.getId(), fileId, entityLogEntryIds);
merge(unprocessedManualRedactions, entityLogEntries, dossier, analysisNumber, entityLogEntries, allManualChanges);
merge(unprocessedManualRedactions, entityLogEntries, dossier, analysisNumber, entityLogEntries, allManualChanges, Collections.emptyList());
return entityLogEntries;
}
@ -108,19 +111,28 @@ public class EntityLogMergeService {
DossierEntity dossier,
int analysisNumber,
List<EntityLogEntry> entityLogEntries,
Map<String, List<BaseAnnotation>> allManualChanges) {
Map<String, List<BaseAnnotation>> allManualChanges,
List<GroupRedactionEntity> unprocessedGroupRedactions) {
Map<String, String> trackLocalChangesBasedOnDictEntriesMap = new HashMap<>();
Map<String, EntityLogEntry> addedLocalManualEntries = buildUnprocessedLocalManualRedactions(unprocessedManualRedactions, entityLog, dossier, analysisNumber)//
.collect(Collectors.toMap(EntityLogEntry::getId, Function.identity()));
entityLogEntries.addAll(addedLocalManualEntries.values());
buildPendingDictionaryChanges(unprocessedManualRedactions).forEach(entityLogEntries::add);
buildGroupRedactions(unprocessedGroupRedactions).forEach(entityLogEntries::add);
processEntityLogEntries(dossier, entityLogEntries, addedLocalManualEntries, analysisNumber, allManualChanges, trackLocalChangesBasedOnDictEntriesMap);
adjustEntityLogEntriesAfterLocalChangesBasedOnDict(entityLogEntries, trackLocalChangesBasedOnDictEntriesMap, analysisNumber);
}
private Stream<EntityLogEntry> buildGroupRedactions(List<GroupRedactionEntity> unprocessedGroupRedactions) {
return unprocessedGroupRedactions.stream()
.map(pendingGroupRedactionFactory::buildGroupRedactionEntry);
}
private void adjustEntityLogEntriesAfterLocalChangesBasedOnDict(List<EntityLogEntry> entityLogEntries,
Map<String, String> trackLocalChangesBasedOnDictEntriesMap,
int analysisNumber) {

View File

@ -37,7 +37,12 @@ public class EntityLogMongoWrapperService {
if (includeUnprocessed) {
DossierEntity dossier = dossierService.getDossierById(dossierId);
ManualRedactions unprocessedManualRedactions = manualRedactionProviderService.getManualRedactions(fileId, ManualChangesQueryOptions.unprocessedOnlyForIds(ids));
entityLogEntries = entityLogMergeService.mergeEntityLogEntries(unprocessedManualRedactions, entityLogEntries.stream().map(EntityLogEntry::getId).toList(), dossier, fileId);
entityLogEntries = entityLogMergeService.mergeEntityLogEntries(unprocessedManualRedactions,
entityLogEntries.stream()
.map(EntityLogEntry::getId)
.toList(),
dossier,
fileId);
}
return entityLogEntries;
}
@ -47,4 +52,5 @@ public class EntityLogMongoWrapperService {
return String.format("EntityLogEntry does not exist for annotationId ID \"%s\"!", annotationId);
}
}

View File

@ -9,8 +9,11 @@ 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.group.GroupRedactionEntity;
import com.iqser.red.service.persistence.management.v1.processor.model.ManualChangesQueryOptions;
import com.iqser.red.service.persistence.management.v1.processor.service.group.GroupRedactionPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionProviderService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.group.GroupRedactionRepository;
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.FilteredEntityLogRequest;
@ -35,6 +38,7 @@ public class EntityLogService {
DossierService dossierService;
CommentService commentService;
EntityLogMergeService entityLogMergeService;
GroupRedactionRepository groupRedactionRepository;
@Observed(name = "EntityLogService", contextualName = "get-entity-log")
@ -74,7 +78,8 @@ public class EntityLogService {
if (includeUnprocessed) {
DossierEntity dossier = dossierService.getDossierById(dossierId);
ManualRedactions unprocessedManualRedactions = manualRedactionProviderService.getManualRedactions(fileId, ManualChangesQueryOptions.unprocessedOnly());
processedEntityLog = entityLogMergeService.mergeEntityLog(unprocessedManualRedactions, processedEntityLog, dossier);
List<GroupRedactionEntity> unprocessedGroupRedactions = groupRedactionRepository.findAllUnprocessedGroupRedactions(fileId);
processedEntityLog = entityLogMergeService.mergeEntityLog(unprocessedManualRedactions, processedEntityLog, dossier, unprocessedGroupRedactions);
}
if (fileStatus.getExcludedPages() != null && !fileStatus.getExcludedPages().isEmpty()) {

View File

@ -1,12 +1,11 @@
package com.iqser.red.service.persistence.management.v1.processor.service;
import org.apache.commons.lang3.StringUtils;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException;
import com.iqser.red.service.persistence.management.v1.processor.service.group.GroupRedactionService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService;
@ -33,6 +32,7 @@ public class FileStatusProcessingUpdateService {
private final ManualRedactionService manualRedactionService;
private final FileManagementServiceSettings settings;
private final FileStatusPersistenceService fileStatusPersistenceService;
private final GroupRedactionService groupRedactionService;
public void analysisSuccessful(String dossierId, String fileId, AnalyzeResult analyzeResult) {
@ -54,6 +54,7 @@ public class FileStatusProcessingUpdateService {
}
manualRedactionService.updateProcessedDate(analyzeResult.getManualRedactions());
groupRedactionService.updateProcessedDate(analyzeResult.getGroupRedactions());
if (analyzeResult.getAddedFileAttributes() != null && !analyzeResult.getAddedFileAttributes().isEmpty()) {
fileStatusPersistenceService.addFileAttributes(dossierId, fileId, analyzeResult.getAddedFileAttributes());

View File

@ -0,0 +1,50 @@
package com.iqser.red.service.persistence.management.v1.processor.service.group;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.UUID;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.GroupChangeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.GroupRedactionEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.PropertyChangeEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.group.GroupChangeRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupChangeType;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class GroupChangePersistenceService {
GroupChangeRepository groupChangeRepository;
public GroupChangeEntity addGroupChange(GroupChangeType changeType, GroupRedactionEntity groupRedaction, List<PropertyChangeEntity> propertyChangeEntities) {
GroupChangeEntity groupChange = GroupChangeEntity.builder()
.userId(KeycloakSecurity.getUserId())
.changeDate(OffsetDateTime.now())
.changeId(UUID.randomUUID().toString())
.changeType(changeType)
.group(groupRedaction)
.build();
List<PropertyChangeEntity> initializedPropertyChanges = propertyChangeEntities.stream().peek(propertyChange -> {
propertyChange.setPropertyChangeId(UUID.randomUUID().toString());
propertyChange.setGroupChange(groupChange);
}).toList();
groupChange.setPropertyChanges(initializedPropertyChanges);
return groupChangeRepository.saveAndFlush(groupChange);
}
}

View File

@ -0,0 +1,160 @@
package com.iqser.red.service.persistence.management.v1.processor.service.group;
import java.util.ArrayList;
import java.util.List;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.AreaGroupEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.GroupChangeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.GroupRedactionEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.PageRangeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.PositionOnPageEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.PropertyChangeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.TextGroupEntity;
import com.iqser.red.service.persistence.service.v1.api.shared.model.PageRange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.AreaGroupRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupChange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupPropertyChange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.PositionOnPage;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.TextGroupRedaction;
import lombok.experimental.UtilityClass;
@UtilityClass
public class GroupRedactionMapper {
public static GroupRedaction toGroupRedaction(GroupRedactionEntity groupRedactionEntity) {
if (groupRedactionEntity instanceof TextGroupEntity textGroupEntity) {
return toTextGroupRedaction(textGroupEntity);
} else if (groupRedactionEntity instanceof AreaGroupEntity areaGroupEntity) {
return toAreaGroupRedaction(areaGroupEntity);
} else {
return null;
}
}
public static AreaGroupRedaction toAreaGroupRedaction(AreaGroupEntity areaGroupEntity) {
List<PageRange> pageRanges = new ArrayList<>();
areaGroupEntity.getPageRanges()
.forEach(pageRangeEntity -> pageRanges.add(toPageRange(pageRangeEntity)));
return AreaGroupRedaction.builder()
.groupRedactionType(GroupRedactionType.AREA)
.groupId(areaGroupEntity.getGroupId())
.legalBasis(areaGroupEntity.getLegalBasis())
.value(areaGroupEntity.getValue())
.entryType(areaGroupEntity.getEntryType())
.processedDate(areaGroupEntity.getProcessedDate())
.requestDate(areaGroupEntity.getRequestDate())
.softDeletedTime(areaGroupEntity.getSoftDeletedTime())
.typeId(areaGroupEntity.getTypeId())
.userId(areaGroupEntity.getUserId())
.fileId(areaGroupEntity.getFileId())
.groupChanges(toGroupChanges(areaGroupEntity.getGroupChanges()))
.positionOnPage(toPositionOnPage(areaGroupEntity.getPositionsOnPage()))
.pageRanges(pageRanges)
.section(areaGroupEntity.getSection())
.build();
}
public static PositionOnPage toPositionOnPage(PositionOnPageEntity positionOnPageEntity) {
return PositionOnPage.builder()
.x(positionOnPageEntity.getX())
.y(positionOnPageEntity.getY())
.width(positionOnPageEntity.getWidth())
.height(positionOnPageEntity.getHeight())
.pageNumber(positionOnPageEntity.getPageNumber())
.build();
}
public static PositionOnPageEntity toPositionOnPageEntity(PositionOnPage positionOnPage) {
return PositionOnPageEntity.builder()
.x(positionOnPage.getX())
.y(positionOnPage.getY())
.width(positionOnPage.getWidth())
.height(positionOnPage.getHeight())
.pageNumber(positionOnPage.getPageNumber())
.build();
}
public static PageRange toPageRange(PageRangeEntity pageRangeEntity) {
return PageRange.builder().startPage(pageRangeEntity.getStartPage()).endPage(pageRangeEntity.getEndPage()).build();
}
public static PageRangeEntity toPageRangeEntity(PageRange pageRangeEntity) {
return PageRangeEntity.builder().startPage(pageRangeEntity.getStartPage()).endPage(pageRangeEntity.getEndPage()).build();
}
public static TextGroupRedaction toTextGroupRedaction(TextGroupEntity textGroupEntity) {
return TextGroupRedaction.builder()
.groupRedactionType(GroupRedactionType.TEXT)
.groupId(textGroupEntity.getGroupId())
.legalBasis(textGroupEntity.getLegalBasis())
.value(textGroupEntity.getValue())
.entryType(textGroupEntity.getEntryType())
.processedDate(textGroupEntity.getProcessedDate())
.requestDate(textGroupEntity.getRequestDate())
.softDeletedTime(textGroupEntity.getSoftDeletedTime())
.typeId(textGroupEntity.getTypeId())
.userId(textGroupEntity.getUserId())
.fileId(textGroupEntity.getFileId())
.positionOnPage(toPositionOnPage(textGroupEntity.getPositionsOnPage()))
.groupChanges(toGroupChanges(textGroupEntity.getGroupChanges()))
.build();
}
public static List<GroupChange> toGroupChanges(List<GroupChangeEntity> groupChangeEntities) {
List<GroupChange> groupChanges = new ArrayList<>();
if (groupChangeEntities == null || groupChangeEntities.isEmpty()) {
return groupChanges;
}
groupChangeEntities.forEach(groupChangeEntity -> groupChanges.add(GroupChange.builder()
.changeDate(groupChangeEntity.getChangeDate())
.changeId(groupChangeEntity.getChangeId())
.changeType(groupChangeEntity.getChangeType())
.userId(groupChangeEntity.getUserId())
.propertyChanges(toGroupPropertyChanges(groupChangeEntity.getPropertyChanges()))
.build()));
return groupChanges;
}
public static List<GroupPropertyChange> toGroupPropertyChanges(List<PropertyChangeEntity> propertyChangeEntities) {
List<GroupPropertyChange> groupPropertyChanges = new ArrayList<>();
if (propertyChangeEntities == null || propertyChangeEntities.isEmpty()) {
return groupPropertyChanges;
}
propertyChangeEntities.forEach(propertyChangeEntity -> groupPropertyChanges.add(GroupPropertyChange.builder()
.propertyChangeId(propertyChangeEntity.getPropertyChangeId())
.propertyName(propertyChangeEntity.getPropertyName())
.propertyOldValue(propertyChangeEntity.getPropertyOldValue())
.propertyNewValue(propertyChangeEntity.getPropertyNewValue())
.build()));
return groupPropertyChanges;
}
}

View File

@ -0,0 +1,179 @@
package com.iqser.red.service.persistence.management.v1.processor.service.group;
import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.AreaGroupEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.GroupRedactionEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.PageRangeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.PositionOnPageEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.TextGroupEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.group.GroupRedactionRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.model.PageRange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.AddAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.AddGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.PositionOnPage;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class GroupRedactionPersistenceService {
GroupRedactionRepository groupRedactionRepository;
public TextGroupEntity addTextGroupEntity(String fileId, AddGroupRedactionRequestModel addGroupRedactionRequestModel) {
PositionOnPageEntity positionOnPageEntity = GroupRedactionMapper.toPositionOnPageEntity(addGroupRedactionRequestModel.getPositionOnPage());
TextGroupEntity textGroupEntity = TextGroupEntity.builder()
.groupId(UUID.randomUUID().toString())
.entryType(addGroupRedactionRequestModel.getEntryType())
.fileId(fileId)
.legalBasis(addGroupRedactionRequestModel.getLegalBasis())
.typeId(addGroupRedactionRequestModel.getType())
.value(addGroupRedactionRequestModel.getValue())
.positionsOnPage(positionOnPageEntity)
.requestDate(OffsetDateTime.now())
.userId(KeycloakSecurity.getUserId())
.build();
positionOnPageEntity.setGroupRedaction(textGroupEntity);
return groupRedactionRepository.saveAndFlush(textGroupEntity);
}
public AreaGroupEntity addAreaGroupEntity(String fileId, AddAreaGroupRedactionRequestModel addAreaGroupRedactionRequestModel) {
PositionOnPageEntity positionOnPageEntity = GroupRedactionMapper.toPositionOnPageEntity(addAreaGroupRedactionRequestModel.getPositionOnPage());
AreaGroupEntity areaGroupEntity = AreaGroupEntity.builder()
.groupId(UUID.randomUUID().toString())
.entryType(addAreaGroupRedactionRequestModel.getEntryType())
.fileId(fileId)
.legalBasis(addAreaGroupRedactionRequestModel.getLegalBasis())
.typeId(addAreaGroupRedactionRequestModel.getType())
.value(addAreaGroupRedactionRequestModel.getValue())
.positionsOnPage(positionOnPageEntity)
.requestDate(OffsetDateTime.now())
.userId(KeycloakSecurity.getUserId())
.section(addAreaGroupRedactionRequestModel.getSection())
.build();
positionOnPageEntity.setGroupRedaction(areaGroupEntity);
List<PageRangeEntity> pageRanges = new ArrayList<>();
addAreaGroupRedactionRequestModel.getPageRanges().forEach(pageRange -> {
PageRangeEntity pageRangeEntity = GroupRedactionMapper.toPageRangeEntity(pageRange);
pageRangeEntity.setAreaGroup(areaGroupEntity);
pageRanges.add(pageRangeEntity);
});
areaGroupEntity.setPageRanges(pageRanges);
return groupRedactionRepository.saveAndFlush(areaGroupEntity);
}
public List<GroupRedactionEntity> findAllGroupEntitiesForFile(String fileId, boolean includeSoftDeleted) {
if (includeSoftDeleted) {
return groupRedactionRepository.findAllByFileId(fileId);
}
else {
return groupRedactionRepository.findAllByFileIdAndNotSoftDeleted(fileId);
}
}
public GroupRedactionEntity findGroupRedaction(String groupId) {
return groupRedactionRepository.findById(groupId)
.orElseThrow(() -> new NotFoundException(String.format("Group redaction with id %s not found.", groupId)));
}
public void markAsProcessed(String groupId) {
groupRedactionRepository.markAsProcessed(groupId, OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
}
public GroupRedactionEntity updateLegalBasis(GroupRedactionEntity groupRedaction, String legalBasis) {
groupRedaction.setLegalBasis(legalBasis);
return groupRedactionRepository.saveAndFlush(groupRedaction);
}
public GroupRedactionEntity forceGroupRedaction(GroupRedactionEntity groupRedaction) {
groupRedaction.setEntryType(EntryType.ENTITY);
return groupRedactionRepository.saveAndFlush(groupRedaction);
}
public GroupRedactionEntity recategorizeGroupRedaction(GroupRedactionEntity groupRedaction, String typeId) {
groupRedaction.setTypeId(typeId);
return groupRedactionRepository.saveAndFlush(groupRedaction);
}
public AreaGroupEntity resizeAreaGroupRedaction(AreaGroupEntity areaGroupEntity, PositionOnPage positionOnPage) {
PositionOnPageEntity positionEntity = GroupRedactionMapper.toPositionOnPageEntity(positionOnPage);
positionEntity.setGroupRedaction(areaGroupEntity);
areaGroupEntity.setPositionsOnPage(positionEntity);
return groupRedactionRepository.saveAndFlush(areaGroupEntity);
}
public AreaGroupEntity changeValueAreaGroupRedaction(AreaGroupEntity areaGroupEntity, String value) {
areaGroupEntity.setValue(value);
return groupRedactionRepository.saveAndFlush(areaGroupEntity);
}
public AreaGroupEntity changePageRangesAreaGroupRedaction(AreaGroupEntity areaGroupEntity, List<PageRange> pageRanges) {
List<PageRangeEntity> existingPageRanges = areaGroupEntity.getPageRanges();
existingPageRanges.clear();
pageRanges.forEach(pageRange -> {
PageRangeEntity pageRangeEntity = GroupRedactionMapper.toPageRangeEntity(pageRange);
pageRangeEntity.setAreaGroup(areaGroupEntity);
existingPageRanges.add(pageRangeEntity);
});
areaGroupEntity.setPageRanges(existingPageRanges);
return groupRedactionRepository.saveAndFlush(areaGroupEntity);
}
public GroupRedactionEntity removeGroupRedaction(GroupRedactionEntity groupRedaction) {
groupRedaction.setSoftDeletedTime(OffsetDateTime.now());
return groupRedactionRepository.saveAndFlush(groupRedaction);
}
}

View File

@ -0,0 +1,442 @@
package com.iqser.red.service.persistence.management.v1.processor.service.group;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.AreaGroupEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.GroupChangeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.GroupRedactionEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.PageRangeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.PositionOnPageEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.PropertyChangeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.TextGroupEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.AddAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.AddTextGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.AreaGroupRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.ChangePageRangeAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.ChangeValueAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupChange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupChangeType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionInternalResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.RecategorizeGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.ResizeAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.TextGroupRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.UpdateLegalBasisRequestModel;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
@Service
@RequiredArgsConstructor
@Slf4j
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class GroupRedactionService {
GroupRedactionPersistenceService groupRedactionPersistenceService;
GroupChangePersistenceService groupChangePersistenceService;
FileStatusPersistenceService fileStatusPersistenceService;
FileStatusService fileStatusService;
@Transactional
public GroupRedactionResponse addTextGroupRedaction(String dossierId, String fileId, List<AddTextGroupRedactionRequestModel> addRedactionRequests) {
List<GroupRedaction> groupRedactions = new ArrayList<>();
addRedactionRequests.forEach(addGroupRedactionRequestModel -> {
validateAddTextGroupRequest(addGroupRedactionRequestModel);
TextGroupEntity textGroupEntity = groupRedactionPersistenceService.addTextGroupEntity(fileId, addGroupRedactionRequestModel);
GroupRedaction groupRedaction = GroupRedactionMapper.toGroupRedaction(textGroupEntity);
groupRedactions.add(groupRedaction);
});
reprocess(dossierId, fileId);
fileStatusPersistenceService.setLastManualChangeDate(fileId, OffsetDateTime.now());
return GroupRedactionResponse.builder().groupRedactions(groupRedactions).build();
}
@Transactional
public GroupRedactionResponse addAreaGroupRedaction(String dossierId, String fileId, List<AddAreaGroupRedactionRequestModel> addRedactionRequests) {
List<GroupRedaction> groupRedactions = new ArrayList<>();
addRedactionRequests.forEach(addGroupRedactionRequestModel -> {
validateAddAreaGroupRedactionRequest(addGroupRedactionRequestModel);
AreaGroupEntity areaGroupEntity = groupRedactionPersistenceService.addAreaGroupEntity(fileId, addGroupRedactionRequestModel);
AreaGroupRedaction areaGroupRedaction = GroupRedactionMapper.toAreaGroupRedaction(areaGroupEntity);
groupRedactions.add(areaGroupRedaction);
});
reprocess(dossierId, fileId);
fileStatusPersistenceService.setLastManualChangeDate(fileId, OffsetDateTime.now());
return GroupRedactionResponse.builder().groupRedactions(groupRedactions).build();
}
private void validateAddAreaGroupRedactionRequest(AddAreaGroupRedactionRequestModel addAreaGroupRedactionRequestModel) {
if (!addAreaGroupRedactionRequestModel.getGroupRedactionType().equals(GroupRedactionType.AREA)) {
throw new BadRequestException("Area group redaction needs group redaction type to be AREA!");
}
if (addAreaGroupRedactionRequestModel.getPositionOnPage() == null) {
throw new BadRequestException("Area group redaction requires positions on page!");
}
if (addAreaGroupRedactionRequestModel.getValue().length() > 4000) {
throw new BadRequestException("Group redaction value can not exceed 4000 characters!");
}
}
private void validateAddTextGroupRequest(AddTextGroupRedactionRequestModel addTextGroupRedactionRequestModel) {
if (!addTextGroupRedactionRequestModel.getGroupRedactionType().equals(GroupRedactionType.TEXT)) {
throw new BadRequestException("Text group redaction needs group redaction type to be TEXT!");
}
if (addTextGroupRedactionRequestModel.getValue().length() > 4000) {
throw new BadRequestException("Group redaction value can not exceed 4000 characters!");
}
}
private void reprocess(String dossierId, String fileId) {
fileStatusService.setStatusReprocessForManual(dossierId, fileId, true);
}
@Transactional(readOnly = true)
public GroupRedactionResponse getAllGroupRedactions(String fileId, boolean includeSoftDeleted) {
GroupRedactionResponse groupRedactionResponse = new GroupRedactionResponse();
List<GroupRedactionEntity> groupRedactionEntities = groupRedactionPersistenceService.findAllGroupEntitiesForFile(fileId, includeSoftDeleted);
groupRedactionResponse.setGroupRedactions(groupRedactionEntities.stream()
.map(GroupRedactionMapper::toGroupRedaction)
.collect(Collectors.toList()));
return groupRedactionResponse;
}
@Transactional(readOnly = true)
public GroupRedactionInternalResponse getAllGroupRedactionsInternal(String fileId, boolean includeSoftDeleted) {
List<TextGroupRedaction> textGroupRedactions = new ArrayList<>();
List<AreaGroupRedaction> areaGroupRedactions = new ArrayList<>();
List<GroupRedactionEntity> groupRedactionEntities = groupRedactionPersistenceService.findAllGroupEntitiesForFile(fileId, includeSoftDeleted);
groupRedactionEntities.forEach(groupRedaction -> {
if (groupRedaction instanceof TextGroupEntity textGroupEntity) {
textGroupRedactions.add(GroupRedactionMapper.toTextGroupRedaction(textGroupEntity));
} else if (groupRedaction instanceof AreaGroupEntity areaGroupEntity) {
areaGroupRedactions.add(GroupRedactionMapper.toAreaGroupRedaction(areaGroupEntity));
}
});
return GroupRedactionInternalResponse.builder().areaGroupRedactions(areaGroupRedactions).textGroupRedactions(textGroupRedactions).build();
}
@Transactional(readOnly = true)
public GroupRedaction getGroupRedaction(String groupId) {
GroupRedactionEntity groupRedactionEn = groupRedactionPersistenceService.findGroupRedaction(groupId);
return GroupRedactionMapper.toGroupRedaction(groupRedactionEn);
}
@Transactional
public void updateProcessedDate(Set<GroupRedaction> groupRedactions) {
if (groupRedactions != null) {
groupRedactions.forEach(groupRedaction -> {
if (groupRedaction.getProcessedDate() == null || groupRedaction.getGroupChanges()
.stream()
.anyMatch(change -> change.getChangeDate().isAfter(groupRedaction.getProcessedDate()))) {
groupRedactionPersistenceService.markAsProcessed(groupRedaction.getGroupId());
}
});
}
}
@Transactional
public GroupRedactionResponse updateLegalBasis(String dossierId, String fileId, List<UpdateLegalBasisRequestModel> updateLegalBasisRequestModels) {
List<GroupRedaction> groupRedactions = new ArrayList<>();
updateLegalBasisRequestModels.forEach(updateLegalBasisRequestModel -> {
GroupRedactionEntity oldGroupEntity = groupRedactionPersistenceService.findGroupRedaction(updateLegalBasisRequestModel.getGroupId());
String oldLegalBasis = oldGroupEntity.getLegalBasis();
GroupRedactionEntity newGroupEntity = groupRedactionPersistenceService.updateLegalBasis(oldGroupEntity, updateLegalBasisRequestModel.getLegalBasis());
GroupRedaction groupRedaction = GroupRedactionMapper.toGroupRedaction(newGroupEntity);
List<PropertyChangeEntity> propertyChangeEntities = new ArrayList<>();
propertyChangeEntities.add(PropertyChangeEntity.builder()
.propertyName("legalBasis")
.propertyOldValue(oldLegalBasis)
.propertyNewValue(newGroupEntity.getLegalBasis())
.build());
addChange(newGroupEntity, GroupChangeType.LEGAL_BASIS_CHANGE, propertyChangeEntities, groupRedaction);
groupRedactions.add(groupRedaction);
});
reprocess(dossierId, fileId);
fileStatusPersistenceService.setLastManualChangeDate(fileId, OffsetDateTime.now());
return GroupRedactionResponse.builder().groupRedactions(groupRedactions).build();
}
@Transactional
public GroupRedactionResponse recategorizeGroupRedaction(String dossierId, String fileId, List<RecategorizeGroupRedactionRequestModel> groupRedactionRequestModels) {
List<GroupRedaction> groupRedactions = new ArrayList<>();
groupRedactionRequestModels.forEach(groupRedactionRequestModel -> {
GroupRedactionEntity oldGroupEntity = groupRedactionPersistenceService.findGroupRedaction(groupRedactionRequestModel.getGroupId());
String oldType = oldGroupEntity.getTypeId();
GroupRedactionEntity newGroupEntity = groupRedactionPersistenceService.recategorizeGroupRedaction(oldGroupEntity, groupRedactionRequestModel.getTypeId());
GroupRedaction groupRedaction = GroupRedactionMapper.toGroupRedaction(newGroupEntity);
List<PropertyChangeEntity> propertyChangeEntities = new ArrayList<>();
propertyChangeEntities.add(PropertyChangeEntity.builder()
.propertyName("type_id")
.propertyOldValue(oldType)
.propertyNewValue(newGroupEntity.getTypeId())
.build());
addChange(newGroupEntity, GroupChangeType.RECATEGORIZE, propertyChangeEntities, groupRedaction);
groupRedactions.add(groupRedaction);
});
reprocess(dossierId, fileId);
fileStatusPersistenceService.setLastManualChangeDate(fileId, OffsetDateTime.now());
return GroupRedactionResponse.builder().groupRedactions(groupRedactions).build();
}
@Transactional
public GroupRedactionResponse forceGroupRedaction(String dossierId, String fileId, List<GroupRedactionRequestModel> groupRedactionRequestModels) {
List<GroupRedaction> groupRedactions = new ArrayList<>();
groupRedactionRequestModels.forEach(groupRedactionRequestModel -> {
GroupRedactionEntity oldGroupEntity = groupRedactionPersistenceService.findGroupRedaction(groupRedactionRequestModel.getGroupId());
String oldEntryType = oldGroupEntity.getEntryType().name();
GroupRedactionEntity newGroupEntity = groupRedactionPersistenceService.forceGroupRedaction(oldGroupEntity);
GroupRedaction groupRedaction = GroupRedactionMapper.toGroupRedaction(newGroupEntity);
List<PropertyChangeEntity> propertyChangeEntities = new ArrayList<>();
propertyChangeEntities.add(PropertyChangeEntity.builder()
.propertyName("entryType")
.propertyOldValue(oldEntryType)
.propertyNewValue(newGroupEntity.getEntryType().name())
.build());
addChange(newGroupEntity, GroupChangeType.FORCE, propertyChangeEntities, groupRedaction);
groupRedactions.add(groupRedaction);
});
reprocess(dossierId, fileId);
fileStatusPersistenceService.setLastManualChangeDate(fileId, OffsetDateTime.now());
return GroupRedactionResponse.builder().groupRedactions(groupRedactions).build();
}
@Transactional
public GroupRedactionResponse resizeAreaGroupRedaction(String dossierId, String fileId, List<ResizeAreaGroupRedactionRequestModel> groupRedactionRequestModels) {
List<GroupRedaction> groupRedactions = new ArrayList<>();
groupRedactionRequestModels.forEach(groupRedactionRequestModel -> {
GroupRedactionEntity oldGroupEntity = groupRedactionPersistenceService.findGroupRedaction(groupRedactionRequestModel.getGroupId());
if (oldGroupEntity instanceof AreaGroupEntity oldAreaGroupEntity) {
PositionOnPageEntity oldPositionOnPage = oldAreaGroupEntity.getPositionsOnPage();
AreaGroupEntity newAreaGroupEntity = groupRedactionPersistenceService.resizeAreaGroupRedaction(oldAreaGroupEntity, groupRedactionRequestModel.getPositionOnPage());
GroupRedaction groupRedaction = GroupRedactionMapper.toAreaGroupRedaction(newAreaGroupEntity);
List<PropertyChangeEntity> propertyChangeEntities = new ArrayList<>();
propertyChangeEntities.add(PropertyChangeEntity.builder()
.propertyName("positions")
.propertyOldValue(buildPositionOnPageString(oldPositionOnPage))
.propertyNewValue(buildPositionOnPageString(newAreaGroupEntity.getPositionsOnPage()))
.build());
addChange(newAreaGroupEntity, GroupChangeType.RESIZE, propertyChangeEntities, groupRedaction);
groupRedactions.add(groupRedaction);
} else {
throw new BadRequestException("Can not resize TEXT group redactions!");
}
});
reprocess(dossierId, fileId);
fileStatusPersistenceService.setLastManualChangeDate(fileId, OffsetDateTime.now());
return GroupRedactionResponse.builder().groupRedactions(groupRedactions).build();
}
private String buildPositionOnPageString(PositionOnPageEntity positionOnPageEntity) {
return "["
+ positionOnPageEntity.getX()
+ ", "
+ positionOnPageEntity.getY()
+ ", "
+ positionOnPageEntity.getWidth()
+ ", "
+ positionOnPageEntity.getHeight()
+ ", "
+ positionOnPageEntity.getPageNumber()
+ "]";
}
@Transactional
public GroupRedactionResponse removeGroupRedaction(String dossierId, String fileId, List<GroupRedactionRequestModel> groupRedactionRequestModels) {
List<GroupRedaction> groupRedactions = new ArrayList<>();
groupRedactionRequestModels.forEach(groupRedactionRequestModel -> {
GroupRedactionEntity oldGroupEntity = groupRedactionPersistenceService.findGroupRedaction(groupRedactionRequestModel.getGroupId());
GroupRedactionEntity newGroupEntity = groupRedactionPersistenceService.removeGroupRedaction(oldGroupEntity);
GroupRedaction groupRedaction = GroupRedactionMapper.toGroupRedaction(newGroupEntity);
List<PropertyChangeEntity> propertyChangeEntities = new ArrayList<>();
propertyChangeEntities.add(PropertyChangeEntity.builder()
.propertyName("softDeletedTime")
.propertyOldValue(null)
.propertyNewValue(newGroupEntity.getSoftDeletedTime().toString())
.build());
addChange(newGroupEntity, GroupChangeType.REMOVE, propertyChangeEntities, groupRedaction);
groupRedactions.add(groupRedaction);
});
reprocess(dossierId, fileId);
fileStatusPersistenceService.setLastManualChangeDate(fileId, OffsetDateTime.now());
return GroupRedactionResponse.builder().groupRedactions(groupRedactions).build();
}
@Transactional
public GroupRedactionResponse changeValueGroupRedaction(String dossierId, String fileId, List<ChangeValueAreaGroupRedactionRequestModel> groupRedactionRequestModels) {
List<GroupRedaction> groupRedactions = new ArrayList<>();
groupRedactionRequestModels.forEach(groupRedactionRequestModel -> {
GroupRedactionEntity oldGroupEntity = groupRedactionPersistenceService.findGroupRedaction(groupRedactionRequestModel.getGroupId());
if (oldGroupEntity instanceof AreaGroupEntity oldAreaGroupEntity) {
String oldValue = oldAreaGroupEntity.getValue();
AreaGroupEntity newAreaGroupEntity = groupRedactionPersistenceService.changeValueAreaGroupRedaction(oldAreaGroupEntity, groupRedactionRequestModel.getValue());
GroupRedaction groupRedaction = GroupRedactionMapper.toAreaGroupRedaction(newAreaGroupEntity);
List<PropertyChangeEntity> propertyChangeEntities = new ArrayList<>();
propertyChangeEntities.add(PropertyChangeEntity.builder()
.propertyName("value")
.propertyOldValue(oldValue)
.propertyNewValue(newAreaGroupEntity.getValue())
.build());
addChange(newAreaGroupEntity, GroupChangeType.VALUE_CHANGE, propertyChangeEntities, groupRedaction);
groupRedactions.add(groupRedaction);
} else {
throw new BadRequestException("Can not change value for TEXT group redactions!");
}
});
reprocess(dossierId, fileId);
fileStatusPersistenceService.setLastManualChangeDate(fileId, OffsetDateTime.now());
return GroupRedactionResponse.builder().groupRedactions(groupRedactions).build();
}
@Transactional
public GroupRedactionResponse changePageRangesGroupRedaction(String dossierId, String fileId, List<ChangePageRangeAreaGroupRedactionRequestModel> groupRedactionRequestModels) {
List<GroupRedaction> groupRedactions = new ArrayList<>();
groupRedactionRequestModels.forEach(groupRedactionRequestModel -> {
GroupRedactionEntity oldGroupEntity = groupRedactionPersistenceService.findGroupRedaction(groupRedactionRequestModel.getGroupId());
if (oldGroupEntity instanceof AreaGroupEntity oldAreaGroupEntity) {
List<PageRangeEntity> oldPageRanges = oldAreaGroupEntity.getPageRanges();
validateChangePageRangeRequest(groupRedactionRequestModel);
AreaGroupEntity newAreaGroupEntity = groupRedactionPersistenceService.changePageRangesAreaGroupRedaction(oldAreaGroupEntity,
groupRedactionRequestModel.getPageRanges());
AreaGroupRedaction groupRedaction = GroupRedactionMapper.toAreaGroupRedaction(newAreaGroupEntity);
List<PropertyChangeEntity> propertyChangeEntities = new ArrayList<>();
propertyChangeEntities.add(PropertyChangeEntity.builder()
.propertyName("pageRanges")
.propertyOldValue(buildPageRangeString(oldPageRanges))
.propertyNewValue(buildPageRangeString(newAreaGroupEntity.getPageRanges()))
.build());
addChange(newAreaGroupEntity, GroupChangeType.PAGE_RANGE_CHANGE, propertyChangeEntities, groupRedaction);
groupRedactions.add(groupRedaction);
} else {
throw new BadRequestException("Can not change page range for TEXT group redactions!");
}
});
reprocess(dossierId, fileId);
fileStatusPersistenceService.setLastManualChangeDate(fileId, OffsetDateTime.now());
return GroupRedactionResponse.builder().groupRedactions(groupRedactions).build();
}
private String buildPageRangeString(List<PageRangeEntity> pageRangeEntities) {
StringBuilder sb = new StringBuilder();
int pageRangeSize = pageRangeEntities.size();
if (pageRangeSize > 1) {
sb.append("[");
}
pageRangeEntities.forEach(pageRangeEntity -> {
sb.append("[");
sb.append(pageRangeEntity.getStartPage());
sb.append(", ");
sb.append(pageRangeEntity.getEndPage());
sb.append("]");
});
if (pageRangeSize > 1) {
sb.append("[");
}
return sb.toString();
}
private void validateChangePageRangeRequest(ChangePageRangeAreaGroupRedactionRequestModel changePageRangeAreaGroupRedactionRequestModel) {
if (changePageRangeAreaGroupRedactionRequestModel.getPageRanges()
.stream()
.anyMatch(pageRange -> pageRange.getStartPage() > pageRange.getEndPage())) {
throw new BadRequestException("Page range start can not be higher than page range end!");
}
}
@Transactional
private void addChange(GroupRedactionEntity groupRedactionEntity,
GroupChangeType groupChangeType,
List<PropertyChangeEntity> propertyChangeEntities,
GroupRedaction groupRedaction) {
GroupChangeEntity groupChangeEntity = groupChangePersistenceService.addGroupChange(groupChangeType, groupRedactionEntity, propertyChangeEntities);
List<GroupChange> groupChanges = GroupRedactionMapper.toGroupChanges(List.of(groupChangeEntity));
groupRedaction.getGroupChanges().addAll(groupChanges);
}
}

View File

@ -96,13 +96,13 @@ public class ManualRedactionMapper {
.toList(),
includeUnprocessed);
Map<String, EntityLogEntry> iddToEntityLogEntryMap = entityLogEntries.stream()
Map<String, EntityLogEntry> idToEntityLogEntryMap = entityLogEntries.stream()
.collect(Collectors.toMap(EntityLogEntry::getId, Function.identity()));
List<RequestEntryPair<RemoveRedactionRequest>> requests = new ArrayList<>();
for (var removeRedactionRequest : removeRedactionRequests) {
EntityLogEntry entityLogEntry = iddToEntityLogEntryMap.get(removeRedactionRequest.getAnnotationId());
EntityLogEntry entityLogEntry = idToEntityLogEntryMap.get(removeRedactionRequest.getAnnotationId());
if (entityLogEntry != null) {
var request = RemoveRedactionRequest.builder()

View File

@ -0,0 +1,131 @@
package com.iqser.red.service.persistence.management.v1.processor.service.manualredactions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.AreaGroupEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.GroupRedactionEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.PositionOnPageEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.TextGroupEntity;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryState;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Position;
@Service
public class PendingGroupRedactionFactory {
public EntityLogEntry buildGroupRedactionEntry(GroupRedactionEntity groupRedactionEntity) {
String reason = String.format("%s has been added as a group redaction", shortenValueIfNecessary(groupRedactionEntity.getValue()));
if (groupRedactionEntity instanceof TextGroupEntity textGroupEntity) {
return buildTextGroupRedactionEntry(textGroupEntity, reason);
} else if (groupRedactionEntity instanceof AreaGroupEntity areaGroupEntity) {
return buildAreaGroupRedactionEntry(areaGroupEntity, reason);
} else {
return null;
}
}
private EntityLogEntry buildAreaGroupRedactionEntry(AreaGroupEntity areaGroupEntity, String reason) {
return EntityLogEntry.builder()
.id(UUID.randomUUID().toString())
.value(areaGroupEntity.getValue())
.type(areaGroupEntity.getTypeId())
.entryType(areaGroupEntity.getEntryType())
.state(EntryState.PENDING)
.dictionaryEntry(false)
.dossierDictionaryEntry(false)
.reason(reason)
.legalBasis(reason)
.matchedRule("")
.containingNodeId(Collections.emptyList())
.closestHeadline("")
.section(areaGroupEntity.getSection())
.positions(convertPositions(areaGroupEntity.getPositionsOnPage()))
.textAfter("")
.textBefore("")
.startOffset(-1)
.endOffset(-1)
.changes(Collections.emptyList())
.manualChanges(Collections.emptyList())
.engines(Set.of(Engine.DICTIONARY))
.reference(Collections.emptySet())
.importedRedactionIntersections(Collections.emptySet())
.build();
}
private EntityLogEntry buildTextGroupRedactionEntry(TextGroupEntity textGroupEntity, String reason) {
return EntityLogEntry.builder()
.id(UUID.randomUUID().toString())
.value(textGroupEntity.getValue())
.type(textGroupEntity.getTypeId())
.entryType(textGroupEntity.getEntryType())
.state(EntryState.PENDING)
.dictionaryEntry(false)
.dossierDictionaryEntry(false)
.reason(reason)
.legalBasis(reason)
.matchedRule("")
.containingNodeId(Collections.emptyList())
.closestHeadline("")
.section("")
.positions(convertPositions(textGroupEntity.getPositionsOnPage()))
.textAfter("")
.textBefore("")
.startOffset(-1)
.endOffset(-1)
.changes(Collections.emptyList())
.manualChanges(Collections.emptyList())
.engines(Set.of(Engine.DICTIONARY))
.reference(Collections.emptySet())
.importedRedactionIntersections(Collections.emptySet())
.build();
}
private String shortenValueIfNecessary(String value) {
final int MAX_LENGTH = 100;
if (value.length() <= MAX_LENGTH) {
return value;
}
String[] words = value.split(" ");
if (words.length == 1) {
return value.substring(0, MAX_LENGTH) + "...";
}
int bound = Math.min(words.length, 2);
List<String> list = new ArrayList<>(Arrays.asList(words).subList(0, bound));
return String.join(" ", list) + "...";
}
private List<Position> convertPositions(PositionOnPageEntity positionOnPageEntity) {
List<Position> positions = new ArrayList<>();
positions.add(new Position(positionOnPageEntity.getX(),
positionOnPageEntity.getY(),
positionOnPageEntity.getWidth(),
positionOnPageEntity.getHeight(),
positionOnPageEntity.getPageNumber()));
return positions;
}
}

View File

@ -0,0 +1,9 @@
package com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.group;
import org.springframework.data.jpa.repository.JpaRepository;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.GroupChangeEntity;
public interface GroupChangeRepository extends JpaRepository<GroupChangeEntity, String> {
}

View File

@ -0,0 +1,34 @@
package com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.group;
import java.time.OffsetDateTime;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import com.iqser.red.service.persistence.management.v1.processor.entity.group.GroupRedactionEntity;
public interface GroupRedactionRepository extends JpaRepository<GroupRedactionEntity, String> {
List<GroupRedactionEntity> findAllByFileId(String fileId);
@Query("select g from GroupRedactionEntity g where g.fileId = :fileId and g.softDeletedTime is null")
List<GroupRedactionEntity> findAllByFileIdAndNotSoftDeleted(@Param("fileId") String fileId);
@Modifying
@Query("update GroupRedactionEntity t set t.processedDate = :processedDate where t.id = :groupId")
void markAsProcessed(@Param("groupId") String groupId, @Param("processedDate") OffsetDateTime processedDate);
@Query("select g from GroupRedactionEntity g "
+ "where g.fileId = :fileId "
+ "and g.softDeletedTime is null "
+ "and (g.processedDate is null "
+ "or exists (select gc from g.groupChanges gc where gc.changeDate > g.processedDate))")
List<GroupRedactionEntity> findAllUnprocessedGroupRedactions(@Param("fileId") String fileId);
}

View File

@ -218,4 +218,14 @@ databaseChangeLog:
- include:
file: db/changelog/tenant/132-add-based-on-dict-annotation-id-to-manual_changes.yaml
- include:
file: db/changelog/tenant/133-add-technical-name-to-legal_basis.yaml
file: db/changelog/tenant/133-add-technical-name-to-legal_basis.yaml
- include:
file: db/changelog/tenant/134-add-group-entity-table.yaml
- include:
file: db/changelog/tenant/135-add-group-changes-table.yaml
- include:
file: db/changelog/tenant/136-add-property-changes-table.yaml
- include:
file: db/changelog/tenant/137-add-area-position-table.yaml
- include:
file: db/changelog/tenant/138-add-page-range-table.yaml

View File

@ -0,0 +1,71 @@
databaseChangeLog:
- changeSet:
id: 134-add-group-entity-table
author: aisvoran
changes:
- createTable:
tableName: group_redaction
columns:
- column:
name: group_id
type: VARCHAR(36)
constraints:
nullable: false
primaryKey: true
primaryKeyName: group_entity_pkey
- column:
name: group_type
type: VARCHAR(50)
constraints:
nullable: false
- column:
name: file_id
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: type_id
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: value
type: VARCHAR(4000)
constraints:
nullable: true
- column:
name: legal_basis
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: entry_type
type: VARCHAR(255)
defaultValue: ENTITY
constraints:
nullable: false
- column:
name: user_id
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: section
type: VARCHAR(255)
constraints:
nullable: true
- column:
name: request_date
type: TIMESTAMP WITH TIME ZONE
constraints:
nullable: true
- column:
name: processed_date
type: TIMESTAMP WITH TIME ZONE
constraints:
nullable: true
- column:
name: soft_deleted_time
type: TIMESTAMP WITH TIME ZONE
constraints:
nullable: true

View File

@ -0,0 +1,41 @@
databaseChangeLog:
- changeSet:
id: 135-create-group-changes-table
author: aisvoran
changes:
- createTable:
tableName: group_change
columns:
- column:
name: change_id
type: VARCHAR(36)
constraints:
nullable: false
primaryKey: true
primaryKeyName: group_change_pkey
- column:
name: group_id
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: user_id
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: change_date
type: TIMESTAMP WITH TIME ZONE
constraints:
nullable: false
- column:
name: change_type
type: VARCHAR(50)
constraints:
nullable: false
- addForeignKeyConstraint:
baseTableName: group_change
baseColumnNames: group_id
referencedTableName: group_redaction
referencedColumnNames: group_id
constraintName: fk_group_change_change_id

View File

@ -0,0 +1,37 @@
databaseChangeLog:
- changeSet:
id: 136-create-property-change-table
author: aisvoran
changes:
- createTable:
tableName: property_change
columns:
- column:
name: property_change_id
type: VARCHAR(36)
constraints:
nullable: false
primaryKey: true
primaryKeyName: property_change_pkey
- column:
name: change_id
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: property_name
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: property_old_value
type: VARCHAR(255)
- column:
name: property_new_value
type: VARCHAR(255)
- addForeignKeyConstraint:
baseTableName: property_change
baseColumnNames: change_id
referencedTableName: group_change
referencedColumnNames: change_id
constraintName: fk_property_change_change_id

View File

@ -0,0 +1,51 @@
databaseChangeLog:
- changeSet:
id: 137-add-area-position-table
author: aisvoran
changes:
- createTable:
tableName: area_group_position
columns:
- column:
name: position_id
type: VARCHAR(36)
constraints:
nullable: false
primaryKey: true
primaryKeyName: area_group_positions_pkey
- column:
name: group_id
type: VARCHAR(36)
constraints:
nullable: false
- column:
name: x
type: FLOAT
constraints:
nullable: false
- column:
name: y
type: FLOAT
constraints:
nullable: false
- column:
name: width
type: FLOAT
constraints:
nullable: false
- column:
name: height
type: FLOAT
constraints:
nullable: false
- column:
name: page_number
type: INTEGER
constraints:
nullable: false
- addForeignKeyConstraint:
baseTableName: area_group_position
baseColumnNames: group_id
referencedTableName: group_redaction
referencedColumnNames: group_id
constraintName: fk_area_group_positions_group_id

View File

@ -0,0 +1,36 @@
databaseChangeLog:
- changeSet:
id: 138-add-page-range-table
author: aisvoran
changes:
- createTable:
tableName: area_group_page_range
columns:
- column:
name: page_range_id
type: VARCHAR(36)
constraints:
nullable: false
primaryKey: true
primaryKeyName: area_group_page_range_pkey
- column:
name: group_id
type: VARCHAR(36)
constraints:
nullable: false
- column:
name: start_page
type: INTEGER
constraints:
nullable: false
- column:
name: end_page
type: INTEGER
constraints:
nullable: false
- addForeignKeyConstraint:
baseTableName: area_group_page_range
baseColumnNames: group_id
referencedTableName: group_redaction
referencedColumnNames: group_id
constraintName: fk_area_group_page_ranges_group_id

View File

@ -7,4 +7,5 @@
<include file="/mongo/changelog/tenant/3-add-page-paragraph-idx.xml"/>
<include file="/mongo/changelog/tenant/4-create-component-entities.xml"/>
<include file="/mongo/changelog/tenant/5-add-duplicate-text-ranges.xml"/>
<include file="/mongo/changelog/tenant/6-add-group-id.xml"/>
</databaseChangeLog>

View File

@ -0,0 +1,25 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
<changeSet id="6" author="aisvoran">
<ext:runCommand>
<ext:command>
{
update: "entity-log-entries",
updates: [
{
q: {},
u: { $set: { "groupId": null } },
multi: true
}
]
}
</ext:command>
</ext:runCommand>
</changeSet>
</databaseChangeLog>

View File

@ -0,0 +1,10 @@
package com.iqser.red.service.peristence.v1.server.integration.client;
import org.springframework.cloud.openfeign.FeignClient;
import com.iqser.red.service.persistence.service.v1.api.external.resource.GroupRedactionResource;
@FeignClient(name = "GroupRedactionExternalClient", url = "http://localhost:${server.port}", configuration = FeignSupportConfig.class)
public interface GroupRedactionExternalClient extends GroupRedactionResource {
}

View File

@ -28,6 +28,7 @@ import com.iqser.red.service.peristence.v1.server.integration.service.DossierTes
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.service.DossierManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.group.GroupRedactionService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierStatusRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.WatermarkModel;

View File

@ -33,8 +33,10 @@ import com.iqser.red.service.persistence.management.v1.processor.service.Dossier
import com.iqser.red.service.persistence.management.v1.processor.service.EntityLogMergeService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
import com.iqser.red.service.persistence.management.v1.processor.service.group.GroupRedactionPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionProviderService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.PendingDictionaryEntryFactory;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.PendingGroupRedactionFactory;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService;
@ -94,6 +96,9 @@ public class EntityLogMergeTest {
@MockBean
private EntityLogMongoService entityLogMongoService;
@MockBean
private GroupRedactionPersistenceService groupRedactionPersistenceService;
@Captor
private ArgumentCaptor<AnalyzeRequest> captor;
@ -103,7 +108,7 @@ public class EntityLogMergeTest {
@BeforeEach
public void setUp() {
entityLogMergeService = new EntityLogMergeService(dictionaryPersistenceService, new PendingDictionaryEntryFactory(), entityLogMongoService);
entityLogMergeService = new EntityLogMergeService(dictionaryPersistenceService, new PendingDictionaryEntryFactory(), entityLogMongoService, new PendingGroupRedactionFactory());
}
@ -141,7 +146,7 @@ public class EntityLogMergeTest {
when(fileStatusPersistenceService.getStatus(fileId)).thenReturn(FileEntity.builder().id(fileId).fileAttributes(Collections.emptyList()).build());
when(fileStatusService.convertAttributes(any(), anyString())).thenReturn(Collections.emptyList());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build(), Collections.emptyList());
assertNotNull(response);
assertFalse(response.getEntityLogEntry().isEmpty());
@ -315,7 +320,7 @@ public class EntityLogMergeTest {
when(fileStatusPersistenceService.getStatus(fileId)).thenReturn(FileEntity.builder().id(fileId).fileAttributes(Collections.emptyList()).build());
when(fileStatusService.convertAttributes(any(), anyString())).thenReturn(Collections.emptyList());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build(), Collections.emptyList());
assertNotNull(response);
assertFalse(response.getEntityLogEntry().isEmpty());
@ -388,7 +393,7 @@ public class EntityLogMergeTest {
when(fileStatusPersistenceService.getStatus(fileId)).thenReturn(FileEntity.builder().id(fileId).fileAttributes(Collections.emptyList()).build());
when(fileStatusService.convertAttributes(any(), anyString())).thenReturn(Collections.emptyList());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build(), Collections.emptyList());
var resizedEntry = response.getEntityLogEntry()
.stream()
@ -433,7 +438,7 @@ public class EntityLogMergeTest {
when(fileStatusPersistenceService.getStatus(FILE_ID)).thenReturn(FileEntity.builder().id(FILE_ID).fileAttributes(Collections.emptyList()).build());
when(fileStatusService.convertAttributes(any(), anyString())).thenReturn(Collections.emptyList());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build(), Collections.emptyList());
assertNotNull(response);
assertFalse(response.getEntityLogEntry().isEmpty());
@ -488,7 +493,7 @@ public class EntityLogMergeTest {
when(fileStatusPersistenceService.getStatus(FILE_ID)).thenReturn(FileEntity.builder().id(FILE_ID).fileAttributes(Collections.emptyList()).build());
when(fileStatusService.convertAttributes(any(), anyString())).thenReturn(Collections.emptyList());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build(), Collections.emptyList());
assertNotNull(response);
assertFalse(response.getEntityLogEntry().isEmpty());
@ -542,7 +547,7 @@ public class EntityLogMergeTest {
when(fileStatusPersistenceService.getStatus(FILE_ID)).thenReturn(FileEntity.builder().id(FILE_ID).fileAttributes(Collections.emptyList()).build());
when(fileStatusService.convertAttributes(any(), anyString())).thenReturn(Collections.emptyList());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build(), Collections.emptyList());
assertNotNull(response);
assertFalse(response.getEntityLogEntry().isEmpty());
@ -588,7 +593,7 @@ public class EntityLogMergeTest {
when(fileStatusPersistenceService.getStatus(FILE_ID)).thenReturn(FileEntity.builder().id(FILE_ID).fileAttributes(Collections.emptyList()).build());
when(fileStatusService.convertAttributes(any(), anyString())).thenReturn(Collections.emptyList());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build(), Collections.emptyList());
assertNotNull(response);
assertFalse(response.getEntityLogEntry().isEmpty());
@ -657,7 +662,7 @@ public class EntityLogMergeTest {
when(fileStatusPersistenceService.getStatus(FILE_ID)).thenReturn(FileEntity.builder().id(FILE_ID).fileAttributes(Collections.emptyList()).build());
when(fileStatusService.convertAttributes(any(), anyString())).thenReturn(Collections.emptyList());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build(), Collections.emptyList());
assertNotNull(response);
assertFalse(response.getEntityLogEntry().isEmpty());
@ -714,7 +719,7 @@ public class EntityLogMergeTest {
when(fileStatusPersistenceService.getStatus(FILE_ID)).thenReturn(FileEntity.builder().id(FILE_ID).fileAttributes(Collections.emptyList()).build());
when(fileStatusService.convertAttributes(any(), anyString())).thenReturn(Collections.emptyList());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build());
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, entityLog, DossierEntity.builder().dossierTemplateId(dossierTemplateId).build(), Collections.emptyList());
assertNotNull(response);
assertFalse(response.getEntityLogEntry().isEmpty());

View File

@ -0,0 +1,318 @@
package com.iqser.red.service.peristence.v1.server.integration.tests;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.iqser.red.service.peristence.v1.server.integration.client.GroupRedactionExternalClient;
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.FileTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.PageRange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.AddAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.AddTextGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.ChangePageRangeAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.ChangeValueAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedactionType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.PositionOnPage;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.RecategorizeGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.ResizeAreaGroupRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.UpdateLegalBasisRequestModel;
import feign.FeignException;
public class GroupRedactionTest extends AbstractPersistenceServerServiceTest {
@Autowired
private DossierTemplateTesterAndProvider dossierTemplateTesterAndProvider;
@Autowired
private DossierTesterAndProvider dossierTesterAndProvider;
@Autowired
private FileTesterAndProvider fileTesterAndProvider;
@Autowired
private GroupRedactionExternalClient groupRedactionExternalClient;
@Test
public void testCreateTextGroupRedaction() {
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
var file = fileTesterAndProvider.testAndProvideFile(dossier);
List<AddTextGroupRedactionRequestModel> addTextGroupRedactionRequestModels = getAddTextGroupRedactionRequestModels();
var response = groupRedactionExternalClient.addTextGroupRedaction(dossier.getId(), file.getFileId(), addTextGroupRedactionRequestModels);
assertNotNull(response);
assertEquals(response.getGroupRedactions().size(), 1);
assertEquals(response.getGroupRedactions()
.get(0).getValue(), "value");
}
private static @NotNull List<AddTextGroupRedactionRequestModel> getAddTextGroupRedactionRequestModels() {
List<AddTextGroupRedactionRequestModel> addTextGroupRedactionRequestModels = new ArrayList<>();
addTextGroupRedactionRequestModels.add(new AddTextGroupRedactionRequestModel("CBI_author",
"value",
"legalBasis",
EntryType.ENTITY,
PositionOnPage.builder().height(1).width(1).x(1).y(1).build(),
GroupRedactionType.TEXT));
return addTextGroupRedactionRequestModels;
}
@Test
public void testCreateAreaGroupRedaction() {
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
var file = fileTesterAndProvider.testAndProvideFile(dossier);
List<AddAreaGroupRedactionRequestModel> addAreaGroupRedactionRequestModels = getAddAreaGroupRedactionRequestModels();
var response = groupRedactionExternalClient.addAreaGroupRedaction(dossier.getId(), file.getFileId(), addAreaGroupRedactionRequestModels);
assertNotNull(response);
assertEquals(response.getGroupRedactions().size(), 1);
assertEquals(response.getGroupRedactions()
.get(0).getValue(), "value");
}
@Test
public void testGetAllGroupRedaction() {
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
var file = fileTesterAndProvider.testAndProvideFile(dossier);
List<AddTextGroupRedactionRequestModel> addTextGroupRedactionRequestModels = getAddTextGroupRedactionRequestModels();
groupRedactionExternalClient.addTextGroupRedaction(dossier.getId(), file.getFileId(), addTextGroupRedactionRequestModels);
List<AddAreaGroupRedactionRequestModel> addAreaGroupRedactionRequestModels = getAddAreaGroupRedactionRequestModels();
var area = groupRedactionExternalClient.addAreaGroupRedaction(dossier.getId(), file.getFileId(), addAreaGroupRedactionRequestModels);
var response = groupRedactionExternalClient.getGroupRedactions(dossier.getId(), file.getFileId(), false);
assertEquals(response.getGroupRedactions().size(), 2);
groupRedactionExternalClient.removeGroupRedaction(dossier.getId(),
file.getFileId(),
List.of(GroupRedactionRequestModel.builder()
.groupId(area.getGroupRedactions()
.get(0).getGroupId())
.build()));
response = groupRedactionExternalClient.getGroupRedactions(dossier.getId(), file.getFileId(), false);
assertEquals(response.getGroupRedactions().size(), 1);
response = groupRedactionExternalClient.getGroupRedactions(dossier.getId(), file.getFileId(), true);
assertEquals(response.getGroupRedactions().size(), 2);
}
private static @NotNull List<AddAreaGroupRedactionRequestModel> getAddAreaGroupRedactionRequestModels() {
List<AddAreaGroupRedactionRequestModel> addAreaGroupRedactionRequestModels = new ArrayList<>();
addAreaGroupRedactionRequestModels.add(new AddAreaGroupRedactionRequestModel("CBI_author",
"value",
"legalBasis",
EntryType.ENTITY,
GroupRedactionType.AREA,
PositionOnPage.builder().height(1).width(1).x(1).y(1).build(),
List.of(PageRange.builder().startPage(1).endPage(2).build()),
"section"));
return addAreaGroupRedactionRequestModels;
}
@Test
public void testUpdateLegalBasis() {
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
var file = fileTesterAndProvider.testAndProvideFile(dossier);
List<AddTextGroupRedactionRequestModel> addTextGroupRedactionRequestModels = getAddTextGroupRedactionRequestModels();
var text = groupRedactionExternalClient.addTextGroupRedaction(dossier.getId(), file.getFileId(), addTextGroupRedactionRequestModels);
var response = groupRedactionExternalClient.updateLegalBasis(dossier.getId(),
file.getFileId(),
List.of(UpdateLegalBasisRequestModel.builder()
.groupId(text.getGroupRedactions()
.get(0).getGroupId())
.legalBasis("new legal basis")
.build()));
assertEquals(response.getGroupRedactions()
.get(0).getLegalBasis(), "new legal basis");
}
@Test
public void testRecategorize() {
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
var file = fileTesterAndProvider.testAndProvideFile(dossier);
List<AddTextGroupRedactionRequestModel> addTextGroupRedactionRequestModels = getAddTextGroupRedactionRequestModels();
var text = groupRedactionExternalClient.addTextGroupRedaction(dossier.getId(), file.getFileId(), addTextGroupRedactionRequestModels);
var response = groupRedactionExternalClient.recategorizeGroupRedaction(dossier.getId(),
file.getFileId(),
List.of(RecategorizeGroupRedactionRequestModel.builder()
.groupId(text.getGroupRedactions()
.get(0).getGroupId())
.typeId("PII")
.build()));
assertEquals(response.getGroupRedactions()
.get(0).getTypeId(), "PII");
}
@Test
public void testResize() {
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
var file = fileTesterAndProvider.testAndProvideFile(dossier);
List<AddAreaGroupRedactionRequestModel> addAreaGroupRedactionRequestModels = getAddAreaGroupRedactionRequestModels();
var area = groupRedactionExternalClient.addAreaGroupRedaction(dossier.getId(), file.getFileId(), addAreaGroupRedactionRequestModels);
var response = groupRedactionExternalClient.resizeGroupRedaction(dossier.getId(),
file.getFileId(),
List.of(ResizeAreaGroupRedactionRequestModel.builder()
.groupId(area.getGroupRedactions()
.get(0).getGroupId())
.positionOnPage(PositionOnPage.builder()
.pageNumber(1)
.width(2)
.height(3)
.x(4)
.y(5)
.build())
.build()));
assertEquals(response.getGroupRedactions()
.get(0).getPositionOnPage().getPageNumber(), 1);
assertEquals(response.getGroupRedactions()
.get(0).getPositionOnPage().getHeight(), 3);
assertEquals(response.getGroupRedactions()
.get(0).getPositionOnPage().getWidth(), 2);
assertEquals(response.getGroupRedactions()
.get(0).getPositionOnPage().getX(), 4);
assertEquals(response.getGroupRedactions()
.get(0).getPositionOnPage().getY(), 5);
}
@Test
public void testForce() {
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
var file = fileTesterAndProvider.testAndProvideFile(dossier);
List<AddTextGroupRedactionRequestModel> addTextGroupRedactionRequestModels = new ArrayList<>();
addTextGroupRedactionRequestModels.add(new AddTextGroupRedactionRequestModel("CBI_author",
"value",
"legalBasis",
EntryType.HINT,
PositionOnPage.builder().height(1).width(1).x(1).y(1).build(),
GroupRedactionType.TEXT));
var text = groupRedactionExternalClient.addTextGroupRedaction(dossier.getId(), file.getFileId(), addTextGroupRedactionRequestModels);
var response = groupRedactionExternalClient.forceGroupRedaction(dossier.getId(),
file.getFileId(),
List.of(GroupRedactionRequestModel.builder()
.groupId(text.getGroupRedactions()
.get(0).getGroupId())
.build()));
assertEquals(response.getGroupRedactions()
.get(0).getEntryType(), EntryType.ENTITY);
}
@Test
public void testChangeValue() {
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
var file = fileTesterAndProvider.testAndProvideFile(dossier);
List<AddAreaGroupRedactionRequestModel> addAreaGroupRedactionRequestModels = getAddAreaGroupRedactionRequestModels();
var area = groupRedactionExternalClient.addAreaGroupRedaction(dossier.getId(), file.getFileId(), addAreaGroupRedactionRequestModels);
var response = groupRedactionExternalClient.changeValueGroupRedaction(dossier.getId(),
file.getFileId(),
List.of(ChangeValueAreaGroupRedactionRequestModel.builder()
.groupId(area.getGroupRedactions()
.get(0).getGroupId())
.value("new value")
.build()));
assertEquals(response.getGroupRedactions()
.get(0).getValue(), "new value");
}
@Test
public void testChangePageRanges() {
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
var file = fileTesterAndProvider.testAndProvideFile(dossier);
List<AddAreaGroupRedactionRequestModel> addAreaGroupRedactionRequestModels = getAddAreaGroupRedactionRequestModels();
var area = groupRedactionExternalClient.addAreaGroupRedaction(dossier.getId(), file.getFileId(), addAreaGroupRedactionRequestModels);
var error = assertThrows(FeignException.class,
() -> groupRedactionExternalClient.changePageRangeGroupRedaction(dossier.getId(),
file.getFileId(),
List.of(ChangePageRangeAreaGroupRedactionRequestModel.builder()
.groupId(area.getGroupRedactions()
.get(0).getGroupId())
.pageRanges(List.of(PageRange.builder()
.startPage(20)
.endPage(10)
.build()))
.build())));
assertTrue(error.getMessage().contains("Page range start can not be higher than page range end!"));
var response = groupRedactionExternalClient.changePageRangeGroupRedaction(dossier.getId(),
file.getFileId(),
List.of(ChangePageRangeAreaGroupRedactionRequestModel.builder()
.groupId(area.getGroupRedactions()
.get(0).getGroupId())
.pageRanges(List.of(PageRange.builder().startPage(10).endPage(20).build()))
.build()));
assertEquals("[10, 20]",
response.getGroupRedactions()
.get(0).getGroupChanges()
.get(0).getPropertyChanges()
.get(0).getPropertyNewValue());
}
}

View File

@ -70,6 +70,7 @@ import com.iqser.red.service.persistence.management.v1.processor.roles.Applicati
import com.iqser.red.service.persistence.management.v1.processor.service.ApplicationConfigService;
import com.iqser.red.service.persistence.management.v1.processor.service.EntityLogService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.group.GroupRedactionService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ApplicationConfigRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.AuditRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.CommentRepository;
@ -102,6 +103,7 @@ import com.iqser.red.service.persistence.management.v1.processor.service.persist
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.dictionaryentry.EntryRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.dictionaryentry.FalsePositiveEntryRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.dictionaryentry.FalseRecommendationEntryRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.group.GroupRedactionRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.users.UserService;
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.dossiertemplate.configuration.ApplicationConfig;
@ -236,6 +238,8 @@ public abstract class AbstractPersistenceServerServiceTest {
protected FalseRecommendationEntryRepository falseRecommendationEntryRepository;
@Autowired
protected ApplicationConfigRepository applicationConfigRepository;
@Autowired
protected GroupRedactionRepository groupRedactionRepository;
@MockBean
protected RedactionClient redactionClient;
@Autowired
@ -268,6 +272,8 @@ public abstract class AbstractPersistenceServerServiceTest {
protected CommentRepository commentRepository;
@Autowired
protected ResizeRedactionRepository resizeRedactionRepository;
@Autowired
protected GroupRedactionService groupRedactionService;
@MockBean
protected TenantAuthenticationManagerResolver tenantAuthenticationManagerResolver;

View File

@ -5,6 +5,7 @@ import java.util.Set;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata;
import com.iqser.red.service.persistence.service.v1.api.shared.model.group.GroupRedaction;
import lombok.AllArgsConstructor;
import lombok.Builder;
@ -37,6 +38,8 @@ public class AnalyzeResult {
private ManualRedactions manualRedactions;
private Set<GroupRedaction> groupRedactions;
private Set<FileAttribute> addedFileAttributes;
private List<ComponentMappingMetadata> usedComponentMappings;

View File

@ -6,5 +6,6 @@ public enum Engine {
RULE,
MANUAL,
IMPORTED,
DOSSIER_DICTIONARY
DOSSIER_DICTIONARY,
GROUP
}

View File

@ -79,4 +79,6 @@ public class EntityLogEntry {
List<DuplicatedTextRange> duplicatedTextRanges;
String groupId;
}

View File

@ -75,5 +75,6 @@ public class EntityLogEntryResponse {
int numberOfComments;
int paragraphPageIdx;
String groupId;
}

View File

@ -0,0 +1,41 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import java.util.List;
import com.iqser.red.service.persistence.service.v1.api.shared.model.PageRange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
public class AddAreaGroupRedactionRequestModel extends AddGroupRedactionRequestModel {
public AddAreaGroupRedactionRequestModel(String type,
String value,
String legalBasis,
EntryType entryType,
@NotNull(message = "GroupRedactionType is required") GroupRedactionType groupRedactionType,
PositionOnPage positionsOnPage,
List<PageRange> pageRanges,
String section) {
super(type, value, legalBasis, entryType, positionsOnPage, groupRedactionType);
this.pageRanges = pageRanges;
this.section = section;
}
@NotEmpty(message = "Page ranges are required for AREA group redaction")
private List<PageRange> pageRanges;
private String section;
}

View File

@ -0,0 +1,34 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "groupRedactionType", visible = true)
@JsonSubTypes({@JsonSubTypes.Type(value = AddTextGroupRedactionRequestModel.class, name = "TEXT"), @JsonSubTypes.Type(value = AddAreaGroupRedactionRequestModel.class, name = "AREA")})
public class AddGroupRedactionRequestModel {
private String type;
private String value;
private String legalBasis;
private EntryType entryType;
private PositionOnPage positionOnPage;
@NotNull(message = "GroupRedactionType is required")
private GroupRedactionType groupRedactionType;
}

View File

@ -0,0 +1,27 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Data
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@ToString(callSuper = true)
public class AddTextGroupRedactionRequestModel extends AddGroupRedactionRequestModel {
public AddTextGroupRedactionRequestModel(String type,
String value,
String legalBasis,
EntryType entryType,
PositionOnPage positionOnPage,
@NotNull(message = "GroupRedactionType is required") GroupRedactionType groupRedactionType) {
super(type, value, legalBasis, entryType, positionOnPage, groupRedactionType);
}
}

View File

@ -0,0 +1,24 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import java.util.List;
import com.iqser.red.service.persistence.service.v1.api.shared.model.PageRange;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class AreaGroupRedaction extends GroupRedaction {
private List<PageRange> pageRanges;
private String section;
}

View File

@ -0,0 +1,21 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import java.util.List;
import com.iqser.red.service.persistence.service.v1.api.shared.model.PageRange;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ChangePageRangeAreaGroupRedactionRequestModel {
private String groupId;
private List<PageRange> pageRanges;
}

View File

@ -0,0 +1,18 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ChangeValueAreaGroupRedactionRequestModel {
private String groupId;
private String value;
}

View File

@ -0,0 +1,29 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GroupChange {
private String changeId;
private String userId;
private OffsetDateTime changeDate;
private GroupChangeType changeType;
@Builder.Default
private List<GroupPropertyChange> propertyChanges = new ArrayList<>();
}

View File

@ -0,0 +1,12 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
public enum GroupChangeType {
REMOVE,
RECATEGORIZE,
LEGAL_BASIS_CHANGE,
FORCE,
RESIZE,
VALUE_CHANGE,
PAGE_RANGE_CHANGE
}

View File

@ -0,0 +1,22 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GroupPropertyChange {
private String propertyChangeId;
private String propertyName;
private String propertyOldValue;
private String propertyNewValue;
}

View File

@ -0,0 +1,50 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class GroupRedaction {
private String groupId;
private String value;
private String fileId;
private String typeId;
private String userId;
private String legalBasis;
private EntryType entryType;
private GroupRedactionType groupRedactionType;
private OffsetDateTime requestDate;
private OffsetDateTime processedDate;
private OffsetDateTime softDeletedTime;
@Builder.Default
private List<GroupChange> groupChanges = new ArrayList<>();
private PositionOnPage positionOnPage;
}

View File

@ -0,0 +1,20 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class GroupRedactionInternalResponse {
List<TextGroupRedaction> textGroupRedactions = new ArrayList<>();
List<AreaGroupRedaction> areaGroupRedactions = new ArrayList<>();
}

View File

@ -0,0 +1,15 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class GroupRedactionRequestModel {
private String groupId;
}

View File

@ -0,0 +1,21 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class GroupRedactionResponse {
List<GroupRedaction> groupRedactions = new ArrayList<>();
}

View File

@ -0,0 +1,8 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
public enum GroupRedactionType {
TEXT,
AREA
}

View File

@ -0,0 +1,22 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
@AllArgsConstructor
public class PositionOnPage {
private float x;
private float y;
private float width;
private float height;
private int pageNumber;
}

View File

@ -0,0 +1,17 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RecategorizeGroupRedactionRequestModel {
private String groupId;
private String typeId;
}

View File

@ -0,0 +1,20 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ResizeAreaGroupRedactionRequestModel {
private String groupId;
private PositionOnPage positionOnPage;
}

View File

@ -0,0 +1,15 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.SuperBuilder;
@Data
@SuperBuilder
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class TextGroupRedaction extends GroupRedaction {
}

View File

@ -0,0 +1,17 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.group;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UpdateLegalBasisRequestModel {
private String groupId;
private String legalBasis;
}

View File

@ -77,4 +77,6 @@ public class EntityLogEntryDocument {
List<DuplicatedTextRange> duplicatedTextRanges;
String groupId;
}