Merge branch 'RED-9225-findings2-4.0' into 'release/2.349.x'

Resolve RED-9225 "Findings2 4.0"

See merge request redactmanager/persistence-service!527
This commit is contained in:
Dominique Eifländer 2024-06-06 14:23:28 +02:00
commit 7c1b720281
13 changed files with 113 additions and 13 deletions

View File

@ -3,8 +3,10 @@ package com.iqser.red.persistence.service.v2.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_DOSSIER_ATTRIBUTES;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource.DOSSIER_TEMPLATE_ID_PARAM;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
@ -12,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.google.common.collect.Sets;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierTemplateController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.DownloadController;
@ -29,8 +32,8 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.Audit
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttribute;
import com.iqser.red.service.persistence.service.v2.api.external.model.DocuMineDossierRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttributes;
import com.iqser.red.service.persistence.service.v2.api.external.model.DocuMineDossierRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.DossierList;
import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatus;
@ -55,6 +58,9 @@ public class DossierControllerV2 implements DossierResource {
private final StatusController statusController;
private final DownloadStatusPersistenceService downloadStatusPersistenceService;
@Value("${application.type}")
private String applicationType;
public DossierList getDossiers(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@RequestParam(name = INCLUDE_ACTIVE_PARAM, defaultValue = "true", required = false) boolean includeActive,
@ -95,11 +101,16 @@ public class DossierControllerV2 implements DossierResource {
public void deleteDossier(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier to retrieve.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId) {
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier to retrieve.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestParam(name = DELETE_PERMANENTLY_PARAM, defaultValue = "false", required = false) boolean deletePermanently) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
dossierController.deleteDossier(dossierId);
if (deletePermanently) {
dossierController.hardDeleteDossiers(Sets.newHashSet(dossierId));
} else {
dossierController.deleteDossier(dossierId);
}
}
@ -169,7 +180,7 @@ public class DossierControllerV2 implements DossierResource {
}
private static DossierRequest mapToDossierRequest(String dossierTemplateId, DocuMineDossierRequest dossier) {
private DossierRequest mapToDossierRequest(String dossierTemplateId, DocuMineDossierRequest dossier) {
return DossierRequest.builder()
.dossierId(dossier.getId())
@ -178,7 +189,7 @@ public class DossierControllerV2 implements DossierResource {
.description(dossier.getDescription())
.ownerId(dossier.getOwnerId())
.memberIds(dossier.getMemberIds())
.approverIds(dossier.getMemberIds()) // for DocuMine, the members are always set as approvers
.approverIds(applicationType.equals("DocuMine") ? dossier.getMemberIds() : dossier.getApproverIds()) // for DocuMine, the members are always set as approvers
.downloadFileTypes(Set.of(DownloadFileType.ORIGINAL))
.reportTemplateIds(dossier.getReportTemplateIds())
.watermarkId(null)

View File

@ -1,6 +1,7 @@
package com.iqser.red.persistence.service.v2.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.EXPERIMENTAL;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_REPORT_TEMPLATES;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_DOSSIER_ATTRIBUTES_CONFIG;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_DOSSIER_STATUS;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_FILE_ATTRIBUTES_CONFIG;
@ -32,6 +33,7 @@ import com.iqser.red.service.persistence.management.v1.processor.service.RulesVa
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ReportTemplatePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.RulesPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
@ -45,6 +47,8 @@ import com.iqser.red.service.persistence.service.v2.api.external.model.DossierSt
import com.iqser.red.service.persistence.service.v2.api.external.model.DossierStatusDefinitionList;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinition;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinitionList;
import com.iqser.red.service.persistence.service.v2.api.external.model.ReportTemplate;
import com.iqser.red.service.persistence.service.v2.api.external.model.ReportTemplateList;
import com.iqser.red.service.persistence.service.v2.api.external.model.RulesValidationMessage;
import com.iqser.red.service.persistence.service.v2.api.external.model.RulesValidationResponse;
import com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource;
@ -72,6 +76,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource {
private final FileAttributesController fileAttributesController;
private final DossierStatusPersistenceService dossierStatusPersistenceService;
private final DossierAttributeConfigPersistenceService dossierAttributeConfigPersistenceService;
private final ReportTemplatePersistenceService reportTemplatePersistenceService;
public List<DossierTemplateModel> getAllDossierTemplates() {
@ -202,6 +207,23 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource {
}
@PreAuthorize("hasAuthority('" + GET_REPORT_TEMPLATES + "')")
public ReportTemplateList getReportTemplates(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) {
getDossierTemplate(dossierTemplateId);
var templates = reportTemplatePersistenceService.findByDossierTemplateId(dossierTemplateId);
return new ReportTemplateList(templates.stream()
.map(t -> ReportTemplate.builder()
.id(t.getTemplateId())
.name(t.getFileName())
.createdOn(t.getUploadDate())
.multiFile(t.isMultiFileReport())
.preSelect(t.isActiveByDefault())
.build())
.toList());
}
@SneakyThrows
private ResponseEntity<RulesValidationResponse> uploadRules(String dossierTemplateId, RuleFileType ruleFileType, MultipartFile file, boolean dryRun) {

View File

@ -44,7 +44,7 @@ public class DownloadControllerV2 implements DownloadResource {
.creationDate(status.getCreationDate())
.lastDownload(status.getLastDownload())
.fileSize(status.getFileSize())
.dossierId(status.getDossier().getId())
.dossierId(status.getDossier() != null ? status.getDossier().getId() : null)
.fileIds(status.getFiles()
.stream()
.map(FileEntity::getId)

View File

@ -41,6 +41,10 @@ public class DocuMineDossierRequest {
@Schema(description = "The id(s) of members associated to this dossier.")
private Set<String> memberIds = new HashSet<>();
@Builder.Default
@Schema(description = "The id(s) of approvers associated to this dossier.")
private Set<String> approverIds = new HashSet<>();
@Builder.Default
@Schema(description = "Id(s) of the word report templates used to generate downloads")
private Set<String> reportTemplateIds = new HashSet<>();

View File

@ -0,0 +1,22 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import java.time.OffsetDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ReportTemplate {
private String id;
private String name;
private OffsetDateTime createdOn;
private boolean multiFile;
private boolean preSelect;
}

View File

@ -0,0 +1,20 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
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 ReportTemplateList {
@Builder.Default
private List<ReportTemplate> reportTemplates = new ArrayList<>();
}

View File

@ -42,6 +42,8 @@ public interface DossierResource {
String INCLUDE_ARCHIVED_PARAM = "includeArchived";
String INCLUDE_SOFT_DELETED_PARAM = "includeSoftDeleted";
String DELETE_PERMANENTLY_PARAM = "deletePermanently";
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
@ -77,7 +79,8 @@ public interface DossierResource {
@Operation(summary = "Deletes an existing dossier.", description = "Dossiers get soft-deleted unless specified other. A soft-deleted dossier can be restored during a retention period that is configured in the application settings. The default retention period is 96h.")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully deleted the dossier."), @ApiResponse(responseCode = "404", description = "Not found")})
void deleteDossier(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier to retrieve.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId);
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier to retrieve.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@Parameter(name = DELETE_PERMANENTLY_PARAM, description = "A toggle to determine the deletion mode for a dossier: `true`: The dossier will be permanently deleted and can't be restored. `false` (default): Soft-delete, allowing restoration within the application-configured retention period.") @RequestParam(name = DELETE_PERMANENTLY_PARAM, defaultValue = "false", required = false) boolean deletePermanently);
@ResponseStatus(value = HttpStatus.NO_CONTENT)

View File

@ -5,6 +5,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileTyp
import com.iqser.red.service.persistence.service.v2.api.external.model.DossierAttributeDefinitionList;
import com.iqser.red.service.persistence.service.v2.api.external.model.DossierStatusDefinitionList;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinitionList;
import com.iqser.red.service.persistence.service.v2.api.external.model.ReportTemplateList;
import com.iqser.red.service.persistence.service.v2.api.external.model.RulesValidationResponse;
import io.swagger.v3.oas.annotations.Operation;
@ -44,6 +45,7 @@ public interface DossierTemplateResource {
String FILE_ATTRIBUTE_DEFINITIONS_PATH = "/file-attribute-definitions";
String DOSSIER_TEMPLATE_ID_PARAM = "dossierTemplateId";
String DOSSIER_TEMPLATE_ID_PATH_VARIABLE = "/{" + DOSSIER_TEMPLATE_ID_PARAM + "}";
String REPORT_TEMPLATES_PATH = "/report-templates";
String DRY_RUN_PARAM = "dryRun";
@ -113,4 +115,10 @@ public interface DossierTemplateResource {
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully returned the dossier attribute definitions for the specified dossier template."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
DossierAttributeDefinitionList getDossierAttributeDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);
@GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + REPORT_TEMPLATES_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Returns the list of all available report templates", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully returned the report templates for the specified dossier template."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
ReportTemplateList getReportTemplates(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);
}

View File

@ -50,7 +50,7 @@ public interface DownloadResource {
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Download the download package.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully downloaded the requested file."), @ApiResponse(responseCode = "404", description = "Download not found. This happens if the requested download does not exist for the current user.")})
@GetMapping(value = PATH + DOWNLOAD_ID_PATH_PARAM + DOWNLOAD_PATH)
@GetMapping(value = PATH + DOWNLOAD_ID_PATH_PARAM + DOWNLOAD_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_OCTET_STREAM_VALUE})
void download(@Parameter(name = DOWNLOAD_ID_PARAM, description = "The identifier for the file to download.", required = true) @PathVariable(DOWNLOAD_ID_PARAM) String downloadId);
}

View File

@ -118,6 +118,7 @@ public class DossierTemplateExportService {
objectMapper.registerModule(new JavaTimeModule());
DownloadStatusEntity downloadStatus = downloadStatusPersistenceService.getStatus(downloadJob.getStorageId());
downloadStatusPersistenceService.updateStatus(downloadJob.getStorageId(), DownloadStatusValue.GENERATING);
String dossierTemplateId = extractDossierTemplateId(downloadStatus.getFilename());
var dossierTemplate = dossierTemplatePersistenceService.getDossierTemplate(dossierTemplateId);
@ -141,7 +142,7 @@ public class DossierTemplateExportService {
objectMapper.writeValueAsBytes(convert(colors, Colors.class))));
// add dossier statuses
var dossierStatusList = dossierStatusPersistenceService.getAllDossierStatusForTemplate(dossierTemplateId);
var dossierStatusList = dossierStatusPersistenceService.getAllDossierStatusForTemplateWithoutSecurity(dossierTemplateId);
fileSystemBackedArchiver.addEntries(new FileSystemBackedArchiver.ArchiveModel(null,
getFilename(ExportFilename.DOSSIER_STATUS, JSON_EXT),
objectMapper.writeValueAsBytes(convert(dossierStatusList, DossierStatusInfo.class))));

View File

@ -104,6 +104,17 @@ public class DossierStatusPersistenceService {
}
@Transactional
public List<DossierStatusInfo> getAllDossierStatusForTemplateWithoutSecurity(String dossierTemplateId) {
return dossierStatusRepository.findByDossierTemplateIdIn(Collections.singletonList(dossierTemplateId))
.stream()
.map(d -> MagicConverter.convert(d, DossierStatusInfo.class))
.collect(Collectors.toList());
}
@Transactional
public List<DossierStatusInfo> getAllDossierStatuses(List<String> dossierTemplateIds) {

View File

@ -60,6 +60,7 @@ public class DownloadStatusPersistenceService {
downloadStatus.setDownloadFileTypes(downloadFileTypes != null ? new HashSet<>(downloadFileTypes) : new HashSet<>());
downloadStatus.setReports(reportTemplateIds != null ? reportTemplateRepository.findAllById(reportTemplateIds) : new ArrayList<>());
downloadStatus.setRedactionPreviewColor(redactionPreviewColor);
downloadStatus.setStatus(DownloadStatusValue.QUEUED);
downloadStatusRepository.save(downloadStatus);
}

View File

@ -386,10 +386,7 @@ public class FileStatusPersistenceService {
var now = OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS);
int countUpdate = fileRepository.setHardDelete(fileId, ProcessingStatus.PROCESSED, now, now, now);
if (countUpdate == 0) {
throw new NotFoundException(String.format("File with ID \"%s\" not found!", fileId));
}
fileRepository.setHardDelete(fileId, ProcessingStatus.PROCESSED, now, now, now);
fileAttributesRepository.deleteByFileId(fileId);
}