From a21d34e18fe35daf3909e1ded4f579cbbcb473d5 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Thu, 16 May 2024 21:43:51 +0200 Subject: [PATCH 01/26] RED-8670: integrate table inference from research --- .../build.gradle.kts | 2 + .../DossierTemplateControllerV2.java | 97 +++++- .../impl/mapper/ComponentMappingMapper.java | 28 ++ .../model/ComponentMappingSummaries.java | 22 ++ .../model/ComponentMappingSummary.java | 31 ++ .../resource/DossierTemplateResource.java | 62 +++- .../src/main/resources/api/openapi.yaml | 317 ++++++++++++++++++ .../build.gradle.kts | 1 + .../entity/ComponentMappingEntity.java | 78 +++++ .../ManualRedactionEntryEntity.java | 2 +- .../mapper/ComponentMappingEntityMapper.java | 24 ++ .../v1/processor/model/ComponentMapping.java | 9 + .../model/ComponentMappingDownloadModel.java | 7 + .../ComponentMappingPersistenceService.java | 99 ++++++ .../service/ComponentMappingService.java | 216 ++++++++++++ .../service/DossierTemplateImportService.java | 13 +- .../export/DossierTemplateExportService.java | 26 ++ .../ComponentMappingRepository.java | 17 + .../queue/OCRProcessingMessageReceiver.java | 4 +- .../db/changelog/db.changelog-tenant.yaml | 4 +- .../127-add-component-mapping-table.yaml | 97 ++++++ .../build.gradle.kts | 1 + .../peristence/v1/server/Application.java | 30 +- ...eCloneAndExportWithDuplicateRanksTest.java | 4 + .../importexport/ExportFilename.java | 3 +- publish-custom-image.sh | 25 +- 26 files changed, 1193 insertions(+), 26 deletions(-) create mode 100644 persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/mapper/ComponentMappingMapper.java create mode 100644 persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingSummaries.java create mode 100644 persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingSummary.java create mode 100644 persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/ComponentMappingEntity.java create mode 100644 persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java create mode 100644 persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/model/ComponentMapping.java create mode 100644 persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/model/ComponentMappingDownloadModel.java create mode 100644 persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java create mode 100644 persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java create mode 100644 persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/ComponentMappingRepository.java create mode 100644 persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/127-add-component-mapping-table.yaml diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/build.gradle.kts b/persistence-service-v1/persistence-service-external-api-impl-v2/build.gradle.kts index 88086af39..1cc96b271 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/build.gradle.kts +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/build.gradle.kts @@ -7,6 +7,8 @@ dependencies { api(project(":persistence-service-processor-v1")) api(project(":persistence-service-external-api-v2")) api(project(":persistence-service-external-api-impl-v1")) + implementation("org.mapstruct:mapstruct:1.5.5.Final") + annotationProcessor("org.mapstruct:mapstruct-processor:1.5.5.Final") } description = "persistence-service-external-api-impl-v2" diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java index 2787e550f..578c67268 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java @@ -6,8 +6,12 @@ import static com.iqser.red.service.persistence.management.v1.processor.roles.Ac import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_RULES; import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.Locale; @@ -23,9 +27,14 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import com.google.common.base.Strings; import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierTemplateController; import com.iqser.red.persistence.service.v1.external.api.impl.controller.FileAttributesController; +import com.iqser.red.persistence.service.v2.external.api.impl.mapper.ComponentMappingMapper; +import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMappingDownloadModel; +import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.RuleSetEntity; import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException; +import com.iqser.red.service.persistence.management.v1.processor.service.ComponentMappingService; import com.iqser.red.service.persistence.management.v1.processor.service.RulesValidationService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.RulesPersistenceService; @@ -35,8 +44,11 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCatego import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel; import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType; import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest; +import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetaData; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsValidationResponse; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesUploadRequest; +import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummaries; +import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummary; 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.resource.DossierTemplateResource; @@ -61,6 +73,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { private final RulesValidationService rulesValidationService; private final AuditPersistenceService auditPersistenceService; private final FileAttributesController fileAttributesController; + private final ComponentMappingService componentMappingService; public List getAllDossierTemplates() { @@ -162,6 +175,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { .dossierTemplateId(dossierTemplateId) .ruleFileType(ruleFileType) .build(); + DroolsValidationResponse rulesValidationResponse = new DroolsValidationResponse(); try { @@ -194,7 +208,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { private ResponseEntity downloadRules(String dossierTemplateId, RuleFileType ruleFileType) { - var ruleEntity = rulesPersistenceService.getRules(dossierTemplateId, ruleFileType); + RuleSetEntity ruleEntity = rulesPersistenceService.getRules(dossierTemplateId, ruleFileType); var data = ruleEntity.getValue().getBytes(StandardCharsets.UTF_8); @@ -209,4 +223,85 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { return new ResponseEntity<>(new InputStreamResource(is), httpHeaders, HttpStatus.OK); } + + @Override + @PreAuthorize("hasAuthority('" + READ_RULES + "')") + public ComponentMappingSummaries getComponentMappingSummaries(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) { + + List summaries = componentMappingService.getMetaDataByDossierTemplateId(dossierTemplateId); + List componentMappingSummaryList = ComponentMappingMapper.INSTANCE.toComponentMappingSummaryList(summaries); + return new ComponentMappingSummaries(dossierTemplateId, componentMappingSummaryList); + } + + + @Override + @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") + public ComponentMappingSummary uploadMapping(String dossierTemplateId, MultipartFile file, String name, String encoding, char delimiter) { + + String nameToUse = Strings.isNullOrEmpty(name) ? file.getName().split("\\.")[0] : name; + + if (Strings.isNullOrEmpty(nameToUse)) { + throw new BadRequestException("The provided file name is not valid!"); + } + + File mappingFile = saveToFile(file); + + ComponentMappingMetaData metaData = componentMappingService.create(dossierTemplateId, nameToUse, file.getName(), delimiter, encoding, mappingFile); + + return ComponentMappingMapper.INSTANCE.toComponentMappingSummary(metaData); + } + + + @Override + @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") + public ComponentMappingSummary updateMapping(String dossierTemplateId, String componentMappingId, MultipartFile file, String encoding, char delimiter) { + + File mappingFile = saveToFile(file); + + ComponentMappingMetaData resultMetaData = componentMappingService.update(componentMappingId, encoding, delimiter, mappingFile); + + return ComponentMappingMapper.INSTANCE.toComponentMappingSummary(resultMetaData); + } + + + @Override + @PreAuthorize("hasAuthority('" + READ_RULES + "')") + public ResponseEntity downloadMapping(String dossierTemplateId, String componentMappingId) { + + ComponentMappingDownloadModel mappingDownloadModel = componentMappingService.getMappingForDownload(componentMappingId); + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.TEXT_PLAIN); + + httpHeaders.add("Content-Disposition", + "attachment" + + "; filename*=" + + mappingDownloadModel.encoding().toLowerCase(Locale.US) + + "''" + + StringEncodingUtils.urlEncode(mappingDownloadModel.fileName())); + + return new ResponseEntity<>(mappingDownloadModel.mappingFileResource(), httpHeaders, HttpStatus.OK); + } + + + @Override + @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") + public ResponseEntity deleteMapping(String dossierTemplateId, String componentMappingId) { + + componentMappingService.delete(componentMappingId); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + + @SneakyThrows + private static File saveToFile(MultipartFile file) { + + Path mappingFile = Files.createTempFile(file.getName(), ".csv"); + + try (var out = new FileOutputStream(mappingFile.toFile())) { + out.write(file.getBytes()); + } + return mappingFile.toFile(); + } + } diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/mapper/ComponentMappingMapper.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/mapper/ComponentMappingMapper.java new file mode 100644 index 000000000..c859f0508 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/mapper/ComponentMappingMapper.java @@ -0,0 +1,28 @@ +package com.iqser.red.persistence.service.v2.external.api.impl.mapper; + +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetaData; +import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummary; + +@Mapper +public interface ComponentMappingMapper { + + ComponentMappingMapper INSTANCE = Mappers.getMapper(ComponentMappingMapper.class); + + + ComponentMappingSummary toComponentMappingSummary(ComponentMappingMetaData componentMappingMetaData); + + + List toComponentMappingSummaryList(List componentMappingMetaData); + + + ComponentMappingMetaData toComponentMappingMetaData(ComponentMappingSummary componentMappingSummary); + + + List toComponentMappingMetaDataList(List componentMappingSummary); + +} diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingSummaries.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingSummaries.java new file mode 100644 index 000000000..3ee82278d --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingSummaries.java @@ -0,0 +1,22 @@ +package com.iqser.red.service.persistence.service.v2.api.external.model; + +import java.util.List; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.FieldDefaults; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE) +public class ComponentMappingSummaries { + + String dossierTemplateId; + List componentMappingSummaries; + +} diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingSummary.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingSummary.java new file mode 100644 index 000000000..932c2d743 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingSummary.java @@ -0,0 +1,31 @@ +package com.iqser.red.service.persistence.service.v2.api.external.model; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +import org.springframework.cglib.core.Local; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.FieldDefaults; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE) +public class ComponentMappingSummary { + + String id; + String name; + String fileName; + Integer version; + List columnLabels; + Integer numberOfLines; + String encoding; + char delimiter; + +} diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java index 78c1ec44e..97e6827c1 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java @@ -2,6 +2,8 @@ package com.iqser.red.service.persistence.service.v2.api.external.resource; import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsValidationResponse; +import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummaries; +import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummary; import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinitionList; import io.swagger.v3.oas.annotations.Operation; @@ -13,9 +15,11 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; 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.PutMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.ResponseBody; @@ -34,6 +38,10 @@ public interface DossierTemplateResource { String ENTITY_RULES_PATH = "/entity-rules"; String COMPONENT_RULES_PATH = "/component-rules"; + String COMPONENT_MAPPINGS_PATH = "/component-mappings"; + String COMPONENT_MAPPING_ID_PARAM = "componentMappingId"; + String COMPONENT_MAPPING_ID_PATH_VARIABLE = "/{" + COMPONENT_MAPPING_ID_PARAM + "}"; + String FILE_ATTRIBUTE_DEFINITIONS_PATH = "/file-attribute-definitions"; String DOSSIER_TEMPLATE_ID_PARAM = "dossierTemplateId"; String DOSSIER_TEMPLATE_ID_PATH_VARIABLE = "/{" + DOSSIER_TEMPLATE_ID_PARAM + "}"; @@ -41,6 +49,9 @@ public interface DossierTemplateResource { String RULE_FILE_TYPE_PARAMETER_NAME = "ruleFileType"; String DRY_RUN_PARAM = "dryRun"; + String ENCODING_PARAM = "encoding"; + String DELIMITER_PARAM = "delimiter"; + String MAPPING_NAME_PARAM = "name"; @GetMapping(value = PATH, produces = MediaType.APPLICATION_JSON_VALUE) @@ -60,8 +71,8 @@ public interface DossierTemplateResource { @Operation(summary = "Upload a component or entity rules file in drools format for a specific DossierTemplate.") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified."), @ApiResponse(responseCode = "422", description = "Uploaded rules could not be compiled.")}) ResponseEntity uploadEntityRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, - @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, - @Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun); + @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, + @Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun); @ResponseBody @@ -96,4 +107,51 @@ public interface DossierTemplateResource { @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "File attribute definitions returned successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) FileAttributeDefinitionList getFileAttributeDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId); + + @Operation(summary = "Get the component mapping summaries of a DossierTemplate.", description = "None") + @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH, produces = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping views returned successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) + ComponentMappingSummaries getComponentMappingSummaries(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId); + + + @Operation(summary = "Upload a new component mapping to a DossierTemplate.", description = "None") + @PostMapping(value = PATH + + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + + COMPONENT_MAPPINGS_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping uploaded successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) + ComponentMappingSummary uploadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, + @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, + @Parameter(name = MAPPING_NAME_PARAM, description = "String of what the mapping should be accessible under. If left empty, the name of the file without the ending will be used as name.") @RequestParam(value = MAPPING_NAME_PARAM, required = false, defaultValue = "") String name, + @Parameter(name = ENCODING_PARAM, description = "The encoding of the file. Default is UTF-8.") @RequestParam(value = ENCODING_PARAM, required = false, defaultValue = "UTF-8") String encoding, + @Parameter(name = DELIMITER_PARAM, description = "The delimiter used in the file. Default is ','") @RequestParam(value = DELIMITER_PARAM, required = false, defaultValue = ",") char delimiter); + + + @Operation(summary = "Upload a new component mapping to a DossierTemplate.", description = "None") + @PutMapping(value = PATH + + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + + COMPONENT_MAPPINGS_PATH + + COMPONENT_MAPPING_ID_PATH_VARIABLE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping updated successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) + ComponentMappingSummary updateMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, + @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId, + @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, + @Parameter(name = ENCODING_PARAM, description = "The encoding of the file. Default is utf8.") @RequestParam(value = ENCODING_PARAM, required = false, defaultValue = "UTF-8") String encoding, + @Parameter(name = DELIMITER_PARAM, description = "The delimiter used in the file. Default is ','") @RequestParam(value = DELIMITER_PARAM, required = false, defaultValue = ",") char delimiter); + + + @ResponseBody + @ResponseStatus(value = HttpStatus.OK) + @Operation(summary = "Returns file containing the specified mapping as a file.") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) + @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE, produces = MediaType.MULTIPART_FORM_DATA_VALUE) + ResponseEntity downloadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId); + + + @ResponseBody + @ResponseStatus(value = HttpStatus.OK) + @Operation(summary = "Deletes a specified mapping.") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) + @DeleteMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE, produces = MediaType.MULTIPART_FORM_DATA_VALUE) + ResponseEntity deleteMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId); + } diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml index 4ccd4fb90..d6eebe731 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml @@ -290,6 +290,179 @@ paths: $ref: '#/components/schemas/FileAttributeDefinitionList' description: | Successfully returned the file attribute definitions for the specified dossier template. + /api/dossier-templates/{dossierTemplateId}/component-mappings: + get: + operationId: listAllMappings + tags: + - 1. Dossier Templates + summary: Returns a list of all existing component mappings in a dossier template + description: | + Retrieves a collection of component mapping views associated with a specific dossier template. Each component mapping view includes details such as name, number of lines, delimiter, encoding and other relevant metadata. This endpoint is useful for clients needing to understand what mappings are available under a particular dossier template. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ComponentMappingSummaries' + description: | + Successfully returned the component mapping summaries for the specified dossier template. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + post: + operationId: uploadMapping + summary: Upload a new component mapping to a DossierTemplate. + description: | + Utilize this endpoint to upload a new component mapping to a designated DossierTemplate. + The file is expected to be a comma separated file, whose first row are the labels for the columns of the file. + Further, it is expected that the data is rectangular, this means each row has the same size. + The rows in the file will be sorted the values in each column, starting with the left most and moving right recursively. + This enables much faster lookups down the line. + This means, it is highly beneficial to structure the csv such that the keys to query for appear in the first rows and the results to map in the last. + tags: + - 1. Dossier Templates + requestBody: + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/UploadRequest' + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/mappingName' + - $ref: '#/components/parameters/encoding' + - $ref: '#/components/parameters/delimiter' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ComponentMappingSummary' + description: | + Component mapping uploaded successfully and returned the component mapping summary. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + + /api/dossier-templates/{dossierTemplateId}/component-mappings/{comonentMappingId}: + get: + operationId: downloadMappingFile + tags: + - 1. Dossier Templates + summary: Download a specific component mapping file of a specific dossier template. + description: | + Utilize this endpoint to download a specific component mapping file of a designated dossier template. + The file is named the same and encoded the same as when it was uploaded, but it's sorting might have changed to provide faster lookups. + A component mapping file may be used in the component rules to relate components to existing master data. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/componentMappingId' + responses: + "200": + headers: + Content-Disposition: + schema: + type: string + example: attachment; filename*=utf-8''mapping.csv + content: + text/plain; charset=utf-8: + schema: + type: string + description: | + Successfully downloaded the requested component mapping. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + put: + operationId: updateMapping + summary: Update an existing component mapping of a DossierTemplate. + description: Updates an existing component mapping, + tags: + - 1. Dossier Templates + requestBody: + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/UploadRequest' + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/componentMappingId' + - $ref: '#/components/parameters/encoding' + - $ref: '#/components/parameters/delimiter' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ComponentMappingSummary' + description: | + Component mapping uploaded successfully and returned the component mapping summary. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + delete: + operationId: deleteMappingFile + tags: + - 1. Dossier Templates + summary: Delete a specific component mapping file of a specific dossier template. + description: | + Utilize this endpoint to delete a specific component mapping file of a designated dossier template. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/componentMappingId' + responses: + "204": + description: | + Successfully deleted the requested component mapping. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' /api/dossier-templates/{dossierTemplateId}/dossiers: get: operationId: getDossiers @@ -876,6 +1049,15 @@ components: style: simple explode: false description: The identifier of a dossier template + componentMappingId: + name: componentMappingId + in: path + required: true + schema: + type: string + style: simple + explode: false + description: The identifier of a component mapping dryRun: name: dryRun in: query @@ -888,6 +1070,38 @@ components: description: | A toggle to activate the dry-run mode: If set to `false` (default), the request will update the system. If set to `true`, the request will just be evaluated without actual changes in the system. + encoding: + name: encoding + required: false + in: query + schema: + type: string + enum: + - UTF-8 + - UTF-16 + - UTF_16BE + - UTF_16LE + - ISO-8859-1 + - US-ASCII + description: "An identifier for the used encoding of the file. Only java's standard charsets are supported, default value is 'UTF-8'" + delimiter: + name: delimiter + required: false + in: query + schema: + type: string + minLength: 1 + maxLength: 1 + example: ',' + description: "The delimiter used in a csv file. Default is ','." + mappingName: + name: name + required: false + in: query + schema: + type: string + example: "MasterDataMapping" + description: "The name with which the mapping should be associated with. If none is provided, the file name will be used." dossierId: name: dossierId in: path @@ -1848,6 +2062,109 @@ components: - daadea5f-917b-482a-b7d2-e65afe8f80ca - 8130acf6-4910-4123-827c-caacd8111402 dossierStatusId: b8280985-f558-43c0-852a-a3fa86803548 + + ComponentMappingSummary: + description: | + The `ComponentMappingSummary` object represents the metadata of a component mapping csv file. These csv files may be used in the component rules to relate components to an existing knowledge base. + type: object + properties: + id: + description: | + A unique identifier for the component mapping summary. It's generated automatically. Use this to retrieve a specific component mapping file. + type: string + name: + description: | + The name of the component mapping. + type: string + fileName: + description: | + The name of the uploaded component mapping file. + type: string + version: + description: | + The version of the file. It is incremented with each update to the file. + type: integer + columnLabels: + description: | + A list of column labels found in the component mapping file. + type: array + items: + type: string + numberOfLines: + description: | + The number of lines in the component mapping file. + type: integer + format: int32 + encoding: + description: | + The encoding used for the component mapping file. + type: string + delimiter: + description: | + The delimiter used for separating values in the component mapping file. + type: string + required: + - id + - name + - fileName + - columnLabels + - numberOfLines + - encoding + - delimiter + example: + id: 24ff9c3c-4863-4aea-8eda-cab8838b9192 + name: MasterDataMapping + fileName: master_data.csv + columnLabels: + - Column1 + - Column2 + - Column3 + numberOfLines: 100 + encoding: UTF-8 + delimiter: ',' + ComponentMappingSummaries: + description: | + The `ComponentMappingSummaries` object represents a collection of ComponentMappingSummary. + type: object + example: + dossierTemplateId: 1e07cde0-d36a-4ab7-b389-494ca694a0cb + componentMappingSummaries: + - id: 24ff9c3c-4863-4aea-8eda-cab8838b9192 + name: MasterDataMapping + fileName: master_data.csv + columnLabels: + - Column1 + - Column2 + - Column3 + numberOfLines: 100 + encoding: UTF-8 + delimiter: ',' + - id: 2e07cde0-d36a-4ab7-b389-494ca694a0cb + name: RegulationNameMapping + fileName: regulation-names.csv + columnLabels: + - Year + - Title + - Identifier + - Name + numberOfLines: 150 + encoding: UTF-16 + delimiter: ; + properties: + dossierTemplateId: + description: | + The identifier of the dossier template associated with the component mapping summaries. + type: string + format: uuid + componentMappingSummaries: + description: | + A list of component mapping summaries associated with this dossier template. + type: array + items: + $ref: "#/components/schemas/ComponentMappingSummary" + required: + - dossierTemplateId + - componentMappingSummaries DossierTemplate: description: | The `DossierTemplate` object represents the blueprint for creating and diff --git a/persistence-service-v1/persistence-service-processor-v1/build.gradle.kts b/persistence-service-v1/persistence-service-processor-v1/build.gradle.kts index dbf220411..6ec669f08 100644 --- a/persistence-service-v1/persistence-service-processor-v1/build.gradle.kts +++ b/persistence-service-v1/persistence-service-processor-v1/build.gradle.kts @@ -61,6 +61,7 @@ dependencies { api("com.opencsv:opencsv:5.4") api("org.springframework.cloud:spring-cloud-starter-openfeign:${springCloudVersion}") api("commons-validator:commons-validator:1.7") + api("com.opencsv:opencsv:5.9") implementation("org.mapstruct:mapstruct:1.5.5.Final") annotationProcessor("org.mapstruct:mapstruct-processor:1.5.5.Final") diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/ComponentMappingEntity.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/ComponentMappingEntity.java new file mode 100644 index 000000000..67f27f504 --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/ComponentMappingEntity.java @@ -0,0 +1,78 @@ +package com.iqser.red.service.persistence.management.v1.processor.entity; + +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.annotations.Fetch; +import org.springframework.data.annotation.LastModifiedDate; + +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity; + +import jakarta.persistence.CollectionTable; +import jakarta.persistence.Column; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Data +@Entity +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "component_mappings") +public class ComponentMappingEntity { + + @Id + @Column(name = "id") + String id; + + @NonNull + @ManyToOne(fetch = FetchType.LAZY) + private DossierTemplateEntity dossierTemplate; + + @NonNull + @Builder.Default + String storageId = ""; + + @NonNull + @Builder.Default + String name = ""; + + @NonNull + @Builder.Default + String fileName = ""; + + @NonNull + @Builder.Default + Integer version = -1; + + @LastModifiedDate + OffsetDateTime changedDate; + + @NonNull + @Builder.Default + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable(name = "component_mapping_column_labels") + List columnLabels = new ArrayList<>(); + + @NonNull + @Builder.Default + Integer numberOfLines = 0; + + @NonNull + @Builder.Default + String encoding = "UTF-8"; + + @Builder.Default + char delimiter = ','; + +} diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/annotations/ManualRedactionEntryEntity.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/annotations/ManualRedactionEntryEntity.java index fc3d0f1a3..030752a2b 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/annotations/ManualRedactionEntryEntity.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/annotations/ManualRedactionEntryEntity.java @@ -69,8 +69,8 @@ public class ManualRedactionEntryEntity implements IBaseAnnotation { @Column private OffsetDateTime softDeletedTime; - @ElementCollection(fetch = FetchType.EAGER) @Fetch(FetchMode.SUBSELECT) + @ElementCollection(fetch = FetchType.EAGER) private List positions = new ArrayList<>(); @ManyToOne(fetch = FetchType.LAZY) diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java new file mode 100644 index 000000000..e9c9e5788 --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java @@ -0,0 +1,24 @@ +package com.iqser.red.service.persistence.management.v1.processor.mapper; + +import java.util.List; + +import org.mapstruct.Mapper; + +import com.iqser.red.service.persistence.management.v1.processor.entity.ComponentMappingEntity; +import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetaData; + +@Mapper +public interface ComponentMappingEntityMapper { + + ComponentMappingMetaData toComponentMappingMetaData(ComponentMappingEntity componentMappingMetaData); + + + List toComponentMappingMetaDataList(List componentMappingMetaData); + + + ComponentMappingEntity toComponentMappingEntity(ComponentMappingMetaData componentMappingSummary); + + + List toComponentMappingEntityList(List componentMappingSummary); + +} diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/model/ComponentMapping.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/model/ComponentMapping.java new file mode 100644 index 000000000..aa45c3811 --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/model/ComponentMapping.java @@ -0,0 +1,9 @@ +package com.iqser.red.service.persistence.management.v1.processor.model; + +import java.io.File; + +import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetaData; + +public record ComponentMapping(ComponentMappingMetaData componentMappingMetaData, File mappingFile) { + +} diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/model/ComponentMappingDownloadModel.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/model/ComponentMappingDownloadModel.java new file mode 100644 index 000000000..399c9d76c --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/model/ComponentMappingDownloadModel.java @@ -0,0 +1,7 @@ +package com.iqser.red.service.persistence.management.v1.processor.model; + +import org.springframework.core.io.InputStreamResource; + +public record ComponentMappingDownloadModel(InputStreamResource mappingFileResource, String encoding, String fileName) { + +} diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java new file mode 100644 index 000000000..105b83b4c --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java @@ -0,0 +1,99 @@ +package com.iqser.red.service.persistence.management.v1.processor.service; + +import java.io.File; +import java.io.FileInputStream; +import java.nio.file.Path; +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMappingDownloadModel; +import com.iqser.red.service.persistence.management.v1.processor.entity.ComponentMappingEntity; +import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ComponentMappingRepository; +import com.iqser.red.storage.commons.service.StorageService; +import com.knecon.fforesight.tenantcommons.TenantContext; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.experimental.FieldDefaults; + +@Service +@Transactional +@RequiredArgsConstructor +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +public class ComponentMappingPersistenceService { + + StorageService storageService; + + ComponentMappingRepository repository; + + + public List getByDossierTemplateId(String dossierTemplateId) { + + return repository.findByDossierTemplateId(dossierTemplateId); + } + + + public void deleteById(String id) { + + ComponentMappingEntity entity = getEntityById(id); + delete(entity); + } + + + public ComponentMappingEntity getEntityById(String id) { + + return repository.findById(id) + .orElseThrow(() -> new NotFoundException("ComponentMapping with id " + id + " not found!")); + } + + + private void delete(ComponentMappingEntity entity) { + + repository.delete(entity); + storageService.deleteObject(TenantContext.getTenantId(), entity.getStorageId()); + } + + + public void deleteByDossierTemplateId(String dossierTemplateId) { + + repository.findByDossierTemplateId(dossierTemplateId) + .forEach(this::delete); + } + + + @SneakyThrows + public void updateOrCreate(String storageId, File mappingFile, ComponentMappingEntity entity) { + + repository.saveAndFlush(entity); + try (var in = new FileInputStream(mappingFile)) { + storageService.storeObject(TenantContext.getTenantId(), storageId, in); + } + } + + + public boolean existsByNameAndDossierTemplateId(String name, String dossierTemplateId) { + + return repository.existsByNameAndDossierTemplateId(name, dossierTemplateId); + } + + + @SneakyThrows + public ComponentMappingDownloadModel getMappingFileForDownload(String componentMappingId) { + + var entity = getEntityById(componentMappingId); + return new ComponentMappingDownloadModel(storageService.getObject(TenantContext.getTenantId(), entity.getStorageId()), entity.getEncoding(), entity.getFileName()); + } + + + public File downloadMappingFileToFolder(String storageId, String fileName, Path outputDir) { + + File outputFile = outputDir.resolve(fileName).toFile(); + storageService.downloadTo(TenantContext.getTenantId(), storageId, outputFile); + return outputFile; + } + +} diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java new file mode 100644 index 000000000..170fe41f9 --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java @@ -0,0 +1,216 @@ +package com.iqser.red.service.persistence.management.v1.processor.service; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Reader; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.UUID; + +import org.springframework.stereotype.Service; + +import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMappingDownloadModel; +import com.iqser.red.service.persistence.management.v1.processor.entity.ComponentMappingEntity; +import com.iqser.red.service.persistence.management.v1.processor.mapper.ComponentMappingEntityMapper; +import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMapping; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService; +import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetaData; +import com.opencsv.CSVParserBuilder; +import com.opencsv.CSVReader; +import com.opencsv.CSVReaderBuilder; +import com.opencsv.CSVWriter; +import com.opencsv.exceptions.CsvException; + +import io.undertow.util.BadRequestException; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.experimental.FieldDefaults; + +@Service +@RequiredArgsConstructor +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +public class ComponentMappingService { + + ComponentMappingPersistenceService componentMappingPersistenceService; + DossierTemplatePersistenceService dossierTemplatePersistenceService; + ComponentMappingEntityMapper mappingEntityMapper; + + static CSVSorter CSV_SORTER = new CSVSorter(); + + + public List getMetaDataByDossierTemplateId(String dossierTemplateId) { + + List entities = componentMappingPersistenceService.getByDossierTemplateId(dossierTemplateId); + return mappingEntityMapper.toComponentMappingMetaDataList(entities); + } + + + public ComponentMappingMetaData getMetaData(String mappingId) { + + return mappingEntityMapper.toComponentMappingMetaData(componentMappingPersistenceService.getEntityById(mappingId)); + } + + + @SneakyThrows + public ComponentMappingMetaData update(String mappingId, String encoding, char delimiter, File mappingFile) { + + ComponentMappingEntity entity = componentMappingPersistenceService.getEntityById(mappingId); + + return updateOrCreate(entity, encoding, delimiter, mappingFile); + } + + + @SneakyThrows + public ComponentMappingMetaData create(String dossierTemplateId, String name, String fileName, char delimiter, String encoding, File mappingFile) { + + if (componentMappingPersistenceService.existsByNameAndDossierTemplateId(name, dossierTemplateId)) { + throw new BadRequestException("A mapping with this name already exists in the dossier template!"); + } + + String id = UUID.randomUUID().toString(); + + ComponentMappingEntity entity = ComponentMappingEntity.builder() + .id(id) + .dossierTemplate(dossierTemplatePersistenceService.getDossierTemplate(dossierTemplateId)) + .storageId(dossierTemplateId + "/" + id) + .name(name) + .fileName(fileName) + .build(); + + return updateOrCreate(entity, encoding, delimiter, mappingFile); + } + + + @SneakyThrows + private ComponentMappingMetaData updateOrCreate(ComponentMappingEntity entity, String encoding, char delimiter, File mappingFile) { + + Charset charset = getCharset(entity); + + CsvStats stats = sortCSVFile(delimiter, mappingFile, charset); + + entity.setDelimiter(delimiter); + entity.setEncoding(encoding); + entity.setNumberOfLines(stats.numberOfLines()); + entity.setColumnLabels(stats.columnLabels()); + entity.setVersion(entity.getVersion() + 1); + + componentMappingPersistenceService.updateOrCreate(entity.getStorageId(), mappingFile, entity); + + return mappingEntityMapper.toComponentMappingMetaData(entity); + } + + + @SneakyThrows + private static Charset getCharset(ComponentMappingEntity entity) { + + try { + return Charset.forName(entity.getEncoding()); + + } catch (Exception e) { + throw new BadRequestException(e); + } + } + + + private static CsvStats sortCSVFile(char delimiter, File mappingFile, Charset charset) throws CsvException, BadRequestException, IOException { + + Path tempFile = Files.createTempFile("mapping", ".tmp"); + + Files.move(mappingFile.toPath(), tempFile, StandardCopyOption.REPLACE_EXISTING); + + String[] columnLabels; + int numberOfLines = 0; + try (Reader fileReader = new FileReader(tempFile.toFile(), charset);// + CSVReader reader = buildReader(fileReader, delimiter);// + CSVWriter writer = new CSVWriter(new FileWriter(mappingFile, charset))) { + + List rows = reader.readAll(); + + columnLabels = rows.remove(0); + + numberOfLines = (int) reader.getLinesRead(); + + rows.sort(CSV_SORTER); + + writer.writeNext(columnLabels); + + writer.writeAll(rows); + + } catch (IOException e) { + throw new BadRequestException("Error while sorting the csv file", e); + } + + Files.deleteIfExists(tempFile); + + return new CsvStats(Arrays.asList(columnLabels), numberOfLines); + } + + + private static CSVReader buildReader(Reader reader, char delimiter) throws IOException { + + return new CSVReaderBuilder(reader).withCSVParser(new CSVParserBuilder().withSeparator(delimiter).build()).build(); + } + + + public void delete(String componentMappingId) { + + componentMappingPersistenceService.deleteById(componentMappingId); + } + + + public ComponentMappingDownloadModel getMappingForDownload(String componentMappingId) { + + return componentMappingPersistenceService.getMappingFileForDownload(componentMappingId); + } + + + @SneakyThrows + public List getMappingFilesByDossierTemplateId(String dossierTemplateId, Path outputDir) { + + var entities = componentMappingPersistenceService.getByDossierTemplateId(dossierTemplateId); + + return entities.stream() + .map(mappingEntityMapper::toComponentMappingMetaData) + .map(metaData -> downloadFileAndCreateMapping(outputDir, metaData)) + .toList(); + + } + + + private ComponentMapping downloadFileAndCreateMapping(Path outputDir, ComponentMappingMetaData metaData) { + + File mappingFile = componentMappingPersistenceService.downloadMappingFileToFolder(metaData.getStorageId(), metaData.getFileName(), outputDir); + return new ComponentMapping(metaData, mappingFile); + } + + + private static class CSVSorter implements Comparator { + + @Override + public int compare(String[] line1, String[] line2) { + + for (int column = 0; column < line1.length; column++) { + if (line1[column].equals(line2[column])) { + continue; + } + return line1[column].compareTo(line2[column]); + } + + return 0; + } + + } + + private record CsvStats(List columnLabels, int numberOfLines) { + + } + +} diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java index fb0c6d290..b7945ee72 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java @@ -16,6 +16,7 @@ import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -60,6 +61,7 @@ import com.iqser.red.service.persistence.management.v1.processor.settings.FileMa import com.iqser.red.service.persistence.management.v1.processor.utils.FileUtils; import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType; import com.iqser.red.service.persistence.service.v1.api.shared.model.WatermarkModel; +import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetaData; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierAttributeConfig; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplate; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplateStatus; @@ -109,6 +111,7 @@ public class DossierTemplateImportService { private final StorageService storageService; private final ObjectMapper objectMapper = new ObjectMapper(); private final FileManagementServiceSettings settings; + private final ComponentMappingService componentMappingService; public String importDossierTemplate(ImportDossierTemplateRequest request) { @@ -145,6 +148,7 @@ public class DossierTemplateImportService { Map> typeEntriesMap = new HashMap<>(); Map> typeFalsePositivesMap = new HashMap<>(); Map> typeFalseRecommendationsMap = new HashMap<>(); + List componentMappingMetaData = new LinkedList<>(); while ((ze = zis.getNextZipEntry()) != null) { log.debug("---> " + ze.getName() + " ---- " + ze.isDirectory()); @@ -274,12 +278,15 @@ public class DossierTemplateImportService { reportTemplateFilenameList = reportTemplateList.stream() .map(rt -> rt.isMultiFileReport() ? rt.getFileName() + ExportFilename.REPORT_TEMPLATE_MULTI_FILE.getFilename() : rt.getFileName()) .toList(); + } else if (ze.getName().contains(ExportFilename.COMPONENT_MAPPINGS.getFilename()) ) { + log.info(ze.getName()); } else { reportTemplateBytesMap.put(ze.getName(), bos); } bos.close(); } } + importTemplateResult.setEntries(typeEntriesMap); importTemplateResult.setFalsePositives(typeFalsePositivesMap); importTemplateResult.setFalseRecommendations(typeFalseRecommendationsMap); @@ -537,7 +544,8 @@ public class DossierTemplateImportService { DroolsValidation droolsValidation = rulesValidationService.validateRules(RuleFileType.ENTITY, request.getRuleSet()); if (!droolsValidation.isCompiled()) { - droolsValidation.getSyntaxErrorMessages().forEach(errorMessage -> log.error(errorMessage.getMessage())); + droolsValidation.getSyntaxErrorMessages() + .forEach(errorMessage -> log.error(errorMessage.getMessage())); droolsValidation.getBlacklistErrorMessages() .forEach(errorMessage -> log.error(errorMessage.getMessage())); throw new BadRequestException("The entity rules do not compile or contain blacklisted keywords!"); @@ -546,7 +554,8 @@ public class DossierTemplateImportService { if (request.getComponentRuleSet() != null) { DroolsValidation componentDroolsValidation = rulesValidationService.validateRules(RuleFileType.COMPONENT, request.getComponentRuleSet()); if (!componentDroolsValidation.isCompiled()) { - componentDroolsValidation.getSyntaxErrorMessages().forEach(errorMessage -> log.error(errorMessage.getMessage())); + componentDroolsValidation.getSyntaxErrorMessages() + .forEach(errorMessage -> log.error(errorMessage.getMessage())); componentDroolsValidation.getBlacklistErrorMessages() .forEach(errorMessage -> log.error(errorMessage.getMessage())); throw new BadRequestException("The component rules do not compile or contain blacklisted keywords!"); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/export/DossierTemplateExportService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/export/DossierTemplateExportService.java index 166c86fa7..532003510 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/export/DossierTemplateExportService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/export/DossierTemplateExportService.java @@ -8,12 +8,15 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.stereotype.Service; +import org.springframework.util.FileSystemUtils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -24,8 +27,11 @@ import com.iqser.red.service.persistence.management.v1.processor.entity.configur import com.iqser.red.service.persistence.management.v1.processor.entity.download.DownloadStatusEntity; import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException; import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException; +import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMapping; import com.iqser.red.service.persistence.management.v1.processor.model.DownloadJob; import com.iqser.red.service.persistence.management.v1.processor.service.ColorsService; +import com.iqser.red.service.persistence.management.v1.processor.service.ComponentMappingPersistenceService; +import com.iqser.red.service.persistence.management.v1.processor.service.ComponentMappingService; import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService; import com.iqser.red.service.persistence.management.v1.processor.service.WatermarkService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService; @@ -79,6 +85,7 @@ public class DossierTemplateExportService { private final RulesPersistenceService rulesPersistenceService; private final FileManagementStorageService fileManagementStorageService; private final ReportTemplatePersistenceService reportTemplatePersistenceService; + private final ComponentMappingService componentMappingService; private final ColorsService colorsService; private final EntryPersistenceService entryPersistenceService; private final ObjectMapper objectMapper = new ObjectMapper(); @@ -234,6 +241,25 @@ public class DossierTemplateExportService { writeEntriesListToFile(fileSystemBackedArchiver, falseRecommendationValuesList, typeEntity.getType(), getFilename(ExportFilename.FALSE_RECOMMENDATION, TXT_EXT)); } + Path mappingDir = Files.createTempDirectory("mappings"); + + List componentMappings = componentMappingService.getMappingFilesByDossierTemplateId(dossierTemplateId, mappingDir); + + for (ComponentMapping componentMapping : componentMappings) { + + Path mappingFilePath = mappingDir.resolve(componentMapping.componentMappingMetaData().getFileName()); + + fileSystemBackedArchiver.addEntries(new FileSystemBackedArchiver.ArchiveModel(ExportFilename.COMPONENT_MAPPINGS.getFilename(), + componentMapping.componentMappingMetaData().getName() + JSON_EXT, + objectMapper.writeValueAsBytes(componentMapping))); + + fileSystemBackedArchiver.addEntries(new FileSystemBackedArchiver.ArchiveModel(ExportFilename.COMPONENT_MAPPINGS.getFilename(), + componentMapping.componentMappingMetaData().getFileName(), + Files.readAllBytes(mappingFilePath))); + } + + FileSystemUtils.deleteRecursively(mappingDir); + storeZipFile(downloadStatus.getStorageId(), fileSystemBackedArchiver); downloadStatusPersistenceService.updateStatus(downloadStatus, DownloadStatusValue.READY, fileSystemBackedArchiver.getContentLength()); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/ComponentMappingRepository.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/ComponentMappingRepository.java new file mode 100644 index 000000000..8f1a7c6c8 --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/ComponentMappingRepository.java @@ -0,0 +1,17 @@ +package com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.iqser.red.service.persistence.management.v1.processor.entity.ComponentMappingEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.DigitalSignatureEntity; + +public interface ComponentMappingRepository extends JpaRepository { + + List findByDossierTemplateId(String dossierTemplateId); + + + boolean existsByNameAndDossierTemplateId(String name, String dossierTemplateId); + +} diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/queue/OCRProcessingMessageReceiver.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/queue/OCRProcessingMessageReceiver.java index 7730046c4..ab16ad7f1 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/queue/OCRProcessingMessageReceiver.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/queue/OCRProcessingMessageReceiver.java @@ -53,7 +53,7 @@ public class OCRProcessingMessageReceiver { response.getNumberOfOCRedPages()); } - log.info("Received message {} in {}", response, MessagingConfiguration.OCR_STATUS_UPDATE_RESPONSE_QUEUE); + log.debug("Received message {} in {}", response, MessagingConfiguration.OCR_STATUS_UPDATE_RESPONSE_QUEUE); } @@ -63,7 +63,7 @@ public class OCRProcessingMessageReceiver { var response = objectMapper.readValue(failedMessage.getBody(), OCRStatusUpdateResponse.class); - log.info("Received message {} in {}", response, MessagingConfiguration.OCR_STATUS_UPDATE_RESPONSE_DQL); + log.debug("Received message {} in {}", response, MessagingConfiguration.OCR_STATUS_UPDATE_RESPONSE_DQL); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml b/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml index f01c902e0..9cf34c503 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml @@ -202,4 +202,6 @@ databaseChangeLog: - include: file: db/changelog/tenant/125-add-max-size-for-legal-basis-in-manual-legal-basis-change.yaml - include: - file: db/changelog/tenant/126-add-experimental-flag-to-entity.yaml \ No newline at end of file + file: db/changelog/tenant/126-add-experimental-flag-to-entity.yaml + - include: + file: db/changelog/tenant/127-add-component-mapping-table.yaml diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/127-add-component-mapping-table.yaml b/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/127-add-component-mapping-table.yaml new file mode 100644 index 000000000..f5abbd7da --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/127-add-component-mapping-table.yaml @@ -0,0 +1,97 @@ +databaseChangeLog: + - changeSet: + id: create-table-component_mappings + author: kilian + changes: + - createTable: + tableName: component_mappings + columns: + - column: + name: id + type: VARCHAR(255) + constraints: + primaryKey: true + - column: + name: dossier_template_id + type: VARCHAR(255) + constraints: + nullable: false + - column: + name: storage_id + type: VARCHAR(255) + constraints: + nullable: false + - column: + name: name + type: VARCHAR(255) + constraints: + nullable: false + - column: + name: file_name + type: VARCHAR(255) + constraints: + nullable: false + - column: + name: version + type: integer + constraints: + nullable: false + defaultValue: '-1' + - column: + name: changed_date + type: TIMESTAMP WITH TIME ZONE + constraints: + nullable: false + - column: + name: number_of_lines + type: INTEGER + constraints: + nullable: false + defaultValue: '0' + - column: + name: encoding + type: VARCHAR(255) + constraints: + nullable: false + defaultValue: 'UTF-8' + - column: + name: delimiter + type: CHAR(1) + constraints: + nullable: false + defaultValue: ',' + - addUniqueConstraint: + columnNames: name, dossier_template_id + constraintName: unique_name_dossier_template + tableName: component_mappings + - addForeignKeyConstraint: + baseColumnNames: dossier_template_id + baseTableName: component_mappings + constraintName: fk_entity_dossier_template_id + referencedColumnNames: id + referencedTableName: dossier_template + onDelete: CASCADE + onUpdate: CASCADE + + - createTable: + tableName: component_mapping_column_labels + columns: + - column: + name: component_mapping_entity_id + type: VARCHAR(255) + constraints: + nullable: false + - column: + name: label + type: VARCHAR(255) + constraints: + nullable: false + + - addForeignKeyConstraint: + baseColumnNames: component_mapping_entity_id + baseTableName: component_mapping_column_labels + constraintName: fk_component_mapping_column_labels_entity_id + referencedColumnNames: id + referencedTableName: component_mappings + onDelete: CASCADE + onUpdate: CASCADE diff --git a/persistence-service-v1/persistence-service-server-v1/build.gradle.kts b/persistence-service-v1/persistence-service-server-v1/build.gradle.kts index 19ed8474c..6e14c8152 100644 --- a/persistence-service-v1/persistence-service-server-v1/build.gradle.kts +++ b/persistence-service-v1/persistence-service-server-v1/build.gradle.kts @@ -27,6 +27,7 @@ dependencies { api("junit:junit:4.13.2") api("org.apache.logging.log4j:log4j-slf4j-impl:2.19.0") api("net.logstash.logback:logstash-logback-encoder:7.4") + testImplementation("org.springframework.amqp:spring-rabbit-test:3.0.2") testImplementation("org.springframework.security:spring-security-test:6.0.2") testImplementation("org.testcontainers:postgresql:1.17.1") diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/Application.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/Application.java index c4f7974eb..4bb95ecdb 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/Application.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/Application.java @@ -102,21 +102,21 @@ public class Application implements ApplicationContextAware { } - @Bean - @ConditionalOnProperty(value = "cors.enabled", havingValue = "true") - public WebMvcConfigurer corsConfigurer() { - - return new WebMvcConfigurer() { - - @Override - public void addCorsMappings(CorsRegistry registry) { - - log.info("Cross Origin Requests are enabled !!!"); - registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET", "POST", "DELETE", "PUT", "HEAD"); - - } - }; - } +// @Bean +// @ConditionalOnProperty(value = "cors.enabled", havingValue = "true") +// public WebMvcConfigurer corsConfigurer() { +// +// return new WebMvcConfigurer() { +// +// @Override +// public void addCorsMappings(CorsRegistry registry) { +// +// log.info("Cross Origin Requests are enabled !!!"); +// registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET", "POST", "DELETE", "PUT", "HEAD"); +// +// } +// }; +// } @Bean diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateCloneAndExportWithDuplicateRanksTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateCloneAndExportWithDuplicateRanksTest.java index cc9497ce1..a331e7341 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateCloneAndExportWithDuplicateRanksTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateCloneAndExportWithDuplicateRanksTest.java @@ -26,6 +26,7 @@ import com.iqser.red.service.persistence.management.v1.processor.entity.dossier. import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException; import com.iqser.red.service.persistence.management.v1.processor.migration.RankDeDuplicationService; import com.iqser.red.service.persistence.management.v1.processor.service.ColorsService; +import com.iqser.red.service.persistence.management.v1.processor.service.ComponentMappingService; import com.iqser.red.service.persistence.management.v1.processor.service.DossierTemplateCloneService; import com.iqser.red.service.persistence.management.v1.processor.service.DossierTemplateImportService; import com.iqser.red.service.persistence.management.v1.processor.service.DossierTemplateManagementService; @@ -73,6 +74,8 @@ public class DossierTemplateCloneAndExportWithDuplicateRanksTest { @MockBean private ColorsService colorsService; @MockBean + private ComponentMappingService componentMappingService; + @MockBean private StorageService storageService; @MockBean private DossierStatusPersistenceService dossierStatusPersistenceService; @@ -136,6 +139,7 @@ public class DossierTemplateCloneAndExportWithDuplicateRanksTest { rulesPersistenceService, fileManagementStorageService, reportTemplatePersistenceService, + componentMappingService, colorsService, entryPersistenceService, watermarkService, diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ExportFilename.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ExportFilename.java index aff455da0..82fe6b4a2 100644 --- a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ExportFilename.java +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ExportFilename.java @@ -19,7 +19,8 @@ public enum ExportFilename { DOSSIER_TYPE("dossierType"), ENTRIES("entries"), FALSE_POSITIVES("falsePositives"), - FALSE_RECOMMENDATION("falseRecommendations"); + FALSE_RECOMMENDATION("falseRecommendations"), + COMPONENT_MAPPINGS("componentMappings"); @Getter private final String filename; diff --git a/publish-custom-image.sh b/publish-custom-image.sh index b680eb16a..c909eb8ef 100755 --- a/publish-custom-image.sh +++ b/publish-custom-image.sh @@ -12,4 +12,27 @@ commit_hash=$(git rev-parse --short=5 HEAD) buildName="${USER}-${branch}-${commit_hash}" gradle bootBuildImage --publishImage -PbuildbootDockerHostNetwork=true -Pversion=$buildName -echo "nexus.knecon.com:5001/red/${dir}-server-v1:$buildName" + +newImageName="nexus.knecon.com:5001/red/${dir}-server-v1:$buildName" + +echo ${newImageName} + +if [ -z "$1" ]; then + exit 0 +fi + +namespace=${1} + +echo "deploying to ${namespace}" + +oldImageName=$(rancher kubectl -n ${namespace} get deployment persistence-service-v1 -o=jsonpath='{.spec.template.spec.containers[*].image}') + +if [ "${newImageName}" = "${oldImageName}" ]; then + echo "Image tag did not change, redeploying..." + rancher kubectl rollout restart deployment persistence-service-v1 -n ${namespace} +else + echo "upgrading..." + rancher kubectl set image deployment/persistence-service-v1 persistence-service=${newImageName} -n ${namespace} +fi +rancher kubectl rollout status deployment persistence-service-v1 -n ${namespace} +echo "Built ${dir} and deployed to ${namespace}" -- 2.47.2 From 8ceef60a26e5216120e3bcc58b9798d67b335dd3 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Thu, 16 May 2024 21:55:29 +0200 Subject: [PATCH 02/26] RED-8670: integrate table inference from research --- .../processor/mapper/ComponentMappingEntityMapper.java | 5 +++++ .../v1/processor/service/ComponentMappingService.java | 2 +- publish-custom-image.sh | 9 +++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java index e9c9e5788..a380d39fe 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java @@ -3,6 +3,8 @@ package com.iqser.red.service.persistence.management.v1.processor.mapper; import java.util.List; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.factory.Mappers; import com.iqser.red.service.persistence.management.v1.processor.entity.ComponentMappingEntity; import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetaData; @@ -10,6 +12,9 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.component.C @Mapper public interface ComponentMappingEntityMapper { + ComponentMappingEntityMapper INSTANCE = Mappers.getMapper(ComponentMappingEntityMapper.class); + + ComponentMappingMetaData toComponentMappingMetaData(ComponentMappingEntity componentMappingMetaData); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java index 170fe41f9..df0f6fc63 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java @@ -41,7 +41,7 @@ public class ComponentMappingService { ComponentMappingPersistenceService componentMappingPersistenceService; DossierTemplatePersistenceService dossierTemplatePersistenceService; - ComponentMappingEntityMapper mappingEntityMapper; + ComponentMappingEntityMapper mappingEntityMapper = ComponentMappingEntityMapper.INSTANCE; static CSVSorter CSV_SORTER = new CSVSorter(); diff --git a/publish-custom-image.sh b/publish-custom-image.sh index c909eb8ef..82a74219f 100755 --- a/publish-custom-image.sh +++ b/publish-custom-image.sh @@ -22,17 +22,18 @@ if [ -z "$1" ]; then fi namespace=${1} +deployment_name="persistence-service-v1" echo "deploying to ${namespace}" -oldImageName=$(rancher kubectl -n ${namespace} get deployment persistence-service-v1 -o=jsonpath='{.spec.template.spec.containers[*].image}') +oldImageName=$(rancher kubectl -n ${namespace} get deployment ${deployment_name} -o=jsonpath='{.spec.template.spec.containers[*].image}') if [ "${newImageName}" = "${oldImageName}" ]; then echo "Image tag did not change, redeploying..." - rancher kubectl rollout restart deployment persistence-service-v1 -n ${namespace} + rancher kubectl rollout restart deployment ${deployment_name} -n ${namespace} else echo "upgrading..." - rancher kubectl set image deployment/persistence-service-v1 persistence-service=${newImageName} -n ${namespace} + rancher kubectl set image deployment/${deployment_name} ${deployment_name}=${newImageName} -n ${namespace} fi -rancher kubectl rollout status deployment persistence-service-v1 -n ${namespace} +rancher kubectl rollout status deployment ${deployment_name} -n ${namespace} echo "Built ${dir} and deployed to ${namespace}" -- 2.47.2 From ddd24bb65c59ded9d7513d49beebd71a382c0c4d Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Thu, 16 May 2024 22:03:54 +0200 Subject: [PATCH 03/26] RED-8670: integrate table inference from research --- .../v1/processor/entity/ComponentMappingEntity.java | 2 +- .../changelog/tenant/127-add-component-mapping-table.yaml | 2 +- publish-custom-image.sh | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/ComponentMappingEntity.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/ComponentMappingEntity.java index 67f27f504..75cab62bb 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/ComponentMappingEntity.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/ComponentMappingEntity.java @@ -61,7 +61,7 @@ public class ComponentMappingEntity { @NonNull @Builder.Default @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "component_mapping_column_labels") + @CollectionTable(name = "component_mapping_column_labels", catalog = ) List columnLabels = new ArrayList<>(); @NonNull diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/127-add-component-mapping-table.yaml b/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/127-add-component-mapping-table.yaml index f5abbd7da..e9679364e 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/127-add-component-mapping-table.yaml +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/127-add-component-mapping-table.yaml @@ -82,7 +82,7 @@ databaseChangeLog: constraints: nullable: false - column: - name: label + name: column_labels type: VARCHAR(255) constraints: nullable: false diff --git a/publish-custom-image.sh b/publish-custom-image.sh index 82a74219f..4d8cdde10 100755 --- a/publish-custom-image.sh +++ b/publish-custom-image.sh @@ -15,7 +15,9 @@ gradle bootBuildImage --publishImage -PbuildbootDockerHostNetwork=true -Pversion newImageName="nexus.knecon.com:5001/red/${dir}-server-v1:$buildName" +echo "full image name:" echo ${newImageName} +echo "" if [ -z "$1" ]; then exit 0 @@ -32,8 +34,8 @@ if [ "${newImageName}" = "${oldImageName}" ]; then echo "Image tag did not change, redeploying..." rancher kubectl rollout restart deployment ${deployment_name} -n ${namespace} else - echo "upgrading..." + echo "upgrading the image tag..." rancher kubectl set image deployment/${deployment_name} ${deployment_name}=${newImageName} -n ${namespace} fi rancher kubectl rollout status deployment ${deployment_name} -n ${namespace} -echo "Built ${dir} and deployed to ${namespace}" +echo "Built ${deployment_name}:${buildName} and deployed to ${namespace}" -- 2.47.2 From aa04cb3ccd3950ec53ac6563df0f0849716fdfd4 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Thu, 16 May 2024 22:10:09 +0200 Subject: [PATCH 04/26] RED-8670: integrate table inference from research --- .../management/v1/processor/entity/ComponentMappingEntity.java | 2 +- .../processor/service/ComponentMappingPersistenceService.java | 2 ++ publish-custom-image.sh | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/ComponentMappingEntity.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/ComponentMappingEntity.java index 75cab62bb..67f27f504 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/ComponentMappingEntity.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/ComponentMappingEntity.java @@ -61,7 +61,7 @@ public class ComponentMappingEntity { @NonNull @Builder.Default @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "component_mapping_column_labels", catalog = ) + @CollectionTable(name = "component_mapping_column_labels") List columnLabels = new ArrayList<>(); @NonNull diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java index 105b83b4c..25ebd46f8 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java @@ -3,6 +3,7 @@ package com.iqser.red.service.persistence.management.v1.processor.service; import java.io.File; import java.io.FileInputStream; import java.nio.file.Path; +import java.time.OffsetDateTime; import java.util.List; import org.springframework.stereotype.Service; @@ -68,6 +69,7 @@ public class ComponentMappingPersistenceService { @SneakyThrows public void updateOrCreate(String storageId, File mappingFile, ComponentMappingEntity entity) { + entity.setChangedDate(OffsetDateTime.now()); repository.saveAndFlush(entity); try (var in = new FileInputStream(mappingFile)) { storageService.storeObject(TenantContext.getTenantId(), storageId, in); diff --git a/publish-custom-image.sh b/publish-custom-image.sh index 4d8cdde10..a02f13aaa 100755 --- a/publish-custom-image.sh +++ b/publish-custom-image.sh @@ -2,6 +2,8 @@ dir=${PWD##*/} gradle assemble +set -e + # Get the current Git branch branch=$(git rev-parse --abbrev-ref HEAD) -- 2.47.2 From 682af154f40160b5954bbc320257537dbf38b6ca Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Thu, 16 May 2024 22:21:26 +0200 Subject: [PATCH 05/26] RED-8670: integrate table inference from research --- .../impl/controller/DossierTemplateControllerV2.java | 11 +++++++---- publish-custom-image.sh | 10 ++++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java index 578c67268..701f5efa8 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java @@ -15,6 +15,7 @@ import java.nio.file.Path; import java.util.List; import java.util.Locale; +import org.mapstruct.factory.Mappers; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -74,6 +75,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { private final AuditPersistenceService auditPersistenceService; private final FileAttributesController fileAttributesController; private final ComponentMappingService componentMappingService; + private final ComponentMappingMapper componentMappingMapper = ComponentMappingMapper.INSTANCE; public List getAllDossierTemplates() { @@ -229,7 +231,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { public ComponentMappingSummaries getComponentMappingSummaries(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) { List summaries = componentMappingService.getMetaDataByDossierTemplateId(dossierTemplateId); - List componentMappingSummaryList = ComponentMappingMapper.INSTANCE.toComponentMappingSummaryList(summaries); + List componentMappingSummaryList = componentMappingMapper.toComponentMappingSummaryList(summaries); return new ComponentMappingSummaries(dossierTemplateId, componentMappingSummaryList); } @@ -245,10 +247,11 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { } File mappingFile = saveToFile(file); + String fileName = file.getOriginalFilename() == null ? nameToUse + ".csv" : file.getOriginalFilename(); - ComponentMappingMetaData metaData = componentMappingService.create(dossierTemplateId, nameToUse, file.getName(), delimiter, encoding, mappingFile); + ComponentMappingMetaData metaData = componentMappingService.create(dossierTemplateId, nameToUse, fileName , delimiter, encoding, mappingFile); - return ComponentMappingMapper.INSTANCE.toComponentMappingSummary(metaData); + return componentMappingMapper.toComponentMappingSummary(metaData); } @@ -260,7 +263,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { ComponentMappingMetaData resultMetaData = componentMappingService.update(componentMappingId, encoding, delimiter, mappingFile); - return ComponentMappingMapper.INSTANCE.toComponentMappingSummary(resultMetaData); + return componentMappingMapper.toComponentMappingSummary(resultMetaData); } diff --git a/publish-custom-image.sh b/publish-custom-image.sh index a02f13aaa..5a0def0ea 100755 --- a/publish-custom-image.sh +++ b/publish-custom-image.sh @@ -1,9 +1,11 @@ #!/bin/bash -dir=${PWD##*/} -gradle assemble set -e +dir=${PWD##*/} + +gradle assemble + # Get the current Git branch branch=$(git rev-parse --abbrev-ref HEAD) @@ -13,9 +15,9 @@ commit_hash=$(git rev-parse --short=5 HEAD) # Combine branch and commit hash buildName="${USER}-${branch}-${commit_hash}" -gradle bootBuildImage --publishImage -PbuildbootDockerHostNetwork=true -Pversion=$buildName +gradle bootBuildImage --publishImage -PbuildbootDockerHostNetwork=true -Pversion=${buildName} -newImageName="nexus.knecon.com:5001/red/${dir}-server-v1:$buildName" +newImageName="nexus.knecon.com:5001/red/${dir}-server-v1:${buildName}" echo "full image name:" echo ${newImageName} -- 2.47.2 From 9e2a603fc92e4465db959c2a533b48af61550b82 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Fri, 17 May 2024 00:39:55 +0200 Subject: [PATCH 06/26] RED-8670: integrate table inference from research --- .../controller/ComponentControllerV2.java | 129 +++++- .../DossierTemplateControllerV2.java | 97 +---- .../impl/mapper/ComponentMappingMapper.java | 12 +- ...ava => ComponentMappingMetadataModel.java} | 12 +- .../model/ComponentMappingSummary.java | 13 +- .../external/resource/ComponentResource.java | 74 +++- .../resource/DossierTemplateResource.java | 60 +-- .../src/main/resources/api/openapi.yaml | 366 +++++++++--------- .../mapper/ComponentMappingEntityMapper.java | 11 +- .../v1/processor/model/ComponentMapping.java | 4 +- .../service/ComponentMappingService.java | 46 ++- .../service/DossierTemplateImportService.java | 4 +- .../processor/service/FileStatusService.java | 3 + .../export/DossierTemplateExportService.java | 8 +- .../v1/api/shared/model/AnalyzeRequest.java | 4 + 15 files changed, 446 insertions(+), 397 deletions(-) rename persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/{ComponentMappingSummaries.java => ComponentMappingMetadataModel.java} (63%) diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ComponentControllerV2.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ComponentControllerV2.java index 097de9abb..a21eb22fd 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ComponentControllerV2.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ComponentControllerV2.java @@ -1,25 +1,45 @@ package com.iqser.red.persistence.service.v2.external.api.impl.controller; +import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_RULES; +import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_RULES; import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierResource.DOSSIER_ID_PARAM; import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource.DOSSIER_TEMPLATE_ID_PARAM; import static com.iqser.red.service.persistence.service.v2.api.external.resource.FileResource.FILE_ID_PARAM; +import java.io.FileOutputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; -import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierTemplateController; +import com.google.common.base.Strings; import com.iqser.red.persistence.service.v1.external.api.impl.controller.StatusController; +import com.iqser.red.persistence.service.v2.external.api.impl.mapper.ComponentMappingMapper; +import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException; +import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMappingDownloadModel; import com.iqser.red.service.persistence.management.v1.processor.service.ComponentLogService; +import com.iqser.red.service.persistence.management.v1.processor.service.ComponentMappingService; import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService; +import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntityReference; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntryValue; import com.iqser.red.service.persistence.service.v2.api.external.model.Component; +import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingMetadataModel; +import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummary; import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentValue; import com.iqser.red.service.persistence.service.v2.api.external.model.EntityReference; import com.iqser.red.service.persistence.service.v2.api.external.model.FileComponents; @@ -29,6 +49,7 @@ import com.iqser.red.service.persistence.service.v2.api.external.resource.Compon import io.swagger.v3.oas.annotations.tags.Tag; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import lombok.experimental.FieldDefaults; @RestController @@ -37,10 +58,12 @@ import lombok.experimental.FieldDefaults; @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) public class ComponentControllerV2 implements ComponentResource { - DossierTemplateController dossierTemplateController; ComponentLogService componentLogService; StatusController statusController; FileStatusService fileStatusService; + ComponentMappingService componentMappingService; + ComponentMappingMapper componentMappingMapper = ComponentMappingMapper.INSTANCE; + DossierTemplatePersistenceService dossierTemplatePersistenceService; @Override @@ -49,7 +72,7 @@ public class ComponentControllerV2 implements ComponentResource { @PathVariable(FILE_ID_PARAM) String fileId, @RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails) { - dossierTemplateController.getDossierTemplate(dossierTemplateId); + dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); var componentLog = componentLogService.getComponentLog(dossierId, fileId, true); Map> basicComponent = new LinkedHashMap<>(); @@ -119,11 +142,109 @@ public class ComponentControllerV2 implements ComponentResource { @PathVariable(DOSSIER_ID_PARAM) String dossierId, @RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails) { - dossierTemplateController.getDossierTemplate(dossierTemplateId); + dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); var dossierFiles = statusController.getDossierStatus(dossierId); return new FileComponentsList(dossierFiles.stream() .map(file -> getComponents(dossierTemplateId, dossierId, file.getFileId(), includeDetails)) .toList()); } + + @Override + @PreAuthorize("hasAuthority('" + READ_RULES + "')") + public ComponentMappingSummary getComponentMappingSummaries(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) { + + dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); + List summaries = componentMappingService.getMetaDataByDossierTemplateId(dossierTemplateId); + List componentMappingMetadataModelList = componentMappingMapper.toModelList(summaries); + return new ComponentMappingSummary(dossierTemplateId, componentMappingMetadataModelList); + } + + + @Override + @SneakyThrows + @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") + public ComponentMappingMetadataModel uploadMapping(String dossierTemplateId, MultipartFile file, String name, String encoding, char delimiter) { + + dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); + + String nameToUse = Strings.isNullOrEmpty(name) ? file.getName().split("\\.")[0] : name; + + if (Strings.isNullOrEmpty(nameToUse)) { + throw new BadRequestException("The provided file name is not valid!"); + } + + Path mappingFile = saveToFile(file); + String fileName = file.getOriginalFilename() == null ? nameToUse + ".csv" : file.getOriginalFilename(); + + com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata metaData = componentMappingService.create(dossierTemplateId, nameToUse, fileName, delimiter, encoding, mappingFile.toFile()); + + Files.deleteIfExists(mappingFile); + + return componentMappingMapper.toModel(metaData); + } + + + @Override + @SneakyThrows + @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") + public ComponentMappingMetadataModel updateMapping(String dossierTemplateId, String componentMappingId, MultipartFile file, String encoding, char delimiter) { + + dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); + + Path mappingFile = saveToFile(file); + + com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata resultMetaData = componentMappingService.update(componentMappingId, encoding, delimiter, mappingFile.toFile()); + + Files.deleteIfExists(mappingFile); + + return componentMappingMapper.toModel(resultMetaData); + } + + + @Override + @PreAuthorize("hasAuthority('" + READ_RULES + "')") + public ResponseEntity downloadMapping(String dossierTemplateId, String componentMappingId) { + + dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); + + ComponentMappingDownloadModel mappingDownloadModel = componentMappingService.getMappingForDownload(componentMappingId); + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.TEXT_PLAIN); + + httpHeaders.add("Content-Disposition", + "attachment" + + "; filename*=" + + mappingDownloadModel.encoding().toLowerCase(Locale.US) + + "''" + + StringEncodingUtils.urlEncode(mappingDownloadModel.fileName())); + + return new ResponseEntity<>(mappingDownloadModel.mappingFileResource(), httpHeaders, HttpStatus.OK); + } + + + @Override + @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") + public ResponseEntity deleteMapping(String dossierTemplateId, String componentMappingId) { + + dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); + + componentMappingService.delete(componentMappingId); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + + @SneakyThrows + private static Path saveToFile(MultipartFile file) { + + Path mappingFile = Files.createTempFile(file.getName(), ".csv"); + + try (var out = new FileOutputStream(mappingFile.toFile())) { + out.write(file.getBytes()); + } + return mappingFile; + } + } diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java index 701f5efa8..77cb78cad 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java @@ -6,16 +6,11 @@ import static com.iqser.red.service.persistence.management.v1.processor.roles.Ac import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_RULES; import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileOutputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.List; import java.util.Locale; -import org.mapstruct.factory.Mappers; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -28,14 +23,10 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import com.google.common.base.Strings; import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierTemplateController; import com.iqser.red.persistence.service.v1.external.api.impl.controller.FileAttributesController; -import com.iqser.red.persistence.service.v2.external.api.impl.mapper.ComponentMappingMapper; -import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMappingDownloadModel; import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.RuleSetEntity; import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException; -import com.iqser.red.service.persistence.management.v1.processor.service.ComponentMappingService; import com.iqser.red.service.persistence.management.v1.processor.service.RulesValidationService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.RulesPersistenceService; @@ -45,11 +36,8 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCatego import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel; import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType; import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest; -import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetaData; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsValidationResponse; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesUploadRequest; -import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummaries; -import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummary; 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.resource.DossierTemplateResource; @@ -74,8 +62,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { private final RulesValidationService rulesValidationService; private final AuditPersistenceService auditPersistenceService; private final FileAttributesController fileAttributesController; - private final ComponentMappingService componentMappingService; - private final ComponentMappingMapper componentMappingMapper = ComponentMappingMapper.INSTANCE; + public List getAllDossierTemplates() { @@ -225,86 +212,4 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { return new ResponseEntity<>(new InputStreamResource(is), httpHeaders, HttpStatus.OK); } - - @Override - @PreAuthorize("hasAuthority('" + READ_RULES + "')") - public ComponentMappingSummaries getComponentMappingSummaries(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) { - - List summaries = componentMappingService.getMetaDataByDossierTemplateId(dossierTemplateId); - List componentMappingSummaryList = componentMappingMapper.toComponentMappingSummaryList(summaries); - return new ComponentMappingSummaries(dossierTemplateId, componentMappingSummaryList); - } - - - @Override - @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") - public ComponentMappingSummary uploadMapping(String dossierTemplateId, MultipartFile file, String name, String encoding, char delimiter) { - - String nameToUse = Strings.isNullOrEmpty(name) ? file.getName().split("\\.")[0] : name; - - if (Strings.isNullOrEmpty(nameToUse)) { - throw new BadRequestException("The provided file name is not valid!"); - } - - File mappingFile = saveToFile(file); - String fileName = file.getOriginalFilename() == null ? nameToUse + ".csv" : file.getOriginalFilename(); - - ComponentMappingMetaData metaData = componentMappingService.create(dossierTemplateId, nameToUse, fileName , delimiter, encoding, mappingFile); - - return componentMappingMapper.toComponentMappingSummary(metaData); - } - - - @Override - @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") - public ComponentMappingSummary updateMapping(String dossierTemplateId, String componentMappingId, MultipartFile file, String encoding, char delimiter) { - - File mappingFile = saveToFile(file); - - ComponentMappingMetaData resultMetaData = componentMappingService.update(componentMappingId, encoding, delimiter, mappingFile); - - return componentMappingMapper.toComponentMappingSummary(resultMetaData); - } - - - @Override - @PreAuthorize("hasAuthority('" + READ_RULES + "')") - public ResponseEntity downloadMapping(String dossierTemplateId, String componentMappingId) { - - ComponentMappingDownloadModel mappingDownloadModel = componentMappingService.getMappingForDownload(componentMappingId); - - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.setContentType(MediaType.TEXT_PLAIN); - - httpHeaders.add("Content-Disposition", - "attachment" - + "; filename*=" - + mappingDownloadModel.encoding().toLowerCase(Locale.US) - + "''" - + StringEncodingUtils.urlEncode(mappingDownloadModel.fileName())); - - return new ResponseEntity<>(mappingDownloadModel.mappingFileResource(), httpHeaders, HttpStatus.OK); - } - - - @Override - @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") - public ResponseEntity deleteMapping(String dossierTemplateId, String componentMappingId) { - - componentMappingService.delete(componentMappingId); - return new ResponseEntity<>(HttpStatus.NO_CONTENT); - } - - - @SneakyThrows - private static File saveToFile(MultipartFile file) { - - Path mappingFile = Files.createTempFile(file.getName(), ".csv"); - - try (var out = new FileOutputStream(mappingFile.toFile())) { - out.write(file.getBytes()); - } - return mappingFile.toFile(); - } - } diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/mapper/ComponentMappingMapper.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/mapper/ComponentMappingMapper.java index c859f0508..975db2bf8 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/mapper/ComponentMappingMapper.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/mapper/ComponentMappingMapper.java @@ -5,8 +5,8 @@ import java.util.List; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; -import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetaData; -import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummary; +import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata; +import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingMetadataModel; @Mapper public interface ComponentMappingMapper { @@ -14,15 +14,15 @@ public interface ComponentMappingMapper { ComponentMappingMapper INSTANCE = Mappers.getMapper(ComponentMappingMapper.class); - ComponentMappingSummary toComponentMappingSummary(ComponentMappingMetaData componentMappingMetaData); + ComponentMappingMetadataModel toModel(ComponentMappingMetadata componentMappingMetadata); - List toComponentMappingSummaryList(List componentMappingMetaData); + List toModelList(List componentMappingMetadata); - ComponentMappingMetaData toComponentMappingMetaData(ComponentMappingSummary componentMappingSummary); + ComponentMappingMetadata toDto(ComponentMappingMetadataModel componentMappingMetadataModel); - List toComponentMappingMetaDataList(List componentMappingSummary); + List toDtoList(List componentMappingMetadataModels); } diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingSummaries.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingMetadataModel.java similarity index 63% rename from persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingSummaries.java rename to persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingMetadataModel.java index 3ee82278d..b8da5546b 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingSummaries.java +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingMetadataModel.java @@ -14,9 +14,15 @@ import lombok.experimental.FieldDefaults; @NoArgsConstructor @AllArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE) -public class ComponentMappingSummaries { +public class ComponentMappingMetadataModel { - String dossierTemplateId; - List componentMappingSummaries; + String id; + String name; + String fileName; + Integer version; + List columnLabels; + Integer numberOfLines; + String encoding; + char delimiter; } diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingSummary.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingSummary.java index 932c2d743..7a807225a 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingSummary.java +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/ComponentMappingSummary.java @@ -1,10 +1,7 @@ package com.iqser.red.service.persistence.service.v2.api.external.model; -import java.nio.charset.StandardCharsets; import java.util.List; -import org.springframework.cglib.core.Local; - import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; @@ -19,13 +16,7 @@ import lombok.experimental.FieldDefaults; @FieldDefaults(level = AccessLevel.PRIVATE) public class ComponentMappingSummary { - String id; - String name; - String fileName; - Integer version; - List columnLabels; - Integer numberOfLines; - String encoding; - char delimiter; + String dossierTemplateId; + List componentMappingMetadata; } diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/ComponentResource.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/ComponentResource.java index ca0cdbec1..c2c7aef7d 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/ComponentResource.java +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/ComponentResource.java @@ -8,20 +8,29 @@ import static com.iqser.red.service.persistence.service.v2.api.external.resource import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource.DOSSIER_TEMPLATE_PATH; import static com.iqser.red.service.persistence.service.v2.api.external.resource.FileResource.FILE_ID_PARAM; import static com.iqser.red.service.persistence.service.v2.api.external.resource.FileResource.FILE_ID_PATH_VARIABLE; -import static com.iqser.red.service.persistence.service.v2.api.external.resource.FileResource.FILE_PATH; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; 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.PutMapping; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.multipart.MultipartFile; +import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingMetadataModel; +import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummary; import com.iqser.red.service.persistence.service.v2.api.external.model.FileComponents; import com.iqser.red.service.persistence.service.v2.api.external.model.FileComponentsList; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; @@ -29,7 +38,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; @ResponseStatus(value = HttpStatus.OK) public interface ComponentResource { - String PATH = ExternalApiConstants.BASE_PATH + DOSSIER_TEMPLATE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DOSSIER_PATH + DOSSIER_ID_PATH_PARAM + FILE_PATH; + String PATH = ExternalApiConstants.BASE_PATH + DOSSIER_TEMPLATE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE; + String FILE_PATH = PATH + DOSSIER_PATH + DOSSIER_ID_PATH_PARAM + FileResource.FILE_PATH; String COMPONENTS_PATH = "/components"; @@ -44,8 +54,17 @@ public interface ComponentResource { - false (default): The component object does not contain a field componentDetails. """; + String COMPONENT_MAPPINGS_PATH = COMPONENTS_PATH + "/mappings"; + String COMPONENT_MAPPING_ID_PARAM = "componentMappingId"; + String COMPONENT_MAPPING_ID_PATH_VARIABLE = "/{" + COMPONENT_MAPPING_ID_PARAM + "}"; - @GetMapping(value = PATH + FILE_ID_PATH_VARIABLE + COMPONENTS_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}) + String RULE_FILE_TYPE_PARAMETER_NAME = "ruleFileType"; + String ENCODING_PARAM = "encoding"; + String DELIMITER_PARAM = "delimiter"; + String MAPPING_NAME_PARAM = "name"; + + + @GetMapping(value = FILE_PATH + FILE_ID_PATH_VARIABLE + COMPONENTS_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}) @Operation(summary = "Returns the components for a file", description = "None") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")}) FileComponents getComponents(@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, @@ -54,11 +73,58 @@ public interface ComponentResource { @Parameter(name = INCLUDE_DETAILS_PARAM, description = INCLUDE_DETAILS_DESCRIPTION) @RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails); - @GetMapping(value = PATH + BULK_COMPONENTS_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}) + @GetMapping(value = FILE_PATH + BULK_COMPONENTS_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}) @Operation(summary = "Returns the components for all files of a dossier", description = "None") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")}) FileComponentsList getComponentsOfDossier(@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 that contains the file.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId, @Parameter(name = INCLUDE_DETAILS_PARAM, description = INCLUDE_DETAILS_DESCRIPTION) @RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails); + + @Operation(summary = "Get the component mapping summaries of a DossierTemplate.", description = "None") + @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH, produces = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping views returned successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) + ComponentMappingSummary getComponentMappingSummaries(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId); + + + @Operation(summary = "Upload a new component mapping to a DossierTemplate.", description = "None") + @PostMapping(value = PATH + + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + + COMPONENT_MAPPINGS_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping uploaded successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) + ComponentMappingMetadataModel uploadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, + @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, + @Parameter(name = MAPPING_NAME_PARAM, description = "String of what the mapping should be accessible under. If left empty, the name of the file without the ending will be used as name.") @RequestParam(value = MAPPING_NAME_PARAM, required = false, defaultValue = "") String name, + @Parameter(name = ENCODING_PARAM, description = "The encoding of the file. Default is UTF-8.") @RequestParam(value = ENCODING_PARAM, required = false, defaultValue = "UTF-8") String encoding, + @Parameter(name = DELIMITER_PARAM, description = "The delimiter used in the file. Default is ','") @RequestParam(value = DELIMITER_PARAM, required = false, defaultValue = ",") char delimiter); + + + @Operation(summary = "Update an existing component mapping of a DossierTemplate.", description = "None") + @PutMapping(value = PATH + + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + + COMPONENT_MAPPINGS_PATH + + COMPONENT_MAPPING_ID_PATH_VARIABLE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping updated successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) + ComponentMappingMetadataModel updateMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, + @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId, + @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, + @Parameter(name = ENCODING_PARAM, description = "The encoding of the file. Default is UTF-8.") @RequestParam(value = ENCODING_PARAM, required = false, defaultValue = "UTF-8") String encoding, + @Parameter(name = DELIMITER_PARAM, description = "The delimiter used in the file. Default is ','") @RequestParam(value = DELIMITER_PARAM, required = false, defaultValue = ",") char delimiter); + + + @ResponseBody + @ResponseStatus(value = HttpStatus.OK) + @Operation(summary = "Returns file containing the specified mapping as a file.") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) + @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE, produces = MediaType.MULTIPART_FORM_DATA_VALUE) + ResponseEntity downloadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId); + + + @ResponseBody + @ResponseStatus(value = HttpStatus.OK) + @Operation(summary = "Deletes a specified mapping.") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) + @DeleteMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE, produces = MediaType.MULTIPART_FORM_DATA_VALUE) + ResponseEntity deleteMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId); + } diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java index 97e6827c1..116cb7717 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java @@ -2,8 +2,6 @@ package com.iqser.red.service.persistence.service.v2.api.external.resource; import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsValidationResponse; -import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummaries; -import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummary; import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinitionList; import io.swagger.v3.oas.annotations.Operation; @@ -15,11 +13,9 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; 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.PutMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.ResponseBody; @@ -38,20 +34,15 @@ public interface DossierTemplateResource { String ENTITY_RULES_PATH = "/entity-rules"; String COMPONENT_RULES_PATH = "/component-rules"; - String COMPONENT_MAPPINGS_PATH = "/component-mappings"; - String COMPONENT_MAPPING_ID_PARAM = "componentMappingId"; - String COMPONENT_MAPPING_ID_PATH_VARIABLE = "/{" + COMPONENT_MAPPING_ID_PARAM + "}"; + 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 RULE_FILE_TYPE_PARAMETER_NAME = "ruleFileType"; String DRY_RUN_PARAM = "dryRun"; - String ENCODING_PARAM = "encoding"; - String DELIMITER_PARAM = "delimiter"; - String MAPPING_NAME_PARAM = "name"; + @GetMapping(value = PATH, produces = MediaType.APPLICATION_JSON_VALUE) @@ -107,51 +98,4 @@ public interface DossierTemplateResource { @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "File attribute definitions returned successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) FileAttributeDefinitionList getFileAttributeDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId); - - @Operation(summary = "Get the component mapping summaries of a DossierTemplate.", description = "None") - @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH, produces = MediaType.APPLICATION_JSON_VALUE) - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping views returned successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) - ComponentMappingSummaries getComponentMappingSummaries(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId); - - - @Operation(summary = "Upload a new component mapping to a DossierTemplate.", description = "None") - @PostMapping(value = PATH - + DOSSIER_TEMPLATE_ID_PATH_VARIABLE - + COMPONENT_MAPPINGS_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping uploaded successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) - ComponentMappingSummary uploadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, - @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, - @Parameter(name = MAPPING_NAME_PARAM, description = "String of what the mapping should be accessible under. If left empty, the name of the file without the ending will be used as name.") @RequestParam(value = MAPPING_NAME_PARAM, required = false, defaultValue = "") String name, - @Parameter(name = ENCODING_PARAM, description = "The encoding of the file. Default is UTF-8.") @RequestParam(value = ENCODING_PARAM, required = false, defaultValue = "UTF-8") String encoding, - @Parameter(name = DELIMITER_PARAM, description = "The delimiter used in the file. Default is ','") @RequestParam(value = DELIMITER_PARAM, required = false, defaultValue = ",") char delimiter); - - - @Operation(summary = "Upload a new component mapping to a DossierTemplate.", description = "None") - @PutMapping(value = PATH - + DOSSIER_TEMPLATE_ID_PATH_VARIABLE - + COMPONENT_MAPPINGS_PATH - + COMPONENT_MAPPING_ID_PATH_VARIABLE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping updated successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) - ComponentMappingSummary updateMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, - @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId, - @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, - @Parameter(name = ENCODING_PARAM, description = "The encoding of the file. Default is utf8.") @RequestParam(value = ENCODING_PARAM, required = false, defaultValue = "UTF-8") String encoding, - @Parameter(name = DELIMITER_PARAM, description = "The delimiter used in the file. Default is ','") @RequestParam(value = DELIMITER_PARAM, required = false, defaultValue = ",") char delimiter); - - - @ResponseBody - @ResponseStatus(value = HttpStatus.OK) - @Operation(summary = "Returns file containing the specified mapping as a file.") - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) - @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE, produces = MediaType.MULTIPART_FORM_DATA_VALUE) - ResponseEntity downloadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId); - - - @ResponseBody - @ResponseStatus(value = HttpStatus.OK) - @Operation(summary = "Deletes a specified mapping.") - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) - @DeleteMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE, produces = MediaType.MULTIPART_FORM_DATA_VALUE) - ResponseEntity deleteMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId); - } diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml index d6eebe731..baf9fe7b9 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml @@ -290,179 +290,6 @@ paths: $ref: '#/components/schemas/FileAttributeDefinitionList' description: | Successfully returned the file attribute definitions for the specified dossier template. - /api/dossier-templates/{dossierTemplateId}/component-mappings: - get: - operationId: listAllMappings - tags: - - 1. Dossier Templates - summary: Returns a list of all existing component mappings in a dossier template - description: | - Retrieves a collection of component mapping views associated with a specific dossier template. Each component mapping view includes details such as name, number of lines, delimiter, encoding and other relevant metadata. This endpoint is useful for clients needing to understand what mappings are available under a particular dossier template. - parameters: - - $ref: '#/components/parameters/dossierTemplateId' - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/ComponentMappingSummaries' - description: | - Successfully returned the component mapping summaries for the specified dossier template. - "400": - $ref: '#/components/responses/400' - "401": - $ref: '#/components/responses/401' - "403": - $ref: '#/components/responses/403' - "404": - $ref: '#/components/responses/404-dossier-template' - "429": - $ref: '#/components/responses/429' - "500": - $ref: '#/components/responses/500' - post: - operationId: uploadMapping - summary: Upload a new component mapping to a DossierTemplate. - description: | - Utilize this endpoint to upload a new component mapping to a designated DossierTemplate. - The file is expected to be a comma separated file, whose first row are the labels for the columns of the file. - Further, it is expected that the data is rectangular, this means each row has the same size. - The rows in the file will be sorted the values in each column, starting with the left most and moving right recursively. - This enables much faster lookups down the line. - This means, it is highly beneficial to structure the csv such that the keys to query for appear in the first rows and the results to map in the last. - tags: - - 1. Dossier Templates - requestBody: - content: - multipart/form-data: - schema: - $ref: '#/components/schemas/UploadRequest' - parameters: - - $ref: '#/components/parameters/dossierTemplateId' - - $ref: '#/components/parameters/mappingName' - - $ref: '#/components/parameters/encoding' - - $ref: '#/components/parameters/delimiter' - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/ComponentMappingSummary' - description: | - Component mapping uploaded successfully and returned the component mapping summary. - "400": - $ref: '#/components/responses/400' - "401": - $ref: '#/components/responses/401' - "403": - $ref: '#/components/responses/403' - "404": - $ref: '#/components/responses/404-dossier-template' - "429": - $ref: '#/components/responses/429' - "500": - $ref: '#/components/responses/500' - - /api/dossier-templates/{dossierTemplateId}/component-mappings/{comonentMappingId}: - get: - operationId: downloadMappingFile - tags: - - 1. Dossier Templates - summary: Download a specific component mapping file of a specific dossier template. - description: | - Utilize this endpoint to download a specific component mapping file of a designated dossier template. - The file is named the same and encoded the same as when it was uploaded, but it's sorting might have changed to provide faster lookups. - A component mapping file may be used in the component rules to relate components to existing master data. - parameters: - - $ref: '#/components/parameters/dossierTemplateId' - - $ref: '#/components/parameters/componentMappingId' - responses: - "200": - headers: - Content-Disposition: - schema: - type: string - example: attachment; filename*=utf-8''mapping.csv - content: - text/plain; charset=utf-8: - schema: - type: string - description: | - Successfully downloaded the requested component mapping. - "400": - $ref: '#/components/responses/400' - "401": - $ref: '#/components/responses/401' - "403": - $ref: '#/components/responses/403' - "404": - $ref: '#/components/responses/404-dossier-template' - "429": - $ref: '#/components/responses/429' - "500": - $ref: '#/components/responses/500' - put: - operationId: updateMapping - summary: Update an existing component mapping of a DossierTemplate. - description: Updates an existing component mapping, - tags: - - 1. Dossier Templates - requestBody: - content: - multipart/form-data: - schema: - $ref: '#/components/schemas/UploadRequest' - parameters: - - $ref: '#/components/parameters/dossierTemplateId' - - $ref: '#/components/parameters/componentMappingId' - - $ref: '#/components/parameters/encoding' - - $ref: '#/components/parameters/delimiter' - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/ComponentMappingSummary' - description: | - Component mapping uploaded successfully and returned the component mapping summary. - "400": - $ref: '#/components/responses/400' - "401": - $ref: '#/components/responses/401' - "403": - $ref: '#/components/responses/403' - "404": - $ref: '#/components/responses/404-dossier-template' - "429": - $ref: '#/components/responses/429' - "500": - $ref: '#/components/responses/500' - delete: - operationId: deleteMappingFile - tags: - - 1. Dossier Templates - summary: Delete a specific component mapping file of a specific dossier template. - description: | - Utilize this endpoint to delete a specific component mapping file of a designated dossier template. - parameters: - - $ref: '#/components/parameters/dossierTemplateId' - - $ref: '#/components/parameters/componentMappingId' - responses: - "204": - description: | - Successfully deleted the requested component mapping. - "400": - $ref: '#/components/responses/400' - "401": - $ref: '#/components/responses/401' - "403": - $ref: '#/components/responses/403' - "404": - $ref: '#/components/responses/404-dossier-template' - "429": - $ref: '#/components/responses/429' - "500": - $ref: '#/components/responses/500' /api/dossier-templates/{dossierTemplateId}/dossiers: get: operationId: getDossiers @@ -918,6 +745,179 @@ paths: $ref: '#/components/responses/403' "500": $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/component-mappings: + get: + operationId: listAllMappings + tags: + - 4. Components + summary: Returns a list of all existing component mappings in a dossier template + description: | + Retrieves a collection of component mapping views associated with a specific dossier template. Each component mapping view includes details such as name, number of lines, delimiter, encoding and other relevant metadata. This endpoint is useful for clients needing to understand what mappings are available under a particular dossier template. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ComponentMappingSummary' + description: | + Successfully returned the component mapping summary for the specified dossier template. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + post: + operationId: uploadMapping + summary: Upload a new component mapping to a DossierTemplate. + description: | + Utilize this endpoint to upload a new component mapping to a designated DossierTemplate. + The file is expected to be a comma separated file, whose first row are the labels for the columns of the file. + Further, it is expected that the data is rectangular, this means each row has the same size. + The rows in the file will be sorted the values in each column, starting with the left most and moving right recursively. + This enables much faster lookups down the line. + This means, it is highly beneficial to structure the CSV File such that the keys to query for appear in the first rows and the results to map in the last. + tags: + - 4. Components + requestBody: + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/UploadRequest' + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/mappingName' + - $ref: '#/components/parameters/encoding' + - $ref: '#/components/parameters/delimiter' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ComponentMappingMetadata' + description: | + Component mapping uploaded successfully and returned the component mapping metadata. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + + /api/dossier-templates/{dossierTemplateId}/component-mappings/{comonentMappingId}: + get: + operationId: downloadMappingFile + tags: + - 4. Components + summary: Download a specific component mapping file of a specific dossier template. + description: | + Utilize this endpoint to download a specific component mapping file of a designated dossier template. + The file is named the same and encoded the same as when it was uploaded, but it's sorting might have changed to provide faster lookups. + A component mapping file may be used in the component rules to relate components to existing master data. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/componentMappingId' + responses: + "200": + headers: + Content-Disposition: + schema: + type: string + example: attachment; filename*=utf-8''mapping.csv + content: + text/plain; charset=utf-8: + schema: + type: string + description: | + Successfully downloaded the requested component mapping. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + put: + operationId: updateMapping + summary: Update an existing component mapping of a DossierTemplate. + description: Updates an existing component mapping, + tags: + - 4. Components + requestBody: + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/UploadRequest' + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/componentMappingId' + - $ref: '#/components/parameters/encoding' + - $ref: '#/components/parameters/delimiter' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ComponentMappingMetadata' + description: | + Component mapping uploaded successfully and returned the component mapping metadata. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + delete: + operationId: deleteMappingFile + tags: + - 4. Components + summary: Delete a specific component mapping file of a specific dossier template. + description: | + Utilize this endpoint to delete a specific component mapping file of a designated dossier template. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/componentMappingId' + responses: + "204": + description: | + Successfully deleted the requested component mapping. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' /api/license/active/usage: post: operationId: getReport @@ -2063,14 +2063,14 @@ components: - 8130acf6-4910-4123-827c-caacd8111402 dossierStatusId: b8280985-f558-43c0-852a-a3fa86803548 - ComponentMappingSummary: + ComponentMappingMetadata: description: | - The `ComponentMappingSummary` object represents the metadata of a component mapping csv file. These csv files may be used in the component rules to relate components to an existing knowledge base. + The `ComponentMappingMetadata` object represents the metadata of a component mapping csv file. These CSV files may be used in the component rules to relate components to an existing knowledge base. type: object properties: id: description: | - A unique identifier for the component mapping summary. It's generated automatically. Use this to retrieve a specific component mapping file. + A unique identifier for the component mapping metadata. It's generated automatically. Use this to retrieve a specific component mapping file. type: string name: description: | @@ -2122,13 +2122,13 @@ components: numberOfLines: 100 encoding: UTF-8 delimiter: ',' - ComponentMappingSummaries: + ComponentMappingSummary: description: | - The `ComponentMappingSummaries` object represents a collection of ComponentMappingSummary. + The `ComponentMappingSummary` object represents a collection of ComponentMappingMetadata. type: object example: dossierTemplateId: 1e07cde0-d36a-4ab7-b389-494ca694a0cb - componentMappingSummaries: + componentMappingMetadata: - id: 24ff9c3c-4863-4aea-8eda-cab8838b9192 name: MasterDataMapping fileName: master_data.csv @@ -2156,15 +2156,15 @@ components: The identifier of the dossier template associated with the component mapping summaries. type: string format: uuid - componentMappingSummaries: + ComponentMappingMetadata: description: | - A list of component mapping summaries associated with this dossier template. + A list of component mapping metadata associated with this dossier template. type: array items: - $ref: "#/components/schemas/ComponentMappingSummary" + $ref: "#/components/schemas/ComponentMappingMetadata" required: - dossierTemplateId - - componentMappingSummaries + - ComponentMappingSummary DossierTemplate: description: | The `DossierTemplate` object represents the blueprint for creating and diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java index a380d39fe..76f0df0f4 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java @@ -3,11 +3,10 @@ package com.iqser.red.service.persistence.management.v1.processor.mapper; import java.util.List; import org.mapstruct.Mapper; -import org.mapstruct.MappingConstants; import org.mapstruct.factory.Mappers; import com.iqser.red.service.persistence.management.v1.processor.entity.ComponentMappingEntity; -import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetaData; +import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata; @Mapper public interface ComponentMappingEntityMapper { @@ -15,15 +14,15 @@ public interface ComponentMappingEntityMapper { ComponentMappingEntityMapper INSTANCE = Mappers.getMapper(ComponentMappingEntityMapper.class); - ComponentMappingMetaData toComponentMappingMetaData(ComponentMappingEntity componentMappingMetaData); + ComponentMappingMetadata toComponentMappingMetaData(ComponentMappingEntity componentMappingMetaData); - List toComponentMappingMetaDataList(List componentMappingMetaData); + List toComponentMappingMetaDataList(List componentMappingMetaData); - ComponentMappingEntity toComponentMappingEntity(ComponentMappingMetaData componentMappingSummary); + ComponentMappingEntity toComponentMappingEntity(ComponentMappingMetadata componentMappingSummary); - List toComponentMappingEntityList(List componentMappingSummary); + List toComponentMappingEntityList(List componentMappingSummary); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/model/ComponentMapping.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/model/ComponentMapping.java index aa45c3811..c1528c29d 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/model/ComponentMapping.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/model/ComponentMapping.java @@ -2,8 +2,8 @@ package com.iqser.red.service.persistence.management.v1.processor.model; import java.io.File; -import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetaData; +import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata; -public record ComponentMapping(ComponentMappingMetaData componentMappingMetaData, File mappingFile) { +public record ComponentMapping(ComponentMappingMetadata metaData, File file) { } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java index df0f6fc63..751fa1f91 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java @@ -6,6 +6,8 @@ import java.io.FileWriter; import java.io.IOException; import java.io.Reader; import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; @@ -16,19 +18,19 @@ import java.util.UUID; import org.springframework.stereotype.Service; -import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMappingDownloadModel; import com.iqser.red.service.persistence.management.v1.processor.entity.ComponentMappingEntity; +import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException; import com.iqser.red.service.persistence.management.v1.processor.mapper.ComponentMappingEntityMapper; import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMapping; +import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMappingDownloadModel; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService; -import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetaData; +import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata; import com.opencsv.CSVParserBuilder; import com.opencsv.CSVReader; import com.opencsv.CSVReaderBuilder; import com.opencsv.CSVWriter; import com.opencsv.exceptions.CsvException; -import io.undertow.util.BadRequestException; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; @@ -46,21 +48,21 @@ public class ComponentMappingService { static CSVSorter CSV_SORTER = new CSVSorter(); - public List getMetaDataByDossierTemplateId(String dossierTemplateId) { + public List getMetaDataByDossierTemplateId(String dossierTemplateId) { List entities = componentMappingPersistenceService.getByDossierTemplateId(dossierTemplateId); return mappingEntityMapper.toComponentMappingMetaDataList(entities); } - public ComponentMappingMetaData getMetaData(String mappingId) { + public ComponentMappingMetadata getMetaData(String mappingId) { return mappingEntityMapper.toComponentMappingMetaData(componentMappingPersistenceService.getEntityById(mappingId)); } @SneakyThrows - public ComponentMappingMetaData update(String mappingId, String encoding, char delimiter, File mappingFile) { + public ComponentMappingMetadata update(String mappingId, String encoding, char delimiter, File mappingFile) { ComponentMappingEntity entity = componentMappingPersistenceService.getEntityById(mappingId); @@ -69,18 +71,18 @@ public class ComponentMappingService { @SneakyThrows - public ComponentMappingMetaData create(String dossierTemplateId, String name, String fileName, char delimiter, String encoding, File mappingFile) { + public ComponentMappingMetadata create(String dossierTemplateId, String name, String fileName, char delimiter, String encoding, File mappingFile) { if (componentMappingPersistenceService.existsByNameAndDossierTemplateId(name, dossierTemplateId)) { throw new BadRequestException("A mapping with this name already exists in the dossier template!"); } String id = UUID.randomUUID().toString(); - + String storageId = buildStorageId(dossierTemplateId, id, name, fileName); ComponentMappingEntity entity = ComponentMappingEntity.builder() .id(id) .dossierTemplate(dossierTemplatePersistenceService.getDossierTemplate(dossierTemplateId)) - .storageId(dossierTemplateId + "/" + id) + .storageId(storageId) .name(name) .fileName(fileName) .build(); @@ -90,9 +92,9 @@ public class ComponentMappingService { @SneakyThrows - private ComponentMappingMetaData updateOrCreate(ComponentMappingEntity entity, String encoding, char delimiter, File mappingFile) { + private ComponentMappingMetadata updateOrCreate(ComponentMappingEntity entity, String encoding, char delimiter, File mappingFile) { - Charset charset = getCharset(entity); + Charset charset = resolveCharset(encoding); CsvStats stats = sortCSVFile(delimiter, mappingFile, charset); @@ -108,14 +110,16 @@ public class ComponentMappingService { } - @SneakyThrows - private static Charset getCharset(ComponentMappingEntity entity) { + private static Charset resolveCharset(String encoding) { try { - return Charset.forName(entity.getEncoding()); - - } catch (Exception e) { - throw new BadRequestException(e); + return Charset.forName(encoding); + } catch (IllegalCharsetNameException e) { + throw new BadRequestException("Invalid character encoding: " + encoding); + } catch (UnsupportedCharsetException e) { + throw new BadRequestException("Unsupported character encoding: " + encoding); + } catch (IllegalArgumentException e) { + throw new BadRequestException("Encoding can't be null."); } } @@ -185,13 +189,19 @@ public class ComponentMappingService { } - private ComponentMapping downloadFileAndCreateMapping(Path outputDir, ComponentMappingMetaData metaData) { + private ComponentMapping downloadFileAndCreateMapping(Path outputDir, ComponentMappingMetadata metaData) { File mappingFile = componentMappingPersistenceService.downloadMappingFileToFolder(metaData.getStorageId(), metaData.getFileName(), outputDir); return new ComponentMapping(metaData, mappingFile); } + public static String buildStorageId(String dossierTemplateId, String id, String name, String fileName) { + + return dossierTemplateId + "/" + id + "_" + name + "_" + fileName; + } + + private static class CSVSorter implements Comparator { @Override diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java index b7945ee72..d859d4468 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java @@ -61,7 +61,7 @@ import com.iqser.red.service.persistence.management.v1.processor.settings.FileMa import com.iqser.red.service.persistence.management.v1.processor.utils.FileUtils; import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType; import com.iqser.red.service.persistence.service.v1.api.shared.model.WatermarkModel; -import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetaData; +import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierAttributeConfig; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplate; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplateStatus; @@ -148,7 +148,7 @@ public class DossierTemplateImportService { Map> typeEntriesMap = new HashMap<>(); Map> typeFalsePositivesMap = new HashMap<>(); Map> typeFalseRecommendationsMap = new HashMap<>(); - List componentMappingMetaData = new LinkedList<>(); + List componentMappingMetaData = new LinkedList<>(); while ((ze = zis.getNextZipEntry()) != null) { log.debug("---> " + ze.getName() + " ---- " + ze.isDirectory()); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusService.java index 9818fefee..ee262b7a1 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusService.java @@ -90,6 +90,8 @@ public class FileStatusService { ViewedPagesPersistenceService viewedPagesPersistenceService; FileManagementServiceSettings fileManagementServiceSettings; LayoutParsingRequestFactory layoutParsingRequestFactory; + ComponentMappingService componentMappingService; + WebsocketService websocketService; @Transactional @@ -252,6 +254,7 @@ public class FileStatusService { .sectionsToReanalyse(sectionsToReanalyse) .fileId(fileId) .manualRedactions(manualRedactionProviderService.getManualRedactions(fileId, ManualChangesQueryOptions.allWithoutDeleted())) + .componentMappings(componentMappingService.getMetaDataByDossierTemplateId(dossierTemplate.getId())) .dossierTemplateId(dossier.getDossierTemplateId()) .lastProcessed(fileModel.getLastProcessed()) .fileAttributes(convertAttributes(fileEntity.getFileAttributes(), dossier.getDossierTemplateId())) diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/export/DossierTemplateExportService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/export/DossierTemplateExportService.java index 532003510..daf20b270 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/export/DossierTemplateExportService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/export/DossierTemplateExportService.java @@ -30,7 +30,6 @@ import com.iqser.red.service.persistence.management.v1.processor.exception.NotFo import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMapping; import com.iqser.red.service.persistence.management.v1.processor.model.DownloadJob; import com.iqser.red.service.persistence.management.v1.processor.service.ColorsService; -import com.iqser.red.service.persistence.management.v1.processor.service.ComponentMappingPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.ComponentMappingService; import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService; import com.iqser.red.service.persistence.management.v1.processor.service.WatermarkService; @@ -247,14 +246,15 @@ public class DossierTemplateExportService { for (ComponentMapping componentMapping : componentMappings) { - Path mappingFilePath = mappingDir.resolve(componentMapping.componentMappingMetaData().getFileName()); + Path mappingFilePath = mappingDir.resolve(componentMapping.metaData().getFileName()); fileSystemBackedArchiver.addEntries(new FileSystemBackedArchiver.ArchiveModel(ExportFilename.COMPONENT_MAPPINGS.getFilename(), - componentMapping.componentMappingMetaData().getName() + JSON_EXT, + componentMapping.metaData().getName() + JSON_EXT, objectMapper.writeValueAsBytes(componentMapping))); fileSystemBackedArchiver.addEntries(new FileSystemBackedArchiver.ArchiveModel(ExportFilename.COMPONENT_MAPPINGS.getFilename(), - componentMapping.componentMappingMetaData().getFileName(), + componentMapping.metaData().getName() + "-" + componentMapping.metaData() + .getFileName(), Files.readAllBytes(mappingFilePath))); } diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/AnalyzeRequest.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/AnalyzeRequest.java index b00739042..29014cc47 100644 --- a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/AnalyzeRequest.java +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/AnalyzeRequest.java @@ -7,6 +7,7 @@ import java.util.List; 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 lombok.AllArgsConstructor; import lombok.Builder; @@ -34,5 +35,8 @@ public class AnalyzeRequest { @Builder.Default private List fileAttributes = new ArrayList<>(); + @Builder.Default + private List componentMappings = new ArrayList<>(); + } -- 2.47.2 From e5a37a77e1ab8e4306862092049a32980843a9b7 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Fri, 17 May 2024 00:42:37 +0200 Subject: [PATCH 07/26] RED-8670: integrate table inference from research --- .../peristence/v1/server/Application.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/Application.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/Application.java index 4bb95ecdb..c4f7974eb 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/Application.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/Application.java @@ -102,21 +102,21 @@ public class Application implements ApplicationContextAware { } -// @Bean -// @ConditionalOnProperty(value = "cors.enabled", havingValue = "true") -// public WebMvcConfigurer corsConfigurer() { -// -// return new WebMvcConfigurer() { -// -// @Override -// public void addCorsMappings(CorsRegistry registry) { -// -// log.info("Cross Origin Requests are enabled !!!"); -// registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET", "POST", "DELETE", "PUT", "HEAD"); -// -// } -// }; -// } + @Bean + @ConditionalOnProperty(value = "cors.enabled", havingValue = "true") + public WebMvcConfigurer corsConfigurer() { + + return new WebMvcConfigurer() { + + @Override + public void addCorsMappings(CorsRegistry registry) { + + log.info("Cross Origin Requests are enabled !!!"); + registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET", "POST", "DELETE", "PUT", "HEAD"); + + } + }; + } @Bean -- 2.47.2 From cf9b71a96cf7d92dde05a62ec51c604ba453a7f7 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Thu, 23 May 2024 18:58:28 +0200 Subject: [PATCH 08/26] RED-8670: integrate table inference from research --- .../build.gradle.kts | 1 + .../controller/ComponentControllerV2.java | 120 ------ .../DossierTemplateControllerV2.java | 125 ++++++- .../ExternalControllerAdviceV2.java | 184 +++++++++ .../external/resource/ComponentResource.java | 54 --- .../resource/DossierTemplateResource.java | 62 +++- .../src/main/resources/api/openapi.yaml | 350 +++++++++--------- .../ComponentMappingPersistenceService.java | 3 + .../service/ComponentMappingService.java | 2 +- .../service/DossierTemplateCloneService.java | 55 ++- .../service/DossierTemplateImportService.java | 60 ++- .../v1/processor/service/DownloadService.java | 2 +- .../export/DossierTemplateExportService.java | 25 +- ...eCloneAndExportWithDuplicateRanksTest.java | 3 +- .../DossierTemplateImportExportTest.java | 189 ++++++++++ .../dossiertemplates/DossierTemplate.zip | Bin 0 -> 554274 bytes .../dossier/file/ProcessingStatus.java | 1 - .../ComponentMappingImportModel.java | 9 + .../importexport/ExportFilename.java | 3 +- .../importexport/ImportTemplateResult.java | 3 + 20 files changed, 853 insertions(+), 398 deletions(-) create mode 100644 persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ExternalControllerAdviceV2.java create mode 100644 persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateImportExportTest.java create mode 100644 persistence-service-v1/persistence-service-server-v1/src/test/resources/files/dossiertemplates/DossierTemplate.zip create mode 100644 persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ComponentMappingImportModel.java diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/build.gradle.kts b/persistence-service-v1/persistence-service-external-api-impl-v2/build.gradle.kts index 1cc96b271..fc349e6ef 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/build.gradle.kts +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/build.gradle.kts @@ -7,6 +7,7 @@ dependencies { api(project(":persistence-service-processor-v1")) api(project(":persistence-service-external-api-v2")) api(project(":persistence-service-external-api-impl-v1")) + implementation("org.mapstruct:mapstruct:1.5.5.Final") annotationProcessor("org.mapstruct:mapstruct-processor:1.5.5.Final") } diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ComponentControllerV2.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ComponentControllerV2.java index a21eb22fd..943fba2d0 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ComponentControllerV2.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ComponentControllerV2.java @@ -1,45 +1,25 @@ package com.iqser.red.persistence.service.v2.external.api.impl.controller; -import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_RULES; -import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_RULES; import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierResource.DOSSIER_ID_PARAM; import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource.DOSSIER_TEMPLATE_ID_PARAM; import static com.iqser.red.service.persistence.service.v2.api.external.resource.FileResource.FILE_ID_PARAM; -import java.io.FileOutputStream; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.LinkedHashMap; import java.util.List; -import java.util.Locale; import java.util.Map; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; -import com.google.common.base.Strings; import com.iqser.red.persistence.service.v1.external.api.impl.controller.StatusController; -import com.iqser.red.persistence.service.v2.external.api.impl.mapper.ComponentMappingMapper; -import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException; -import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMappingDownloadModel; import com.iqser.red.service.persistence.management.v1.processor.service.ComponentLogService; -import com.iqser.red.service.persistence.management.v1.processor.service.ComponentMappingService; import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService; -import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntityReference; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntryValue; import com.iqser.red.service.persistence.service.v2.api.external.model.Component; -import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingMetadataModel; -import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummary; import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentValue; import com.iqser.red.service.persistence.service.v2.api.external.model.EntityReference; import com.iqser.red.service.persistence.service.v2.api.external.model.FileComponents; @@ -49,7 +29,6 @@ import com.iqser.red.service.persistence.service.v2.api.external.resource.Compon import io.swagger.v3.oas.annotations.tags.Tag; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; import lombok.experimental.FieldDefaults; @RestController @@ -61,8 +40,6 @@ public class ComponentControllerV2 implements ComponentResource { ComponentLogService componentLogService; StatusController statusController; FileStatusService fileStatusService; - ComponentMappingService componentMappingService; - ComponentMappingMapper componentMappingMapper = ComponentMappingMapper.INSTANCE; DossierTemplatePersistenceService dossierTemplatePersistenceService; @@ -150,101 +127,4 @@ public class ComponentControllerV2 implements ComponentResource { } - @Override - @PreAuthorize("hasAuthority('" + READ_RULES + "')") - public ComponentMappingSummary getComponentMappingSummaries(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) { - - dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); - List summaries = componentMappingService.getMetaDataByDossierTemplateId(dossierTemplateId); - List componentMappingMetadataModelList = componentMappingMapper.toModelList(summaries); - return new ComponentMappingSummary(dossierTemplateId, componentMappingMetadataModelList); - } - - - @Override - @SneakyThrows - @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") - public ComponentMappingMetadataModel uploadMapping(String dossierTemplateId, MultipartFile file, String name, String encoding, char delimiter) { - - dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); - - String nameToUse = Strings.isNullOrEmpty(name) ? file.getName().split("\\.")[0] : name; - - if (Strings.isNullOrEmpty(nameToUse)) { - throw new BadRequestException("The provided file name is not valid!"); - } - - Path mappingFile = saveToFile(file); - String fileName = file.getOriginalFilename() == null ? nameToUse + ".csv" : file.getOriginalFilename(); - - com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata metaData = componentMappingService.create(dossierTemplateId, nameToUse, fileName, delimiter, encoding, mappingFile.toFile()); - - Files.deleteIfExists(mappingFile); - - return componentMappingMapper.toModel(metaData); - } - - - @Override - @SneakyThrows - @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") - public ComponentMappingMetadataModel updateMapping(String dossierTemplateId, String componentMappingId, MultipartFile file, String encoding, char delimiter) { - - dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); - - Path mappingFile = saveToFile(file); - - com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata resultMetaData = componentMappingService.update(componentMappingId, encoding, delimiter, mappingFile.toFile()); - - Files.deleteIfExists(mappingFile); - - return componentMappingMapper.toModel(resultMetaData); - } - - - @Override - @PreAuthorize("hasAuthority('" + READ_RULES + "')") - public ResponseEntity downloadMapping(String dossierTemplateId, String componentMappingId) { - - dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); - - ComponentMappingDownloadModel mappingDownloadModel = componentMappingService.getMappingForDownload(componentMappingId); - - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.setContentType(MediaType.TEXT_PLAIN); - - httpHeaders.add("Content-Disposition", - "attachment" - + "; filename*=" - + mappingDownloadModel.encoding().toLowerCase(Locale.US) - + "''" - + StringEncodingUtils.urlEncode(mappingDownloadModel.fileName())); - - return new ResponseEntity<>(mappingDownloadModel.mappingFileResource(), httpHeaders, HttpStatus.OK); - } - - - @Override - @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") - public ResponseEntity deleteMapping(String dossierTemplateId, String componentMappingId) { - - dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); - - componentMappingService.delete(componentMappingId); - - return new ResponseEntity<>(HttpStatus.NO_CONTENT); - } - - - @SneakyThrows - private static Path saveToFile(MultipartFile file) { - - Path mappingFile = Files.createTempFile(file.getName(), ".csv"); - - try (var out = new FileOutputStream(mappingFile.toFile())) { - out.write(file.getBytes()); - } - return mappingFile; - } - } diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java index 77cb78cad..9aaf1eb7e 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java @@ -6,8 +6,11 @@ import static com.iqser.red.service.persistence.management.v1.processor.roles.Ac import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_RULES; import java.io.ByteArrayInputStream; +import java.io.FileOutputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.Locale; @@ -23,12 +26,17 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import com.google.common.base.Strings; import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierTemplateController; import com.iqser.red.persistence.service.v1.external.api.impl.controller.FileAttributesController; +import com.iqser.red.persistence.service.v2.external.api.impl.mapper.ComponentMappingMapper; import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.RuleSetEntity; import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException; +import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMappingDownloadModel; +import com.iqser.red.service.persistence.management.v1.processor.service.ComponentMappingService; import com.iqser.red.service.persistence.management.v1.processor.service.RulesValidationService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.RulesPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.utils.RulesValidationMapper; import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils; @@ -38,6 +46,8 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileTyp import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsValidationResponse; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesUploadRequest; +import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingMetadataModel; +import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummary; 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.resource.DossierTemplateResource; @@ -47,22 +57,27 @@ import feign.FeignException; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; +import lombok.experimental.FieldDefaults; @RestController @RequiredArgsConstructor @Tag(name = "1. Dossier templates endpoints", description = "Provides operations related to dossier templates") +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) public class DossierTemplateControllerV2 implements DossierTemplateResource { private static final String RULES_DOWNLOAD_FILE_NAME_SUFFIX = "-rules.drl"; - private final DossierTemplateController dossierTemplateController; - private final RulesPersistenceService rulesPersistenceService; - private final RulesValidationService rulesValidationService; - private final AuditPersistenceService auditPersistenceService; - private final FileAttributesController fileAttributesController; - + DossierTemplateController dossierTemplateController; + RulesPersistenceService rulesPersistenceService; + RulesValidationService rulesValidationService; + AuditPersistenceService auditPersistenceService; + FileAttributesController fileAttributesController; + ComponentMappingService componentMappingService; + ComponentMappingMapper componentMappingMapper = ComponentMappingMapper.INSTANCE; + DossierTemplatePersistenceService dossierTemplatePersistenceService; public List getAllDossierTemplates() { @@ -212,4 +227,102 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { return new ResponseEntity<>(new InputStreamResource(is), httpHeaders, HttpStatus.OK); } + + @Override + @PreAuthorize("hasAuthority('" + READ_RULES + "')") + public ComponentMappingSummary getComponentMappingSummaries(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) { + + dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); + List summaries = componentMappingService.getMetaDataByDossierTemplateId(dossierTemplateId); + List componentMappingMetadataModelList = componentMappingMapper.toModelList(summaries); + return new ComponentMappingSummary(dossierTemplateId, componentMappingMetadataModelList); + } + + + @Override + @SneakyThrows + @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") + public ComponentMappingMetadataModel uploadMapping(String dossierTemplateId, MultipartFile file, String name, String encoding, char delimiter) { + + dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); + + String nameToUse = Strings.isNullOrEmpty(name) ? file.getName().split("\\.")[0] : name; + + if (Strings.isNullOrEmpty(nameToUse)) { + throw new BadRequestException("The provided file name is not valid!"); + } + + Path mappingFile = saveToFile(file); + String fileName = file.getOriginalFilename() == null ? nameToUse + ".csv" : file.getOriginalFilename(); + + com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata metaData = componentMappingService.create(dossierTemplateId, nameToUse, fileName, delimiter, encoding, mappingFile.toFile()); + + Files.deleteIfExists(mappingFile); + + return componentMappingMapper.toModel(metaData); + } + + + @Override + @SneakyThrows + @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") + public ComponentMappingMetadataModel updateMapping(String dossierTemplateId, String componentMappingId, MultipartFile file, String encoding, char delimiter) { + + dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); + + Path mappingFile = saveToFile(file); + + com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata resultMetaData = componentMappingService.update(componentMappingId, encoding, delimiter, mappingFile.toFile()); + + Files.deleteIfExists(mappingFile); + + return componentMappingMapper.toModel(resultMetaData); + } + + + @Override + @PreAuthorize("hasAuthority('" + READ_RULES + "')") + public ResponseEntity downloadMapping(String dossierTemplateId, String componentMappingId) { + + dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); + + ComponentMappingDownloadModel mappingDownloadModel = componentMappingService.getMappingForDownload(componentMappingId); + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.TEXT_PLAIN); + + httpHeaders.add("Content-Disposition", + "attachment" + + "; filename*=" + + mappingDownloadModel.encoding().toLowerCase(Locale.US) + + "''" + + StringEncodingUtils.urlEncode(mappingDownloadModel.fileName())); + + return new ResponseEntity<>(mappingDownloadModel.mappingFileResource(), httpHeaders, HttpStatus.OK); + } + + + @Override + @PreAuthorize("hasAuthority('" + WRITE_RULES + "')") + public ResponseEntity deleteMapping(String dossierTemplateId, String componentMappingId) { + + dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); + + componentMappingService.delete(componentMappingId); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + + @SneakyThrows + private static Path saveToFile(MultipartFile file) { + + Path mappingFile = Files.createTempFile(file.getName(), ".csv"); + + try (var out = new FileOutputStream(mappingFile.toFile())) { + out.write(file.getBytes()); + } + return mappingFile; + } + } diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ExternalControllerAdviceV2.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ExternalControllerAdviceV2.java new file mode 100644 index 000000000..6276722e1 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ExternalControllerAdviceV2.java @@ -0,0 +1,184 @@ +package com.iqser.red.persistence.service.v2.external.api.impl.controller; + +import java.time.OffsetDateTime; +import java.util.Map; +import java.util.stream.Collectors; + +import org.quartz.JobDataMap; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.multipart.support.MissingServletRequestPartException; + +import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import com.iqser.red.commons.spring.ErrorMessage; +import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException; +import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException; +import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException; +import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException; +import com.knecon.fforesight.tenantcommons.TenantContext; +import com.mchange.rmi.NotAuthorizedException; + +import io.swagger.v3.oas.annotations.Hidden; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RequiredArgsConstructor +@RestControllerAdvice +@Order(Ordered.HIGHEST_PRECEDENCE) +public class ExternalControllerAdviceV2 { + + private final Scheduler scheduler; + + + @Hidden + @ResponseBody + @ResponseStatus(value = HttpStatus.NOT_FOUND) + @ExceptionHandler(value = NotFoundException.class) + public ErrorMessage handleContentNotFoundException(NotFoundException e) { + + return new ErrorMessage(OffsetDateTime.now(), e.getMessage()); + } + + + /* error handling */ + + + @Hidden + @ResponseBody + @ResponseStatus(value = HttpStatus.BAD_REQUEST) + @ExceptionHandler(value = BadRequestException.class) + public ErrorMessage handleBadRequestException(BadRequestException e) { + + return new ErrorMessage(OffsetDateTime.now(), e.getMessage()); + } + + + @Hidden + @ResponseBody + @ResponseStatus(value = HttpStatus.CONFLICT) + @ExceptionHandler(value = {ConflictException.class}) + protected ErrorMessage handleConflictException(ConflictException e) { + + return new ErrorMessage(OffsetDateTime.now(), e.getMessage()); + } + + + @ResponseBody + @ResponseStatus(value = HttpStatus.FORBIDDEN) + @ExceptionHandler({AccessDeniedException.class}) + public ErrorMessage handleAccessDeniedException(AccessDeniedException e) { + + return new ErrorMessage(OffsetDateTime.now(), e.getMessage()); + } + + + @ResponseBody + @ResponseStatus(value = HttpStatus.UNAUTHORIZED) + @ExceptionHandler({NotAuthorizedException.class}) + public ErrorMessage handleNotAuthorizedException(NotAuthorizedException e) { + + return new ErrorMessage(OffsetDateTime.now(), e.getMessage()); + } + + + @ResponseBody + @ResponseStatus(value = HttpStatus.FORBIDDEN) + @ExceptionHandler({NotAllowedException.class}) + public ErrorMessage handleNotAllowedException(NotAllowedException e) { + + return new ErrorMessage(OffsetDateTime.now(), e.getMessage()); + } + + + @Hidden + @ResponseBody + @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) + @ExceptionHandler({org.springframework.security.acls.model.NotFoundException.class}) + public ErrorMessage handleACLNotFound(org.springframework.security.acls.model.NotFoundException e) { + + // in case this error occurs on a rest request / force trigger the sync job + try { + scheduler.triggerJob(new JobKey("SyncUserPermissionsJob"), new JobDataMap(Map.of("tenantId", TenantContext.getTenantId()))); + } catch (SchedulerException ex) { + log.debug("Failed to force trigger SyncUserPermissionsJob", ex); + } + + return new ErrorMessage(OffsetDateTime.now(), e.getMessage()); + } + + + @Hidden + @ResponseBody + @ResponseStatus(value = HttpStatus.BAD_REQUEST) + @ExceptionHandler({MethodArgumentNotValidException.class}) + public ErrorMessage handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + + var errorList = e.getFieldErrors(); + String errorListAsString = errorList.stream() + .map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage()) + .collect(Collectors.joining(", ")); + return new ErrorMessage(OffsetDateTime.now(), String.format("You have empty/wrong formatted parameters: %s", errorListAsString)); + } + + + @Hidden + @ResponseBody + @ResponseStatus(value = HttpStatus.BAD_REQUEST) + @ExceptionHandler({MissingServletRequestPartException.class}) + public ErrorMessage handleMissingServletRequestPartException(MissingServletRequestPartException e) { + + return new ErrorMessage(OffsetDateTime.now(), e.getMessage()); + } + + + @Hidden + @ResponseBody + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler({HttpMessageNotReadableException.class}) + public ErrorMessage handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { + + var cause = e.getCause(); + if (cause instanceof InvalidFormatException) { + InvalidFormatException invalidFormatException = (InvalidFormatException) cause; + + Class targetType = invalidFormatException.getTargetType(); + if (targetType != null && targetType.isEnum()) { + return new ErrorMessage(OffsetDateTime.now(), String.format("Unsupported value for %s", targetType.getSimpleName())); + } + + return new ErrorMessage(OffsetDateTime.now(), cause.getMessage()); + } + + return new ErrorMessage(OffsetDateTime.now(), e.getMessage()); + } + + + @Order(10000) + public static class BinderControllerAdvice { + + @InitBinder + public void setAllowedFields(WebDataBinder dataBinder) { + // This code protects Spring Core from a "Remote Code Execution" attack (dubbed "Spring4Shell"). + // By applying this mitigation, you prevent the "Class Loader Manipulation" attack vector from firing. + // For more details, see this post: https://www.lunasec.io/docs/blog/spring-rce-vulnerabilities/ + String[] denylist = new String[]{"class.*", "Class.*", "*.class.*", "*.Class.*"}; + dataBinder.setDisallowedFields(denylist); + } + + } + +} diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/ComponentResource.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/ComponentResource.java index c2c7aef7d..a67b78e9c 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/ComponentResource.java +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/ComponentResource.java @@ -49,19 +49,10 @@ public interface ComponentResource { String INCLUDE_DETAILS_DESCRIPTION = """ A toggle to decide whether to include detailed component information in the response: - - true: The component object's field componentDetails stores detailed information about the source of its respective value(s). - false (default): The component object does not contain a field componentDetails. """; - String COMPONENT_MAPPINGS_PATH = COMPONENTS_PATH + "/mappings"; - String COMPONENT_MAPPING_ID_PARAM = "componentMappingId"; - String COMPONENT_MAPPING_ID_PATH_VARIABLE = "/{" + COMPONENT_MAPPING_ID_PARAM + "}"; - - String RULE_FILE_TYPE_PARAMETER_NAME = "ruleFileType"; - String ENCODING_PARAM = "encoding"; - String DELIMITER_PARAM = "delimiter"; - String MAPPING_NAME_PARAM = "name"; @GetMapping(value = FILE_PATH + FILE_ID_PATH_VARIABLE + COMPONENTS_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}) @@ -81,50 +72,5 @@ public interface ComponentResource { @Parameter(name = INCLUDE_DETAILS_PARAM, description = INCLUDE_DETAILS_DESCRIPTION) @RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails); - @Operation(summary = "Get the component mapping summaries of a DossierTemplate.", description = "None") - @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH, produces = MediaType.APPLICATION_JSON_VALUE) - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping views returned successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) - ComponentMappingSummary getComponentMappingSummaries(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId); - - - @Operation(summary = "Upload a new component mapping to a DossierTemplate.", description = "None") - @PostMapping(value = PATH - + DOSSIER_TEMPLATE_ID_PATH_VARIABLE - + COMPONENT_MAPPINGS_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping uploaded successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) - ComponentMappingMetadataModel uploadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, - @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, - @Parameter(name = MAPPING_NAME_PARAM, description = "String of what the mapping should be accessible under. If left empty, the name of the file without the ending will be used as name.") @RequestParam(value = MAPPING_NAME_PARAM, required = false, defaultValue = "") String name, - @Parameter(name = ENCODING_PARAM, description = "The encoding of the file. Default is UTF-8.") @RequestParam(value = ENCODING_PARAM, required = false, defaultValue = "UTF-8") String encoding, - @Parameter(name = DELIMITER_PARAM, description = "The delimiter used in the file. Default is ','") @RequestParam(value = DELIMITER_PARAM, required = false, defaultValue = ",") char delimiter); - - - @Operation(summary = "Update an existing component mapping of a DossierTemplate.", description = "None") - @PutMapping(value = PATH - + DOSSIER_TEMPLATE_ID_PATH_VARIABLE - + COMPONENT_MAPPINGS_PATH - + COMPONENT_MAPPING_ID_PATH_VARIABLE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping updated successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) - ComponentMappingMetadataModel updateMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, - @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId, - @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, - @Parameter(name = ENCODING_PARAM, description = "The encoding of the file. Default is UTF-8.") @RequestParam(value = ENCODING_PARAM, required = false, defaultValue = "UTF-8") String encoding, - @Parameter(name = DELIMITER_PARAM, description = "The delimiter used in the file. Default is ','") @RequestParam(value = DELIMITER_PARAM, required = false, defaultValue = ",") char delimiter); - - - @ResponseBody - @ResponseStatus(value = HttpStatus.OK) - @Operation(summary = "Returns file containing the specified mapping as a file.") - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) - @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE, produces = MediaType.MULTIPART_FORM_DATA_VALUE) - ResponseEntity downloadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId); - - - @ResponseBody - @ResponseStatus(value = HttpStatus.OK) - @Operation(summary = "Deletes a specified mapping.") - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) - @DeleteMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE, produces = MediaType.MULTIPART_FORM_DATA_VALUE) - ResponseEntity deleteMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId); } diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java index 116cb7717..9f7046383 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java @@ -2,6 +2,8 @@ package com.iqser.red.service.persistence.service.v2.api.external.resource; import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsValidationResponse; +import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingMetadataModel; +import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummary; import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinitionList; import io.swagger.v3.oas.annotations.Operation; @@ -13,9 +15,11 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; 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.PutMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.ResponseBody; @@ -33,17 +37,20 @@ public interface DossierTemplateResource { String ENTITY_RULES_PATH = "/entity-rules"; String COMPONENT_RULES_PATH = "/component-rules"; - - + String COMPONENT_MAPPINGS_PATH = "/component-mappings"; 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 COMPONENT_MAPPING_ID_PARAM = "componentMappingId"; + String COMPONENT_MAPPING_ID_PATH_VARIABLE = "/{" + COMPONENT_MAPPING_ID_PARAM + "}"; + String DRY_RUN_PARAM = "dryRun"; - - + String ENCODING_PARAM = "encoding"; + String DELIMITER_PARAM = "delimiter"; + String MAPPING_NAME_PARAM = "name"; @GetMapping(value = PATH, produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Lists all existing DossierTemplates.", description = "None") @@ -98,4 +105,51 @@ public interface DossierTemplateResource { @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "File attribute definitions returned successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) FileAttributeDefinitionList getFileAttributeDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId); + + @Operation(summary = "Get the component mapping summaries of a DossierTemplate.", description = "None") + @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH, produces = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping views returned successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) + ComponentMappingSummary getComponentMappingSummaries(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId); + + + @Operation(summary = "Upload a new component mapping to a DossierTemplate.", description = "None") + @PostMapping(value = PATH + + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + + COMPONENT_MAPPINGS_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping uploaded successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) + ComponentMappingMetadataModel uploadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, + @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, + @Parameter(name = MAPPING_NAME_PARAM, description = "String of what the mapping should be accessible under. If left empty, the name of the file without the ending will be used as name.") @RequestParam(value = MAPPING_NAME_PARAM, required = false, defaultValue = "") String name, + @Parameter(name = ENCODING_PARAM, description = "The encoding of the file. Default is UTF-8.") @RequestParam(value = ENCODING_PARAM, required = false, defaultValue = "UTF-8") String encoding, + @Parameter(name = DELIMITER_PARAM, description = "The delimiter used in the file. Default is ','") @RequestParam(value = DELIMITER_PARAM, required = false, defaultValue = ",") char delimiter); + + + @Operation(summary = "Update an existing component mapping of a DossierTemplate.", description = "None") + @PutMapping(value = PATH + + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + + COMPONENT_MAPPINGS_PATH + + COMPONENT_MAPPING_ID_PATH_VARIABLE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping updated successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) + ComponentMappingMetadataModel updateMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, + @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId, + @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, + @Parameter(name = ENCODING_PARAM, description = "The encoding of the file. Default is UTF-8.") @RequestParam(value = ENCODING_PARAM, required = false, defaultValue = "UTF-8") String encoding, + @Parameter(name = DELIMITER_PARAM, description = "The delimiter used in the file. Default is ','") @RequestParam(value = DELIMITER_PARAM, required = false, defaultValue = ",") char delimiter); + + + @ResponseBody + @ResponseStatus(value = HttpStatus.OK) + @Operation(summary = "Returns file containing the specified mapping as a file.") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) + @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE, produces = MediaType.MULTIPART_FORM_DATA_VALUE) + ResponseEntity downloadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId); + + + @ResponseBody + @ResponseStatus(value = HttpStatus.OK) + @Operation(summary = "Deletes a specified mapping.") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) + @DeleteMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE, produces = MediaType.MULTIPART_FORM_DATA_VALUE) + ResponseEntity deleteMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId); + } diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml index baf9fe7b9..e715f335b 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml @@ -290,6 +290,179 @@ paths: $ref: '#/components/schemas/FileAttributeDefinitionList' description: | Successfully returned the file attribute definitions for the specified dossier template. + /api/dossier-templates/{dossierTemplateId}/component-mappings: + get: + operationId: listAllMappings + tags: + - 1. Dossier Templates + summary: Returns a list of all existing component mappings in a dossier template + description: | + Retrieves a collection of component mapping views associated with a specific dossier template. Each component mapping view includes details such as name, number of lines, delimiter, encoding and other relevant metadata. This endpoint is useful for clients needing to understand what mappings are available under a particular dossier template. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ComponentMappingSummary' + description: | + Successfully returned the component mapping summary for the specified dossier template. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + post: + operationId: uploadMapping + summary: Upload a new component mapping to a DossierTemplate. + description: | + Utilize this endpoint to upload a new component mapping to a designated DossierTemplate. + The file is expected to be a comma separated file, whose first row are the labels for the columns of the file. + Further, it is expected that the data is rectangular, this means each row has the same size. + The rows in the file will be sorted the values in each column, starting with the left most and moving right recursively. + This enables much faster lookups down the line. + This means, it is highly beneficial to structure the CSV File such that the keys to query for appear in the first rows and the results to map in the last. + tags: + - 1. Dossier Templates + requestBody: + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/UploadRequest' + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/mappingName' + - $ref: '#/components/parameters/encoding' + - $ref: '#/components/parameters/delimiter' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ComponentMappingMetadata' + description: | + Component mapping uploaded successfully and returned the component mapping metadata. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + + /api/dossier-templates/{dossierTemplateId}/component-mappings/{comonentMappingId}: + get: + operationId: downloadMappingFile + tags: + - 1. Dossier Templates + summary: Download a specific component mapping file of a specific dossier template. + description: | + Utilize this endpoint to download a specific component mapping file of a designated dossier template. + The file is named the same and encoded the same as when it was uploaded, but it's sorting might have changed to provide faster lookups. + A component mapping file may be used in the component rules to relate components to existing master data. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/componentMappingId' + responses: + "200": + headers: + Content-Disposition: + schema: + type: string + example: attachment; filename*=utf-8''mapping.csv + content: + text/plain; charset=utf-8: + schema: + type: string + description: | + Successfully downloaded the requested component mapping. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + put: + operationId: updateMapping + summary: Update an existing component mapping of a DossierTemplate. + description: Updates an existing component mapping, + tags: + - 1. Dossier Templates + requestBody: + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/UploadRequest' + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/componentMappingId' + - $ref: '#/components/parameters/encoding' + - $ref: '#/components/parameters/delimiter' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ComponentMappingMetadata' + description: | + Component mapping uploaded successfully and returned the component mapping metadata. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + delete: + operationId: deleteMappingFile + tags: + - 1. Dossier Templates + summary: Delete a specific component mapping file of a specific dossier template. + description: | + Utilize this endpoint to delete a specific component mapping file of a designated dossier template. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/componentMappingId' + responses: + "204": + description: | + Successfully deleted the requested component mapping. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' /api/dossier-templates/{dossierTemplateId}/dossiers: get: operationId: getDossiers @@ -304,7 +477,7 @@ paths: Use this endpoint to fetch the required dossiers before performing actions on specific ones. Use the query parameters to modify the response. E.g., set the `includeArchivedDossiers` parameter to `true` so that the response also contains - *archived* dossiers. + *archived* dossiers. parameters: - $ref: '#/components/parameters/dossierTemplateId' - $ref: '#/components/parameters/includeActiveDossiers' @@ -745,179 +918,6 @@ paths: $ref: '#/components/responses/403' "500": $ref: '#/components/responses/500' - /api/dossier-templates/{dossierTemplateId}/component-mappings: - get: - operationId: listAllMappings - tags: - - 4. Components - summary: Returns a list of all existing component mappings in a dossier template - description: | - Retrieves a collection of component mapping views associated with a specific dossier template. Each component mapping view includes details such as name, number of lines, delimiter, encoding and other relevant metadata. This endpoint is useful for clients needing to understand what mappings are available under a particular dossier template. - parameters: - - $ref: '#/components/parameters/dossierTemplateId' - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/ComponentMappingSummary' - description: | - Successfully returned the component mapping summary for the specified dossier template. - "400": - $ref: '#/components/responses/400' - "401": - $ref: '#/components/responses/401' - "403": - $ref: '#/components/responses/403' - "404": - $ref: '#/components/responses/404-dossier-template' - "429": - $ref: '#/components/responses/429' - "500": - $ref: '#/components/responses/500' - post: - operationId: uploadMapping - summary: Upload a new component mapping to a DossierTemplate. - description: | - Utilize this endpoint to upload a new component mapping to a designated DossierTemplate. - The file is expected to be a comma separated file, whose first row are the labels for the columns of the file. - Further, it is expected that the data is rectangular, this means each row has the same size. - The rows in the file will be sorted the values in each column, starting with the left most and moving right recursively. - This enables much faster lookups down the line. - This means, it is highly beneficial to structure the CSV File such that the keys to query for appear in the first rows and the results to map in the last. - tags: - - 4. Components - requestBody: - content: - multipart/form-data: - schema: - $ref: '#/components/schemas/UploadRequest' - parameters: - - $ref: '#/components/parameters/dossierTemplateId' - - $ref: '#/components/parameters/mappingName' - - $ref: '#/components/parameters/encoding' - - $ref: '#/components/parameters/delimiter' - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/ComponentMappingMetadata' - description: | - Component mapping uploaded successfully and returned the component mapping metadata. - "400": - $ref: '#/components/responses/400' - "401": - $ref: '#/components/responses/401' - "403": - $ref: '#/components/responses/403' - "404": - $ref: '#/components/responses/404-dossier-template' - "429": - $ref: '#/components/responses/429' - "500": - $ref: '#/components/responses/500' - - /api/dossier-templates/{dossierTemplateId}/component-mappings/{comonentMappingId}: - get: - operationId: downloadMappingFile - tags: - - 4. Components - summary: Download a specific component mapping file of a specific dossier template. - description: | - Utilize this endpoint to download a specific component mapping file of a designated dossier template. - The file is named the same and encoded the same as when it was uploaded, but it's sorting might have changed to provide faster lookups. - A component mapping file may be used in the component rules to relate components to existing master data. - parameters: - - $ref: '#/components/parameters/dossierTemplateId' - - $ref: '#/components/parameters/componentMappingId' - responses: - "200": - headers: - Content-Disposition: - schema: - type: string - example: attachment; filename*=utf-8''mapping.csv - content: - text/plain; charset=utf-8: - schema: - type: string - description: | - Successfully downloaded the requested component mapping. - "400": - $ref: '#/components/responses/400' - "401": - $ref: '#/components/responses/401' - "403": - $ref: '#/components/responses/403' - "404": - $ref: '#/components/responses/404-dossier-template' - "429": - $ref: '#/components/responses/429' - "500": - $ref: '#/components/responses/500' - put: - operationId: updateMapping - summary: Update an existing component mapping of a DossierTemplate. - description: Updates an existing component mapping, - tags: - - 4. Components - requestBody: - content: - multipart/form-data: - schema: - $ref: '#/components/schemas/UploadRequest' - parameters: - - $ref: '#/components/parameters/dossierTemplateId' - - $ref: '#/components/parameters/componentMappingId' - - $ref: '#/components/parameters/encoding' - - $ref: '#/components/parameters/delimiter' - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/ComponentMappingMetadata' - description: | - Component mapping uploaded successfully and returned the component mapping metadata. - "400": - $ref: '#/components/responses/400' - "401": - $ref: '#/components/responses/401' - "403": - $ref: '#/components/responses/403' - "404": - $ref: '#/components/responses/404-dossier-template' - "429": - $ref: '#/components/responses/429' - "500": - $ref: '#/components/responses/500' - delete: - operationId: deleteMappingFile - tags: - - 4. Components - summary: Delete a specific component mapping file of a specific dossier template. - description: | - Utilize this endpoint to delete a specific component mapping file of a designated dossier template. - parameters: - - $ref: '#/components/parameters/dossierTemplateId' - - $ref: '#/components/parameters/componentMappingId' - responses: - "204": - description: | - Successfully deleted the requested component mapping. - "400": - $ref: '#/components/responses/400' - "401": - $ref: '#/components/responses/401' - "403": - $ref: '#/components/responses/403' - "404": - $ref: '#/components/responses/404-dossier-template' - "429": - $ref: '#/components/responses/429' - "500": - $ref: '#/components/responses/500' /api/license/active/usage: post: operationId: getReport @@ -1093,7 +1093,7 @@ components: minLength: 1 maxLength: 1 example: ',' - description: "The delimiter used in a csv file. Default is ','." + description: "The delimiter used as a separator in a csv file. Default is ','." mappingName: name: name required: false diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java index 25ebd46f8..bfbd4642b 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java @@ -87,6 +87,9 @@ public class ComponentMappingPersistenceService { public ComponentMappingDownloadModel getMappingFileForDownload(String componentMappingId) { var entity = getEntityById(componentMappingId); + if (!storageService.objectExists(TenantContext.getTenantId(), entity.getStorageId())) { + throw new NotFoundException("ComponentMapping with id " + componentMappingId + " does not exist!"); + } return new ComponentMappingDownloadModel(storageService.getObject(TenantContext.getTenantId(), entity.getStorageId()), entity.getEncoding(), entity.getFileName()); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java index 751fa1f91..4ea8c31fe 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java @@ -140,7 +140,7 @@ public class ComponentMappingService { columnLabels = rows.remove(0); - numberOfLines = (int) reader.getLinesRead(); + numberOfLines = (int) reader.getLinesRead() - 1; rows.sort(CSV_SORTER); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateCloneService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateCloneService.java index e9bc1c8cc..f09b35fb7 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateCloneService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateCloneService.java @@ -1,5 +1,7 @@ package com.iqser.red.service.persistence.management.v1.processor.service; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.OffsetDateTime; import java.time.temporal.ChronoUnit; import java.util.ArrayList; @@ -10,6 +12,7 @@ import java.util.UUID; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; +import org.springframework.util.FileSystemUtils; import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.ColorsEntity; import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.FileAttributesGeneralConfigurationEntity; @@ -20,6 +23,7 @@ import com.iqser.red.service.persistence.management.v1.processor.entity.dossier. import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.ReportTemplateEntity; import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException; import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException; +import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMapping; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributeConfigPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierStatusPersistenceService; @@ -40,29 +44,33 @@ import com.iqser.red.storage.commons.service.StorageService; import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter; import com.knecon.fforesight.tenantcommons.TenantContext; +import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; +import lombok.experimental.FieldDefaults; import lombok.extern.slf4j.Slf4j; @Slf4j @Service @RequiredArgsConstructor +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) public class DossierTemplateCloneService { - private final DossierTemplateRepository dossierTemplateRepository; - private final LegalBasisMappingPersistenceService legalBasisMappingPersistenceService; - private final RulesPersistenceService rulesPersistenceService; - private final DossierTemplatePersistenceService dossierTemplatePersistenceService; - private final DossierAttributeConfigPersistenceService dossierAttributeConfigPersistenceService; - private final DictionaryPersistenceService dictionaryPersistenceService; - private final EntryPersistenceService entryPersistenceService; - private final FileAttributeConfigPersistenceService fileAttributeConfigPersistenceService; - private final ReportTemplatePersistenceService reportTemplatePersistenceService; - private final ColorsService colorsService; - private final StorageService storageService; - private final DossierStatusPersistenceService dossierStatusPersistenceService; - private final WatermarkService watermarkService; - private final FileManagementStorageService fileManagementStorageService; + DossierTemplateRepository dossierTemplateRepository; + LegalBasisMappingPersistenceService legalBasisMappingPersistenceService; + RulesPersistenceService rulesPersistenceService; + DossierTemplatePersistenceService dossierTemplatePersistenceService; + DossierAttributeConfigPersistenceService dossierAttributeConfigPersistenceService; + DictionaryPersistenceService dictionaryPersistenceService; + EntryPersistenceService entryPersistenceService; + FileAttributeConfigPersistenceService fileAttributeConfigPersistenceService; + ReportTemplatePersistenceService reportTemplatePersistenceService; + ColorsService colorsService; + StorageService storageService; + DossierStatusPersistenceService dossierStatusPersistenceService; + WatermarkService watermarkService; + FileManagementStorageService fileManagementStorageService; + ComponentMappingService componentMappingService; public DossierTemplateEntity cloneDossierTemplate(String dossierTemplateId, CloneDossierTemplateRequest cloneDossierTemplateRequest) { @@ -129,6 +137,8 @@ public class DossierTemplateCloneService { // set the watermark configurations cloneWatermarks(dossierTemplateId, clonedDossierTemplate.getId()); + cloneComponentMappings(dossierTemplateId, clonedDossierTemplate.getId()); + return clonedDossierTemplate; } @@ -153,6 +163,23 @@ public class DossierTemplateCloneService { } + @SneakyThrows + private void cloneComponentMappings(String dossierTemplateId, String clonedDossierTemplateId) { + + Path dir = Files.createTempDirectory("componentMappingsForClone"); + List componentMappings = componentMappingService.getMappingFilesByDossierTemplateId(dossierTemplateId, dir); + for (ComponentMapping componentMapping : componentMappings) { + componentMappingService.create(clonedDossierTemplateId, + componentMapping.metaData().getName(), + componentMapping.metaData().getFileName(), + componentMapping.metaData().getDelimiter(), + componentMapping.metaData().getEncoding(), + componentMapping.file()); + } + FileSystemUtils.deleteRecursively(dir); + } + + private void cloneDictionariesWithEntries(String dossierTemplateId, String clonedDossierTemplateId) { var types = dictionaryPersistenceService.getAllTypesForDossierTemplate(dossierTemplateId, false); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java index d859d4468..30fd04279 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java @@ -8,9 +8,12 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.OffsetDateTime; import java.time.temporal.ChronoUnit; import java.util.ArrayList; @@ -29,6 +32,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; import org.apache.commons.compress.utils.FileNameUtils; +import org.apache.commons.compress.utils.IOUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; @@ -72,6 +76,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemp import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.CreateOrUpdateDossierStatusRequest; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierStatusInfo; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileAttributeConfig; +import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.importexport.ComponentMappingImportModel; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.importexport.ExportFilename; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.importexport.ImportDossierTemplateRequest; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.importexport.ImportTemplateResult; @@ -83,6 +88,7 @@ import com.iqser.red.storage.commons.service.StorageService; import com.knecon.fforesight.tenantcommons.TenantContext; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @Slf4j @@ -148,8 +154,8 @@ public class DossierTemplateImportService { Map> typeEntriesMap = new HashMap<>(); Map> typeFalsePositivesMap = new HashMap<>(); Map> typeFalseRecommendationsMap = new HashMap<>(); - List componentMappingMetaData = new LinkedList<>(); - + Map mappingDataMap = new HashMap<>(); + List mappingMetadataList = new LinkedList<>(); while ((ze = zis.getNextZipEntry()) != null) { log.debug("---> " + ze.getName() + " ---- " + ze.isDirectory()); totalEntryArchive++; @@ -278,8 +284,15 @@ public class DossierTemplateImportService { reportTemplateFilenameList = reportTemplateList.stream() .map(rt -> rt.isMultiFileReport() ? rt.getFileName() + ExportFilename.REPORT_TEMPLATE_MULTI_FILE.getFilename() : rt.getFileName()) .toList(); - } else if (ze.getName().contains(ExportFilename.COMPONENT_MAPPINGS.getFilename()) ) { - log.info(ze.getName()); + } else if (ze.getName().startsWith(ExportFilename.COMPONENT_MAPPINGS_FOLDER.getFilename())) { + if (ze.getName().contains(ExportFilename.COMPONENT_MAPPINGS_FILE.getFilename())) { + mappingMetadataList = objectMapper.readValue(bytes, new TypeReference<>() { + }); + } else if (ze.getName().endsWith(".csv")) { + String fileName = ze.getName().replace(ExportFilename.COMPONENT_MAPPINGS_FOLDER.getFilename() + "/", ""); + mappingDataMap.put(fileName, bytes); + } + } else { reportTemplateBytesMap.put(ze.getName(), bos); } @@ -305,6 +318,13 @@ public class DossierTemplateImportService { } } + + for (var metadata : mappingMetadataList) { + String fileName = metadata.getName() + ".csv"; + if (mappingDataMap.containsKey(fileName)) { + importTemplateResult.componentMappings.add(new ComponentMappingImportModel(metadata, mappingDataMap.get(fileName))); + } + } if (importTemplateResult.getDossierTemplate() == null) { throw new BadRequestException("Provided archive is faulty"); } @@ -342,9 +362,6 @@ public class DossierTemplateImportService { updateDossierTemplateMeta(existingDossierTemplate, dossierTemplateMeta, request.getUserId()); dossierTemplateRepository.save(existingDossierTemplate); - // set rules - setRulesWhenCompiled(request, dossierTemplateId); - existingDossierTemplate.setDossierTemplateStatus(DossierTemplateStatus.valueOf(dossierTemplatePersistenceService.computeDossierTemplateStatus(existingDossierTemplate) .name())); @@ -455,12 +472,11 @@ public class DossierTemplateImportService { dossierTemplateEntity.setDateAdded(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS)); dossierTemplateEntity.setCreatedBy(request.getUserId()); //set rules - setRulesWhenCompiled(request, dossierTemplateEntity.getId()); var loadedDossierTemplate = dossierTemplateRepository.save(dossierTemplateEntity); loadedDossierTemplate.setDossierTemplateStatus(dossierTemplatePersistenceService.computeDossierTemplateStatus(loadedDossierTemplate)); - dossierTemplateId = loadedDossierTemplate.getId(); + dossierTemplateId = loadedDossierTemplate.getId(); // set colors this.setColors(dossierTemplateId, request.getColors()); @@ -534,12 +550,38 @@ public class DossierTemplateImportService { FileAttributesGeneralConfigurationEntity.class)); } + setRulesWhenCompiled(request, dossierTemplateId); + setComponentMappings(dossierTemplateId, request.getComponentMappings()); + long elapsedTime = System.currentTimeMillis() - start; log.info("stop import dossier template elapsedTime: " + elapsedTime + "for: " + dossierTemplateId); return dossierTemplateId; } + @SneakyThrows + private void setComponentMappings(String dossierTemplateId, List componentMappings) { + + List existingMappings = componentMappingService.getMetaDataByDossierTemplateId(dossierTemplateId); + existingMappings.forEach(metadata -> componentMappingService.delete(metadata.getId())); + + for (ComponentMappingImportModel componentMapping : componentMappings) { + File tmpFile = Files.createTempFile("mapping", ".csv").toFile(); + try (var out = new FileOutputStream(tmpFile)) { + out.write(componentMapping.csvData()); + } + componentMappingService.create(dossierTemplateId, + componentMapping.metadata().getName(), + componentMapping.metadata().getFileName(), + componentMapping.metadata().getDelimiter(), + componentMapping.metadata().getEncoding(), + tmpFile); + assert tmpFile.delete(); + } + + } + + private void setRulesWhenCompiled(ImportTemplateResult request, String dossierTemplateId) { DroolsValidation droolsValidation = rulesValidationService.validateRules(RuleFileType.ENTITY, request.getRuleSet()); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DownloadService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DownloadService.java index a60f18cd5..ba3903fa3 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DownloadService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DownloadService.java @@ -89,7 +89,7 @@ public class DownloadService { request.getReportTemplateIds(), request.getRedactionPreviewColor()); websocketService.sendDownloadEvent(storageId, request.getUserId(), DownloadStatusValue.QUEUED); - addToDownloadQueue(DownloadJob.builder().storageId(storageId).userId(request.getUserId()).includeUnprocessed(request.getIncludeUnprocessed()).build(), 1); + addToDownloadQueue(DownloadJob.builder().storageId(storageId).userId(request.getUserId()).build(), 1); return new JSONPrimitive<>(storageId); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/export/DossierTemplateExportService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/export/DossierTemplateExportService.java index daf20b270..03d332582 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/export/DossierTemplateExportService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/export/DossierTemplateExportService.java @@ -4,6 +4,7 @@ import static com.knecon.fforesight.databasetenantcommons.providers.utils.MagicC import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; @@ -48,6 +49,7 @@ import com.iqser.red.service.persistence.management.v1.processor.utils.StorageId import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType; import com.iqser.red.service.persistence.service.v1.api.shared.model.WatermarkModel; import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive; +import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierAttributeConfig; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplate; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.FileAttributesGeneralConfiguration; @@ -123,6 +125,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); @@ -244,18 +247,18 @@ public class DossierTemplateExportService { List componentMappings = componentMappingService.getMappingFilesByDossierTemplateId(dossierTemplateId, mappingDir); + List metadata = componentMappings.stream() + .map(ComponentMapping::metaData) + .toList(); + fileSystemBackedArchiver.addEntries(new FileSystemBackedArchiver.ArchiveModel(ExportFilename.COMPONENT_MAPPINGS_FOLDER.getFilename(), + ExportFilename.COMPONENT_MAPPINGS_FILE.getFilename() + JSON_EXT, + objectMapper.writeValueAsBytes(metadata))); for (ComponentMapping componentMapping : componentMappings) { - - Path mappingFilePath = mappingDir.resolve(componentMapping.metaData().getFileName()); - - fileSystemBackedArchiver.addEntries(new FileSystemBackedArchiver.ArchiveModel(ExportFilename.COMPONENT_MAPPINGS.getFilename(), - componentMapping.metaData().getName() + JSON_EXT, - objectMapper.writeValueAsBytes(componentMapping))); - - fileSystemBackedArchiver.addEntries(new FileSystemBackedArchiver.ArchiveModel(ExportFilename.COMPONENT_MAPPINGS.getFilename(), - componentMapping.metaData().getName() + "-" + componentMapping.metaData() - .getFileName(), - Files.readAllBytes(mappingFilePath))); + try (var in = new FileInputStream(componentMapping.file())) { + fileSystemBackedArchiver.addEntries(new FileSystemBackedArchiver.ArchiveModel(ExportFilename.COMPONENT_MAPPINGS_FOLDER.getFilename(), + componentMapping.metaData().getName() + ".csv", + in.readAllBytes())); + } } FileSystemUtils.deleteRecursively(mappingDir); diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateCloneAndExportWithDuplicateRanksTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateCloneAndExportWithDuplicateRanksTest.java index a331e7341..19d4f8ced 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateCloneAndExportWithDuplicateRanksTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateCloneAndExportWithDuplicateRanksTest.java @@ -128,7 +128,8 @@ public class DossierTemplateCloneAndExportWithDuplicateRanksTest { storageService, dossierStatusPersistenceService, watermarkService, - fileManagementStorageService); + fileManagementStorageService, + componentMappingService); dossierTemplateExportService = new DossierTemplateExportService(dossierTemplatePersistenceService, downloadStatusPersistenceService, dossierAttributeConfigPersistenceService, diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateImportExportTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateImportExportTest.java new file mode 100644 index 000000000..0178bee6c --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateImportExportTest.java @@ -0,0 +1,189 @@ +package com.iqser.red.service.peristence.v1.server.integration.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.core.io.ClassPathResource; +import org.springframework.util.FileSystemUtils; + +import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest; +import com.iqser.red.service.persistence.management.v1.processor.entity.download.DownloadStatusEntity; +import com.iqser.red.service.persistence.management.v1.processor.model.DownloadJob; +import com.iqser.red.service.persistence.management.v1.processor.service.DossierTemplateImportService; +import com.iqser.red.service.persistence.management.v1.processor.service.export.DossierTemplateExportService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService; +import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.importexport.ImportDossierTemplateRequest; +import com.iqser.red.storage.commons.service.StorageService; +import com.iqser.red.storage.commons.utils.FileSystemBackedStorageService; +import com.knecon.fforesight.tenantcommons.TenantContext; + +import lombok.SneakyThrows; + +public class DossierTemplateImportExportTest extends AbstractPersistenceServerServiceTest { + + public static final String IMPORTED_TEMPLATE_NAME = "imported-template"; + public static final String USER_ID = "Deine Mutter"; + public static final String AFTER = "after"; + public static final String BEFORE = "before"; + + @Autowired + DossierTemplateImportService dossierTemplateImportService; + + @Autowired + DossierTemplateExportService dossierTemplateExportService; + + @Autowired + StorageService storageService; + + @MockBean + DownloadStatusPersistenceService downloadStatusPersistenceService; + + + @AfterEach + public void clearStorage() { + + ((FileSystemBackedStorageService) storageService).clearStorage(); + } + + + @Test + @SneakyThrows + public void testImportExportRoundtrip() { + + TenantContext.setTenantId("redaction"); + Path outDir = Files.createTempDirectory(IMPORTED_TEMPLATE_NAME); + + Path dossierTemplateExportArchive = new ClassPathResource("files/dossiertemplates/DossierTemplate.zip").getFile().toPath(); + + String importedDossierTemplateId = dossierTemplateImportService.importDossierTemplate(ImportDossierTemplateRequest.builder() + .archive(Files.readAllBytes(dossierTemplateExportArchive)) + .userId(USER_ID) + .build()); + + when(downloadStatusPersistenceService.getStatus(anyString()))// + .thenReturn(DownloadStatusEntity.builder().filename(importedDossierTemplateId + ".zip").storageId(IMPORTED_TEMPLATE_NAME).build()); + + dossierTemplateExportService.createDownloadArchive(DownloadJob.builder().storageId(importedDossierTemplateId).userId(USER_ID).build()); + + File tmpFile = Files.createTempFile(IMPORTED_TEMPLATE_NAME, ".zip").toFile(); + storageService.downloadTo(TenantContext.getTenantId(), IMPORTED_TEMPLATE_NAME, tmpFile); + + unzip(dossierTemplateExportArchive.toFile().toString(), outDir.resolve(BEFORE)); + unzip(tmpFile.toString(), outDir.resolve(AFTER)); + + assert tmpFile.delete(); + + Map beforeContents = getDirectoryContents(outDir.resolve(BEFORE)); + Map afterContents = getDirectoryContents(outDir.resolve(AFTER)); + + assertEquals(beforeContents.size(), afterContents.size()); + assertEquals(beforeContents.keySet(), afterContents.keySet()); + + // can't assert equality on contents as UUID's are supposed to change on import + + FileSystemUtils.deleteRecursively(outDir); + } + + + public static void unzip(String zipFilePath, Path destDirectory) throws IOException { + + File destDir = destDirectory.toFile(); + if (!destDir.exists()) { + destDir.mkdir(); + } + try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFilePath))) { + ZipEntry entry = zipIn.getNextEntry(); + while (entry != null) { + String filePath = destDirectory + File.separator + entry.getName(); + if (!entry.isDirectory()) { + // if the entry is a file, extracts it + extractFile(zipIn, filePath); + } else { + // if the entry is a directory, make the directory + File dir = new File(filePath); + dir.mkdirs(); + } + zipIn.closeEntry(); + entry = zipIn.getNextEntry(); + } + } + } + + + private static void extractFile(ZipInputStream zipIn, String filePath) throws IOException { + + try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath))) { + byte[] bytesIn = new byte[4096]; + int read; + while ((read = zipIn.read(bytesIn)) != -1) { + bos.write(bytesIn, 0, read); + } + } + } + + + private static boolean areDirectoriesIdentical(Path dir1, Path dir2) throws IOException, NoSuchAlgorithmException { + + if (!Files.isDirectory(dir1) || !Files.isDirectory(dir2)) { + throw new IllegalArgumentException("Both paths should be directories."); + } + + Map dir1Contents = getDirectoryContents(dir1); + Map dir2Contents = getDirectoryContents(dir2); + + return dir1Contents.equals(dir2Contents); + } + + + private static Map getDirectoryContents(Path dir) throws IOException, NoSuchAlgorithmException { + + Map contents = new HashMap<>(); + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + + Files.walkFileTree(dir, new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + + contents.put(dir.relativize(file), digest.digest(Files.readAllBytes(file))); + return FileVisitResult.CONTINUE; + } + + + @Override + public FileVisitResult preVisitDirectory(Path subDir, BasicFileAttributes attrs) throws IOException { + + contents.put(dir.relativize(subDir), new byte[0]); + return FileVisitResult.CONTINUE; + } + }); + + return contents; + } + +} diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/resources/files/dossiertemplates/DossierTemplate.zip b/persistence-service-v1/persistence-service-server-v1/src/test/resources/files/dossiertemplates/DossierTemplate.zip new file mode 100644 index 0000000000000000000000000000000000000000..bc076a8294740b3940e53e0cae929005eadfd459 GIT binary patch literal 554274 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 z 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:KfF6jo}(uEKuAvx`oxk1p!rr4UXv?#bjKS~;N; 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
  • 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ℑPFAoWHhBMa(L%oI~_ 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�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

    TvAH;As1&$Ipl3xD%3-*Qb1^eP<1r8M! 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 QG5q$Nh){PL{NEAif5|SD9{>OV literal 0 HcmV?d00001 diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/file/ProcessingStatus.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/file/ProcessingStatus.java index a2125b0c2..80112f08f 100644 --- a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/file/ProcessingStatus.java +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/file/ProcessingStatus.java @@ -19,6 +19,5 @@ public enum ProcessingStatus { PRE_PROCESSED, FIGURE_DETECTION_ANALYZING, TABLE_PARSING_ANALYZING, - VISUAL_LAYOUT_PARSING_ANALYZING } diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ComponentMappingImportModel.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ComponentMappingImportModel.java new file mode 100644 index 000000000..1aa95b411 --- /dev/null +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ComponentMappingImportModel.java @@ -0,0 +1,9 @@ +package com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.importexport; + +import java.io.File; + +import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata; + +public record ComponentMappingImportModel(ComponentMappingMetadata metadata, byte[] csvData) { + +} diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ExportFilename.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ExportFilename.java index 82fe6b4a2..905b4fed0 100644 --- a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ExportFilename.java +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ExportFilename.java @@ -20,7 +20,8 @@ public enum ExportFilename { ENTRIES("entries"), FALSE_POSITIVES("falsePositives"), FALSE_RECOMMENDATION("falseRecommendations"), - COMPONENT_MAPPINGS("componentMappings"); + COMPONENT_MAPPINGS_FOLDER("mappings"), + COMPONENT_MAPPINGS_FILE("componentMappingsList"); @Getter private final String filename; diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ImportTemplateResult.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ImportTemplateResult.java index fe4a7e29a..6f20e791e 100644 --- a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ImportTemplateResult.java +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/importexport/ImportTemplateResult.java @@ -50,6 +50,9 @@ public class ImportTemplateResult { @Builder.Default public List fileAttributesConfigs = new ArrayList<>(); + @Builder.Default + public List componentMappings = new ArrayList<>(); + @Builder.Default public List legalBases = new ArrayList<>(); -- 2.47.2 From 92a277b24eca7667a773586b6d9ce2f684802208 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Thu, 23 May 2024 19:29:21 +0200 Subject: [PATCH 09/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package --- .../ExternalControllerAdviceV2.java | 2 +- .../resource/DossierTemplateResource.java | 4 +- .../src/main/resources/api/openapi.yaml | 41 +++++++++++-------- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ExternalControllerAdviceV2.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ExternalControllerAdviceV2.java index 6276722e1..b22db8eb7 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ExternalControllerAdviceV2.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/ExternalControllerAdviceV2.java @@ -36,8 +36,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j -@RequiredArgsConstructor @RestControllerAdvice +@RequiredArgsConstructor @Order(Ordered.HIGHEST_PRECEDENCE) public class ExternalControllerAdviceV2 { diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java index 9f7046383..0b3d5ce29 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java @@ -141,7 +141,7 @@ public interface DossierTemplateResource { @ResponseStatus(value = HttpStatus.OK) @Operation(summary = "Returns file containing the specified mapping as a file.") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) - @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE, produces = MediaType.MULTIPART_FORM_DATA_VALUE) + @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE) ResponseEntity downloadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId); @@ -149,7 +149,7 @@ public interface DossierTemplateResource { @ResponseStatus(value = HttpStatus.OK) @Operation(summary = "Deletes a specified mapping.") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) - @DeleteMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE, produces = MediaType.MULTIPART_FORM_DATA_VALUE) + @DeleteMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE) ResponseEntity deleteMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId); } diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml index e715f335b..2c7b10965 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml @@ -315,7 +315,7 @@ paths: "403": $ref: '#/components/responses/403' "404": - $ref: '#/components/responses/404-dossier-template' + $ref: '#/components/responses/404-mapping' "429": $ref: '#/components/responses/429' "500": @@ -357,7 +357,7 @@ paths: "403": $ref: '#/components/responses/403' "404": - $ref: '#/components/responses/404-dossier-template' + $ref: '#/components/responses/404-mapping' "429": $ref: '#/components/responses/429' "500": @@ -382,9 +382,9 @@ paths: Content-Disposition: schema: type: string - example: attachment; filename*=utf-8''mapping.csv + example: attachment; filename*=mapping.csv content: - text/plain; charset=utf-8: + text/plain: schema: type: string description: | @@ -396,7 +396,7 @@ paths: "403": $ref: '#/components/responses/403' "404": - $ref: '#/components/responses/404-dossier-template' + $ref: '#/components/responses/404-mapping' "429": $ref: '#/components/responses/429' "500": @@ -432,7 +432,7 @@ paths: "403": $ref: '#/components/responses/403' "404": - $ref: '#/components/responses/404-dossier-template' + $ref: '#/components/responses/404-mapping' "429": $ref: '#/components/responses/429' "500": @@ -458,7 +458,7 @@ paths: "403": $ref: '#/components/responses/403' "404": - $ref: '#/components/responses/404-dossier-template' + $ref: '#/components/responses/404-mapping' "429": $ref: '#/components/responses/429' "500": @@ -965,14 +965,14 @@ components: responses: "400": content: - '*/*': + 'application/json': schema: $ref: '#/components/schemas/ErrorMessage' description: | The request was malformed or invalid. Double-check the request structure or parameters. "401": content: - '*/*': + 'application/json': schema: $ref: '#/components/schemas/ErrorMessage' description: | @@ -980,21 +980,28 @@ components: resource. This error can happen if the access token was revoked or has expired. "403": content: - '*/*': + 'application/json': schema: $ref: '#/components/schemas/ErrorMessage' description: | Forbidden. Your credentials are valid, but you do not have permission to access this resource. "404-dossier-template": content: - '*/*': + 'application/json': schema: $ref: '#/components/schemas/ErrorMessage' description: | Dossier template not found. This happens if the requested dossier template does not exist. + "404-mapping": + content: + 'application/json': + schema: + $ref: '#/components/schemas/ErrorMessage' + description: | + Dossier template or component mapping not found. This happens if the requested dossier template or component mapping does not exist. "404-dossier": content: - '*/*': + 'application/json': schema: $ref: '#/components/schemas/ErrorMessage' description: | @@ -1004,7 +1011,7 @@ components: for a previously existing dossier only if it is actually deleted permanently. "404-file": content: - '*/*': + 'application/json': schema: $ref: '#/components/schemas/ErrorMessage' description: | @@ -1014,28 +1021,28 @@ components: only if the file is deleted permanently. "409-dossier-conflict": content: - '*/*': + 'application/json': schema: $ref: '#/components/schemas/ErrorMessage' description: | Name conflict: The provided name is already in use by another dossier. It needs to be unique in the scope of your workspace. 422-rules: content: - '*/*': + 'application/json': schema: $ref: '#/components/schemas/RuleValidation' description: | Invalid rules file: There were validation errors, the rules file is unprocessable. "429": content: - '*/*': + 'application/json': schema: $ref: '#/components/schemas/ErrorMessage' description: | Too many requests have been made in a given time frame. Rate limiting is in place to prevent abuse. "500": content: - '*/*': + 'application/json': schema: $ref: '#/components/schemas/ErrorMessage' description: Internal Server Error. An unexpected error occurred on the server side. Please try again later or contact support. -- 2.47.2 From d592c683219249863295b248ab7d8e8c9dcb1520 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Thu, 23 May 2024 20:09:22 +0200 Subject: [PATCH 10/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package --- .../DossierTemplateControllerV2.java | 30 ++++++++++++------- .../resource/DossierTemplateResource.java | 2 +- .../ComponentMappingPersistenceService.java | 12 ++++---- .../service/ComponentMappingService.java | 16 +++++----- .../service/DossierTemplateImportService.java | 4 +-- .../ComponentMappingRepository.java | 4 +++ 6 files changed, 40 insertions(+), 28 deletions(-) diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java index 9aaf1eb7e..3662294a1 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java @@ -70,11 +70,11 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { private static final String RULES_DOWNLOAD_FILE_NAME_SUFFIX = "-rules.drl"; - DossierTemplateController dossierTemplateController; - RulesPersistenceService rulesPersistenceService; - RulesValidationService rulesValidationService; - AuditPersistenceService auditPersistenceService; - FileAttributesController fileAttributesController; + DossierTemplateController dossierTemplateController; + RulesPersistenceService rulesPersistenceService; + RulesValidationService rulesValidationService; + AuditPersistenceService auditPersistenceService; + FileAttributesController fileAttributesController; ComponentMappingService componentMappingService; ComponentMappingMapper componentMappingMapper = ComponentMappingMapper.INSTANCE; DossierTemplatePersistenceService dossierTemplatePersistenceService; @@ -233,7 +233,8 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { public ComponentMappingSummary getComponentMappingSummaries(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) { dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); - List summaries = componentMappingService.getMetaDataByDossierTemplateId(dossierTemplateId); + List summaries = componentMappingService.getMetaDataByDossierTemplateId( + dossierTemplateId); List componentMappingMetadataModelList = componentMappingMapper.toModelList(summaries); return new ComponentMappingSummary(dossierTemplateId, componentMappingMetadataModelList); } @@ -255,7 +256,12 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { Path mappingFile = saveToFile(file); String fileName = file.getOriginalFilename() == null ? nameToUse + ".csv" : file.getOriginalFilename(); - com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata metaData = componentMappingService.create(dossierTemplateId, nameToUse, fileName, delimiter, encoding, mappingFile.toFile()); + com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata metaData = componentMappingService.create(dossierTemplateId, + nameToUse, + fileName, + delimiter, + encoding, + mappingFile.toFile()); Files.deleteIfExists(mappingFile); @@ -272,7 +278,11 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { Path mappingFile = saveToFile(file); - com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata resultMetaData = componentMappingService.update(componentMappingId, encoding, delimiter, mappingFile.toFile()); + com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata resultMetaData = componentMappingService.update(dossierTemplateId, + componentMappingId, + encoding, + delimiter, + mappingFile.toFile()); Files.deleteIfExists(mappingFile); @@ -286,7 +296,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); - ComponentMappingDownloadModel mappingDownloadModel = componentMappingService.getMappingForDownload(componentMappingId); + ComponentMappingDownloadModel mappingDownloadModel = componentMappingService.getMappingForDownload(dossierTemplateId, componentMappingId); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.TEXT_PLAIN); @@ -308,7 +318,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId); - componentMappingService.delete(componentMappingId); + componentMappingService.delete(dossierTemplateId, componentMappingId); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java index 0b3d5ce29..7257cdd41 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java @@ -140,7 +140,7 @@ public interface DossierTemplateResource { @ResponseBody @ResponseStatus(value = HttpStatus.OK) @Operation(summary = "Returns file containing the specified mapping as a file.") - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")}) @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE) ResponseEntity downloadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java index bfbd4642b..43a75883b 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingPersistenceService.java @@ -38,16 +38,16 @@ public class ComponentMappingPersistenceService { } - public void deleteById(String id) { + public void deleteById(String dossierTemplateId, String id) { - ComponentMappingEntity entity = getEntityById(id); + ComponentMappingEntity entity = getEntityById(dossierTemplateId, id); delete(entity); } - public ComponentMappingEntity getEntityById(String id) { + public ComponentMappingEntity getEntityById(String dossierTemplateId, String id) { - return repository.findById(id) + return repository.findByIdAndDossierTemplateId(id, dossierTemplateId) .orElseThrow(() -> new NotFoundException("ComponentMapping with id " + id + " not found!")); } @@ -84,9 +84,9 @@ public class ComponentMappingPersistenceService { @SneakyThrows - public ComponentMappingDownloadModel getMappingFileForDownload(String componentMappingId) { + public ComponentMappingDownloadModel getMappingFileForDownload(String dossierTemplateId, String componentMappingId) { - var entity = getEntityById(componentMappingId); + var entity = getEntityById(dossierTemplateId, componentMappingId); if (!storageService.objectExists(TenantContext.getTenantId(), entity.getStorageId())) { throw new NotFoundException("ComponentMapping with id " + componentMappingId + " does not exist!"); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java index 4ea8c31fe..2898a2899 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java @@ -55,16 +55,16 @@ public class ComponentMappingService { } - public ComponentMappingMetadata getMetaData(String mappingId) { + public ComponentMappingMetadata getMetaData(String dossierTemplateId, String mappingId) { - return mappingEntityMapper.toComponentMappingMetaData(componentMappingPersistenceService.getEntityById(mappingId)); + return mappingEntityMapper.toComponentMappingMetaData(componentMappingPersistenceService.getEntityById(dossierTemplateId, mappingId)); } @SneakyThrows - public ComponentMappingMetadata update(String mappingId, String encoding, char delimiter, File mappingFile) { + public ComponentMappingMetadata update(String dossierTemplateId, String mappingId, String encoding, char delimiter, File mappingFile) { - ComponentMappingEntity entity = componentMappingPersistenceService.getEntityById(mappingId); + ComponentMappingEntity entity = componentMappingPersistenceService.getEntityById(dossierTemplateId, mappingId); return updateOrCreate(entity, encoding, delimiter, mappingFile); } @@ -164,15 +164,15 @@ public class ComponentMappingService { } - public void delete(String componentMappingId) { + public void delete(String dossierTemplateId, String componentMappingId) { - componentMappingPersistenceService.deleteById(componentMappingId); + componentMappingPersistenceService.deleteById(dossierTemplateId, componentMappingId); } - public ComponentMappingDownloadModel getMappingForDownload(String componentMappingId) { + public ComponentMappingDownloadModel getMappingForDownload(String dossierTemplateId, String componentMappingId) { - return componentMappingPersistenceService.getMappingFileForDownload(componentMappingId); + return componentMappingPersistenceService.getMappingFileForDownload(dossierTemplateId, componentMappingId); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java index 30fd04279..012946acd 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/DossierTemplateImportService.java @@ -13,7 +13,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.Path; import java.time.OffsetDateTime; import java.time.temporal.ChronoUnit; import java.util.ArrayList; @@ -32,7 +31,6 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; import org.apache.commons.compress.utils.FileNameUtils; -import org.apache.commons.compress.utils.IOUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; @@ -563,7 +561,7 @@ public class DossierTemplateImportService { private void setComponentMappings(String dossierTemplateId, List componentMappings) { List existingMappings = componentMappingService.getMetaDataByDossierTemplateId(dossierTemplateId); - existingMappings.forEach(metadata -> componentMappingService.delete(metadata.getId())); + existingMappings.forEach(metadata -> componentMappingService.delete(dossierTemplateId, metadata.getId())); for (ComponentMappingImportModel componentMapping : componentMappings) { File tmpFile = Files.createTempFile("mapping", ".csv").toFile(); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/ComponentMappingRepository.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/ComponentMappingRepository.java index 8f1a7c6c8..219d6866d 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/ComponentMappingRepository.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/ComponentMappingRepository.java @@ -1,6 +1,7 @@ package com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; @@ -14,4 +15,7 @@ public interface ComponentMappingRepository extends JpaRepository findByIdAndDossierTemplateId(String id, String dossierTemplateId); + } -- 2.47.2 From 844466812b30a3487cc47ee88a05e8a720a28b2a Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Fri, 24 May 2024 14:58:26 +0200 Subject: [PATCH 11/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package --- .../processor/entity/dossier/FileEntity.java | 5 + ...leEntityComponentMappingVersionEntity.java | 17 ++ .../mapper/ComponentMappingEntityMapper.java | 2 +- .../processor/service/FileStatusService.java | 4 +- .../ReanalysisRequiredStatusService.java | 152 +++++++++++++++--- .../service/job/AutomaticAnalysisJob.java | 4 +- .../FileStatusPersistenceService.java | 13 +- .../repository/FileRepository.java | 39 +++-- .../v1/processor/utils/FileModelMapper.java | 5 + .../db/changelog/db.changelog-tenant.yaml | 2 + ...dd-component-mapping-versions-to-file.yaml | 31 ++++ .../tests/DossierTemplateStatsTest.java | 3 +- .../v1/api/shared/model/AnalyzeResult.java | 4 + .../component/ComponentMappingMetadata.java | 38 +++++ .../dossier/file/FileModel.java | 1 + 15 files changed, 279 insertions(+), 41 deletions(-) create mode 100644 persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/FileEntityComponentMappingVersionEntity.java create mode 100644 persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/128-add-component-mapping-versions-to-file.yaml create mode 100644 persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/component/ComponentMappingMetadata.java diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/FileEntity.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/FileEntity.java index 61fda011a..f55773cd6 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/FileEntity.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/FileEntity.java @@ -14,8 +14,10 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemp import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.WorkflowStatus; import jakarta.persistence.CascadeType; +import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; import jakarta.persistence.Convert; +import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; @@ -197,6 +199,9 @@ public class FileEntity { @Column private OffsetDateTime errorTimestamp; + @ElementCollection(fetch = FetchType.EAGER) + private List componentMappingVersions; + public OffsetDateTime getLastOCRTime() { diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/FileEntityComponentMappingVersionEntity.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/FileEntityComponentMappingVersionEntity.java new file mode 100644 index 000000000..f50700668 --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/FileEntityComponentMappingVersionEntity.java @@ -0,0 +1,17 @@ +package com.iqser.red.service.persistence.management.v1.processor.entity.dossier; + +import jakarta.persistence.Embeddable; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Embeddable +@AllArgsConstructor +@NoArgsConstructor +public class FileEntityComponentMappingVersionEntity { + + private String name; + private Integer version; + +} diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java index 76f0df0f4..7fa8ff3be 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/mapper/ComponentMappingEntityMapper.java @@ -17,7 +17,7 @@ public interface ComponentMappingEntityMapper { ComponentMappingMetadata toComponentMappingMetaData(ComponentMappingEntity componentMappingMetaData); - List toComponentMappingMetaDataList(List componentMappingMetaData); + List toComponentMappingMetaDataList(List componentMappingEntities); ComponentMappingEntity toComponentMappingEntity(ComponentMappingMetadata componentMappingSummary); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusService.java index ee262b7a1..0440de55f 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusService.java @@ -14,6 +14,7 @@ import com.iqser.red.service.persistence.management.v1.processor.exception.NotFo import com.iqser.red.service.persistence.management.v1.processor.model.websocket.AnalyseStatus; import com.iqser.red.service.persistence.management.v1.processor.model.websocket.FileEventType; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService; + import org.apache.commons.lang3.StringUtils; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.stereotype.Service; @@ -520,7 +521,8 @@ public class FileStatusService { analyzeResult.getDuration(), analyzeResult.getDossierDictionaryVersion(), analyzeResult.getAnalysisVersion(), - analyzeResult.getAnalysisNumber()); + analyzeResult.getAnalysisNumber(), + analyzeResult.getUsedComponentMappings()); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ReanalysisRequiredStatusService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ReanalysisRequiredStatusService.java index 453f1e7ea..234b3cee3 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ReanalysisRequiredStatusService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ReanalysisRequiredStatusService.java @@ -7,8 +7,11 @@ import static com.iqser.red.service.persistence.management.v1.processor.service. import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import org.springframework.stereotype.Service; @@ -19,23 +22,28 @@ import com.iqser.red.service.persistence.management.v1.processor.service.persist import com.iqser.red.service.persistence.management.v1.processor.service.persistence.LegalBasisMappingPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.RulesPersistenceService; import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType; +import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.ProcessingStatus; +import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Data; import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; import lombok.extern.slf4j.Slf4j; @Slf4j @Service @RequiredArgsConstructor +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) public class ReanalysisRequiredStatusService { - private final DictionaryPersistenceService dictionaryPersistenceService; - private final RulesPersistenceService rulesPersistenceService; - private final DossierPersistenceService dossierPersistenceService; - private final LegalBasisMappingPersistenceService legalBasisMappingPersistenceService; + DictionaryPersistenceService dictionaryPersistenceService; + RulesPersistenceService rulesPersistenceService; + DossierPersistenceService dossierPersistenceService; + LegalBasisMappingPersistenceService legalBasisMappingPersistenceService; + ComponentMappingService componentMappingService; public FileModel enhanceFileStatusWithAnalysisRequirements(FileModel fileModel) { @@ -118,39 +126,141 @@ public class ReanalysisRequiredStatusService { Map dossierVersionMap) { // get relevant versions - var dossierTemplateVersions = dossierTemplateVersionMap.computeIfAbsent(dossier.getDossierTemplateId(), k -> buildVersionData(dossier.getDossierTemplateId())); - var dossierDictionaryVersion = dossierVersionMap.computeIfAbsent(fileStatus.getDossierId(), k -> getDossierVersionData(fileStatus.getDossierId())); + Map dossierTemplateVersions = dossierTemplateVersionMap.computeIfAbsent(dossier.getDossierTemplateId(), + k -> buildVersionData(dossier.getDossierTemplateId())); + + Map componentMappingVersions = componentMappingService.getMetaDataByDossierTemplateId(dossier.getDossierTemplateId()) + .stream() + .collect(Collectors.toMap(ComponentMappingMetadata::getName, ComponentMappingMetadata::getVersion)); + + Long dossierDictionaryVersion = dossierVersionMap.computeIfAbsent(fileStatus.getDossierId(), k -> getDossierVersionData(fileStatus.getDossierId())); // compute matches + var mappingVersionAllMatch = mappingVersionsEqual(fileStatus, componentMappingVersions); var rulesVersionMatches = fileStatus.getRulesVersion() == dossierTemplateVersions.getOrDefault(RULES, -1L); var componentRulesVersionMatches = fileStatus.getComponentRulesVersion() == dossierTemplateVersions.getOrDefault(COMPONENT_RULES, -1L); var dictionaryVersionMatches = fileStatus.getDictionaryVersion() == dossierTemplateVersions.getOrDefault(DICTIONARY, -1L); var legalBasisVersionMatches = fileStatus.getLegalBasisVersion() == dossierTemplateVersions.getOrDefault(LEGAL_BASIS, -1L); var dossierDictionaryVersionMatches = Math.max(fileStatus.getDossierDictionaryVersion(), 0) == dossierDictionaryVersion; - var reanalysisRequired = !dictionaryVersionMatches || !dossierDictionaryVersionMatches; + var reanalysisRequired = !dictionaryVersionMatches || !dossierDictionaryVersionMatches || !mappingVersionAllMatch; var fullAnalysisRequired = !rulesVersionMatches || !componentRulesVersionMatches || !legalBasisVersionMatches; + if (reanalysisRequired || fullAnalysisRequired) { - log.info( - "For file: {}-{} analysis is required because -> ruleVersionMatches: {}/{}, componentRuleVersionMatches {}/{}, dictionaryVersionMatches: {}/{}, legalBasisVersionMatches: {}/{}, dossierDictionaryVersionMatches: {}/{}", - fileStatus.getId(), - fileStatus.getFilename(), - fileStatus.getRulesVersion(), - dossierTemplateVersions.getOrDefault(RULES, -1L), - fileStatus.getComponentRulesVersion(), - dossierTemplateVersions.getOrDefault(COMPONENT_RULES, -1L), - fileStatus.getDictionaryVersion(), - dossierTemplateVersions.getOrDefault(DICTIONARY, -1L), - fileStatus.getLegalBasisVersion(), - dossierTemplateVersions.getOrDefault(LEGAL_BASIS, -1L), - Math.max(fileStatus.getDossierDictionaryVersion(), 0), - dossierDictionaryVersion); + + log.info(buildMessage(fileStatus, + rulesVersionMatches, + dossierTemplateVersions, + componentRulesVersionMatches, + dictionaryVersionMatches, + legalBasisVersionMatches, + dossierDictionaryVersionMatches, + dossierDictionaryVersion, + mappingVersionAllMatch, + componentMappingVersions)); } return new AnalysisRequiredResult(reanalysisRequired, fullAnalysisRequired); } + private String buildMessage(FileModel fileStatus, + boolean rulesVersionMatches, + Map dossierTemplateVersions, + boolean componentRulesVersionMatches, + boolean dictionaryVersionMatches, + boolean legalBasisVersionMatches, + boolean dossierDictionaryVersionMatches, + Long dossierDictionaryVersion, + boolean mappingVersionAllMatch, + Map componentMappingVersions) { + + StringBuilder messageBuilder = new StringBuilder(); + messageBuilder.append("For file: ").append(fileStatus.getId()).append("-").append(fileStatus.getFilename()).append(" analysis is required because -> "); + + boolean needComma = false; + + if (rulesVersionMatches) { + messageBuilder.append("ruleVersions: ").append(fileStatus.getRulesVersion()).append("/").append(dossierTemplateVersions.getOrDefault(RULES, -1L)); + needComma = true; + } + + if (componentRulesVersionMatches) { + if (needComma) { + messageBuilder.append(", "); + } + messageBuilder.append("componentRuleVersions: ") + .append(fileStatus.getComponentRulesVersion()) + .append("/") + .append(dossierTemplateVersions.getOrDefault(COMPONENT_RULES, -1L)); + needComma = true; + } + + if (dictionaryVersionMatches) { + if (needComma) { + messageBuilder.append(", "); + } + messageBuilder.append("dictionaryVersions: ").append(fileStatus.getDictionaryVersion()).append("/").append(dossierTemplateVersions.getOrDefault(DICTIONARY, -1L)); + needComma = true; + } + + if (legalBasisVersionMatches) { + if (needComma) { + messageBuilder.append(", "); + } + messageBuilder.append("legalBasisVersions: ").append(fileStatus.getLegalBasisVersion()).append("/").append(dossierTemplateVersions.getOrDefault(LEGAL_BASIS, -1L)); + needComma = true; + } + + if (dossierDictionaryVersionMatches) { + if (needComma) { + messageBuilder.append(", "); + } + messageBuilder.append("dossierDictionaryVersions: ").append(Math.max(fileStatus.getDossierDictionaryVersion(), 0)).append("/").append(dossierDictionaryVersion); + needComma = true; + } + + if (mappingVersionAllMatch) { + if (needComma) { + messageBuilder.append(", "); + } + messageBuilder.append("componentMappingVersions: ").append(buildMappingVersionMatchesString(fileStatus.getComponentMappingVersions(), componentMappingVersions)); + needComma = true; + } + + return messageBuilder.toString(); + } + + + private String buildMappingVersionMatchesString(Map fileComponentMappingVersions, Map dbComponentMappingVersions) { + + Set allMappingNames = new HashSet<>(); + allMappingNames.addAll(fileComponentMappingVersions.keySet()); + allMappingNames.addAll(dbComponentMappingVersions.keySet()); + + StringBuilder sb = new StringBuilder(); + allMappingNames.stream() + .sorted() + .forEach(mappingName -> { + long fileVersion = fileComponentMappingVersions.getOrDefault(mappingName, -1); + long dbVersion = dbComponentMappingVersions.getOrDefault(mappingName, -1); + if (fileVersion != dbVersion) { + sb.append(mappingName).append(": ").append(fileVersion).append("/").append(dbVersion); + } + }); + return sb.toString(); + } + + + private static boolean mappingVersionsEqual(FileModel fileStatus, Map componentMappingVersions) { + + return fileStatus.getComponentMappingVersions().keySet().equals(componentMappingVersions.keySet()) && fileStatus.getComponentMappingVersions().keySet() + .stream() + .allMatch(name -> fileStatus.getComponentMappingVersions() + .get(name).equals(componentMappingVersions.get(name))); + } + + private Map buildVersionData(String dossierTemplateId) { var versions = new HashMap(); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/job/AutomaticAnalysisJob.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/job/AutomaticAnalysisJob.java index ebc985b83..c4794dbf6 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/job/AutomaticAnalysisJob.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/job/AutomaticAnalysisJob.java @@ -69,7 +69,9 @@ public class AutomaticAnalysisJob implements Job { if (!saasMigrationStatusPersistenceService.migrationFinishedForTenant()) { log.info("[Tenant:{}] Skipping scheduling as there are files that require migration.", tenant.getTenantId()); return; - }var redactionQueueInfo = amqpAdmin.getQueueInfo(MessagingConfiguration.REDACTION_QUEUE); + } + + var redactionQueueInfo = amqpAdmin.getQueueInfo(MessagingConfiguration.REDACTION_QUEUE); if (redactionQueueInfo != null) { log.debug("[Tenant:{}] Checking queue status to see if background analysis can happen. Currently {} holds {} elements and has {} consumers", tenant.getTenantId(), diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java index d47301caa..28c135809 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java @@ -14,6 +14,7 @@ import org.springframework.stereotype.Service; import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileAttributeConfigEntity; import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileAttributeEntity; import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntityComponentMappingVersionEntity; 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.model.FileIdentifier; @@ -23,6 +24,7 @@ import com.iqser.red.service.persistence.management.v1.processor.service.Websock import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileAttributesRepository; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileRepository; import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute; +import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileErrorInfo; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.ProcessingStatus; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.WorkflowStatus; @@ -121,11 +123,17 @@ public class FileStatusPersistenceService { long duration, long dossierDictionaryVersion, int analysisVersion, - int analysisNumber) { + int analysisNumber, + List usedComponentMappings) { if (isFileDeleted(fileId)) { return; } + + List versionEntities = usedComponentMappings.stream() + .map(cm -> new FileEntityComponentMappingVersionEntity(cm.getName(), cm.getVersion())) + .toList(); + fileRepository.updateProcessingStatus(fileId, numberOfPages, ProcessingStatus.PROCESSED, @@ -139,7 +147,8 @@ public class FileStatusPersistenceService { OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS), OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS), analysisNumber, - calculateProcessingErrorCounter(fileId, ProcessingStatus.PROCESSED)); + calculateProcessingErrorCounter(fileId, ProcessingStatus.PROCESSED), + versionEntities); websocketService.sendAnalysisEvent(dossierId, fileId, AnalyseStatus.FINISHED, analysisNumber); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java index 775564d99..afe318ea4 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java @@ -10,6 +10,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntityComponentMappingVersionEntity; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.projection.FilePageCountsProjection; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.projection.FileProcessingStatusProjection; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.projection.FileWorkflowStatusProjection; @@ -47,12 +48,22 @@ public interface FileRepository extends JpaRepository { @Modifying - @Query("update FileEntity f set f.numberOfPages = :numberOfPages, f.processingStatus = :processingStatus, " - + "f.dictionaryVersion = :dictionaryVersion, f.rulesVersion = :rulesVersion, f.componentRulesVersion = :componentRulesVersion, f.legalBasisVersion = :legalBasisVersion, " - + "f.analysisDuration = :analysisDuration, f.dossierDictionaryVersion = :dossierDictionaryVersion, " - + "f.analysisVersion = :analysisVersion, f.numberOfAnalyses = :analysisNumber, f.lastUpdated = :lastUpdated, " - + "f.lastProcessed = :lastProcessed, f.processingErrorCounter = :processingErrorCounter " - + "where f.id = :fileId") + @Query(""" + update FileEntity f set f.numberOfPages = :numberOfPages, \ + f.processingStatus = :processingStatus, \ + f.dictionaryVersion = :dictionaryVersion, \ + f.rulesVersion = :rulesVersion, \ + f.componentRulesVersion = :componentRulesVersion, \ + f.legalBasisVersion = :legalBasisVersion, \ + f.analysisDuration = :analysisDuration, \ + f.dossierDictionaryVersion = :dossierDictionaryVersion, \ + f.analysisVersion = :analysisVersion, \ + f.numberOfAnalyses = :analysisNumber, \ + f.lastUpdated = :lastUpdated, \ + f.lastProcessed = :lastProcessed, \ + f.processingErrorCounter = :processingErrorCounter, \ + f.componentMappingVersions = :componentMappingVersions \ + where f.id = :fileId""") void updateProcessingStatus(@Param("fileId") String fileId, @Param("numberOfPages") int numberOfPages, @Param("processingStatus") ProcessingStatus processingStatus, @@ -66,7 +77,9 @@ public interface FileRepository extends JpaRepository { @Param("lastUpdated") OffsetDateTime lastUpdated, @Param("lastProcessed") OffsetDateTime lastProcessed, @Param("analysisNumber") int analysisNumber, - @Param("processingErrorCounter") int processingErrorCounter); + @Param("processingErrorCounter") int processingErrorCounter, + @Param("componentMappingVersions") + List versionEntities); @Modifying @@ -168,12 +181,13 @@ public interface FileRepository extends JpaRepository { @Param("lastUpdated") OffsetDateTime lastUpdated, @Param("softDeletedTime") OffsetDateTime softDeletedTime); + @Modifying @Query("update FileEntity f set f.processingStatus = :processingStatus, f.lastUpdated = :lastUpdated, " + "f.deleted = :softDeletedTime where f.id in (:fileIds)") int softDeleteFiles(@Param("fileIds") List fileIds, - @Param("processingStatus") ProcessingStatus processingStatus, - @Param("lastUpdated") OffsetDateTime lastUpdated, - @Param("softDeletedTime") OffsetDateTime softDeletedTime); + @Param("processingStatus") ProcessingStatus processingStatus, + @Param("lastUpdated") OffsetDateTime lastUpdated, + @Param("softDeletedTime") OffsetDateTime softDeletedTime); @Modifying @@ -380,10 +394,7 @@ public interface FileRepository extends JpaRepository { + " when f.deleted is not null then f.deleted " + "end " + "where f.id in (:fileIds)") - int hardDeleteFiles(@Param("fileIds") List fileIds, - @Param("processingStatus") ProcessingStatus processingStatus, - @Param("deletionTime") OffsetDateTime deletionTime); - + int hardDeleteFiles(@Param("fileIds") List fileIds, @Param("processingStatus") ProcessingStatus processingStatus, @Param("deletionTime") OffsetDateTime deletionTime); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/FileModelMapper.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/FileModelMapper.java index 8f6fbc97a..fb29fe9e0 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/FileModelMapper.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/FileModelMapper.java @@ -1,7 +1,9 @@ package com.iqser.red.service.persistence.management.v1.processor.utils; import java.util.function.BiConsumer; +import java.util.stream.Collectors; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntityComponentMappingVersionEntity; import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileErrorInfo; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel; @@ -14,6 +16,9 @@ public class FileModelMapper implements BiConsumer { fileEntity.getFileAttributes() .forEach(fa -> fileModel.getFileAttributes().put(fa.getFileAttributeId().getFileAttributeConfigId(), fa.getValue())); fileModel.setFileErrorInfo(new FileErrorInfo(fileEntity.getErrorCause(), fileEntity.getErrorQueue(), fileEntity.getErrorService(), fileEntity.getErrorTimestamp())); + fileModel.setComponentMappingVersions(fileEntity.getComponentMappingVersions() + .stream() + .collect(Collectors.toMap(FileEntityComponentMappingVersionEntity::getName, FileEntityComponentMappingVersionEntity::getVersion))); } } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml b/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml index 9cf34c503..470f13643 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml @@ -205,3 +205,5 @@ databaseChangeLog: file: db/changelog/tenant/126-add-experimental-flag-to-entity.yaml - include: file: db/changelog/tenant/127-add-component-mapping-table.yaml + - include: + file: db/changelog/tenant/128-add-component-mapping-versions-to-file.yaml diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/128-add-component-mapping-versions-to-file.yaml b/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/128-add-component-mapping-versions-to-file.yaml new file mode 100644 index 000000000..e9dbcefa2 --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/128-add-component-mapping-versions-to-file.yaml @@ -0,0 +1,31 @@ +databaseChangeLog: + - changeSet: + id: add_component_mapping_version_to_file_entity + author: kilian + changes: + - createTable: + tableName: file_entity_component_mapping_versions + columns: + - column: + name: file_entity_id + type: VARCHAR(255) + constraints: + nullable: false + - column: + name: name + type: VARCHAR(255) + constraints: + nullable: false + - column: + name: version + type: INT + constraints: + nullable: false + - addForeignKeyConstraint: + baseColumnNames: file_entity_id + baseTableName: file_entity_component_mapping_versions + constraintName: fk_component_mapping_version_entity_file + referencedColumnNames: id + referencedTableName: file + onDelete: CASCADE + onUpdate: CASCADE diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateStatsTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateStatsTest.java index 55535290d..110b05765 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateStatsTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateStatsTest.java @@ -5,6 +5,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.time.OffsetDateTime; import java.time.temporal.ChronoUnit; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -298,7 +299,7 @@ public class DossierTemplateStatsTest extends AbstractPersistenceServerServiceTe var fileId = fileTesterAndProvider.testAndProvideFileQuick(dossier, "file: " + k); if (k == 1) { - fileStatusPersistenceService.updateProcessingStatus(dossier.getId(), fileId, k, 0L, 0L, 0L, 0L, 0L, 0L, 1, 1); + fileStatusPersistenceService.updateProcessingStatus(dossier.getId(), fileId, k, 0L, 0L, 0L, 0L, 0L, 0L, 1, 1, Collections.emptyList()); reanalysisClient.excludePages(dossier.getId(), fileId, new PageExclusionRequest(List.of(new PageRange(k, k)))); } if (k == 2) { diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/AnalyzeResult.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/AnalyzeResult.java index 5ef8e7b29..f6148ec5b 100644 --- a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/AnalyzeResult.java +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/AnalyzeResult.java @@ -1,8 +1,10 @@ package com.iqser.red.service.persistence.service.v1.api.shared.model; +import java.util.List; 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 lombok.AllArgsConstructor; import lombok.Builder; @@ -37,4 +39,6 @@ public class AnalyzeResult { private Set addedFileAttributes; + private List usedComponentMappings; + } diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/component/ComponentMappingMetadata.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/component/ComponentMappingMetadata.java new file mode 100644 index 000000000..410a74afd --- /dev/null +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/component/ComponentMappingMetadata.java @@ -0,0 +1,38 @@ +package com.iqser.red.service.persistence.service.v1.api.shared.model.component; + +import java.util.List; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.experimental.FieldDefaults; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE) +public class ComponentMappingMetadata { + + String id; + + String name; + + String fileName; + + String storageId; + + Integer version; + + List columnLabels; + + Integer numberOfLines; + + String encoding; + + char delimiter; + +} diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/file/FileModel.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/file/FileModel.java index 06e45cee0..6794ba1aa 100644 --- a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/file/FileModel.java +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/file/FileModel.java @@ -73,6 +73,7 @@ public class FileModel { private OffsetDateTime fileManipulationDate; private boolean hasHighlights; private FileErrorInfo fileErrorInfo; + private Map componentMappingVersions = new HashMap<>(); public long getFileSize() { -- 2.47.2 From 46e221a3f67a42cca2eff6deef4136ff20b076e4 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Fri, 24 May 2024 16:00:43 +0200 Subject: [PATCH 12/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package --- ...ser.red.service.java-conventions.gradle.kts | 5 +++++ ...atusProcessingUpdateInternalController.java | 1 + .../FileStatusProcessingUpdateService.java | 1 + .../ReanalysisRequiredStatusService.java | 12 ++++++------ .../FileStatusPersistenceService.java | 18 ++++++++++++------ .../persistence/repository/FileRepository.java | 8 ++------ 6 files changed, 27 insertions(+), 18 deletions(-) diff --git a/buildSrc/src/main/kotlin/com.iqser.red.service.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/com.iqser.red.service.java-conventions.gradle.kts index c6b715907..22b18834b 100644 --- a/buildSrc/src/main/kotlin/com.iqser.red.service.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/com.iqser.red.service.java-conventions.gradle.kts @@ -65,6 +65,11 @@ java { } allprojects { + tasks.withType { + options { + extra["javadoc.options"] = listOf("-quiet", "-Xdoclint:none") + } + } publishing { publications { create(name) { diff --git a/persistence-service-v1/persistence-service-internal-api-impl-v1/src/main/java/com/iqser/red/service/persistence/v1/internal/api/controller/FileStatusProcessingUpdateInternalController.java b/persistence-service-v1/persistence-service-internal-api-impl-v1/src/main/java/com/iqser/red/service/persistence/v1/internal/api/controller/FileStatusProcessingUpdateInternalController.java index 7d530b5d5..846312fc8 100644 --- a/persistence-service-v1/persistence-service-internal-api-impl-v1/src/main/java/com/iqser/red/service/persistence/v1/internal/api/controller/FileStatusProcessingUpdateInternalController.java +++ b/persistence-service-v1/persistence-service-internal-api-impl-v1/src/main/java/com/iqser/red/service/persistence/v1/internal/api/controller/FileStatusProcessingUpdateInternalController.java @@ -53,6 +53,7 @@ public class FileStatusProcessingUpdateInternalController implements FileStatusP public void analysisSuccessful(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody AnalyzeResult analyzeResult) { + log.info("Received analysis result {}", analyzeResult); fileStatusProcessingUpdateService.analysisSuccessful(dossierId, fileId, analyzeResult); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java index e8a48209b..042eb402f 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java @@ -3,6 +3,7 @@ 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; diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ReanalysisRequiredStatusService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ReanalysisRequiredStatusService.java index 234b3cee3..6db7163df 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ReanalysisRequiredStatusService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ReanalysisRequiredStatusService.java @@ -180,12 +180,12 @@ public class ReanalysisRequiredStatusService { boolean needComma = false; - if (rulesVersionMatches) { + if (!rulesVersionMatches) { messageBuilder.append("ruleVersions: ").append(fileStatus.getRulesVersion()).append("/").append(dossierTemplateVersions.getOrDefault(RULES, -1L)); needComma = true; } - if (componentRulesVersionMatches) { + if (!componentRulesVersionMatches) { if (needComma) { messageBuilder.append(", "); } @@ -196,7 +196,7 @@ public class ReanalysisRequiredStatusService { needComma = true; } - if (dictionaryVersionMatches) { + if (!dictionaryVersionMatches) { if (needComma) { messageBuilder.append(", "); } @@ -204,7 +204,7 @@ public class ReanalysisRequiredStatusService { needComma = true; } - if (legalBasisVersionMatches) { + if (!legalBasisVersionMatches) { if (needComma) { messageBuilder.append(", "); } @@ -212,7 +212,7 @@ public class ReanalysisRequiredStatusService { needComma = true; } - if (dossierDictionaryVersionMatches) { + if (!dossierDictionaryVersionMatches) { if (needComma) { messageBuilder.append(", "); } @@ -220,7 +220,7 @@ public class ReanalysisRequiredStatusService { needComma = true; } - if (mappingVersionAllMatch) { + if (!mappingVersionAllMatch) { if (needComma) { messageBuilder.append(", "); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java index 28c135809..928174edc 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java @@ -130,10 +130,6 @@ public class FileStatusPersistenceService { return; } - List versionEntities = usedComponentMappings.stream() - .map(cm -> new FileEntityComponentMappingVersionEntity(cm.getName(), cm.getVersion())) - .toList(); - fileRepository.updateProcessingStatus(fileId, numberOfPages, ProcessingStatus.PROCESSED, @@ -147,8 +143,18 @@ public class FileStatusPersistenceService { OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS), OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS), analysisNumber, - calculateProcessingErrorCounter(fileId, ProcessingStatus.PROCESSED), - versionEntities); + calculateProcessingErrorCounter(fileId, ProcessingStatus.PROCESSED)); + + List versionEntities = usedComponentMappings.stream() + .map(cm -> new FileEntityComponentMappingVersionEntity(cm.getName(), cm.getVersion())) + .toList(); + + FileEntity file = fileRepository.findById(fileId) + .orElseThrow(() -> new NotFoundException("File with id " + fileId + "not found!")); + + file.setComponentMappingVersions(versionEntities); + + fileRepository.save(file); websocketService.sendAnalysisEvent(dossierId, fileId, AnalyseStatus.FINISHED, analysisNumber); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java index afe318ea4..6379bbce2 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java @@ -10,7 +10,6 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity; -import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntityComponentMappingVersionEntity; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.projection.FilePageCountsProjection; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.projection.FileProcessingStatusProjection; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.projection.FileWorkflowStatusProjection; @@ -61,8 +60,7 @@ public interface FileRepository extends JpaRepository { f.numberOfAnalyses = :analysisNumber, \ f.lastUpdated = :lastUpdated, \ f.lastProcessed = :lastProcessed, \ - f.processingErrorCounter = :processingErrorCounter, \ - f.componentMappingVersions = :componentMappingVersions \ + f.processingErrorCounter = :processingErrorCounter \ where f.id = :fileId""") void updateProcessingStatus(@Param("fileId") String fileId, @Param("numberOfPages") int numberOfPages, @@ -77,9 +75,7 @@ public interface FileRepository extends JpaRepository { @Param("lastUpdated") OffsetDateTime lastUpdated, @Param("lastProcessed") OffsetDateTime lastProcessed, @Param("analysisNumber") int analysisNumber, - @Param("processingErrorCounter") int processingErrorCounter, - @Param("componentMappingVersions") - List versionEntities); + @Param("processingErrorCounter") int processingErrorCounter); @Modifying -- 2.47.2 From 454a4d7accbf21a2cf61a588e45a5ea746af8c4c Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Fri, 24 May 2024 17:38:54 +0200 Subject: [PATCH 13/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package --- ...iqser.red.service.java-conventions.gradle.kts | 7 ++++--- .../service/ComponentMappingService.java | 5 ++--- .../FileStatusProcessingUpdateService.java | 7 +++---- .../FileStatusPersistenceService.java | 16 ++++++++++------ 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/buildSrc/src/main/kotlin/com.iqser.red.service.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/com.iqser.red.service.java-conventions.gradle.kts index 22b18834b..6e231f22f 100644 --- a/buildSrc/src/main/kotlin/com.iqser.red.service.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/com.iqser.red.service.java-conventions.gradle.kts @@ -65,11 +65,12 @@ java { } allprojects { + tasks.withType { - options { - extra["javadoc.options"] = listOf("-quiet", "-Xdoclint:none") - } + logging.captureStandardOutput(LogLevel.ERROR) + logging.captureStandardError(LogLevel.ERROR) } + publishing { publications { create(name) { diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java index 2898a2899..cfd7c3483 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ComponentMappingService.java @@ -208,10 +208,9 @@ public class ComponentMappingService { public int compare(String[] line1, String[] line2) { for (int column = 0; column < line1.length; column++) { - if (line1[column].equals(line2[column])) { - continue; + if (!line1[column].equals(line2[column])) { + return line1[column].compareTo(line2[column]); } - return line1[column].compareTo(line2[column]); } return 0; diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java index 042eb402f..dbc7ab2da 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java @@ -46,11 +46,10 @@ public class FileStatusProcessingUpdateService { case ANALYSE: case REANALYSE: default: - retryTemplate.execute(retryContext -> { - log.info("Analysis Successful for dossier {} and file {}, Attempt to update status: {}", dossierId, fileId, retryContext.getRetryCount()); + + log.info("Analysis Successful for dossier {} and file {}, Attempt to update status: {}", dossierId, fileId, 0); fileStatusService.setStatusSuccessful(dossierId, fileId, analyzeResult); - return null; - }); + if (!analyzeResult.isWasReanalyzed()) { indexingService.addToIndexingQueue(IndexMessageType.INSERT, dossier.getDossierTemplateId(), dossierId, fileId, 2); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java index 928174edc..645730eac 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java @@ -5,6 +5,7 @@ import java.time.temporal.ChronoUnit; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -29,6 +30,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemp import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.ProcessingStatus; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.WorkflowStatus; +import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -44,6 +46,7 @@ public class FileStatusPersistenceService { private final FileAttributeConfigPersistenceService fileAttributeConfigPersistenceService; private final DossierPersistenceService dossierService; private final WebsocketService websocketService; + private final EntityManager entityManager; public void createStatus(String dossierId, String fileId, String filename, String uploader, long size) { @@ -130,6 +133,7 @@ public class FileStatusPersistenceService { return; } + fileRepository.updateProcessingStatus(fileId, numberOfPages, ProcessingStatus.PROCESSED, @@ -145,16 +149,16 @@ public class FileStatusPersistenceService { analysisNumber, calculateProcessingErrorCounter(fileId, ProcessingStatus.PROCESSED)); + // must be modifiable, otherwise hibernate fails List versionEntities = usedComponentMappings.stream() .map(cm -> new FileEntityComponentMappingVersionEntity(cm.getName(), cm.getVersion())) - .toList(); + .collect(Collectors.toList()); - FileEntity file = fileRepository.findById(fileId) - .orElseThrow(() -> new NotFoundException("File with id " + fileId + "not found!")); - - file.setComponentMappingVersions(versionEntities); + FileEntity fileEntity = entityManager.find(FileEntity.class, fileId); - fileRepository.save(file); + fileEntity.setComponentMappingVersions(versionEntities); + + entityManager.merge(fileEntity); websocketService.sendAnalysisEvent(dossierId, fileId, AnalyseStatus.FINISHED, analysisNumber); } -- 2.47.2 From d93bdf133004d95dc58bd8152552eda75ea7c961 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Mon, 27 May 2024 16:42:10 +0200 Subject: [PATCH 14/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package * remove unnecessary analysis requirement calculation for deleted files * silence javadoc lint warnings (e.g. no comment) --- .../com.iqser.red.service.java-conventions.gradle.kts | 7 +++++-- .../v1/processor/entity/dossier/FileEntity.java | 2 +- .../v1/processor/service/FileStatusMapper.java | 1 + .../service/ReanalysisRequiredStatusService.java | 6 +++++- .../v1/processor/service/job/AutomaticAnalysisJob.java | 9 +++++---- .../service/v1/api/shared/model/FileStatus.java | 4 +++- 6 files changed, 20 insertions(+), 9 deletions(-) diff --git a/buildSrc/src/main/kotlin/com.iqser.red.service.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/com.iqser.red.service.java-conventions.gradle.kts index 6e231f22f..eeff524f4 100644 --- a/buildSrc/src/main/kotlin/com.iqser.red.service.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/com.iqser.red.service.java-conventions.gradle.kts @@ -67,8 +67,11 @@ java { allprojects { tasks.withType { - logging.captureStandardOutput(LogLevel.ERROR) - logging.captureStandardError(LogLevel.ERROR) + options { + this as StandardJavadocDocletOptions + addBooleanOption("Xdoclint:none", true) + addStringOption("Xmaxwarns", "1") + } } publishing { diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/FileEntity.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/FileEntity.java index f55773cd6..4ba0806b4 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/FileEntity.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/FileEntity.java @@ -199,7 +199,7 @@ public class FileEntity { @Column private OffsetDateTime errorTimestamp; - @ElementCollection(fetch = FetchType.EAGER) + @ElementCollection private List componentMappingVersions; diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusMapper.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusMapper.java index 4dae625a7..79e57a923 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusMapper.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusMapper.java @@ -66,6 +66,7 @@ public class FileStatusMapper { .lastIndexed(status.getLastIndexed()) .fileSize(status.getFileSize()) .fileErrorInfo(status.getFileErrorInfo()) + .componentMappingVersions(status.getComponentMappingVersions()) .build(); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ReanalysisRequiredStatusService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ReanalysisRequiredStatusService.java index 6db7163df..0857d3115 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ReanalysisRequiredStatusService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ReanalysisRequiredStatusService.java @@ -86,8 +86,12 @@ public class ReanalysisRequiredStatusService { fileStatus.setDossierTemplateId(dossier.getDossierTemplateId()); fileStatus.setDossierStatusId(dossier.getDossierStatusId()); + if (fileStatus.isSoftOrHardDeleted()) { + log.debug("File {} is deleted, thus analysis is not required", fileStatus.getId()); + return new AnalysisRequiredResult(false, false); + } if (dossier.getSoftDeletedTime() != null || dossier.getHardDeletedTime() != null || dossier.getArchivedTime() != null) { - log.info("Dossier {} is deleted, thus analysis is not required", fileStatus.getDossierId()); + log.debug("Dossier {} is deleted, thus analysis is not required", fileStatus.getDossierId()); return new AnalysisRequiredResult(false, false); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/job/AutomaticAnalysisJob.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/job/AutomaticAnalysisJob.java index c4794dbf6..970b1a173 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/job/AutomaticAnalysisJob.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/job/AutomaticAnalysisJob.java @@ -2,6 +2,7 @@ package com.iqser.red.service.persistence.management.v1.processor.service.job; import java.util.Comparator; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -82,17 +83,17 @@ public class AutomaticAnalysisJob implements Job { var consumerCount = redactionQueueInfo.getConsumerCount(); if (redactionQueueInfo.getMessageCount() <= consumerCount * 5) { // queue up 5 files - var allStatuses = getAllRelevantStatuses(); + List allStatuses = getAllRelevantStatuses(); allStatuses.sort(Comparator.comparing(FileModel::getLastUpdated)); - var allStatusesIterator = allStatuses.iterator(); + Iterator allStatusesIterator = allStatuses.iterator(); log.debug("[Tenant:{}] Files that require reanalysis: {}", TenantContext.getTenantId(), allStatuses.size()); - var queuedFiles = 0; + int queuedFiles = 0; while (queuedFiles < (consumerCount * 5) && allStatusesIterator.hasNext()) { - var next = allStatusesIterator.next(); + FileModel next = allStatusesIterator.next(); // in case the file doesn't have numberOfPages set, we assume an average. reanalyseFile(next); diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/FileStatus.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/FileStatus.java index 1af0c17f7..d3baf9030 100644 --- a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/FileStatus.java +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/FileStatus.java @@ -1,6 +1,7 @@ package com.iqser.red.service.persistence.service.v1.api.shared.model; import java.time.OffsetDateTime; +import java.util.Map; import java.util.Set; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileErrorInfo; @@ -147,7 +148,8 @@ public class FileStatus { private OffsetDateTime lastIndexed; @Schema(description = "The error information for the error state of the file") private FileErrorInfo fileErrorInfo; - + @Schema(description = "Shows which versions of each mapping the last analysis has been performed") + private Map componentMappingVersions; @Schema(description = "Shows if this file has been OCRed by us. Last Time of OCR.") public OffsetDateTime getLastOCRTime() { -- 2.47.2 From 44c4fa27361584fbe4de5ab4a386abf755f15896 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Mon, 27 May 2024 17:22:51 +0200 Subject: [PATCH 15/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package * remove unnecessary analysis requirement calculation for deleted files * silence javadoc lint warnings (e.g. no comment) * updated spec --- .../src/main/resources/api/openapi.yaml | 76 ++++++++++++++----- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml index 2c7b10965..e2444103e 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml @@ -297,7 +297,17 @@ paths: - 1. Dossier Templates summary: Returns a list of all existing component mappings in a dossier template description: | - Retrieves a collection of component mapping views associated with a specific dossier template. Each component mapping view includes details such as name, number of lines, delimiter, encoding and other relevant metadata. This endpoint is useful for clients needing to understand what mappings are available under a particular dossier template. + Use this endpoint to retrieve a summary of all component mappings associated with a specific DossierTemplate. + + The summary includes: + - Name + - Column Labels + - Number of lines + - Delimiter + - Encoding + of each component mapping. + + This endpoint is useful for clients to understand the available mappings under a particular DossierTemplate. parameters: - $ref: '#/components/parameters/dossierTemplateId' responses: @@ -324,12 +334,23 @@ paths: operationId: uploadMapping summary: Upload a new component mapping to a DossierTemplate. description: | - Utilize this endpoint to upload a new component mapping to a designated DossierTemplate. - The file is expected to be a comma separated file, whose first row are the labels for the columns of the file. - Further, it is expected that the data is rectangular, this means each row has the same size. - The rows in the file will be sorted the values in each column, starting with the left most and moving right recursively. - This enables much faster lookups down the line. - This means, it is highly beneficial to structure the CSV File such that the keys to query for appear in the first rows and the results to map in the last. + Use this endpoint to upload a new component mapping to a specific DossierTemplate. + + - **File Format:** CSV (comma-separated values). + - **Header:** The first row should contain column labels. + - **Data Structure:** Ensure all rows have the same number of columns (rectangular data). + - **Sorting:** Rows will be sorted by the values in each column, from left to right, for faster lookups. + - **Customization:** Users can specify the delimiter and encoding of the CSV file. + - **Usage** The component mapping file can be used in component rules to relate components to existing master data. + + **Tip:** Place keys to be queried in the first columns and results to be mapped in the last column for best performance. + + Example: + | search_value | mapped_value | + | ------------ | ------------ | + | Alice | Manager | + | Bob | Developer | + | Charlie | Analyst | tags: - 1. Dossier Templates requestBody: @@ -347,7 +368,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ComponentMappingMetadata' + $ref: '#/components/schemas/ComponentMapping' description: | Component mapping uploaded successfully and returned the component mapping metadata. "400": @@ -370,9 +391,10 @@ paths: - 1. Dossier Templates summary: Download a specific component mapping file of a specific dossier template. description: | - Utilize this endpoint to download a specific component mapping file of a designated dossier template. - The file is named the same and encoded the same as when it was uploaded, but it's sorting might have changed to provide faster lookups. - A component mapping file may be used in the component rules to relate components to existing master data. + Use this endpoint to download a specific component mapping file of a designated DossierTemplate. + + - The file retains its original name and encoding as when it was uploaded. + - The sorting of the file may have changed to enable faster lookups. parameters: - $ref: '#/components/parameters/dossierTemplateId' - $ref: '#/components/parameters/componentMappingId' @@ -404,7 +426,24 @@ paths: put: operationId: updateMapping summary: Update an existing component mapping of a DossierTemplate. - description: Updates an existing component mapping, + description: | + Use this endpoint to update an existing component mapping of a specific dossier template. + + - **File Format:** CSV (comma-separated values). + - **Header:** The first row should contain column labels. + - **Data Structure:** Ensure all rows have the same number of columns (rectangular data). + - **Sorting:** Rows will be sorted by the values in each column, from left to right, for faster lookups. + - **Customization:** Users can specify the delimiter and encoding of the CSV file. + - **Usage** The component mapping file can be used in component rules to relate components to existing master data. + + **Tip:** Place keys to be queried in the first columns and results to be mapped in the last column for best performance. + + Example: + | search_value | mapped_value | + | ------------ | ------------ | + | Alice | Manager | + | Bob | Developer | + | Charlie | Analyst | tags: - 1. Dossier Templates requestBody: @@ -422,7 +461,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ComponentMappingMetadata' + $ref: '#/components/schemas/ComponentMapping' description: | Component mapping uploaded successfully and returned the component mapping metadata. "400": @@ -1090,7 +1129,9 @@ components: - UTF_16LE - ISO-8859-1 - US-ASCII - description: "An identifier for the used encoding of the file. Only java's standard charsets are supported, default value is 'UTF-8'" + example: UTF-8 + default: UTF-8 + description: "An identifier for the used encoding of the file. Only java's standard charsets are supported." delimiter: name: delimiter required: false @@ -1100,7 +1141,8 @@ components: minLength: 1 maxLength: 1 example: ',' - description: "The delimiter used as a separator in a csv file. Default is ','." + default: ',' + description: "The delimiter used as a separator in a csv file." mappingName: name: name required: false @@ -2070,7 +2112,7 @@ components: - 8130acf6-4910-4123-827c-caacd8111402 dossierStatusId: b8280985-f558-43c0-852a-a3fa86803548 - ComponentMappingMetadata: + ComponentMapping: description: | The `ComponentMappingMetadata` object represents the metadata of a component mapping csv file. These CSV files may be used in the component rules to relate components to an existing knowledge base. type: object @@ -2168,7 +2210,7 @@ components: A list of component mapping metadata associated with this dossier template. type: array items: - $ref: "#/components/schemas/ComponentMappingMetadata" + $ref: "#/components/schemas/ComponentMapping" required: - dossierTemplateId - ComponentMappingSummary -- 2.47.2 From 2b6636d68a76f847b2c61e4a820bf22e8ac468c5 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Mon, 27 May 2024 17:38:33 +0200 Subject: [PATCH 16/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package * remove unnecessary analysis requirement calculation for deleted files * silence javadoc lint warnings (e.g. no comment) --- .../tests/DossierTemplateCloneServiceTest.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateCloneServiceTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateCloneServiceTest.java index fd9846deb..d783fd0fd 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateCloneServiceTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateCloneServiceTest.java @@ -28,6 +28,7 @@ import com.iqser.red.service.persistence.management.v1.processor.entity.configur import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.RuleSetEntity; import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity; import com.iqser.red.service.persistence.management.v1.processor.service.ColorsService; +import com.iqser.red.service.persistence.management.v1.processor.service.ComponentMappingService; import com.iqser.red.service.persistence.management.v1.processor.service.DossierTemplateCloneService; import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService; import com.iqser.red.service.persistence.management.v1.processor.service.WatermarkService; @@ -91,6 +92,8 @@ public class DossierTemplateCloneServiceTest { private FileManagementStorageService fileManagementStorageService; @MockBean private TypeRepository typeRepository; + @MockBean + private ComponentMappingService componentMappingService; private DossierTemplateCloneService dossierTemplateCloneService; @@ -124,7 +127,8 @@ public class DossierTemplateCloneServiceTest { storageService, dossierStatusPersistenceService, watermarkService, - fileManagementStorageService); + fileManagementStorageService, + componentMappingService); dummyTemplate = new DossierTemplateEntity(); setNonDefaultValues(dummyTemplate); @@ -169,7 +173,7 @@ public class DossierTemplateCloneServiceTest { if (getterMethod != null) { value = getterMethod.invoke(obj); - } else if (alternativeGetterMethod != null){ + } else if (alternativeGetterMethod != null) { value = alternativeGetterMethod.invoke(obj); } else { return; -- 2.47.2 From d9fb4259d2c4704c3cad5b68324fc3c9f6153753 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Mon, 27 May 2024 17:40:38 +0200 Subject: [PATCH 17/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package * remove unnecessary analysis requirement calculation for deleted files * silence javadoc lint warnings (e.g. no comment) --- .../src/main/resources/api/openapi.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml index e2444103e..7255e7ade 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml @@ -2111,7 +2111,6 @@ components: - daadea5f-917b-482a-b7d2-e65afe8f80ca - 8130acf6-4910-4123-827c-caacd8111402 dossierStatusId: b8280985-f558-43c0-852a-a3fa86803548 - ComponentMapping: description: | The `ComponentMappingMetadata` object represents the metadata of a component mapping csv file. These CSV files may be used in the component rules to relate components to an existing knowledge base. -- 2.47.2 From 18fce9f38c163bbb2243ca09ea97c6e4c1498c99 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Mon, 27 May 2024 17:50:48 +0200 Subject: [PATCH 18/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package * remove unnecessary analysis requirement calculation for deleted files * silence javadoc lint warnings (e.g. no comment) --- .../src/main/resources/api/openapi.yaml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml index 7255e7ade..9651612f7 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml @@ -299,14 +299,7 @@ paths: description: | Use this endpoint to retrieve a summary of all component mappings associated with a specific DossierTemplate. - The summary includes: - - Name - - Column Labels - - Number of lines - - Delimiter - - Encoding - of each component mapping. - + The summary consists of the stored metadata of a component mapping file. This endpoint is useful for clients to understand the available mappings under a particular DossierTemplate. parameters: - $ref: '#/components/parameters/dossierTemplateId' -- 2.47.2 From 5f007b8da331d802321eceec821759fafabcc405 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Mon, 27 May 2024 20:06:04 +0200 Subject: [PATCH 19/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package * remove unnecessary analysis requirement calculation for deleted files * silence javadoc lint warnings (e.g. no comment) --- .../FileStatusProcessingUpdateService.java | 2 -- .../FileStatusPersistenceService.java | 17 +++++++++++++---- .../service/FileTesterAndProvider.java | 2 ++ .../tests/DossierTemplateImportExportTest.java | 17 ----------------- 4 files changed, 15 insertions(+), 23 deletions(-) diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java index dbc7ab2da..c4bd68750 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java @@ -43,8 +43,6 @@ public class FileStatusProcessingUpdateService { case SURROUNDING_TEXT_ANALYSIS: break; - case ANALYSE: - case REANALYSE: default: log.info("Analysis Successful for dossier {} and file {}, Attempt to update status: {}", dossierId, fileId, 0); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java index 645730eac..f9e286140 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java @@ -2,6 +2,7 @@ package com.iqser.red.service.persistence.management.v1.processor.service.persis import java.time.OffsetDateTime; import java.time.temporal.ChronoUnit; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -133,7 +134,6 @@ public class FileStatusPersistenceService { return; } - fileRepository.updateProcessingStatus(fileId, numberOfPages, ProcessingStatus.PROCESSED, @@ -150,9 +150,7 @@ public class FileStatusPersistenceService { calculateProcessingErrorCounter(fileId, ProcessingStatus.PROCESSED)); // must be modifiable, otherwise hibernate fails - List versionEntities = usedComponentMappings.stream() - .map(cm -> new FileEntityComponentMappingVersionEntity(cm.getName(), cm.getVersion())) - .collect(Collectors.toList()); + List versionEntities = getFileEntityComponentMappingVersionEntities(usedComponentMappings); FileEntity fileEntity = entityManager.find(FileEntity.class, fileId); @@ -164,6 +162,17 @@ public class FileStatusPersistenceService { } + private static List getFileEntityComponentMappingVersionEntities(List usedComponentMappings) { + + if (usedComponentMappings == null) { + return Collections.emptyList(); + } + return usedComponentMappings.stream() + .map(cm -> new FileEntityComponentMappingVersionEntity(cm.getName(), cm.getVersion())) + .collect(Collectors.toList()); + } + + @Transactional public void updateFlags(String fileId, boolean hasRedactions, boolean hasHints, boolean hasImages, boolean hasSuggestions, boolean hasComments, boolean hasUpdates) { diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/service/FileTesterAndProvider.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/service/FileTesterAndProvider.java index 92332f560..175e5ee92 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/service/FileTesterAndProvider.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/service/FileTesterAndProvider.java @@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; +import java.util.Collections; import java.util.List; import org.apache.commons.codec.binary.Base64; @@ -115,6 +116,7 @@ public class FileTesterAndProvider { result.setDossierDictionaryVersion(1); result.setAnalysisNumber(1); result.setAnalysisVersion(1); + result.setUsedComponentMappings(Collections.emptyList()); fileStatusService.setStatusSuccessful(dossierId, fileId, result); fileStatusService.setStatusProcessed(fileId); } diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateImportExportTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateImportExportTest.java index 0178bee6c..cc4f7649e 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateImportExportTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateImportExportTest.java @@ -16,16 +16,12 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import java.util.Objects; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; @@ -148,19 +144,6 @@ public class DossierTemplateImportExportTest extends AbstractPersistenceServerSe } - private static boolean areDirectoriesIdentical(Path dir1, Path dir2) throws IOException, NoSuchAlgorithmException { - - if (!Files.isDirectory(dir1) || !Files.isDirectory(dir2)) { - throw new IllegalArgumentException("Both paths should be directories."); - } - - Map dir1Contents = getDirectoryContents(dir1); - Map dir2Contents = getDirectoryContents(dir2); - - return dir1Contents.equals(dir2Contents); - } - - private static Map getDirectoryContents(Path dir) throws IOException, NoSuchAlgorithmException { Map contents = new HashMap<>(); -- 2.47.2 From 43e581cbc6942b9ba67ec471090fa7d411d5223c Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Tue, 28 May 2024 09:17:15 +0200 Subject: [PATCH 20/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package * remove unnecessary analysis requirement calculation for deleted files * silence javadoc lint warnings (e.g. no comment) --- .../FileStatusProcessingUpdateService.java | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java index c4bd68750..6b5e81fb3 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/FileStatusProcessingUpdateService.java @@ -40,14 +40,12 @@ public class FileStatusProcessingUpdateService { var dossier = dossierPersistenceService.getAndValidateDossier(dossierId); switch (analyzeResult.getMessageType()) { - case SURROUNDING_TEXT_ANALYSIS: break; default: - log.info("Analysis Successful for dossier {} and file {}, Attempt to update status: {}", dossierId, fileId, 0); - fileStatusService.setStatusSuccessful(dossierId, fileId, analyzeResult); - + log.info("Analysis Successful for dossier {} and file {}, Attempt to update status: {}", dossierId, fileId, 0); + fileStatusService.setStatusSuccessful(dossierId, fileId, analyzeResult); if (!analyzeResult.isWasReanalyzed()) { indexingService.addToIndexingQueue(IndexMessageType.INSERT, dossier.getDossierTemplateId(), dossierId, fileId, 2); @@ -77,16 +75,16 @@ public class FileStatusProcessingUpdateService { retryTemplate.execute(retryContext -> { log.info("Preprocessing dossier {} and file {}, Attempt to update status: {}", dossierId, fileId, retryContext.getRetryCount()); fileStatusService.setStatusPreProcessing(fileId, - fileEntity.getProcessingStatus().equals(ProcessingStatus.PRE_PROCESSING) ? fileEntity.getProcessingErrorCounter() + 1 : 0); + fileEntity.getProcessingStatus().equals(ProcessingStatus.PRE_PROCESSING) ? fileEntity.getProcessingErrorCounter() + 1 : 0); return null; }); var updatedFileEntity = fileStatusPersistenceService.getStatus(fileId); if (updatedFileEntity.getProcessingErrorCounter() > settings.getMaxErrorRetries()) { throw new ConflictException(String.format("Max Processing Retries exhausted for dossier %s and file %s with errorCount: %s", - dossierId, - fileId, - updatedFileEntity.getProcessingErrorCounter())); + dossierId, + fileId, + updatedFileEntity.getProcessingErrorCounter())); } } @@ -104,10 +102,10 @@ public class FileStatusProcessingUpdateService { retryTemplate.execute(retryContext -> { log.warn("Retrying {} time to set ERROR status for file {} in dossier {} with reason {} ", - retryContext.getRetryCount(), - fileId, - dossierId, - fileErrorInfo != null ? fileErrorInfo.getCause() : null); + retryContext.getRetryCount(), + fileId, + dossierId, + fileErrorInfo != null ? fileErrorInfo.getCause() : null); fileStatusService.setStatusError(dossierId, fileId, fileErrorInfo); return null; }); @@ -121,7 +119,7 @@ public class FileStatusProcessingUpdateService { ocrFailed(dossierId, fileId, fileErrorInfo); } else { fileStatusService.setStatusOcrProcessing(fileId, - fileEntity.getProcessingStatus().equals(ProcessingStatus.OCR_PROCESSING) ? fileEntity.getProcessingErrorCounter() + 1 : 0); + fileEntity.getProcessingStatus().equals(ProcessingStatus.OCR_PROCESSING) ? fileEntity.getProcessingErrorCounter() + 1 : 0); fileStatusService.addToOcrQueue(dossierId, fileId, 2); } } @@ -134,7 +132,7 @@ public class FileStatusProcessingUpdateService { retryTemplate.execute(retryContext -> { log.info("Ocr processing dossier {} and file {}, Attempt to update status: {}", fileEntity.getDossierId(), fileId, retryContext.getRetryCount()); fileStatusService.setStatusOcrProcessing(fileId, - fileEntity.getProcessingStatus().equals(ProcessingStatus.OCR_PROCESSING) ? fileEntity.getProcessingErrorCounter() + 1 : 0); + fileEntity.getProcessingStatus().equals(ProcessingStatus.OCR_PROCESSING) ? fileEntity.getProcessingErrorCounter() + 1 : 0); return null; }); } @@ -147,9 +145,9 @@ public class FileStatusProcessingUpdateService { var updatedFileEntity = fileStatusPersistenceService.getStatus(fileId); if (updatedFileEntity.getProcessingErrorCounter() > settings.getMaxErrorRetries()) { throw new ConflictException(String.format("Max Ocr Retries exhausted for dossier %s and file %s with errorCount: %s", - updatedFileEntity.getDossierId(), - fileId, - updatedFileEntity.getProcessingErrorCounter())); + updatedFileEntity.getDossierId(), + fileId, + updatedFileEntity.getProcessingErrorCounter())); } } -- 2.47.2 From 7010e4b1f935f69f489c1c89ba2e14e01fd52067 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Tue, 28 May 2024 09:22:59 +0200 Subject: [PATCH 21/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package * remove unnecessary analysis requirement calculation for deleted files * silence javadoc lint warnings (e.g. no comment) --- .../v1/processor/utils/FileModelMapper.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/FileModelMapper.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/FileModelMapper.java index fb29fe9e0..c1d89f926 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/FileModelMapper.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/FileModelMapper.java @@ -1,5 +1,8 @@ package com.iqser.red.service.persistence.management.v1.processor.utils; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; import java.util.function.BiConsumer; import java.util.stream.Collectors; @@ -16,9 +19,18 @@ public class FileModelMapper implements BiConsumer { fileEntity.getFileAttributes() .forEach(fa -> fileModel.getFileAttributes().put(fa.getFileAttributeId().getFileAttributeConfigId(), fa.getValue())); fileModel.setFileErrorInfo(new FileErrorInfo(fileEntity.getErrorCause(), fileEntity.getErrorQueue(), fileEntity.getErrorService(), fileEntity.getErrorTimestamp())); - fileModel.setComponentMappingVersions(fileEntity.getComponentMappingVersions() - .stream() - .collect(Collectors.toMap(FileEntityComponentMappingVersionEntity::getName, FileEntityComponentMappingVersionEntity::getVersion))); + fileModel.setComponentMappingVersions(getComponentMappingVersions(fileEntity)); + } + + + private static Map getComponentMappingVersions(FileEntity fileEntity) { + + if (Objects.isNull(fileEntity.getComponentMappingVersions())) { + return Collections.emptyMap(); + } + return fileEntity.getComponentMappingVersions() + .stream() + .collect(Collectors.toMap(FileEntityComponentMappingVersionEntity::getName, FileEntityComponentMappingVersionEntity::getVersion)); } } -- 2.47.2 From d3e1aa3ba021ea52b562194b8383c97d998357cb Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Tue, 28 May 2024 09:26:18 +0200 Subject: [PATCH 22/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package * remove unnecessary analysis requirement calculation for deleted files * silence javadoc lint warnings (e.g. no comment) --- .../FileStatusPersistenceService.java | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java index f9e286140..16b45a1c9 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java @@ -2,6 +2,7 @@ package com.iqser.red.service.persistence.management.v1.processor.service.persis import java.time.OffsetDateTime; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -67,6 +68,7 @@ public class FileStatusPersistenceService { file.setFileManipulationDate(now); file.setProcessingErrorCounter(0); file.setFileSize(size); + file.setComponentMappingVersions(new ArrayList<>()); fileRepository.save(file); } @@ -97,21 +99,15 @@ public class FileStatusPersistenceService { private int calculateProcessingErrorCounter(String fileId, ProcessingStatus processingStatus) { - switch (processingStatus) { - case ERROR: - return fileRepository.findById(fileId) - .map(FileEntity::getProcessingErrorCounter) - .orElse(0) + 1; - - case PROCESSED: - case REPROCESS: - return 0; - - default: - return fileRepository.findById(fileId) - .map(FileEntity::getProcessingErrorCounter) - .orElse(0); - } + return switch (processingStatus) { + case ERROR -> fileRepository.findById(fileId) + .map(FileEntity::getProcessingErrorCounter) + .orElse(0) + 1; + case PROCESSED, REPROCESS -> 0; + default -> fileRepository.findById(fileId) + .map(FileEntity::getProcessingErrorCounter) + .orElse(0); + }; } -- 2.47.2 From e31cd98a75b17e1d26775c9c7eb3f5b76804d81b Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Tue, 28 May 2024 16:24:58 +0200 Subject: [PATCH 23/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package * remove unnecessary analysis requirement calculation for deleted files * silence javadoc lint warnings (e.g. no comment) --- .../tests/CustomPermissionTest.java | 4 - .../DossierTemplateImportExportTest.java | 7 +- .../AbstractPersistenceServerServiceTest.java | 94 +++++++++++-------- 3 files changed, 58 insertions(+), 47 deletions(-) diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/CustomPermissionTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/CustomPermissionTest.java index 38f5d7d97..50bacb882 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/CustomPermissionTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/CustomPermissionTest.java @@ -10,10 +10,6 @@ import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import com.iqser.red.service.peristence.v1.server.integration.client.CustomPermissionClient; import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest; diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateImportExportTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateImportExportTest.java index cc4f7649e..4ca22ffc9 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateImportExportTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/DossierTemplateImportExportTest.java @@ -44,7 +44,7 @@ import lombok.SneakyThrows; public class DossierTemplateImportExportTest extends AbstractPersistenceServerServiceTest { public static final String IMPORTED_TEMPLATE_NAME = "imported-template"; - public static final String USER_ID = "Deine Mutter"; + public static final String USER_ID = "NutzerIdentifikationsNummer"; public static final String AFTER = "after"; public static final String BEFORE = "before"; @@ -70,9 +70,10 @@ public class DossierTemplateImportExportTest extends AbstractPersistenceServerSe @Test @SneakyThrows +// @DirtiesContext public void testImportExportRoundtrip() { - TenantContext.setTenantId("redaction"); + TenantContext.setTenantId(TENANT_1); Path outDir = Files.createTempDirectory(IMPORTED_TEMPLATE_NAME); Path dossierTemplateExportArchive = new ClassPathResource("files/dossiertemplates/DossierTemplate.zip").getFile().toPath(); @@ -159,7 +160,7 @@ public class DossierTemplateImportExportTest extends AbstractPersistenceServerSe @Override - public FileVisitResult preVisitDirectory(Path subDir, BasicFileAttributes attrs) throws IOException { + public FileVisitResult preVisitDirectory(Path subDir, BasicFileAttributes attrs) { contents.put(dir.relativize(subDir), new byte[0]); return FileVisitResult.CONTINUE; diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java index 8f9d9a87b..9b89563b6 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java @@ -9,14 +9,17 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.CommentRepository; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.annotationentity.ResizeRedactionRepository; + import org.assertj.core.util.Lists; import org.bson.BsonArray; import org.bson.BsonDocument; import org.bson.BsonString; +import org.checkerframework.checker.units.qual.A; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; @@ -38,6 +41,7 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.StatementCallback; import org.springframework.jdbc.datasource.SingleConnectionDataSource; @@ -50,8 +54,10 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.support.TestPropertySourceUtils; import com.iqser.red.commons.jackson.ObjectMapperFactory; import com.iqser.red.service.peristence.v1.server.Application; @@ -138,6 +144,9 @@ import lombok.extern.slf4j.Slf4j; @SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, properties = "spring-hibernate-query-utils.n-plus-one-queries-detection.error-level=INFO") public abstract class AbstractPersistenceServerServiceTest { + public static final String TENANT_1 = "redaction"; + public static final String TENANT_2 = "redaction2"; + @MockBean protected Scheduler scheduler; @MockBean @@ -273,7 +282,7 @@ public abstract class AbstractPersistenceServerServiceTest { @BeforeEach public void setupTenantContext() { - TenantContext.setTenantId("redaction"); + TenantContext.setTenantId(TENANT_1); } @@ -289,7 +298,7 @@ public abstract class AbstractPersistenceServerServiceTest { createTenants(); - TenantContext.setTenantId("redaction"); + TenantContext.setTenantId(TENANT_1); ApplicationConfig appConfig = ApplicationConfig.builder().downloadCleanupDownloadFilesHours(8).downloadCleanupNotDownloadFilesHours(72).softDeleteCleanupTime(96).build(); applicationConfigService.saveApplicationConfiguration(MagicConverter.convert(appConfig, ApplicationConfigurationEntity.class)); @@ -346,9 +355,9 @@ public abstract class AbstractPersistenceServerServiceTest { if (tenantsClient.getTenants() == null || tenantsClient.getTenants().isEmpty()) { var redactionTenant = new TenantResponse(); - redactionTenant.setTenantId("redaction"); - redactionTenant.setGuid("redaction"); - redactionTenant.setDisplayName("redaction"); + redactionTenant.setTenantId(TENANT_1); + redactionTenant.setGuid(TENANT_1); + redactionTenant.setDisplayName(TENANT_1); redactionTenant.setAuthDetails(new AuthDetails()); redactionTenant.setDatabaseConnection(DatabaseConnection.builder() .driver("postgresql") @@ -388,9 +397,9 @@ public abstract class AbstractPersistenceServerServiceTest { .build()); var redactionTenant2 = new TenantResponse(); - redactionTenant2.setTenantId("redaction2"); - redactionTenant2.setGuid("redaction2"); - redactionTenant2.setDisplayName("redaction2"); + redactionTenant2.setTenantId(TENANT_2); + redactionTenant2.setGuid(TENANT_2); + redactionTenant2.setDisplayName(TENANT_2); redactionTenant2.setAuthDetails(new AuthDetails()); redactionTenant2.setDatabaseConnection(DatabaseConnection.builder() .driver("postgresql") @@ -425,19 +434,19 @@ public abstract class AbstractPersistenceServerServiceTest { .username(MONGO_USERNAME) .password(encryptionDecryptionService.encrypt(MONGO_PASSWORD)) .address(mongoDbContainer.getHost() + ":" + mongoDbContainer.getFirstMappedPort()) - .database("redaction2") + .database(TENANT_2) .options("") .build()); - when(tenantsClient.getTenant("redaction")).thenReturn(redactionTenant); - when(tenantsClient.getTenant("redaction2")).thenReturn(redactionTenant2); + when(tenantsClient.getTenant(TENANT_1)).thenReturn(redactionTenant); + when(tenantsClient.getTenant(TENANT_2)).thenReturn(redactionTenant2); when(tenantsClient.getTenants()).thenReturn(List.of(redactionTenant, redactionTenant2)); try { - tenantCreatedListener.createTenant(new TenantCreatedEvent("redaction")); - tenantCreatedListener.createTenant(new TenantCreatedEvent("redaction2")); - mongoTenantCreatedListener.createTenant(new MongoTenantCreatedEvent("redaction")); - mongoTenantCreatedListener.createTenant(new MongoTenantCreatedEvent("redaction2")); + tenantCreatedListener.createTenant(new TenantCreatedEvent(TENANT_1)); + tenantCreatedListener.createTenant(new TenantCreatedEvent(TENANT_2)); + mongoTenantCreatedListener.createTenant(new MongoTenantCreatedEvent(TENANT_1)); + mongoTenantCreatedListener.createTenant(new MongoTenantCreatedEvent(TENANT_2)); } catch (Exception e) { e.printStackTrace(); @@ -515,7 +524,6 @@ public abstract class AbstractPersistenceServerServiceTest { notificationPreferencesRepository.deleteAll(); indexInformationRepository.deleteAll(); applicationConfigRepository.deleteAll(); - entityLogEntryDocumentRepository.deleteAll(); entityLogDocumentRepository.deleteAll(); @@ -526,8 +534,15 @@ public abstract class AbstractPersistenceServerServiceTest { @Slf4j static class Initializer implements ApplicationContextInitializer { + static AtomicInteger UniquePortFactory = new AtomicInteger(28081); + + public void initialize(ConfigurableApplicationContext configurableApplicationContext) { + ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment(); + int port = UniquePortFactory.getAndIncrement(); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(environment, "server.port=" + port); + var postgreSQLContainerMaster = SpringPostgreSQLTestContainer.getInstance().withDatabaseName("integration-tests-db-master").withUsername("sa").withPassword("sa"); postgreSQLContainerMaster.start(); @@ -537,8 +552,8 @@ public abstract class AbstractPersistenceServerServiceTest { var mongoInstance = MongoDBTestContainer.getInstance(); mongoInstance.start(); - createMongoDBDatabase(mongoInstance, "redaction"); - createMongoDBDatabase(mongoInstance, "redaction2"); + createMongoDBDatabase(mongoInstance, TENANT_1); + createMongoDBDatabase(mongoInstance, TENANT_2); log.info("Hosts are - Redis: {}, Postgres: {}, MongoDB: {}", redisContainer.getHost(), postgreSQLContainerMaster.getHost(), mongoInstance.getHost()); @@ -553,37 +568,36 @@ public abstract class AbstractPersistenceServerServiceTest { } - } + private static void createMongoDBDatabase(MongoDBTestContainer mongoDBTestContainer, String databaseName) { - private static void createMongoDBDatabase(MongoDBTestContainer mongoDBTestContainer, String databaseName) { + try (MongoClient mongoClient = MongoClients.create(String.format("mongodb://%s:%s@%s:%s/", + MONGO_USERNAME, + MONGO_PASSWORD, + mongoDBTestContainer.getHost(), + mongoDBTestContainer.getFirstMappedPort()))) { + MongoDatabase database = mongoClient.getDatabase(databaseName); + BsonDocument createUserCommand = new BsonDocument(); + createUserCommand.append("createUser", new BsonString(MONGO_USERNAME)); + createUserCommand.append("pwd", new BsonString(MONGO_PASSWORD)); + BsonArray roles = new BsonArray(); + roles.add(new BsonDocument("role", new BsonString("dbOwner")).append("db", new BsonString(databaseName))); + createUserCommand.append("roles", roles); - try (MongoClient mongoClient = MongoClients.create(String.format("mongodb://%s:%s@%s:%s/", - MONGO_USERNAME, - MONGO_PASSWORD, - mongoDBTestContainer.getHost(), - mongoDBTestContainer.getFirstMappedPort()))) { - MongoDatabase database = mongoClient.getDatabase(databaseName); - BsonDocument createUserCommand = new BsonDocument(); - createUserCommand.append("createUser", new BsonString(MONGO_USERNAME)); - createUserCommand.append("pwd", new BsonString(MONGO_PASSWORD)); - BsonArray roles = new BsonArray(); - roles.add(new BsonDocument("role", new BsonString("dbOwner")).append("db", new BsonString(databaseName))); - createUserCommand.append("roles", roles); + try { + database.runCommand(createUserCommand); + } catch (MongoCommandException mongoCommandException) { + // ignore user already exists + if (mongoCommandException.getErrorCode() != 51003) { + throw mongoCommandException; + } - try { - database.runCommand(createUserCommand); - } catch (MongoCommandException mongoCommandException) { - // ignore user already exists - if (mongoCommandException.getErrorCode() != 51003) { - throw mongoCommandException; } } - } - } + } @Configuration @EnableWebSecurity -- 2.47.2 From 6b2e164d9c0358295486b7d652030aee4f2e3268 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Tue, 28 May 2024 16:33:06 +0200 Subject: [PATCH 24/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package * remove unnecessary analysis requirement calculation for deleted files * silence javadoc lint warnings (e.g. no comment) --- .../integration/utils/AbstractPersistenceServerServiceTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java index 9b89563b6..ce27b8a53 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java @@ -539,6 +539,7 @@ public abstract class AbstractPersistenceServerServiceTest { public void initialize(ConfigurableApplicationContext configurableApplicationContext) { + // any time the WebEnvironment is re-instantiated use a new port, as it sometimes leads to PortBindExceptions ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment(); int port = UniquePortFactory.getAndIncrement(); TestPropertySourceUtils.addInlinedPropertiesToEnvironment(environment, "server.port=" + port); -- 2.47.2 From 040b5215237519001bf795634afd23770c666935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominique=20Eifl=C3=A4nder?= Date: Mon, 27 May 2024 10:44:22 +0200 Subject: [PATCH 25/26] RED-9114 - RedactManager API Specs for BASF (Cherry-picked merge-commit 0ecd23304 from release/2.349.x) --- .../api/{openapi.yaml => documine.yaml} | 991 +++++- .../src/main/resources/api/redactmanager.yaml | 3010 +++++++++++++++++ 2 files changed, 3891 insertions(+), 110 deletions(-) rename persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/{openapi.yaml => documine.yaml} (78%) create mode 100644 persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/redactmanager.yaml diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/documine.yaml similarity index 78% rename from persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml rename to persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/documine.yaml index 9651612f7..769a0363c 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/openapi.yaml +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/documine.yaml @@ -6,15 +6,14 @@ info: The DocuMine API provides a comprehensive solution for managing resources such as dossiers and their associated files. Users can also retrieve components of files that have been processed and extracted by the system. - Given that DocuMine supports multi-tenancy, it is essential to include the 'X-Tenant-ID' header with every request. - This tenant ID is referred to as the "Workspace ID" within the application. - - All endpoints are secured using OAuth2, with the "authorizationCode" being the supported authorization flow. + All endpoints are secured using OAuth2, with the "authorizationCode" being the general supported authorization flow. Obtain a JWT token for authentication and send it in the 'Authorization' header with the format `Bearer {JWT_TOKEN}`. + Please also note that the `authorizationUrl` and `tokenUrl` in this specification contain `{workspaceId}` placeholders that + must be replaced by your respective DocuMine workspace identifier. + Example Headers: ```properties - X-Tenant-ID: my-workspace Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI... ``` license: @@ -40,7 +39,11 @@ tags: description: Operations for managing files within a dossier. - name: 4. Components description: Operations related to components of a file within a dossier. - - name: 5. License + - name: 5. Downloads + description: Operations related to download packages. + - name: 6. Users + description: Operations related to users. + - name: 7. License description: Operations related to license information and usage metrics. paths: /api/dossier-templates: @@ -270,6 +273,69 @@ paths: $ref: '#/components/responses/429' "500": $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossier-status-definitions: + get: + summary: Returns the list of all existing dossier status definitions + tags: + - 1. Dossier Templates + description: | + Retrieves a collection of dossier status definitions associated with a specific dossier template. Each dossier + status definition includes details such as the status name, description, and other relevant metadata. This endpoint + is useful for clients needing to display or set the status of a dossier associated with a specific dossier template. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + responses: + "200": + content: + '*/*': + schema: + $ref: '#/components/schemas/DossierStatusDefinitionList' + description: | + Successfully returned the dossier status definitions for the specified dossier template. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossier-attribute-definitions: + get: + summary: Returns the list of all existing dossier attribute definitions + tags: + - 1. Dossier Templates + description: | + Retrieves a collection of dossier attribute definitions associated with a specific dossier template. Each dossier + attribute definition includes details such as attribute type, name, and other relevant metadata. This endpoint + is useful for clients needing to understand what attributes are expected or allowed for dossiers associated with + a specific dossier template. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + responses: + "200": + content: + '*/*': + schema: + $ref: '#/components/schemas/DossierAttributeDefinitionList' + description: | + Successfully returned the dossier attribute definitions for the specified dossier template. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' /api/dossier-templates/{dossierTemplateId}/file-attribute-definitions: get: summary: Returns the list of all existing file attribute definitions @@ -278,8 +344,8 @@ paths: description: | Retrieves a collection of file attribute definitions associated with a specific dossier template. Each file attribute definition includes details such as attribute type, name, and other relevant metadata. This endpoint - is useful for clients needing to understand what attributes are expected or allowed for files under a particular - dossier template. + is useful for clients needing to understand what attributes are expected or allowed for files associated with + a specific dossier template. parameters: - $ref: '#/components/parameters/dossierTemplateId' responses: @@ -290,6 +356,18 @@ paths: $ref: '#/components/schemas/FileAttributeDefinitionList' description: | Successfully returned the file attribute definitions for the specified dossier template. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' /api/dossier-templates/{dossierTemplateId}/component-mappings: get: operationId: listAllMappings @@ -653,6 +731,83 @@ paths: $ref: '#/components/responses/429' "500": $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/attributes: + post: + operationId: setDossierAttributes + tags: + - 2. Dossiers + summary: Update or set attributes for a specific dossier. + description: | + This endpoint facilitates the updating or setting of specific dossier attributes for a given dossier. + Ensure you provide the necessary dossier attributes within the request body. + + Use this route to maintain or enhance dossier metadata and properties. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DossierAttributes' + required: true + responses: + "204": + description: | + Dossier attributes successfully updated. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/create-download: + post: + operationId: prepareDossierDownload + tags: + - 2. Dossiers + summary: Initiate the creation of a download package for all files of a dossier. + description: | + To download files and reports, a download package needs to be prepared. This endpoint + facilitates to define the content of the download package and to start the creation of it for all + files of the dossier. The response of this endpoint contains an identifier for the download that + is needed to query the status until the download package is prepared, and finally to actually + download the file once it is available. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DownloadRequest' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/DownloadStatus' + description: | + Successfully started the creation of the download package for all files of the requested dossier. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files: get: operationId: getDossierStatus @@ -773,10 +928,14 @@ paths: File deletion successful. This confirms the absence of the specified file, irrespective of its previous existence. "400": $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' "403": $ref: '#/components/responses/403' "404": $ref: '#/components/responses/404-file' + "429": + $ref: '#/components/responses/429' "500": $ref: '#/components/responses/500' get: @@ -803,10 +962,56 @@ paths: Successfully retrieved the status of the requested file. "400": $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' "403": $ref: '#/components/responses/403' "404": $ref: '#/components/responses/404-file' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files/{fileId}/create-download: + post: + operationId: prepareFileDownload + tags: + - 3. Files + summary: Initiate the creation of a download package for a single file of a dossier. + description: | + To download files and reports, a download package needs to be prepared. This endpoint + facilitates to define the content of the download package and to start the creation of it for the + file. The response of this endpoint contains an identifier for the download that is needed to + query the status until the download package is prepared for download, and finally to actually + download the file once it is available. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + - $ref: '#/components/parameters/fileId' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DownloadRequest' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/DownloadStatus' + description: | + Successfully started the creation of the download package for the requested file. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-file' + "429": + $ref: '#/components/responses/429' "500": $ref: '#/components/responses/500' /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files/bulk/delete: @@ -839,10 +1044,55 @@ paths: Bulk file deletion successful. "400": $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' "403": $ref: '#/components/responses/403' "404": $ref: '#/components/responses/404-dossier' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files/bulk/create-download: + post: + operationId: prepareBulkDownload + tags: + - 3. Files + summary: Initiate the creation of a download package for specific files within a dossier. + description: | + To download files and reports, a download package needs to be prepared. This endpoint + facilitates to define the content of the download package and to start the creation of it for + multiple files of a dossier. The response of this endpoint contains an identifier for the download + that is needed to query the status until the download package is prepared for download, and finally + to actually download the file once it is available. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/BulkDownloadRequest' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/DownloadStatus' + description: | + Successfully started the creation of the download package for the requested files. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier' + "429": + $ref: '#/components/responses/429' "500": $ref: '#/components/responses/500' /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files/{fileId}/attributes: @@ -867,15 +1117,19 @@ paths: $ref: '#/components/schemas/FileAttributes' required: true responses: - "200": + "204": description: | File attributes successfully updated. "400": $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' "403": $ref: '#/components/responses/403' "404": $ref: '#/components/responses/404-file' + "429": + $ref: '#/components/responses/429' "500": $ref: '#/components/responses/500' /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files/{fileId}/components: @@ -908,10 +1162,14 @@ paths: Successfully retrieved file components. "400": $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' "403": $ref: '#/components/responses/403' "404": $ref: '#/components/responses/404-file' + "429": + $ref: '#/components/responses/429' "500": $ref: '#/components/responses/500' /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files/bulk/get-components: @@ -942,19 +1200,201 @@ paths: $ref: '#/components/schemas/FileComponentsList' description: | Successfully fetched components for all files in the dossier. - "404": - $ref: '#/components/responses/404-dossier' "400": $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' "403": $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/download: + get: + operationId: getDownloadStatusList + tags: + - 5. Downloads + summary: Get the list of downloads for the current user + description: | + This endpoint facilitates to retrieve the list of downloads for the current user. The response contains + status objects that represent each download and provide information on whether the download is still + in preparation, is already available or whether an error occurred when the download package was created. + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/DownloadStatusList' + description: List of all downloads of the current users successfully retrieved. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/downloads/{downloadId}: + get: + operationId: getDownloadStatus + tags: + - 5. Downloads + summary: Get the status for a specific download of the current user + description: | + This endpoint facilitates to retrieve the status for a specific download. In addition to other + information, the status indicates whether the download is still being prepared, is already + available, or whether an error occurred when the download package was created. + parameters: + - $ref: '#/components/parameters/downloadId' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/DownloadStatus' + description: Status of the download successfully retrieved. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-download' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + delete: + operationId: deleteDownload + tags: + - 5. Downloads + summary: Deletes a specific download. + description: | + This endpoint facilitates the removal of a download. + parameters: + - $ref: '#/components/parameters/downloadId' + responses: + "204": + description: | + Download deletion successful. This confirms the absence of the specified download, irrespective of its previous existence. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-download' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/downloads/{downloadId}/download: + get: + operationId: download + tags: + - 5. Downloads + - persistence-service + summary: Download the download package. + description: | + This endpoint facilitates to actually download the created download package. The request will + only be successful if the status of the download is `READY`. + parameters: + - $ref: '#/components/parameters/downloadId' + responses: + "200": + headers: + Content-Disposition: + schema: + type: string + example: attachment; filename*=utf-8''example.zip + content: + application/octet-stream: + schema: + type: string + format: binary + description: Successfully downloaded the requested file. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-download' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/users: + get: + operationId: getUsers + tags: + - 6. Users + - tenant-user-management + summary: Get a list of users + description: | + This endpoint facilitates to retrieve a list of known users. + + With the `username` parameter you can filter for a specific user name. If the parameter is + used, the returned list either contains a single matching entry or is empty. + parameters: + - $ref: '#/components/parameters/username' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/UserList' + description: List of users successfully retrieved. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/users/{userId}: + get: + operationId: getUserById + tags: + - 6. Users + summary: Retrieve a specific user by its identifier. + description: | + This endpoint facilitates to retrieve a specific user. + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/User' + description: User successfully retrieved. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-user' + "429": + $ref: '#/components/responses/429' "500": $ref: '#/components/responses/500' /api/license/active/usage: post: - operationId: getReport + operationId: getLicenseReport tags: - - 5. License + - 7. License summary: Generate and retrieve a license usage report. description: | This endpoint enables users to create and fetch a report detailing the active usage of licenses. The report contains @@ -976,18 +1416,16 @@ paths: License report successfully generated and retrieved. "400": $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' "403": $ref: '#/components/responses/403' + "429": + $ref: '#/components/responses/429' "500": $ref: '#/components/responses/500' components: headers: - X-Tenant-ID: - description: Tenant identifier, also known as the *Workspace ID* in the application. - required: true - schema: - type: string - example: 'my-workspace' Authorization: description: JWT token for authorization. Format should be `Bearer {JWT_TOKEN}`. required: true @@ -1051,6 +1489,20 @@ components: Some endpoints support a `includeSoftDeleted` parameter: If this is set to `true`, this response is returned only if the file is deleted permanently. + "404-download": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorMessage' + description: | + Download not found. This happens if the requested download does not exist for the current user. + "404-user": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorMessage' + description: | + User not found. This happens if the requested user does not exist. "409-dossier-conflict": content: 'application/json': @@ -1352,6 +1804,26 @@ components: - `true`: The component object's field `componentDetails` stores detailed information about the *source* of its respective value(s). - `false` (default): The component object does not contain a field `componentDetails`. + downloadId: + name: downloadId + in: path + required: true + schema: + type: string + style: simple + explode: false + description: The identifier for the file to download. + username: + name: username + in: query + required: false + schema: + type: string + style: form + explode: true + description: | + If the `username` parameter is set, the user list is filtered for that specific user name. This means the list + either has one matching entry or is empty. schemas: EntityReference: type: object @@ -1651,6 +2123,96 @@ components: entityRuleId: DEF.13.37 type: another_entity_type page: 456 + DossierStatusDefinition: + type: object + description: | + The `DossierStatusDefinition` object contains the relevant information to define a dossier status. The dossier status + is used to assign a custom status to a dossier. + properties: + id: + type: string + format: uuid + description: | + A unique identifier for the dossier status definition. This ID is automatically generated by + the system upon creation and is used for referencing the dossier status definition in API calls. + example: bcd22239-cedf-442f-a5a1-1664cba94dc6 + name: + type: string + description: | + User-defined name of the dossier status definition, capturing its essence. The name needs to be unique + for the dossier template. + example: "Done" + description: + type: string + description: | + A text that can be added to provide further details about the status. E.g., what it is intended for or + the circumstances under which it can be used. + example: "Dossiers with this status should only contain approved files and indicate that the users have completed the component extractions." + rank: + format: int32 + type: integer + description: | + A number that allows to define a custom display order. + default: "" + example: 1 + color: + type: string + description: | + A hexadecimal color code that can be set to assign a color to a + the `PREVIEW` file. + + - Yellow is `#ffda05` + - Green is `#5eb160` + example: "#5eb160" + required: + - name + - rank + - color + DossierAttributeDefinition: + type: object + description: | + The `DossierAttributeDefinition` object contains the relevant information to define a dossier attribute. Dossier attributes + are used to manage additional meta-data of dossiers. + properties: + id: + type: string + format: uuid + description: | + A unique identifier for the dossier attribute definition. This ID is automatically generated by + the system upon creation and is used for referencing the dossier attribute definition in API calls. + name: + type: string + description: | + User-defined name of the dossier attribute definition, capturing its essence. The name needs to be unique + for the dossier template. + type: + type: string + enum: + - TEXT + - NUMBER + - DATE + description: | + Determines the type of the dossier attribute's value. Please note that currently the system + does not validate the values against this definition. This is just a hint for a user interface + that needs to handle invalid entries. Possible values for the type: + - `TEXT`: The value is just a string, i.e., any sequence of characters. + - `NUMBER`: The value is a string expressing a number, with or without decimals. + - `DATE`: The value is a string expressing a date information. + reportingPlaceholder: + type: string + description: | + The name of the placeholder of the dossier attribute that can be used in report templates. The + placeholder follows a specific format convention: + `{{dossier.attribute.}}` while the name is transformed into 'PascalCase' and does not contain + whitespaces. The placeholder is unique in a dossier template. + required: + - name + - type + example: + id: "123e4567-e89b-12d3-a456-426614174000" + name: "Document Summary" + type: "TEXT" + reportingPlaceholder: "{{dossier.attribute.DocumentSummary}}" FileAttributeDefinition: type: object description: | @@ -1675,7 +2237,7 @@ components: - NUMBER - DATE description: | - Determines the type of the dossier attribute's value. Please note that currently the system + Determines the type of the file attribute's value. Please note that currently the system does not validate the values against this definition. This is just a hint for a user interface that needs to handle invalid entries. Possible values for the type: - `TEXT`: The value is just a string, i.e., any sequence of characters. @@ -1689,7 +2251,7 @@ components: type: string description: | The name of the placeholder of the file attribute that can be used in report templates. The placeholder follows a specific format convention: - `{{file.attribute.}}` while the name transformed into 'PascalCase' and does not contain whitespaces. The placeholder is unique in a dossier template. + `{{file.attribute.}}` while the name is transformed into 'PascalCase' and does not contain whitespaces. The placeholder is unique in a dossier template. displaySettings: $ref: '#/components/schemas/FileAttributeDisplaySettings' required: @@ -1709,24 +2271,24 @@ components: FileAttributeDisplaySettings: type: object description: | - Display setting for the RedactManager and DocuMine user interface. These settings control how the UI handles and presents the file attributes. + Display setting for the DocuMine user interface. These settings control how the UI handles and presents the file attributes. properties: primaryAttribute: type: boolean description: | - If `true`, the RedactManager and DocuMine user interfaces show the value of the file attribute in the file list below the file name. + If `true`, the DocuMine user interfaces show the value of the file attribute in the file list below the file name. editable: type: boolean description: | - If `true`, the RedactManager and DocuMine user interfaces allow manual editing of the value. Otherwise only importing and setting by rules would be possible. + If `true`, the DocuMine user interfaces allow manual editing of the value. Otherwise only importing and setting by rules would be possible. filterable: type: boolean description: | - If `true`, the RedactManager and DocuMine user interfaces add filter options to the file list. + If `true`, the DocuMine user interfaces add filter options to the file list. displayedInFileList: type: boolean description: | - if `true`, the RedactManager and DocuMine user interfaces show the values in the file list. + if `true`, the DocuMine user interfaces show the values in the file list. required: - primaryAttribute - editable @@ -1737,6 +2299,44 @@ components: editable: true filterable: true displayedInFileList: false + DossierStatusDefinitionList: + type: object + description: A list of dossier status definitions. + properties: + dossierStatusDefinitions: + items: + $ref: '#/components/schemas/DossierStatusDefinition' + type: array + example: + dossierStatusDefinitions: + - id: "123e7567-e89b-12d3-a456-426614174000" + name: "In Progress" + description: "Dossiers with this status are currently being processed by the users." + rank: 0 + color: "#ffda05" + - id: "23e45378-e90b-12d3-a456-765114174321" + name: "Done" + description: "Dossiers with this status should only contain approved files and indicate that the users have completed the component extraction." + rank: 1 + color: "#5eb160" + DossierAttributeDefinitionList: + type: object + description: A list of dossier attribute definitions. + properties: + dossierAttributeDefinitions: + items: + $ref: '#/components/schemas/DossierAttributeDefinition' + type: array + example: + dossierAttributeDefinitions: + - id: "123e4567-e89b-12d3-a456-426614174000" + name: "Dossier Summary" + type: "TEXT" + reportingPlaceholder: "{{dossier.attribute.DossierSummary}}" + - id: "23e45678-e90b-12d3-a456-765114174321" + name: "Comment" + type: "TEXT" + reportingPlaceholder: "{{dossier.attribute.Comment}}" FileAttributeDefinitionList: type: object description: A list of file attribute definitions. @@ -1806,6 +2406,32 @@ components: filenameMappingCsvColumnHeader: "Filename" delimiter: "," encoding: "UTF-8" + ReportTemplateIdList: + type: array + items: + type: string + format: uuid + uniqueItems: true + description: | + List of template identifiers indicating which templates are used for generating reports or other outputs. + The reports are generated when requesting a download package. + example: + - b79cb3ba-745e-5d9a-8903-4a02327a7e09 + - fb3463a0-7d6e-54a3-bcd8-1b93388c648d + DossierAttributes: + type: object + description: Additional dossier attributes that can be set + properties: + attributeIdToValue: + additionalProperties: + type: string + type: object + example: + attributeIdToValue: + "1049a73c-8013-45d6-8217-0845a4ff1c61": This is a dossier attribute value + "79d5a138-d30a-4014-ad7f-43ffba1f4d04": This is yet another dossier attribute value + "1d30d9e8-4a6c-4ef0-96a0-7bef62e138db": "1234" + "b337b65a-0481-48d9-92e6-79e34760ef01": "1. January 1337" Dossier: type: object description: | @@ -1888,42 +2514,19 @@ components: format: uuid deprecated: true description: | - Identifier for the watermark that's to be applied on redacted documents within this dossier. - In DocuMine, watermarks are not supported. previewWatermarkId: type: string format: uuid deprecated: true description: | - Identifier for the watermark pattern used for generated previews documents within this dossier. - In DocuMine, watermarks are not supported. + dossierAttributes: + $ref: '#/components/schemas/DossierAttributes' downloadFileTypes: - type: array - items: - enum: - - ORIGINAL - - PREVIEW - - REDACTED - - ANNOTATED - - FLATTEN - - DELTA_PREVIEW - type: string - uniqueItems: true - description: | - Types of files available for download from the dossier. These types can - differ based on the application. DocuMine only supports `ORIGINAL`. The files are - provided or generated when requesting a download package. + $ref: '#/components/schemas/DownloadFileTypes' reportTemplateIds: - type: array - items: - type: string - format: uuid - uniqueItems: true - description: | - List of template identifiers indicating which templates are to be used for generating reports or outputs - for this dossier. The reports are generated when requesting a download package. + $ref: '#/components/schemas/ReportTemplateIdList' archivedTime: type: string format: date-time @@ -2075,14 +2678,7 @@ components: description: A unique identifier for a member with access to the dossier. uniqueItems: true reportTemplateIds: - type: array - description: | - An array of identifiers representing templates used for generating reports - or exports from this dossier. - items: - description: An identifier for a report template. - type: string - uniqueItems: true + $ref: "#/components/schemas/ReportTemplateIdList" dossierStatusId: type: string description: | @@ -2214,8 +2810,8 @@ components: of individual dossiers that get created based on this template. example: id: 1e07cde0-d36a-4ab7-b389-494ca694a0cb - name: RedactManager Example - description: Typical settings for RedactManager. + name: DocuMine Example + description: Typical settings for DocuMine. dateAdded: 2020-01-23T04:56:07.000+00:00 dateModified: 2021-01-23T04:56:07.000+00:00 createdBy: c2e33246-e50a-4c43-831c-6789a5637db6 @@ -2224,14 +2820,13 @@ components: validTo: 2030-12-31T23:59:59.999+00:00 dossierTemplateStatus: ACTIVE removeWatermark: false - keepImageMetadata: false - ocrByDefault: false - keepHiddenText: false - keepOverlappingObjects: false + keepImageMetadata: true + ocrByDefault: true + keepHiddenText: true + keepOverlappingObjects: true applyDictionaryUpdatesToAllDossiersByDefault: false downloadFileTypes: - - PREVIEW - - REDACTED + - ORIGINAL properties: id: description: | @@ -2276,30 +2871,7 @@ components: format: date-time type: string downloadFileTypes: - description: | - Specifies the types of files that will be set as default types to download for dossiers created from - this template. Valid options may vary depending on the system. - - * Valid options for RedactManager: - * `ORIGINAL`: The optimized version of the PDF like it is used by the system for further analysis. - * `PREVIEW`: The optimized PDF with redaction annotations indicating what gets redacted by the - system. Note that the content to redact is actually still present and readable. - * `REDACTED`: The redacted PDF - * `DELTA_PREVIEW`: If redactions were imported (e.g., by uploading a PDF with redaction annotations), - this PDF highlights the changes made to imported redactions in different colors (green: no change, - red: removed, blue: added). - * Valid options for DocuMine: - * `ORIGINAL`: The optimized version of the PDF like it is used by the system for further analysis. - items: - description: Enumerated type indicating a permissible download file type for dossiers under this template. - enum: - - ORIGINAL - - PREVIEW - - REDACTED - - DELTA_PREVIEW - type: string - type: array - uniqueItems: true + $ref: '#/components/schemas/DownloadFileTypes' status: description: | Indicates the current status of the dossier template: @@ -2333,13 +2905,34 @@ components: description: Flag specifying if the system should try to remove watermarks in documents prior to OCR processing. type: boolean type: object + DownloadFileTypes: + type: array + uniqueItems: true + description: | + Specifies the types of files that will part of the created download package. The defaults can be defined in the dossier template + and can be overwritten individually on each download. + + DocuMine supports `ORIGINAL` and `PREVIEW`: + + - `ORIGINAL` Contrary to intuition, this is not the uploaded file, but the pre-processed, + optimized PDF, which may also contain the OCR results. + This is the PDF that used by the system for further processing. + - `PREVIEW` The annotated version of the PDF, highlighting the found entities that were + evaluated to extract the components. + items: + enum: + - ORIGINAL + - PREVIEW + type: string + example: + - ORIGINAL DossierTemplateList: description: Represents a list of dossier templates, each encapsulating a set of rules and settings. example: dossierTemplates: - id: 1e07cde0-d36a-4ab7-b389-494ca694a0cb - name: RedactManager Example - description: Typical settings for RedactManager. + name: DocuMine Example + description: Typical settings for DocuMine. dateAdded: 2020-01-23T04:56:07.000+00:00 dateModified: 2021-01-23T04:56:07.000+00:00 createdBy: c2e33246-e50a-4c43-831c-6789a5637db6 @@ -2348,16 +2941,15 @@ components: validTo: 2030-12-31T23:59:59.999+00:00 dossierTemplateStatus: ACTIVE removeWatermark: false - keepImageMetadata: false - ocrByDefault: false - keepHiddenText: false - keepOverlappingObjects: false + keepImageMetadata: true + ocrByDefault: true + keepHiddenText: true + keepOverlappingObjects: true applyDictionaryUpdatesToAllDossiersByDefault: false downloadFileTypes: - - PREVIEW - - REDACTED + - ORIGINAL - id: 8d8cae48-5c33-4617-ac27-1643f29b79d8 - name: DocuMine Example + name: Another DocuMine Example description: Typical settings for DocuMine. dateAdded: 2023-09-01T06:54:32.000+00:00 dateModified: 2023-09-01T06:54:32.000+00:00 @@ -2406,9 +2998,11 @@ components: type: string type: object example: - myFileAttribute: This is a file attribute value - yetAnotherFileAttribute: This is yet another file attribute value - numericValuesNeedToBeStrings: "1234" + attributeIdToValue: + "9049a73c-8013-45d6-8217-0845a4ff1c61": This is a file attribute value + "59d5a138-d30a-4014-ad7f-43ffba1f4d04": This is yet another file attribute value + "9d30d9e8-4a6c-4ef0-96a0-7bef62e138db": "1234" + "a337b65a-0481-48d9-92e6-79e34760ef01": "1. January 1337" FileDeleteRequest: type: object description: Request payload to initiate the deletion of specific files. @@ -2509,7 +3103,7 @@ components: The workflow status of a file. As DocuMine does not have an approval workflow the meaning of the states is as follows: - - `NEW` means "New", nothing else. + - `NEW` - Initial status of the uploaded files. - `UNDER_REVIEW` is actually not used in DocuMine. - `UNDER_APPROVAL` means "In progress", i.e. a user is checking the extracted components. - `APPROVED` means "Done", i.e. a user has checked and adjusted the components if necessary. @@ -2748,7 +3342,7 @@ components: hasSuggestions: true FileStatusList: type: object - description: Represents a list detailing the status of a bunch of files. + description: Represents a list detailing the status of multiple files. properties: files: type: array @@ -2954,6 +3548,180 @@ components: $ref: '#/components/schemas/RuleValidationMessage' type: array type: object + DownloadStatus: + type: object + description: Detailed information about a specific download. + properties: + id: + type: string + format: uuid + description: The unique identifier of the download. + example: b5e2cf01-8bb6-4fcd-ad88-0efb611195da + userId: + type: string + format: uuid + description: The unique identifier of the user who initiated the download. + example: caa8b54a-eb5e-4134-8ae2-a3946a428ec7 + filename: + type: string + description: The name of the download file. + example: my-component-dossier.zip + mimeType: + type: string + description: The mime type of the download file. + example: application/octet-stream + errorCause: + type: string + description: | + If the status is `FAILED`, this field contains information about the error that happened + while preparing the download package. This information is intended to be included in a + bug report if the error occurs repeatedly and indicates a general problem with DocuMine. + example: "" + status: + type: string + enum: + - QUEUED + - GENERATING + - COMPRESSING + - READY + - FAILED + description: | + The status of the download file. In particular: + - `QUEUED` - The download job has been created and is waiting to be processed by the system. + - `GENERATING` - The system currently creates the files for the download package. + - `COMPRESSING` - The system creates a ZIP archive that will contain all files of the download package. + - `READY` - The download package is ready for download. Please note that the download will be kept only + for a certain period. This period can be configured in the settings of your DocuMine workspace. + - `FAILED` - An error occurred while preparing the download. The `errorCause` field might contain + additional details on the error. + example: READY + creationDate: + type: string + format: date-time + description: The date and time when the user initiated the download. + example: 2023-03-29T11:41:08.886Z + lastDownload: + type: string + format: date-time + description: The date and time when the user last downloaded the file. + example: 2023-03-29T13:11:05.123Z + fileSize: + type: integer + format: int64 + description: The size of the download file in bytes. + example: 1654231 + dossierId: + type: string + format: uuid + description: The identifier of the dossier to which the content of the download package belongs. + example: 20354d7a-e4fe-47af-8ff6-187bca92f3f9 + fileIds: + type: array + items: + type: string + description: The list of file identifiers to which the content of the download package belongs. + example: + - 51d3f70ac322c98dc4db70a2ac44115a + - 1fdbd888b39059c8cf171df26f62f8a5 + downloadFileTypes: + $ref: '#/components/schemas/DownloadFileTypes' + reportTemplateIds: + $ref: '#/components/schemas/ReportTemplateIdList' + DownloadStatusList: + type: object + description: Represents a list detailing the status of multiple downloads. + properties: + downloadStatus: + type: array + items: + $ref: '#/components/schemas/DownloadStatus' + description: Each item contains the status details of a download. + DownloadRequest: + type: object + description: Request payload to initiate the preparation of the download. + properties: + downloadFileTypes: + $ref: '#/components/schemas/DownloadFileTypes' + reportTemplateIds: + $ref: '#/components/schemas/ReportTemplateIdList' + redactionPreviewColor: + type: string + example: "#9398a0" + description: | + A hexadecimal color code that is used to define the highlighting color of the redaction annotations in + the `PREVIEW` file. + + - Black is `#000000` + - White is `#ffffff` + - Grey is `#cccccc` + BulkDownloadRequest: + allOf: + - $ref: '#/components/schemas/DownloadRequest' + - type: object + description: Request payload to initiate the preparation of the download of multiple files. + properties: + fileIds: + type: array + description: A list with unique identifiers of the files for which the download is to be prepared. + items: + type: string + description: The unique identifier of a file. + User: + type: object + description: Basic information about a user. + properties: + id: + type: string + format: uuid + description: The unique identifier of the user. + example: efe7eedd-89c5-56f5-984c-0712ee41a2eb + username: + type: string + description: The user name that is used for logging in. + example: myusername + email: + type: string + description: The email address of the user. + example: myusername@example.com + firstName: + type: string + description: The first name of the user. + example: John + lastName: + type: string + description: The last name of the user. + example: Doe + roles: + uniqueItems: true + type: array + description: | + The roles of the user. In particular: + - `USER` - default user permission to work with DocuMine + - `MANAGER` - additional privileges to create and manage dossiers + - `USER_ADMIN` - administration privileges limited to manage users only + - `ADMIN` - general administration privileges + items: + type: string + enum: + - USER + - MANAGER + - USER_ADMIN + - ADMIN + example: + - MANAGER + - ADMIN + active: + type: boolean + description: Indicator if the user is active or not. Only active users can log in. + UserList: + type: object + description: A list of multiple users. + properties: + downloadStatus: + type: array + items: + $ref: '#/components/schemas/User' + description: Each item contains the details of a user. LicenseReport: type: object description: A comprehensive report of licensing metrics and usage statistics. @@ -3132,9 +3900,12 @@ components: - file securitySchemes: FF-OAUTH: + type: oauth2 flows: authorizationCode: - authorizationUrl: /auth/realms/redaction/protocol/openid-connect/auth + authorizationUrl: /auth/realms/{workspaceId}/protocol/openid-connect/auth + tokenUrl: /auth/realms/{workspaceId}/protocol/openid-connect/token scopes: { } - tokenUrl: /auth/realms/redaction/protocol/openid-connect/token - type: oauth2 + clientCredentials: + tokenUrl: /auth/realms/{workspaceId}/protocol/openid-connect/token + scopes: {} \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/redactmanager.yaml b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/redactmanager.yaml new file mode 100644 index 000000000..9cd097d79 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/redactmanager.yaml @@ -0,0 +1,3010 @@ +openapi: 3.0.2 +info: + title: RedactManager API + version: "4.1.0-draft" + description: | + The RedactManager API provides a comprehensive solution for managing resources such as dossiers and their associated files. + Users can also retrieve the results package for files that have been processed by the system and reviewed by the users. + The results package can contain the optimized PDF file, the preview PDF, the redacted PDF and correlating redaction reports + in different formats. + + All endpoints are secured using OAuth2, with the "authorizationCode" being the general supported authorization flow. + Obtain a JWT token for authentication and send it in the 'Authorization' header with the format `Bearer {JWT_TOKEN}`. + + Please also note that the `authorizationUrl` and `tokenUrl` in this specification contain `{workspaceId}` placeholders that + must be replaced by your respective RedactManager workspace identifier. + + Example Headers: + ```properties + Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI... + ``` + license: + name: knecon License + contact: + name: knecon Service Desk + email: support@redactmanager.com + url: https://support.redactmanager.com +externalDocs: + description: Find out more about RedactManager + url: https://docs.redactmanager.com +servers: + - url: https://app.redactmanager.com + description: RedactManager Cloud Service +security: + - FF-OAUTH: [] +tags: + - name: 1. Dossier Templates + description: Operations related to dossier templates. + - name: 2. Dossiers + description: Operations for managing dossiers that are based on a dossier template. + - name: 3. Files + description: Operations for managing files within a dossier. + - name: 4. Components + description: Operations related to components of a file within a dossier. These endpoints are not available for RedactManager. + - name: 5. Downloads + description: Operations related to download packages. + - name: 6. Users + description: Operations related to users. + - name: 7. License + description: Operations related to license information and usage metrics. +paths: + /api/dossier-templates: + get: + operationId: getAllDossierTemplates + tags: + - 1. Dossier Templates + summary: Retrieve a list of all available dossier templates. + description: | + This endpoint provides a list of all dossier templates stored in the system. + + It returns an object containing an array of dossier template objects under the key `dossierTemplates`. + Each template object contains an identifier, a name, a dossier template status, and other fields with + further details about the template. + + Use this endpoint to fetch all templates before performing actions on specific ones. + responses: + "200": + content: + '*/*': + schema: + $ref: '#/components/schemas/DossierTemplateList' + description: | + Successfully retrieved the list of dossier templates. + + The response could be an empty list if there are no dossier templates at all. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}: + get: + operationId: getDossierTemplate + tags: + - 1. Dossier Templates + summary: Retrieve a specific dossier template by its identifier. + description: | + Utilize this endpoint to retrieve comprehensive details about a designated dossier template. The response encompasses + various attributes such as the dossier template's identifier, name, status, among other pertinent details. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/DossierTemplate' + description: | + Successfully retrieved the requested dossier template. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossier-status-definitions: + get: + summary: Returns the list of all existing dossier status definitions + tags: + - 1. Dossier Templates + description: | + Retrieves a collection of dossier status definitions associated with a specific dossier template. Each dossier + status definition includes details such as the status name, description, and other relevant metadata. This endpoint + is useful for clients needing to display or set the status of a dossier associated with a specific dossier template. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + responses: + "200": + content: + '*/*': + schema: + $ref: '#/components/schemas/DossierStatusDefinitionList' + description: | + Successfully returned the dossier status definitions for the specified dossier template. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossier-attribute-definitions: + get: + summary: Returns the list of all existing dossier attribute definitions + tags: + - 1. Dossier Templates + description: | + Retrieves a collection of dossier attribute definitions associated with a specific dossier template. Each dossier + attribute definition includes details such as attribute type, name, and other relevant metadata. This endpoint + is useful for clients needing to understand what attributes are expected or allowed for dossiers associated with + a specific dossier template. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + responses: + "200": + content: + '*/*': + schema: + $ref: '#/components/schemas/DossierAttributeDefinitionList' + description: | + Successfully returned the dossier attribute definitions for the specified dossier template. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/file-attribute-definitions: + get: + summary: Returns the list of all existing file attribute definitions + tags: + - 1. Dossier Templates + description: | + Retrieves a collection of file attribute definitions associated with a specific dossier template. Each file + attribute definition includes details such as attribute type, name, and other relevant metadata. This endpoint + is useful for clients needing to understand what attributes are expected or allowed for files associated with + a specific dossier template. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + responses: + "200": + content: + '*/*': + schema: + $ref: '#/components/schemas/FileAttributeDefinitionList' + description: | + Successfully returned the file attribute definitions for the specified dossier template. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossiers: + get: + operationId: getDossiers + tags: + - 2. Dossiers + summary: Retrieve a list of dossiers based on a specific dossier template. + description: | + By default, this endpoint provides a list of all *active* dossiers that are based on a specific dossier template. + + It returns an object containing an array of dossier objects under the key `dossiers`. Each dossier object contains + an identifier, a name, a description, an owner, and other fields with further details about the dossier. + + Use this endpoint to fetch the required dossiers before performing actions on specific ones. Use the query parameters + to modify the response. E.g., set the `includeArchivedDossiers` parameter to `true` so that the response also contains + *archived* dossiers. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/includeActiveDossiers' + - $ref: '#/components/parameters/includeArchivedDossiers' + - $ref: '#/components/parameters/includeSoftDeletedDossiers' + responses: + "200": + content: + '*/*': + schema: + $ref: '#/components/schemas/DossierList' + description: | + Successfully retrieved the dossiers that are based on the requested dossier template. + + The response could be an empty + list if there are no *active* dossiers based on it. If query parameters have been set, an empty list will be returned if + no dossier matches the respective combination. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + post: + operationId: createOrUpdateDossier + tags: + - 2. Dossiers + summary: Creates or updates a dossier for a specific dossier template. + description: | + This endpoint is meant to create a new dossier or to update an existing one. + + To create a new dossier, do not set the identifier of the dossier object provided in the request body. + + To update an existing dossier, the set identifier needs to be valid. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DossierRequest' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/Dossier' + description: | + Successfully saved the dossier. + "201": + content: + application/json: + schema: + $ref: '#/components/schemas/Dossier' + description: | + successfully created the dossier. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier' + "409": + $ref: '#/components/responses/409-dossier-conflict' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}: + delete: + operationId: deleteDossier + tags: + - 2. Dossiers + summary: Deletes a specific dossier by its identifier. + description: | + Removes a dossier either in a recoverable manner (soft-delete) or permanently. By default, a soft-deleted dossier + remains recoverable for a retention period determined by application settings. The default retention period is + 96 hours (4 days) but may vary based on configuration. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + - $ref: '#/components/parameters/permanentlyDeleteDossier' + responses: + "204": + description: | + Dossier deletion successful. Note that the absence of the dossier with the given `dossierId` is confirmed, + regardless of whether it was pre-existing, already removed, or never present. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier-template' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + get: + operationId: getDossier + tags: + - 2. Dossiers + summary: Retrieve a specific dossier by its identifier. + description: | + Utilize this endpoint to retrieve comprehensive details about a designated dossier. The response encompasses various + attributes such as the dossier's unique ID, name, description, associated owner, among other pertinent details. + + If the dossier has been soft-deleted but not yet permanently deleted, the `includeSoftDeleted` parameter can be utilized + to get it. Without this, a soft-deleted dossier would return a `404 Dossier not found` error. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + - $ref: '#/components/parameters/includeSoftDeletedDossier' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/Dossier' + description: | + Successfully retrieved the requested dossier. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/attributes: + post: + operationId: setDossierAttributes + tags: + - 2. Dossiers + summary: Update or set attributes for a specific dossier. + description: | + This endpoint facilitates the updating or setting of specific dossier attributes for a given dossier. + Ensure you provide the necessary dossier attributes within the request body. + + Use this route to maintain or enhance dossier metadata and properties. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DossierAttributes' + required: true + responses: + "204": + description: | + Dossier attributes successfully updated. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/create-download: + post: + operationId: prepareDossierDownload + tags: + - 2. Dossiers + summary: Initiate the creation of a download package for all files of a dossier. + description: | + To download the results of a redaction, a download package needs to be prepared. This endpoint + facilitates to define the content of the download package and to start the creation of it for all + files of the dossier. The response of this endpoint contains an identifier for the download that + is needed to query the status until the download package is prepared, and finally to actually + download the file once it is available. + + Note: The redacted file will be created for `APPROVED` files only. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DownloadRequest' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/DownloadStatus' + description: | + Successfully started the creation of the download package for all files of the requested dossier. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files: + get: + operationId: getDossierStatus + tags: + - 3. Files + summary: Retrieves the status of the files of a specific dossier. + description: | + This endpoint provides status information for each file within a designated dossier. + + The file status encompasses attributes such as identifier, file name, number of pages, file size, + processing and workflow status, among other pertinent details. By default, only active files + are fetched. However, various parameters can be combined to adjust the scope of the response. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + - $ref: '#/components/parameters/includeActiveFiles' + - $ref: '#/components/parameters/includeArchivedFiles' + - $ref: '#/components/parameters/includeSoftDeletedFiles' + responses: + "200": + content: + '*/*': + schema: + $ref: '#/components/schemas/FileStatusList' + description: | + Successfully retrieved file status information. + + The response could be an empty list if the dossier contains no *active* files. If query parameters + have been set, an empty list will be returned if no file matches the respective combination. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + post: + operationId: upload + tags: + - 3. Files + summary: Upload a file and associate it with a specific dossier. + description: | + This endpoint facilitates the upload of files to a specific dossier. + + Upon successful upload, the system returns the unique identifier of the uploaded file. In cases where the dossier already + contains a file with the same name, the new file will replace the existing one. + + Supported file formats include PDF documents, Microsoft Office documents (if supported by your license), CSV files, and + ZIP archives. + + Notably, Microsoft office files (file extensions `docx`, `xlsx`, or `pptx`) are automatically converted into PDF format + upon upload. Be aware that due to potential font discrepancies, the resulting PDF may slightly differ in appearance from + the original. Nevertheless, our system ensures maximum fidelity to the original content. + + CSV files offer support for bulk importing of file attributes. If the dossier is set up to map specific CSV fields to + file attributes and the uploaded CSV aligns with this mapping, the system will process the CSV accordingly. Mismatched + CSVs will be disregarded. For successfully processed CSV files, the response contains information about the file attributes + and the annotated files. + + ZIP archives offer support to upload multiple files at once. In case of an upload of a ZIP archive, the system returns a + list of unique identifiers of the contained files. The archive can even contain a mix of files like some PDF files and + a CSV file that will annotate some file attributes. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + - $ref: '#/components/parameters/keepManualChanges' + requestBody: + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/UploadRequest' + responses: + "201": + content: + application/json: + schema: + $ref: '#/components/schemas/FileUploadResult' + description: | + Successfully uploaded the file. Returns an object with the unique identifier of the uploaded file, a list of unique + identifiers in case of an uploaded ZIP archive, or some annotation information in case of a successfully processing + of an uploaded CSV file. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files/{fileId}: + delete: + operationId: deleteFile + tags: + - 3. Files + summary: Deletes a specific file associated with a dossier and its template. + description: | + This endpoint facilitates the removal of a file linked to a specific dossier under a dossier template. By default, the + file undergoes a soft-delete, meaning it can be restored within the retention period determined by application settings. + The default retention period is 96 hours (4 days) but may vary based on configuration. Use the `deletePermanently` query + parameter to opt for a permanent deletion, preventing any future restoration. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + - $ref: '#/components/parameters/fileId' + - $ref: '#/components/parameters/permanentlyDeleteFile' + responses: + "204": + description: | + File deletion successful. This confirms the absence of the specified file, irrespective of its previous existence. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-file' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + get: + operationId: getFileStatus + tags: + - 3. Files + summary: Retrieves the status of a particular file. + description: | + This endpoint is designed to fetch the status of a specific file associated to a dossier based on a dossier template. If + the file has been soft-deleted but not yet permanently deleted, the `includeSoftDeleted` parameter can be utilized to get + the status of such files. Without this, a soft-deleted file would return a `404 File not found` error. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + - $ref: '#/components/parameters/fileId' + - $ref: '#/components/parameters/includeSoftDeletedFile' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/FileStatus' + description: | + Successfully retrieved the status of the requested file. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-file' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files/{fileId}/create-download: + post: + operationId: prepareFileDownload + tags: + - 3. Files + summary: Initiate the creation of a download package for a single file of a dossier. + description: | + To download the results of a redaction, a download package needs to be prepared. This endpoint + facilitates to define the content of the download package and to start the creation of it for the + file. The response of this endpoint contains an identifier for the download that is needed to + query the status until the download package is prepared for download, and finally to actually + download the file once it is available. + + Note: The redacted PDF will be created for `APPROVED` files only. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + - $ref: '#/components/parameters/fileId' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DownloadRequest' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/DownloadStatus' + description: | + Successfully started the creation of the download package for the requested file. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-file' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files/bulk/delete: + post: + operationId: deleteFiles + tags: + - 3. Files + summary: Bulk delete specific files within a dossier. (DELETE with body payload) + description: | + This endpoint allows for the bulk deletion of specified files associated with a certain dossier. + + It provides the flexibility to perform either a soft-delete (by default) or a permanent deletion. A soft-deleted + file remains restorable within the retention period set by the application (96h by default). In contrast, permanently + deleted files are irrevocably removed and cannot be restored. + + Use this route to manage large file deletions efficiently. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + - $ref: '#/components/parameters/permanentlyDeleteFiles' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/FileDeleteRequest' + required: true + responses: + "204": + description: | + Bulk file deletion successful. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files/bulk/create-download: + post: + operationId: prepareBulkDownload + tags: + - 3. Files + summary: Initiate the creation of a download package for specific files within a dossier. + description: | + To download the results of a redaction, a download package needs to be prepared. This endpoint + facilitates to define the content of the download package and to start the creation of it for + multiple files of a dossier. The response of this endpoint contains an identifier for the download + that is needed to query the status until the download package is prepared for download, and finally + to actually download the file once it is available. + + Note: The redacted PDF will be created for `APPROVED` files only. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/BulkDownloadRequest' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/DownloadStatus' + description: | + Successfully started the creation of the download package for the requested files. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-dossier' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files/{fileId}/attributes: + post: + operationId: setFileAttributes + tags: + - 3. Files + summary: Update or set attributes for a specific file. + description: | + This endpoint facilitates the updating or setting of specific file attributes for a given file within a dossier. + Ensure you provide the necessary file attributes within the request body. + + Use this route to maintain or enhance file metadata and properties. + parameters: + - $ref: '#/components/parameters/dossierTemplateId' + - $ref: '#/components/parameters/dossierId' + - $ref: '#/components/parameters/fileId' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/FileAttributes' + required: true + responses: + "204": + description: | + File attributes successfully updated. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-file' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/download: + get: + operationId: getDownloadStatusList + tags: + - 5. Downloads + summary: Get the list of downloads for the current user + description: | + This endpoint facilitates to retrieve the list of downloads for the current user. The response contains + status objects that represent each download and provide information on whether the download is still + in preparation, is already available or whether an error occurred when the download package was created. + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/DownloadStatusList' + description: List of all downloads of the current users successfully retrieved. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/downloads/{downloadId}: + get: + operationId: getDownloadStatus + tags: + - 5. Downloads + summary: Get the status for a specific download of the current user + description: | + This endpoint facilitates to retrieve the status for a specific download. In addition to other + information, the status indicates whether the download is still being prepared, is already + available, or whether an error occurred when the download package was created. + parameters: + - $ref: '#/components/parameters/downloadId' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/DownloadStatus' + description: Status of the download successfully retrieved. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-download' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + delete: + operationId: deleteDownload + tags: + - 5. Downloads + summary: Deletes a specific download. + description: | + This endpoint facilitates the removal of a download. + parameters: + - $ref: '#/components/parameters/downloadId' + responses: + "204": + description: | + Download deletion successful. This confirms the absence of the specified download, irrespective of its previous existence. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-download' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/downloads/{downloadId}/download: + get: + operationId: download + tags: + - 5. Downloads + - persistence-service + summary: Download the download package. + description: | + This endpoint facilitates to actually download the created download package. The request will + only be successful if the status of the download is `READY`. + parameters: + - $ref: '#/components/parameters/downloadId' + responses: + "200": + headers: + Content-Disposition: + schema: + type: string + example: attachment; filename*=utf-8''example.zip + content: + application/octet-stream: + schema: + type: string + format: binary + description: Successfully downloaded the requested file. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-download' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/users: + get: + operationId: getUsers + tags: + - 6. Users + - tenant-user-management + summary: Get a list of users + description: | + This endpoint facilitates to retrieve a list of known users. + + With the `username` parameter you can filter for a specific user name. If the parameter is + used, the returned list either contains a single matching entry or is empty. + parameters: + - $ref: '#/components/parameters/username' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/UserList' + description: List of users successfully retrieved. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/users/{userId}: + get: + operationId: getUserById + tags: + - 6. Users + summary: Retrieve a specific user by its identifier. + description: | + This endpoint facilitates to retrieve a specific user. + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/User' + description: User successfully retrieved. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "404": + $ref: '#/components/responses/404-user' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' + /api/license/active/usage: + post: + operationId: getLicenseReport + tags: + - 7. License + summary: Generate and retrieve a license usage report. + description: | + This endpoint enables users to create and fetch a report detailing the active usage of licenses. The report contains + valuable insights and metrics that can aid in monitoring and managing license consumption. Ensure your request body + contains the necessary parameters for generating this report. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/LicenseReportRequest' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/LicenseReport' + description: | + License report successfully generated and retrieved. + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "403": + $ref: '#/components/responses/403' + "429": + $ref: '#/components/responses/429' + "500": + $ref: '#/components/responses/500' +components: + headers: + Authorization: + description: JWT token for authorization. Format should be `Bearer {JWT_TOKEN}`. + required: true + schema: + type: string + example: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI...' + responses: + "400": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorMessage' + description: | + The request was malformed or invalid. Double-check the request structure or parameters. + "401": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorMessage' + description: | + Unauthorized access. You must provide valid authentication credentials to access this + resource. This error can happen if the access token was revoked or has expired. + "403": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorMessage' + description: | + Forbidden. Your credentials are valid, but you do not have permission to access this resource. + "404-dossier-template": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorMessage' + description: | + Dossier template not found. This happens if the requested dossier template does not exist. + "404-dossier": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorMessage' + description: | + Dossier not found. This happens if the dossier template or the dossier is deleted or did never exist. + + Some endpoints support a `includeSoftDeleted` parameter: If this is set to `true`, this response is returned + for a previously existing dossier only if it is actually deleted permanently. + "404-file": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorMessage' + description: | + File not found. This indicates that the dossier, dossier template, or file is deleted or did never exist. + + Some endpoints support a `includeSoftDeleted` parameter: If this is set to `true`, this response is returned + only if the file is deleted permanently. + "404-download": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorMessage' + description: | + Download not found. This happens if the requested download does not exist for the current user. + "404-user": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorMessage' + description: | + User not found. This happens if the requested user does not exist. + "409-dossier-conflict": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorMessage' + description: | + Name conflict: The provided name is already in use by another dossier. It needs to be unique in the scope of your workspace. + "429": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorMessage' + description: | + Too many requests have been made in a given time frame. Rate limiting is in place to prevent abuse. + "500": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorMessage' + description: Internal Server Error. An unexpected error occurred on the server side. Please try again later or contact support. + parameters: + dossierTemplateId: + name: dossierTemplateId + in: path + required: true + schema: + type: string + style: simple + explode: false + description: The identifier of a dossier template + dryRun: + name: dryRun + in: query + required: false + schema: + default: false + type: boolean + style: form + explode: true + description: | + A toggle to activate the dry-run mode: If set to `false` (default), the request will update the system. + If set to `true`, the request will just be evaluated without actual changes in the system. + dossierId: + name: dossierId + in: path + required: true + schema: + type: string + style: simple + explode: false + description: The identifier of a dossier + fileId: + name: fileId + in: path + required: true + schema: + type: string + style: simple + explode: false + description: The identifier of a file + includeActiveDossiers: + name: includeActive + in: query + required: false + schema: + default: true + type: boolean + style: form + explode: true + description: | + A toggle to include or exclude active dossiers: If `true` (default), dossiers + that are neither archived nor soft-deleted are included. If `false`, they are + ignored. + includeActiveFiles: + name: includeActive + in: query + required: false + schema: + default: true + type: boolean + style: form + explode: true + description: | + A toggle to include or exclude active files: If set to `true` (default), files + that neither belong to an archived dossier nor have been soft-deleted are + included. If set to `false`, they are ignored. + includeArchivedDossiers: + name: includeArchived + in: query + required: false + schema: + default: false + type: boolean + style: form + explode: true + description: | + A toggle to include or exclude archived dossiers: If set to `true`, archived + dossiers are included. If set to `false` (default), they are ignored. + includeArchivedFiles: + name: includeArchived + in: query + required: false + schema: + default: false + type: boolean + style: form + explode: true + description: | + A toggle to include or exclude archived files (i.e., files of archived dossiers): + If set to `true`, files of archived dossiers are included. If set to `false` + (default), they are ignored. + includeSoftDeletedDossiers: + name: includeSoftDeleted + in: query + required: false + schema: + default: false + type: boolean + style: form + explode: true + description: | + A toggle to include soft-deleted dossiers: + - `true`: The response will include soft-deleted dossiers. + - `false` (default): Soft-deleted dossiers are ignored and will not be part of the response. + + Soft-deleted means that the dossier has not been deleted permanently. + includeSoftDeletedDossier: + name: includeSoftDeleted + in: query + required: false + schema: + default: false + type: boolean + style: form + explode: true + description: | + A Toggle to return a soft-deleted dossier: + - `true`: The system returns the dossier regardless whether it has been soft-deleted or not. + - `false` (default): The system returns only non-deleted dossiers. It would return a `404 Dossier not found` error + if the requested dossier has been soft-deleted. + + Soft-deleted means that the dossier has not been deleted permanently. + includeSoftDeletedFiles: + name: includeSoftDeleted + in: query + required: false + schema: + default: false + type: boolean + style: form + explode: true + description: | + A toggle to include soft-deleted files: + - `true`: The response will include soft-deleted files. + - `false` (default): Soft-deleted files are ignored and will not be part of the response. + + Soft-deleted means that the file or dossier of the file has not been deleted permanently. + includeSoftDeletedFile: + name: includeSoftDeleted + in: query + required: false + schema: + default: false + type: boolean + style: form + explode: true + description: | + A Toggle to return the status of soft-deleted files: + - `true`: The system returns the status of the file regardless whether it has been soft-deleted or not. + - `false` (default): The system returns only non-deleted files. It would return a `404 File not found` error + if the requested file has been soft-deleted. + + Soft-deleted means that the file or dossier of the file has not been deleted permanently. + permanentlyDeleteDossier: + name: deletePermanently + in: query + required: false + schema: + default: false + type: boolean + style: form + explode: true + 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. + + Using this parameter, you can also permanently delete a previously soft-deleted dossier during its retention period. + + Note: Deleting a dossier also deletes all associated files. + permanentlyDeleteFile: + name: deletePermanently + in: query + required: false + schema: + default: false + type: boolean + style: form + explode: true + description: | + Determines the deletion mode for a file. + - `true`: The file will be permanently deleted and can't be restored. + - `false` (default): Soft-delete, allowing restoration within the retention period, provided its parent dossier + hasn't been deleted. If the parent dossier has been deleted meanwhile, it needs to be restored first. + + Using this parameter, you can also permanently delete a previously soft-deleted file during its retention period. + permanentlyDeleteFiles: + name: deletePermanently + in: query + required: false + schema: + default: false + type: boolean + style: form + explode: true + description: | + Determines the deletion mode for a list of files: + - `true`: The files will be permanently deleted and can't be restored. + - `false` (default): Soft-delete, allowing restoration within the retention period, provided the files' parent dossier + hasn't been deleted. If the parent dossier has been deleted meanwhile, it needs to be restored first. + + Using this parameter, you can also permanently delete a previously soft-deleted files during their retention period. + keepManualChanges: + name: keepManualChanges + in: query + required: false + schema: + default: false + type: boolean + style: form + explode: true + description: | + A toggle to keep manual changes, i.e., to preserve manually added annotations or manipulations on existing ones when + overwriting a file: + - `true`: The system keeps the manual changes on re-uploading (overwriting) the file. + - `false` (default): Manual changes get discarded on re-uploading the file. + downloadId: + name: downloadId + in: path + required: true + schema: + type: string + style: simple + explode: false + description: The identifier for the file to download. + username: + name: username + in: query + required: false + schema: + type: string + style: form + explode: true + description: | + If the `username` parameter is set, the user list is filtered for that specific user name. This means the list + either has one matching entry or is empty. + schemas: + EntityReference: + type: object + description: | + Represents a reference to an entity object discovered in a document. Together with the unique identifier + of the entity, the reference contains some basic details of the entity. + properties: + id: + type: string + description: | + The unique identifier for the entity. + + Note: In general, it is a valid UUID. Only if an entity spans over multiple pages, it is split on page breaks. Each + part becomes an own entity object and the first one keeps the UUID while the following get an additional suffix. + + Example: Even is `bcd22239-c3df-442f-a5a1-1664cba94dc6_2` is not a valid UUID it is still a valid identifier for an + entity object. + entityRuleId: + type: string + description: An identifier that represents a specific rule associated with the entity. + type: + type: string + description: The name of the entity. + page: + type: integer + format: int32 + description: The page number in the document where the entity is located. + example: + id: bcd22239-cedf-442f-a5a1-1664cba94dc6 + entityRuleId: ABC.0.0 + type: entity_type + page: 123 + DossierStatusDefinition: + type: object + description: | + The `DossierStatusDefinition` object contains the relevant information to define a dossier status. The dossier status + is used to assign a custom status to a dossier. + properties: + id: + type: string + format: uuid + description: | + A unique identifier for the dossier status definition. This ID is automatically generated by + the system upon creation and is used for referencing the dossier status definition in API calls. + example: bcd22239-cedf-442f-a5a1-1664cba94dc6 + name: + type: string + description: | + User-defined name of the dossier status definition, capturing its essence. The name needs to be unique + for the dossier template. + example: "Done" + description: + type: string + description: | + A text that can be added to provide further details about the status. E.g., what it is intended for or + the circumstances under which it can be used. + example: "Dossiers with this status should only contain approved files and indicate that the users have completed the redactions." + rank: + format: int32 + type: integer + description: | + A number that allows to define a custom display order. + default: "" + example: 1 + color: + type: string + description: | + A hexadecimal color code that can be set to assign a color to a + the `PREVIEW` file. + + - Yellow is `#ffda05` + - Green is `#5eb160` + example: "#5eb160" + required: + - name + - rank + - color + DossierAttributeDefinition: + type: object + description: | + The `DossierAttributeDefinition` object contains the relevant information to define a dossier attribute. Dossier attributes + are used to manage additional meta-data of dossiers. + properties: + id: + type: string + format: uuid + description: | + A unique identifier for the dossier attribute definition. This ID is automatically generated by + the system upon creation and is used for referencing the dossier attribute definition in API calls. + name: + type: string + description: | + User-defined name of the dossier attribute definition, capturing its essence. The name needs to be unique + for the dossier template. + type: + type: string + enum: + - TEXT + - NUMBER + - DATE + description: | + Determines the type of the dossier attribute's value. Please note that currently the system + does not validate the values against this definition. This is just a hint for a user interface + that needs to handle invalid entries. Possible values for the type: + - `TEXT`: The value is just a string, i.e., any sequence of characters. + - `NUMBER`: The value is a string expressing a number, with or without decimals. + - `DATE`: The value is a string expressing a date information. + reportingPlaceholder: + type: string + description: | + The name of the placeholder of the dossier attribute that can be used in report templates. The + placeholder follows a specific format convention: + `{{dossier.attribute.}}` while the name is transformed into 'PascalCase' and does not contain + whitespaces. The placeholder is unique in a dossier template. + required: + - name + - type + example: + id: "123e4567-e89b-12d3-a456-426614174000" + name: "Document Summary" + type: "TEXT" + reportingPlaceholder: "{{dossier.attribute.DocumentSummary}}" + FileAttributeDefinition: + type: object + description: | + The `FileAttributeDefinition` object contains the relevant information to define a file attribute. File attributes + are used to manage additional meta-data of files. + properties: + id: + type: string + format: uuid + description: | + A unique identifier for the file attribute definition. This ID is automatically generated by + the system upon creation and is used for referencing the file attribute definition in API calls. + name: + type: string + description: | + User-defined name of the file attribute definition, capturing its essence. The name needs to be unique + for the dossier template. + type: + type: string + enum: + - TEXT + - NUMBER + - DATE + description: | + Determines the type of the file attribute's value. Please note that currently the system + does not validate the values against this definition. This is just a hint for a user interface + that needs to handle invalid entries. Possible values for the type: + - `TEXT`: The value is just a string, i.e., any sequence of characters. + - `NUMBER`: The value is a string expressing a number, with or without decimals. + - `DATE`: The value is a string expressing a date information. + mappedCsvColumnHeader: + type: string + description: | + The name of a CSV column. When importing a CSV file with additional file information the system gets the value from the respective CSV column. + reportingPlaceholder: + type: string + description: | + The name of the placeholder of the file attribute that can be used in report templates. The placeholder follows a specific format convention: + `{{file.attribute.}}` while the name is transformed into 'PascalCase' and does not contain whitespaces. The placeholder is unique in a dossier template. + displaySettings: + $ref: '#/components/schemas/FileAttributeDisplaySettings' + required: + - name + - type + example: + id: "123e4567-e89b-12d3-a456-426614174000" + name: "Document Type" + type: "TEXT" + mappedCsvColumnHeader: "DocumentCategory" + reportingPlaceholder: "{{file.attribute.DocumentType}}" + displaySettings: + primaryAttribute: true + editable: true + filterable: true + displayedInFileList: false + FileAttributeDisplaySettings: + type: object + description: | + Display setting for the RedactManager user interface. These settings control how the UI handles and presents the file attributes. + properties: + primaryAttribute: + type: boolean + description: | + If `true`, the RedactManager user interfaces show the value of the file attribute in the file list below the file name. + editable: + type: boolean + description: | + If `true`, the RedactManager user interfaces allow manual editing of the value. Otherwise only importing and setting by rules would be possible. + filterable: + type: boolean + description: | + If `true`, the RedactManager user interfaces add filter options to the file list. + displayedInFileList: + type: boolean + description: | + if `true`, the RedactManager user interfaces show the values in the file list. + required: + - primaryAttribute + - editable + - filterable + - displayedInFileList + example: + primaryAttribute: false + editable: true + filterable: true + displayedInFileList: false + DossierStatusDefinitionList: + type: object + description: A list of dossier status definitions. + properties: + dossierStatusDefinitions: + items: + $ref: '#/components/schemas/DossierStatusDefinition' + type: array + example: + dossierStatusDefinitions: + - id: "123e7567-e89b-12d3-a456-426614174000" + name: "In Progress" + description: "Dossiers with this status are currently being processed by the users." + rank: 0 + color: "#ffda05" + - id: "23e45378-e90b-12d3-a456-765114174321" + name: "Done" + description: "Dossiers with this status should only contain approved files and indicate that the users have completed the redactions." + rank: 1 + color: "#5eb160" + DossierAttributeDefinitionList: + type: object + description: A list of dossier attribute definitions. + properties: + dossierAttributeDefinitions: + items: + $ref: '#/components/schemas/DossierAttributeDefinition' + type: array + example: + dossierAttributeDefinitions: + - id: "123e4567-e89b-12d3-a456-426614174000" + name: "Dossier Summary" + type: "TEXT" + reportingPlaceholder: "{{dossier.attribute.DossierSummary}}" + - id: "23e45678-e90b-12d3-a456-765114174321" + name: "Comment" + type: "TEXT" + reportingPlaceholder: "{{dossier.attribute.Comment}}" + FileAttributeDefinitionList: + type: object + description: A list of file attribute definitions. + properties: + csvImportSettings: + $ref: '#/components/schemas/CsvImportSettings' + fileAttributeDefinitions: + items: + $ref: '#/components/schemas/FileAttributeDefinition' + type: array + example: + csvImportSettings: + csvMappingActive: true + filenameMappingCsvColumnHeader: "Filename" + delimiter: "," + encoding: "UTF-8" + fileAttributeDefinitions: + - id: "123e4567-e89b-12d3-a456-426614174000" + name: "Document Type" + type: "TEXT" + mappedCsvColumnHeader: "DocumentCategory" + reportingPlaceholder: "{{file.attribute.DocumentType}}" + displaySettings: + primaryAttribute: true + editable: true + filterable: true + displayedInFileList: false + - id: "23e45678-e90b-12d3-a456-765114174321" + name: "Comment" + type: "TEXT" + reportingPlaceholder: "{{file.attribute.Comment}}" + displaySettings: + primaryAttribute: false + editable: true + filterable: false + displayedInFileList: false + CsvImportSettings: + type: object + description: | + This object defines the settings for importing data from a CSV file. It includes + information that indicates if the CSV mapping is active, specifies the column header + used for filename matching, sets the delimiter for column separation, and determines + the encoding of the CSV file. These settings are crucial for accurately importing and + mapping file attributes based on the corresponding CSV records. + properties: + csvMappingActive: + type: boolean + description: | + If `true`, the CSV mapping is active. + filenameMappingCsvColumnHeader: + type: string + description: | + The header of a specific column in the CSV file that should contain values + matching the filenames. A matching value identifies the record that is used + to import the mapped file attributes. + delimiter: + type: string + maxLength: 1 + description: | + The delimiter of the CSV file that is used to distinguish different columns. + encoding: + type: string + description: | + The encoding of the CSV file that is expected when uploading for the import of file attributes. + example: + csvMappingActive: true + filenameMappingCsvColumnHeader: "Filename" + delimiter: "," + encoding: "UTF-8" + ReportTemplateIdList: + type: array + items: + type: string + format: uuid + uniqueItems: true + description: | + List of template identifiers indicating which templates are used for generating reports or other outputs. + The reports are generated when requesting a download package. + example: + - b79cb3ba-745e-5d9a-8903-4a02327a7e09 + - fb3463a0-7d6e-54a3-bcd8-1b93388c648d + DossierAttributes: + type: object + description: Additional dossier attributes that can be set + properties: + attributeIdToValue: + additionalProperties: + type: string + type: object + example: + attributeIdToValue: + "1049a73c-8013-45d6-8217-0845a4ff1c61": This is a dossier attribute value + "79d5a138-d30a-4014-ad7f-43ffba1f4d04": This is yet another dossier attribute value + "1d30d9e8-4a6c-4ef0-96a0-7bef62e138db": "1234" + "b337b65a-0481-48d9-92e6-79e34760ef01": "1. January 1337" + Dossier: + type: object + description: | + The `Dossier` object captures all relevant information related to a specific dossier. A dossier is a collection + of documents, managed and analyzed based on settings and rules of the `DossierTemplate` it is derived from. + properties: + id: + type: string + format: uuid + description: | + A unique identifier for the dossier. This ID is automatically generated by + the system upon creation and is used for referencing the dossier in API calls. + name: + type: string + description: | + User-defined name of the dossier, capturing its essence. The name needs to be unique + for the entire workspace (i.e., tenant). + date: + type: string + format: date-time + description: | + The date when the dossier was created. This is automatically set by the system upon the dossier's creation. + dossierTemplateId: + type: string + format: uuid + description: The unique identifier for the template that governs the settings and rules for this dossier. + description: + type: string + description: A detailed narrative explaining the purpose and contents of the dossier. + ownerId: + type: string + format: uuid + description: | + The identifier of the user or entity responsible for the dossier. Typically, + this is the creator or main contact point for the dossier. Only users with "Manager" + privileges can create dossiers and become dossier owners. + memberIds: + type: array + items: + type: string + format: uuid + uniqueItems: true + description: List of unique user identifiers associated with this dossier, such as contributors or viewers. + approverIds: + type: array + items: + type: string + format: uuid + uniqueItems: true + description: | + List of unique user identifiers with elevated permissions. Needed if using an approval workflow. + visibility: + type: string + enum: + - PRIVATE + - PUBLIC + description: | + Determines who can see the dossier. Possible values include: + - `PRIVATE`: Visible to dossier members only. + - `PUBLIC`: Visible to all users. + + The system sets the value based on the permissions defined in the application. + startDate: + type: string + format: date-time + description: The starting date for the lifecycle of this dossier. + dueDate: + type: string + format: date-time + description: The end date or deadline for actions related to this dossier. + dossierStatusId: + type: string + format: uuid + description: The unique identifier of the current status of the dossier. The status definitions are managed in the dossier template. + watermarkId: + type: string + format: uuid + deprecated: true + description: | + Identifier for the watermark that's to be applied on redacted documents within this dossier. + previewWatermarkId: + type: string + format: uuid + deprecated: true + description: | + Identifier for the watermark pattern used for generated previews documents within this dossier. + dossierAttributes: + $ref: '#/components/schemas/DossierAttributes' + downloadFileTypes: + $ref: '#/components/schemas/DownloadFileTypes' + reportTemplateIds: + $ref: '#/components/schemas/ReportTemplateIdList' + archivedTime: + type: string + format: date-time + description: The date and time when the dossier was archived. Archived dossiers are read-only and cannot be modified. + softDeletedTime: + type: string + format: date-time + description: | + The timestamp indicating when the dossier was softly deleted, i.e., hidden from + most users but not completely removed from the system. `null` if not deleted. + + A soft-deleted dossier can be restored within a retention period configured in the application. + hardDeletedTime: + type: string + format: date-time + description: The timestamp indicating when the dossier was permanently removed from the system. `null` if not deleted. + example: + id: 2ef05d6a-6fb3-4f27-b3c7-9a6438b38c3b + name: Project Alpha + date: 2000-01-23T04:56:07.000+00:00 + dossierTemplateId: 1ed2db85-9cd5-48b0-855f-77ca2a688501 + description: Collection of documents for Project Alpha. + ownerId: a0044ae9-ddca-4f97-b0a1-3cb2517dbf39 + memberIds: + - da8d5f98-ae61-4696-85bf-27986d93877c + - f4a0beb4-4034-4b92-a75e-5f37591326ea + approverIds: + - da8d5f98-ae61-4696-85bf-27986d93877c + - f4a0beb4-4034-4b92-a75e-5f37591326ea + visibility: PRIVATE + startDate: null + dueDate: 2001-01-23T04:56:07.000+00:00 + dossierStatusId: b8280985-f558-43c0-852a-a3fa86803548 + watermarkId: null + previewWatermarkId: null + downloadFileTypes: + - ORIGINAL + - PREVIEW + reportTemplateIds: + - daadea5f-917b-482a-b7d2-e65afe8f80ca + - 8130acf6-4910-4123-827c-caacd8111402 + archivedTime: null + softDeletedTime: null + hardDeletedTime: null + DossierList: + type: object + description: A list of dossiers. + properties: + dossiers: + items: + $ref: '#/components/schemas/Dossier' + type: array + example: + dossiers: + - id: 2ef05d6a-6fb3-4f27-b3c7-9a6438b38c3b + name: Project Alpha + date: 2000-01-23T04:56:07.000+00:00 + dossierTemplateId: 1ed2db85-9cd5-48b0-855f-77ca2a688501 + description: Collection of documents for Project Alpha. + ownerId: a0044ae9-ddca-4f97-b0a1-3cb2517dbf39 + memberIds: + - da8d5f98-ae61-4696-85bf-27986d93877c + - f4a0beb4-4034-4b92-a75e-5f37591326ea + approverIds: + - da8d5f98-ae61-4696-85bf-27986d93877c + - f4a0beb4-4034-4b92-a75e-5f37591326ea + visibility: PUBLIC + startDate: null + dueDate: 2001-01-23T04:56:07.000+00:00 + dossierStatusId: b8280985-f558-43c0-852a-a3fa86803548 + watermarkId: null + previewWatermarkId: null + downloadFileTypes: + - ORIGINAL + - PREVIEW + reportTemplateIds: + - daadea5f-917b-482a-b7d2-e65afe8f80ca + - 8130acf6-4910-4123-827c-caacd8111402 + archivedTime: null + softDeletedTime: null + hardDeletedTime: null + - id: dc28cc82-2682-4b7f-ae0b-2132b205d47a + name: Project Beta + date: 2001-01-23T04:56:07.000+00:00 + dossierTemplateId: 1ed2db85-9cd5-48b0-855f-77ca2a688501 + description: Collection of documents for Project Beta. + ownerId: 0d53a7c3-7b3a-4c57-857d-ec5e0fc3019b + memberIds: + - a0044ae9-ddca-4f97-b0a1-3cb2517dbf39 + - c2e33246-e50a-4c43-831c-6789a5637db8 + - 6123fa16-6943-4b74-8524-54b0046a0ce6 + visibility: PUBLIC + startDate: null + dueDate: 2001-01-23T04:56:07.000+00:00 + dossierStatusId: b8280985-f558-43c0-852a-a3fa86803548 + watermarkId: null + previewWatermarkId: null + downloadFileTypes: + - ORIGINAL + - PREVIEW + reportTemplateIds: + - daadea5f-917b-482a-b7d2-e65afe8f80ca + - 8130acf6-4910-4123-827c-caacd8111402 + archivedTime: null + softDeletedTime: null + hardDeletedTime: null + DossierRequest: + type: object + description: | + Object containing essential attributes for creating or updating a dossier. + Includes information about ownership, members, approvers, and various configurations + like watermarks and download file types. + properties: + id: + type: string + format: uuid + description: | + The unique identifier of the dossier to update. This property needs to be null for create requests, + as the system will generate it automatically. If the value is not a valid identifier, the + endpoint will return a `400 Bad request` error. + name: + type: string + description: | + A human-readable name for the dossier. The name must be unique among all dossiers + within the scope of the workspace (tenant). + description: + type: string + description: | + A textual description providing additional context or information + about the purpose and content of the dossier. + dueDate: + type: string + format: date-time + description: | + Specifies the deadline by which actions related to this dossier should be completed. + The date is in ISO 8601 date-time format. + ownerId: + type: string + format: uuid + description: | + A unique identifier representing the user who owns or created this dossier. + If `null` when creating the dossier, the current user will be set as the owner. + Must not be `null` when updating a dossier. + memberIds: + type: array + description: An array of unique identifiers for users who have access to the dossier. + items: + type: string + description: A unique identifier for a member with access to the dossier. + uniqueItems: true + reportTemplateIds: + $ref: "#/components/schemas/ReportTemplateIdList" + dossierStatusId: + type: string + description: | + An identifier representing the current status of the dossier. + Use `null` to unset the current status. + required: + - name + example: + id: dc28cc82-2682-4b7f-ae0b-2132b205d47a + name: Project Beta + description: Collection of documents for Project Beta. + dueDate: 2001-01-23T04:56:07.000+00:00 + ownerId: 0d53a7c3-7b3a-4c57-857d-ec5e0fc3019b + memberIds: + - a0044ae9-ddca-4f97-b0a1-3cb2517dbf39 + - c2e33246-e50a-4c43-831c-6789a5637db8 + - 6123fa16-6943-4b74-8524-54b0046a0ce6 + reportTemplateIds: + - daadea5f-917b-482a-b7d2-e65afe8f80ca + - 8130acf6-4910-4123-827c-caacd8111402 + dossierStatusId: b8280985-f558-43c0-852a-a3fa86803548 + DossierTemplate: + description: | + The `DossierTemplate` object represents the blueprint for creating and + managing dossiers and for handling and analyzing the files of dossiers. + It contains various settings, rules, and metadata to control the behavior + of individual dossiers that get created based on this template. + example: + id: 1e07cde0-d36a-4ab7-b389-494ca694a0cb + name: RedactManager Example + description: Typical settings for RedactManager. + dateAdded: 2020-01-23T04:56:07.000+00:00 + dateModified: 2021-01-23T04:56:07.000+00:00 + createdBy: c2e33246-e50a-4c43-831c-6789a5637db6 + modifiedBy: c2e33246-e50a-4c43-831c-6789a5637db6 + validFrom: 2020-01-01T00:00:00.000+00:00 + validTo: 2030-12-31T23:59:59.999+00:00 + dossierTemplateStatus: ACTIVE + removeWatermark: false + keepImageMetadata: false + ocrByDefault: false + keepHiddenText: false + keepOverlappingObjects: false + applyDictionaryUpdatesToAllDossiersByDefault: false + downloadFileTypes: + - PREVIEW + - REDACTED + properties: + id: + description: | + A unique identifier generated by the system when the dossier template + is created. This ID is used to reference the template in API calls. + type: string + format: uuid + name: + description: The user-defined name of the dossier template, required for create and update operations. + type: string + description: + description: A detailed description of the purpose and usage of this dossier template. + type: string + dateAdded: + description: The date when this dossier template was first created. Automatically set by the system. + format: date-time + type: string + dateModified: + description: | + The most recent date when modifications were made to this dossier template. + Automatically set by the system. + format: date-time + type: string + createdBy: + description: The identifier of the user who initially created this dossier template. Automatically set by the system. + type: string + format: uuid + modifiedBy: + description: The identifier of the user who last modified this dossier template. Automatically set by the system. + type: string + format: uuid + validFrom: + description: | + The starting date for the validity of this dossier template. Dossiers can only be created + based on this template after this date. + format: date-time + type: string + validTo: + description: | + The ending date for the validity of this dossier template. Dossiers will no longer be able to + be created based on this template after this date. + format: date-time + type: string + downloadFileTypes: + $ref: '#/components/schemas/DownloadFileTypes' + status: + description: | + Indicates the current status of the dossier template: + * `INCOMPLETE`: The dossier template is missing some information or settings. Dossiers cannot be created based + on this template until the missing information and settings are added. + * `INACTIVE`: The dossier template cannot be used for creating dossiers based on it. + * `ACTIVE`: The dossier template can be used for creating dossiers based on it. + enum: + - INCOMPLETE + - INACTIVE + - ACTIVE + type: string + keepImageMetadata: + description: Flag indicating whether metadata from images in PDFs should be retained or removed. + type: boolean + keepHiddenText: + description: Flag indicating whether hidden text in PDFs should be retained or removed. + type: boolean + keepOverlappingObjects: + description: Flag indicating whether overlapping objects in PDFs should be retained or flattened. + type: boolean + applyDictionaryUpdatesToAllDossiersByDefault: + description: | + Flag indicating whether updates to the dictionary should automatically be applied to all dossiers + created from this template. + type: boolean + ocrByDefault: + description: Flag specifying if OCR should be automatically applied to PDFs that get upload to dossiers based on this template. + type: boolean + removeWatermark: + description: Flag specifying if the system should try to remove watermarks in documents prior to OCR processing. + type: boolean + type: object + DownloadFileTypes: + type: array + uniqueItems: true + description: | + Specifies the types of files that will part of the created download package. The defaults can be defined in the dossier template + and can be overwritten individually on each download. + + RedactManager supports `ORIGINAL`, `PREVIEW`, `DELTA_PREVIEW`, and `REDACTED`: + + - `ORIGINAL` Contrary to intuition, this is not the uploaded file, but the pre-processed, + optimized PDF, which may also contain the OCR results. + This is the PDF that used by the system for further processing. + - `PREVIEW` The annotated version of the PDF indicating what gets redacted by the + system. Note that the content to redact is actually still present and readable. + The redaction information is embedded so you can restore the redactions when uploading the + `PREVIEW` PDF into RedactManager. + - `DELTA_PREVIEW` Shows changes if redactions were imported (e.g., by uploading a Preview PDF + with redaction annotations): + Unchanged imported redactions are highlighted in green, removed imported redactions are + highlighted in red, changed imported redactions (e.g. resized) are highlighted in yellow, + and additional redactions are highlighted in blue. + - `REDACTED` The ISO27038 compliant sanitized PDF file that contains all redactions. + items: + enum: + - ORIGINAL + - PREVIEW + - DELTA_PREVIEW + - REDACTED + type: string + example: + - PREVIEW + - REDACTED + DossierTemplateList: + description: Represents a list of dossier templates, each encapsulating a set of rules and settings. + example: + dossierTemplates: + - id: 1e07cde0-d36a-4ab7-b389-494ca694a0cb + name: RedactManager Example + description: Typical settings for RedactManager. + dateAdded: 2020-01-23T04:56:07.000+00:00 + dateModified: 2021-01-23T04:56:07.000+00:00 + createdBy: c2e33246-e50a-4c43-831c-6789a5637db6 + modifiedBy: c2e33246-e50a-4c43-831c-6789a5637db6 + validFrom: 2020-01-01T00:00:00.000+00:00 + validTo: 2030-12-31T23:59:59.999+00:00 + dossierTemplateStatus: ACTIVE + removeWatermark: false + keepImageMetadata: false + ocrByDefault: false + keepHiddenText: false + keepOverlappingObjects: false + applyDictionaryUpdatesToAllDossiersByDefault: false + downloadFileTypes: + - PREVIEW + - REDACTED + - id: 8d8cae48-5c33-4617-ac27-1643f29b79d8 + name: Another RedactManager Example + description: Typical settings for RedactManager. + dateAdded: 2023-09-01T06:54:32.000+00:00 + dateModified: 2023-09-01T06:54:32.000+00:00 + createdBy: 46a7f9d3-6ba0-41d7-b312-b8e708aa6f4d + modifiedBy: 46a7f9d3-6ba0-41d7-b312-b8e708aa6f4d + validFrom: 2023-01-01T00:00:00.000+00:00 + validTo: 2033-12-31T23:59:59.999+00:00 + dossierTemplateStatus: ACTIVE + removeWatermark: true + keepImageMetadata: false + ocrByDefault: true + keepHiddenText: true + keepOverlappingObjects: false + applyDictionaryUpdatesToAllDossiersByDefault: false + downloadFileTypes: + - ORIGINAL + - PREVIEW + - REDACTED + properties: + dossierTemplates: + description: Each entry is a dossier template with its details. + items: + $ref: '#/components/schemas/DossierTemplate' + type: array + type: object + ErrorMessage: + type: object + description: | + Represents an error message returned by the API, providing details on when the error occurred + and a description of the issue. + properties: + timestamp: + type: string + format: date-time + description: The exact date and time when the error was encountered. This can be useful for logging or troubleshooting. + message: + type: string + description: A detailed description of the error, providing insights on the problem and potentially how to resolve or avoid it. + example: + timestamp: "2023-09-21T12:45:00Z" + message: "An error occurred while processing the request." + FileAttributes: + type: object + description: Additional file attributes that can be set or imported + properties: + attributeIdToValue: + additionalProperties: + type: string + type: object + example: + attributeIdToValue: + "9049a73c-8013-45d6-8217-0845a4ff1c61": This is a file attribute value + "59d5a138-d30a-4014-ad7f-43ffba1f4d04": This is yet another file attribute value + "9d30d9e8-4a6c-4ef0-96a0-7bef62e138db": "1234" + "a337b65a-0481-48d9-92e6-79e34760ef01": "1. January 1337" + FileDeleteRequest: + type: object + description: Request payload to initiate the deletion of specific files. + properties: + fileIds: + type: array + description: A list of unique identifiers for the files to be deleted. + items: + type: string + description: The unique identifier of a file. + example: + fileIds: + - 51d3f70ac322c98dc4db70a2ac44115a + - 1fdbd888b39059c8cf171df26f62f8a5 + FileErrorInfo: + type: object + description: Detailed information about an error encountered with a file. + properties: + cause: + type: string + description: The underlying reason or cause of the error. + queue: + type: string + description: The queue or process where the error was encountered. + service: + type: string + description: The specific service or component that reported the error. + timestamp: + type: string + format: date-time + description: The exact time when the error was recorded. + example: + service: LayoutParsingService + cause: The reason or cause why something went wrong. + queue: LAYOUT_PARSING_REQUEST_QUEUE + timestamp: 2000-01-23T04:56:07.000+00:00 + FileStatus: + type: object + description: Object containing information on a specific file. + properties: + id: + type: string + description: The unique identifier of the file. + dossierId: + type: string + format: uuid + description: The unique identifier linking the file to its associated dossier. + dossierTemplateId: + type: string + format: uuid + description: The identifier of the dossier template of the file's dossier. + dossierStatusId: + type: string + format: uuid + description: The identifier of the dossier status of the file's dossier. + filename: + description: The file's name. + type: string + dossierArchived: + description: Tells if the dossier of this file is archived or not. + type: boolean + processingStatus: + type: string + enum: + - ANALYSE + - ERROR + - FULLREPROCESS + - IMAGE_ANALYZING + - INDEXING + - NER_ANALYZING + - OCR_PROCESSING_QUEUED + - OCR_PROCESSING + - PROCESSED + - PROCESSING + - REPROCESS + - UNPROCESSED + - FULL_PROCESSING + - PRE_PROCESSING_QUEUED + - PRE_PROCESSING + - PRE_PROCESSED + - FIGURE_DETECTION_ANALYZING + - TABLE_PARSING_ANALYZING + description: | + The processing status of a file. The states give detailed information about the current processing step. + In general, this information is useful for debugging is the file is stuck in a status. + + The status `UNPROCESSED` indicates a newly uploaded file. Status `ERROR` indicates that the system could not + process the file for some reason. Finally, `PROCESSED` is what you usually expect as the final state after + all required processing steps are done. + workflowStatus: + type: string + enum: + - NEW + - UNDER_REVIEW + - UNDER_APPROVAL + - APPROVED + description: | + The workflow status of a file. + + - `NEW` - Initial status of the uploaded files. + - `UNDER_REVIEW` - The redactions of the document are under review. In this status, users can make corrections + and add redactions. + - `UNDER_APPROVAL` - The redactions have been reviewed and the document is ready for final approval. With this additional status, + a four-eyes-principle can be applied. However, users can still make final corrections and add redactions before + approving the file. + - `APPROVED` - Files have been reviewed and finally approved. Redactions of files in this final state are no longer modified by the + system's automatic analysis and are also read-only for users. I.e., the files must be returned to a previous status to make + changes to the redactions. + numberOfPages: + description: The number of pages of the file. + format: int32 + type: integer + added: + description: Date and time when the file was added to the system. + format: date-time + type: string + lastUpdated: + description: Date and time when the file was last updated. + format: date-time + type: string + numberOfAnalyses: + description: The number of times the file has been analyzed. + format: int32 + type: integer + assignee: + description: The current assignee's (if any) user id. + type: string + lastReviewer: + description: The last reviewer's (if any) user id. + type: string + lastApprover: + description: The user id of the last approver (if any). + type: string + hasRedactions: + description: Shows if any redactions were found during the analysis. + type: boolean + hasHints: + description: Shows if any hints were found during the analysis. + type: boolean + hasRequests: + description: Shows if any requests were found during the analysis. + type: boolean + hasUpdates: + description: | + Shows if there is any change between the previous and current + analysis. + type: boolean + hasImages: + description: Shows if any images were found during the analysis. + type: boolean + ocrStartTime: + description: | + Shows if this file has been OCRed by us. Start time of OCR + Process + format: date-time + type: string + numberOfPagesToOCR: + description: Number of pages to be OCRed by us + format: int32 + type: integer + numberOfOCRedPages: + description: Number of pages already OCRed by us + format: int32 + type: integer + ocrEndTime: + description: Shows if this file has been OCRed by us. End time of OCR Process + format: date-time + type: string + hasAnnotationComments: + description: Shows if this file has comments on annotations. + type: boolean + uploader: + description: The ID of the user who uploaded the file. + type: string + dictionaryVersion: + description: Shows which dictionary versions was used during the analysis. + format: int64 + type: integer + rulesVersion: + description: Shows which rules versions was used during the analysis. + format: int64 + type: integer + legalBasisVersion: + description: Shows which legal basis versions was used during the analysis. + format: int64 + type: integer + excluded: + description: Shows if the file was excluded from analysis. + type: boolean + lastProcessed: + description: Shows the last date of a successful analysis. + format: date-time + type: string + lastLayoutProcessed: + description: Shows the last date of a layout parsing. + format: date-time + type: string + approvalDate: + description: Shows the date of approval, if approved. + format: date-time + type: string + lastUploaded: + description: Shows last date the document was uploaded. + format: date-time + type: string + analysisDuration: + description: Shows how long the last analysis took + format: int64 + type: integer + fileAttributes: + $ref: '#/components/schemas/FileAttributes' + dossierDictionaryVersion: + description: | + Shows which dossier dictionary versions was used during the + analysis. + format: int64 + type: integer + analysisRequired: + description: Shows if the file requires reanalysis. + type: boolean + excludedPages: + description: Set of excluded pages for this file. + items: + description: Set of excluded pages for this file. + format: int32 + type: integer + type: array + uniqueItems: true + softDeletedTime: + description: Shows if the file is soft deleted. + format: date-time + type: string + lastFileAttributeChange: + description: Date and time when the files attributes was last updated. + format: date-time + type: string + hasSuggestions: + description: Shows if there are any Suggestions in this file. + type: boolean + excludedFromAutomaticAnalysis: + description: Shows if the file is excluded from automatic analysis. + type: boolean + redactionModificationDate: + description: Shows the last redaction modification date of this file. + format: date-time + type: string + fileManipulationDate: + description: Shows the date of the last manipulation of this file. + format: date-time + type: string + lastManualChangeDate: + description: | + Shows the date of the last manual change of an annotation of + this file. + format: date-time + type: string + hasHighlights: + description: | + Shows if there are highlights to remove or convert for this + file. + type: boolean + fileSize: + description: Size of the optimized, internally stored file. + format: int64 + type: integer + analysisVersion: + description: Analysis Version. + format: int32 + type: integer + lastIndexed: + description: Last time the file was indexed in ES. + format: date-time + type: string + fileErrorInfo: + $ref: '#/components/schemas/FileErrorInfo' + lastOCRTime: + description: Shows if this file has been OCRed by us. Last Time of OCR. + format: date-time + type: string + example: + hasRedactions: true + added: 2000-01-23T04:56:07.000+00:00 + workflowStatus: NEW + hasAnnotationComments: true + rulesVersion: 2 + excluded: true + lastUpdated: 2000-01-23T04:56:07.000+00:00 + lastManualChangeDate: 2000-01-23T04:56:07.000+00:00 + lastProcessed: 2000-01-23T04:56:07.000+00:00 + excludedFromAutomaticAnalysis: true + dictionaryVersion: 5 + dossierArchived: true + hasRequests: true + lastFileAttributeChange: 2000-01-23T04:56:07.000+00:00 + id: a7f0303d-a33c-4f2e-bd7a-a2adc1b8f92b + analysisDuration: 9 + hasHighlights: true + dossierStatusId: dossierStatusId + fileErrorInfo: + service: service + cause: cause + queue: queue + timestamp: 2000-01-23T04:56:07.000+00:00 + processingStatus: ANALYSE + numberOfAnalyses: 6 + filename: filename + fileAttributes: + myFileAttribute: This is a file attribute value + yetAnotherFileAttribute: This is yet another file attribute value + numericValuesNeedToBeStrings: "1234" + lastOCRTime: 2000-01-23T04:56:07.000+00:00 + legalBasisVersion: 7 + excludedPages: + - 2 + - 2 + redactionModificationDate: 2000-01-23T04:56:07.000+00:00 + fileSize: 4 + fileManipulationDate: 2000-01-23T04:56:07.000+00:00 + dossierId: dossierId + lastUploaded: 2000-01-23T04:56:07.000+00:00 + assignee: assignee + analysisRequired: true + approvalDate: 2000-01-23T04:56:07.000+00:00 + numberOfPagesToOCR: 1 + ocrEndTime: 2000-01-23T04:56:07.000+00:00 + hasHints: true + softDeletedTime: 2000-01-23T04:56:07.000+00:00 + numberOfPages: 0 + uploader: uploader + lastReviewer: lastReviewer + lastIndexed: 2000-01-23T04:56:07.000+00:00 + numberOfOCRedPages: 5 + analysisVersion: 7 + lastLayoutProcessed: 2000-01-23T04:56:07.000+00:00 + lastApprover: lastApprover + ocrStartTime: 2000-01-23T04:56:07.000+00:00 + dossierDictionaryVersion: 3 + dossierTemplateId: dossierTemplateId + hasImages: true + hasUpdates: true + hasSuggestions: true + FileStatusList: + type: object + description: Represents a list detailing the status of multiple files. + properties: + files: + type: array + description: An array of status details for each individual file. + items: + $ref: '#/components/schemas/FileStatus' + example: + files: + - hasRedactions: true + added: 2000-01-23T04:56:07.000+00:00 + workflowStatus: NEW + hasAnnotationComments: true + rulesVersion: 2 + excluded: true + lastUpdated: 2000-01-23T04:56:07.000+00:00 + lastManualChangeDate: 2000-01-23T04:56:07.000+00:00 + lastProcessed: 2000-01-23T04:56:07.000+00:00 + excludedFromAutomaticAnalysis: true + dictionaryVersion: 5 + dossierArchived: true + hasRequests: true + lastFileAttributeChange: 2000-01-23T04:56:07.000+00:00 + id: id + analysisDuration: 9 + hasHighlights: true + dossierStatusId: dossierStatusId + fileErrorInfo: + service: service + cause: cause + queue: queue + timestamp: 2000-01-23T04:56:07.000+00:00 + processingStatus: ANALYSE + numberOfAnalyses: 6 + filename: filename + fileAttributes: + myFileAttribute: This is a file attribute value + yetAnotherFileAttribute: This is yet another file attribute value + numericValuesNeedToBeStrings: "1234" + lastOCRTime: 2000-01-23T04:56:07.000+00:00 + legalBasisVersion: 7 + excludedPages: + - 2 + - 2 + redactionModificationDate: 2000-01-23T04:56:07.000+00:00 + fileSize: 4 + fileManipulationDate: 2000-01-23T04:56:07.000+00:00 + dossierId: 15849e05-5414-498c-b48b-47afa3fd74da + lastUploaded: 2000-01-23T04:56:07.000+00:00 + assignee: assignee + fileId: 4d2334def5fced0003888e47cbc270f7 + analysisRequired: true + approvalDate: 2000-01-23T04:56:07.000+00:00 + numberOfPagesToOCR: 1 + ocrEndTime: 2000-01-23T04:56:07.000+00:00 + hasHints: true + softDeletedTime: 2000-01-23T04:56:07.000+00:00 + numberOfPages: 0 + uploader: uploader + lastReviewer: lastReviewer + lastIndexed: 2000-01-23T04:56:07.000+00:00 + numberOfOCRedPages: 5 + analysisVersion: 7 + lastLayoutProcessed: 2000-01-23T04:56:07.000+00:00 + lastApprover: lastApprover + ocrStartTime: 2000-01-23T04:56:07.000+00:00 + dossierDictionaryVersion: 3 + dossierTemplateId: dossierTemplateId + hasImages: true + hasUpdates: true + hasSuggestions: true + - hasRedactions: true + added: 2000-01-23T04:56:07.000+00:00 + workflowStatus: NEW + hasAnnotationComments: true + rulesVersion: 2 + excluded: true + lastUpdated: 2000-01-23T04:56:07.000+00:00 + lastManualChangeDate: 2000-01-23T04:56:07.000+00:00 + lastProcessed: 2000-01-23T04:56:07.000+00:00 + excludedFromAutomaticAnalysis: true + dictionaryVersion: 5 + dossierArchived: true + hasRequests: true + lastFileAttributeChange: 2000-01-23T04:56:07.000+00:00 + id: id + analysisDuration: 9 + hasHighlights: true + dossierStatusId: dossierStatusId + fileErrorInfo: + service: service + cause: cause + queue: queue + timestamp: 2000-01-23T04:56:07.000+00:00 + processingStatus: ANALYSE + numberOfAnalyses: 6 + filename: filename + fileAttributes: + myFileAttribute: This is a file attribute value + yetAnotherFileAttribute: This is yet another file attribute value + numericValuesNeedToBeStrings: "1234" + lastOCRTime: 2000-01-23T04:56:07.000+00:00 + legalBasisVersion: 7 + excludedPages: + - 2 + - 2 + redactionModificationDate: 2000-01-23T04:56:07.000+00:00 + fileSize: 4 + fileManipulationDate: 2000-01-23T04:56:07.000+00:00 + dossierId: dossierId + lastUploaded: 2000-01-23T04:56:07.000+00:00 + assignee: assignee + fileId: 1fdbd888b39059c8cf171df26f62f8a5 + analysisRequired: true + approvalDate: 2000-01-23T04:56:07.000+00:00 + numberOfPagesToOCR: 1 + ocrEndTime: 2000-01-23T04:56:07.000+00:00 + hasHints: true + softDeletedTime: 2000-01-23T04:56:07.000+00:00 + numberOfPages: 0 + uploader: uploader + lastReviewer: lastReviewer + lastIndexed: 2000-01-23T04:56:07.000+00:00 + numberOfOCRedPages: 5 + analysisVersion: 7 + lastLayoutProcessed: 2000-01-23T04:56:07.000+00:00 + lastApprover: lastApprover + ocrStartTime: 2000-01-23T04:56:07.000+00:00 + dossierDictionaryVersion: 3 + dossierTemplateId: dossierTemplateId + hasImages: true + hasUpdates: true + hasSuggestions: true + FileUploadResult: + description: Object containing information about a successfully uploaded file. + example: + fileIds: + - fileIds + - fileIds + processedFileIds: + - processedFileIds + - processedFileIds + processedAttributes: + - processedAttributes + - processedAttributes + properties: + fileIds: + description: List of fileIds generated for uploaded file(s). + items: + description: List of fileIds generated for uploaded file(s). + type: string + type: array + processedAttributes: + description: | + List processed file attributes, in case the upload contained + a CSV. + items: + description: | + List processed file attributes, in case the upload contained + a CSV. + type: string + type: array + processedFileIds: + description: List processed fileIds, in case the upload contained a CSV. + items: + description: List processed fileIds, in case the upload contained a CSV. + type: string + type: array + type: object + DownloadStatus: + type: object + description: Detailed information about a specific download. + properties: + id: + type: string + format: uuid + description: The unique identifier of the download. + example: b5e2cf01-8bb6-4fcd-ad88-0efb611195da + userId: + type: string + format: uuid + description: The unique identifier of the user who initiated the download. + example: caa8b54a-eb5e-4134-8ae2-a3946a428ec7 + filename: + type: string + description: The name of the download file. + example: my-redacted-dossier.zip + mimeType: + type: string + description: The mime type of the download file. + example: application/octet-stream + errorCause: + type: string + description: | + If the status is `FAILED`, this field contains information about the error that happened + while preparing the download package. This information is intended to be included in a + bug report if the error occurs repeatedly and indicates a general problem with RedactManager. + example: "" + status: + type: string + enum: + - QUEUED + - GENERATING + - COMPRESSING + - READY + - FAILED + description: | + The status of the download file. In particular: + - `QUEUED` - The download job has been created and is waiting to be processed by the system. + - `GENERATING` - The system currently creates the files for the download package. + - `COMPRESSING` - The system creates a ZIP archive that will contain all files of the download package. + - `READY` - The download package is ready for download. Please note that the download will be kept only + for a certain period. This period can be configured in the settings of your RedactManager workspace. + - `FAILED` - An error occurred while preparing the download. The `errorCause` field might contain + additional details on the error. + example: READY + creationDate: + type: string + format: date-time + description: The date and time when the user initiated the download. + example: 2023-03-29T11:41:08.886Z + lastDownload: + type: string + format: date-time + description: The date and time when the user last downloaded the file. + example: 2023-03-29T13:11:05.123Z + fileSize: + type: integer + format: int64 + description: The size of the download file in bytes. + example: 1654231 + dossierId: + type: string + format: uuid + description: The identifier of the dossier to which the content of the download package belongs. + example: 20354d7a-e4fe-47af-8ff6-187bca92f3f9 + fileIds: + type: array + items: + type: string + description: The list of file identifiers to which the content of the download package belongs. + example: + - 51d3f70ac322c98dc4db70a2ac44115a + - 1fdbd888b39059c8cf171df26f62f8a5 + downloadFileTypes: + $ref: '#/components/schemas/DownloadFileTypes' + reportTemplateIds: + $ref: '#/components/schemas/ReportTemplateIdList' + DownloadStatusList: + type: object + description: Represents a list detailing the status of multiple downloads. + properties: + downloadStatus: + type: array + items: + $ref: '#/components/schemas/DownloadStatus' + description: Each item contains the status details of a download. + DownloadRequest: + type: object + description: Request payload to initiate the preparation of the download. + properties: + downloadFileTypes: + $ref: '#/components/schemas/DownloadFileTypes' + reportTemplateIds: + $ref: '#/components/schemas/ReportTemplateIdList' + redactionPreviewColor: + type: string + example: "#9398a0" + description: | + A hexadecimal color code that is used to define the highlighting color of the redaction annotations in + the `PREVIEW` file. + + - Black is `#000000` + - White is `#ffffff` + - Grey is `#cccccc` + BulkDownloadRequest: + allOf: + - $ref: '#/components/schemas/DownloadRequest' + - type: object + description: Request payload to initiate the preparation of the download of multiple files. + properties: + fileIds: + type: array + description: A list with unique identifiers of the files for which the download is to be prepared. + items: + type: string + description: The unique identifier of a file. + User: + type: object + description: Basic information about a user. + properties: + id: + type: string + format: uuid + description: The unique identifier of the user. + example: efe7eedd-89c5-56f5-984c-0712ee41a2eb + username: + type: string + description: The user name that is used for logging in. + example: myusername + email: + type: string + description: The email address of the user. + example: myusername@example.com + firstName: + type: string + description: The first name of the user. + example: John + lastName: + type: string + description: The last name of the user. + example: Doe + roles: + uniqueItems: true + type: array + description: | + The roles of the user. In particular: + - `USER` - default user permission to work with RedactManager + - `MANAGER` - additional privileges to create and manage dossiers + - `USER_ADMIN` - administration privileges limited to manage users only + - `ADMIN` - general administration privileges + items: + type: string + enum: + - USER + - MANAGER + - USER_ADMIN + - ADMIN + example: + - MANAGER + - ADMIN + active: + type: boolean + description: Indicator if the user is active or not. Only active users can log in. + UserList: + type: object + description: A list of multiple users. + properties: + downloadStatus: + type: array + items: + $ref: '#/components/schemas/User' + description: Each item contains the details of a user. + LicenseReport: + type: object + description: A comprehensive report of licensing metrics and usage statistics. + properties: + startDate: + type: string + format: date-time + description: The starting date of the report. + endDate: + type: string + format: date-time + description: The ending date of the report. + numberOfAnalyzedFiles: + type: integer + format: int32 + description: | + The count of files that have been analyzed in the requested period. The counter is + increased only once for each file, regardless of the number of analysis. + numberOfAnalyzedPages: + type: integer + format: int32 + description: | + The count of pages that have been analyzed in the requested period. The counter is + increased only once for each file, regardless of the number of analysis. + analyzedFilesBytes: + type: integer + format: int64 + description: | + The size in bytes of the files that have been analyzed in the requested period. + The counter is increased only once for each file, regardless of the number of analysis. + totalFilesUploadedBytes: + type: integer + format: int64 + description: The total size in bytes of all present files at the end of the period. + activeFilesUploadedBytes: + type: integer + format: int64 + description: The size in bytes of all active files at the end of the period. + trashFilesUploadedBytes: + type: integer + format: int64 + description: The size in bytes of all soft-deleted files at the end of the period. + archivedFilesUploadedBytes: + type: integer + format: int64 + description: The size in bytes of all archived files at the end of the period. + numberOfOcrFiles: + type: integer + format: int32 + description: | + The count of files that have undergone OCR (Optical Character Recognition) in the + requested period. + numberOfOcrPages: + type: integer + format: int32 + description: The count of pages that have undergone OCR in the requested period. + numberOfDossiers: + type: integer + format: int32 + description: The total number of present dossiers at the end of the period. + monthlyData: + description: A list of data metrics categorized on a monthly basis. + items: + $ref: '#/components/schemas/MonthlyReportData' + type: array + example: + startDate: 2000-01-01T00:00:00.000+00:00 + endDate: 2001-01-01T00:00:00.000+00:00 + numberOfAnalyzedFiles: 5 + numberOfAnalyzedPages: 9 + analyzedFilesBytes: 30000 + totalFilesUploadedBytes: 30000 + activeFilesUploadedBytes: 20000 + archivedFilesUploadedBytes: 5000 + trashFilesUploadedBytes: 5000 + numberOfOcrFiles: 2 + numberOfOcrPages: 2 + numberOfDossiers: 1 + monthlyData: + - startDate: 2000-01-01T00:00:00.000+00:00 + endDate: 2000-02-01T00:00:00.000+00:00 + numberOfAnalyzedPages: 7 + analyzedFilesBytes: 25000 + totalFilesUploadedBytes: 25000 + activeFilesUploadedBytes: 22000 + archivedFilesUploadedBytes: 0 + trashFilesUploadedBytes: 3000 + numberOfOcrPages: 1 + - startDate: 2000-02-01T00:00:00.000+00:00 + endDate: 2000-03-01T00:00:00.000+00:00 + numberOfAnalyzedPages: 2 + analyzedFilesBytes: 5000 + totalFilesUploadedBytes: 30000 + activeFilesUploadedBytes: 20000 + archivedFilesUploadedBytes: 5000 + trashFilesUploadedBytes: 5000 + numberOfOcrPages: 1 + LicenseReportRequest: + type: object + description: Request object to retrieve a license report for a given date range. + properties: + startDate: + type: string + format: date-time + description: The start date for the requested report. + endDate: + type: string + format: date-time + description: The end date for the requested report. + example: + startDate: 2000-01-01T00:00:00.000+00:00 + endDate: 2001-01-01T00:00:00.000+00:00 + MonthlyReportData: + type: object + description: Detailed metrics regarding license usage for a given month. + properties: + startDate: + type: string + format: date-time + description: The starting date of the respective month. + endDate: + type: string + format: date-time + description: The ending date of the respective month. + numberOfAnalyzedPages: + type: integer + format: int32 + description: | + The count of pages that have been analyzed in the respective month. The counter is + increased only once for each file, regardless of the number of analysis. + analyzedFilesBytes: + type: integer + format: int64 + description: | + The size in bytes of the files that have been analyzed in the respective month. + The counter is increased only once for each file, regardless of the number of analysis. + totalFilesUploadedBytes: + type: integer + format: int64 + description: The total size in bytes of all present files at the end of the respective month. + activeFilesUploadedBytes: + type: integer + format: int64 + description: The size in bytes of all active files at the end of the respective month. + trashFilesUploadedBytes: + type: integer + format: int64 + description: The size in bytes of all soft-deleted files at the end of the respective month. + archivedFilesUploadedBytes: + type: integer + format: int64 + description: The size in bytes of all archived files at the end of the respective month. + numberOfOcrPages: + type: integer + format: int32 + description: The count of pages that have undergone OCR in the requested respective month. + example: + startDate: 2000-01-01T00:00:00.000+00:00 + endDate: 2000-02-01T00:00:00.000+00:00 + numberOfAnalyzedPages: 7 + analyzedFilesBytes: 25000 + totalFilesUploadedBytes: 25000 + activeFilesUploadedBytes: 22000 + archivedFilesUploadedBytes: 0 + trashFilesUploadedBytes: 3000 + numberOfOcrPages: 1 + UploadRequest: + type: object + description: Request object to upload a file. + properties: + file: + type: string + format: binary + description: The binary content of the file to be uploaded. + required: + - file + securitySchemes: + FF-OAUTH: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: /auth/realms/{workspaceId}/protocol/openid-connect/auth + tokenUrl: /auth/realms/{workspaceId}/protocol/openid-connect/token + scopes: {} + clientCredentials: + tokenUrl: /auth/realms/{workspaceId}/protocol/openid-connect/token + scopes: {} \ No newline at end of file -- 2.47.2 From 72b8c1a949d6ca1a1570b36a0319501bf69ce129 Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Tue, 28 May 2024 16:45:31 +0200 Subject: [PATCH 26/26] RED-8670: integrate table inference from research * introduce controllerAdvice to ControllerV2 package * remove unnecessary analysis requirement calculation for deleted files * silence javadoc lint warnings (e.g. no comment) --- .../src/main/resources/api/documine.yaml | 62 +++++++++++-------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/documine.yaml b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/documine.yaml index 769a0363c..62e0f1818 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/documine.yaml +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api/documine.yaml @@ -404,24 +404,33 @@ paths: post: operationId: uploadMapping summary: Upload a new component mapping to a DossierTemplate. - description: | + description: | Use this endpoint to upload a new component mapping to a specific DossierTemplate. - - **File Format:** CSV (comma-separated values). - - **Header:** The first row should contain column labels. - - **Data Structure:** Ensure all rows have the same number of columns (rectangular data). - - **Sorting:** Rows will be sorted by the values in each column, from left to right, for faster lookups. - - **Customization:** Users can specify the delimiter and encoding of the CSV file. - - **Usage** The component mapping file can be used in component rules to relate components to existing master data. + #### File Requirements + - **Format:** The file must be in CSV (comma-separated values) format. + - **Header Row:** The first row should contain the column labels. + - **Data Consistency:** All rows must have the same number of columns to ensure rectangular data structure. - **Tip:** Place keys to be queried in the first columns and results to be mapped in the last column for best performance. + #### Sorting and Performance + - **Sorting:** Rows are automatically sorted by the values in each column, from left to right, to enhance lookup speed. + - **Optimization Tip:** Place keys to be queried in the first columns and the results to be mapped in the last column for best performance. + + #### Customization Options + Users can specify the delimiter and encoding used in the CSV file. + + #### Usage + The component mapping file can be utilized in component rules to relate components to existing master data. + + #### Example + + | search_value | mapped_value | + |--------------|--------------| + | Alice | Manager | + | Bob | Developer | + | Charlie | Analyst | + - Example: - | search_value | mapped_value | - | ------------ | ------------ | - | Alice | Manager | - | Bob | Developer | - | Charlie | Analyst | tags: - 1. Dossier Templates requestBody: @@ -500,21 +509,20 @@ paths: description: | Use this endpoint to update an existing component mapping of a specific dossier template. - - **File Format:** CSV (comma-separated values). - - **Header:** The first row should contain column labels. - - **Data Structure:** Ensure all rows have the same number of columns (rectangular data). - - **Sorting:** Rows will be sorted by the values in each column, from left to right, for faster lookups. - - **Customization:** Users can specify the delimiter and encoding of the CSV file. - - **Usage** The component mapping file can be used in component rules to relate components to existing master data. + #### File Requirements + - **Format:** The file must be in CSV (comma-separated values) format. + - **Header Row:** The first row should contain the column labels. + - **Data Consistency:** All rows must have the same number of columns to ensure rectangular data structure. - **Tip:** Place keys to be queried in the first columns and results to be mapped in the last column for best performance. + #### Sorting and Performance + - **Sorting:** Rows are automatically sorted by the values in each column, from left to right, to enhance lookup speed. + - **Optimization Tip:** Place keys to be queried in the first columns and the results to be mapped in the last column for best performance. + + #### Customization Options + Users can specify the delimiter and encoding used in the CSV file. - Example: - | search_value | mapped_value | - | ------------ | ------------ | - | Alice | Manager | - | Bob | Developer | - | Charlie | Analyst | + #### Usage + The component mapping file can be utilized in component rules to relate components to existing master data. tags: - 1. Dossier Templates requestBody: -- 2.47.2