Compare commits

...

60 Commits

Author SHA1 Message Date
Kilian Schuettler
bf6a4b9287 reformat 2024-07-26 13:22:45 +02:00
Ali Oezyetimoglu
71d528269c Merge branch 'RED-8951-bp' into 'release/2.465.x'
RED-8951: made it possible to import entities with dictionary but hasDictionary=false

See merge request redactmanager/persistence-service!620
2024-07-24 15:36:12 +02:00
Ali Oezyetimoglu
054a06b17b RED-8951: made it possible to import entities with dictionary but hasDictionary=false
(cherry picked from commit 784b67ee5a4d0f69e667b26361e3399d8ea256e0)
2024-07-24 15:20:24 +02:00
Kilian Schüttler
47383a280e Merge branch 'RED-9272' into 'release/2.465.x'
RED-9272: increase storageToMongoDb performance

See merge request redactmanager/persistence-service!614
2024-07-23 00:10:19 +02:00
Kilian Schuettler
a7a965a831 RED-9272: increase storageToMongoDb performance 2024-07-22 16:44:35 +02:00
Kilian Schüttler
29d2a911c6 Merge branch 'RED-9255-bp' into 'release/2.465.x'
Resolve RED-9255 "Bp"

See merge request redactmanager/persistence-service!612
2024-07-22 16:30:59 +02:00
Kilian Schüttler
12724b8dfd Resolve RED-9255 "Bp" 2024-07-22 16:30:59 +02:00
Dominique Eifländer
e404bd2d1b Merge branch 'RED-9658-BackupEntitlog' into 'release/2.465.x'
RED-9658: Do not delete entityLogs after mongo migration

See merge request redactmanager/persistence-service!608
2024-07-18 09:13:09 +02:00
Dominique Eifländer
2753ca5bab RED-9658: Do not delete entityLogs after mongo migration 2024-07-17 16:59:21 +02:00
Dominique Eifländer
cee56f9ffa Merge branch 'RED-9658-mongo-4.1' into 'release/2.465.x'
RED-9658: Fixed wrong mongo database name

See merge request redactmanager/persistence-service!606
2024-07-17 10:31:07 +02:00
Dominique Eifländer
0d271ec8c5 RED-9658: Fixed wrong mongo database name 2024-07-17 09:59:18 +02:00
Corina Olariu
751c764873 Merge branch 'RED-9608-bp2' into 'release/2.465.x'
RED-9608 - Comments not removed after uploading multiple files via ZIP-Archive...

See merge request redactmanager/persistence-service!604
2024-07-16 16:47:54 +02:00
Dominique Eifländer
be9b7e9fad Merge branch 'RED-9658' into 'release/2.465.x'
RED-9658: Fixed Remove old ocr service queues in migration

See merge request redactmanager/persistence-service!603
2024-07-16 15:57:38 +02:00
Dominique Eifländer
e045641736 RED-9658: Fixed Remove old ocr service queues in migration 2024-07-16 15:40:52 +02:00
corinaolariu
2141cbecc8 RED-9608 - Comments not removed after uploading multiple files via ZIP-Archive with "Overwrite and start over" selected - bp
- update comment repository
2024-07-16 15:21:22 +03:00
Andrei Isvoran
59c8f27b51 Merge branch 'RED-9606-bp' into 'release/2.465.x'
RED-9606 - Bulk actions on many annotations

See merge request redactmanager/persistence-service!599
2024-07-16 12:08:42 +02:00
Andrei Isvoran
c2307b1121 RED-9606 - Bulk actions on many annotations 2024-07-16 12:54:42 +03:00
Dominique Eifländer
b642eed7ba Merge branch 'RED-9658-queues' into 'release/2.465.x'
RED-9658: Remove old ocr service queues in migration

See merge request redactmanager/persistence-service!598
2024-07-16 10:38:06 +02:00
Dominique Eifländer
ae29f2ec89 RED-9658: Remove old ocr service queues in migration 2024-07-16 10:26:24 +02:00
Corina Olariu
e36872947b Merge branch 'RED-9608-bp' into 'release/2.465.x'
RED-9608 - Comments not removed after uploading multiple files via ZIP-Archive...

See merge request redactmanager/persistence-service!597
2024-07-16 09:53:58 +02:00
corinaolariu
d1ad080e27 RED-9608 - Comments not removed after uploading multiple files via ZIP-Archive with "Overwrite and start over" selected
- solve sonar issues
2024-07-15 16:43:59 +03:00
Andrei Isvoran
26e3ebc850 Merge branch 'RED-9555-analysis-disabled-bp' into 'release/2.465.x'
RED-9555 - Keep force redacted image applied after changing legal basis

See merge request redactmanager/persistence-service!595
2024-07-15 15:43:35 +02:00
Andrei Isvoran
b758c2f747 RED-9555 - Keep force redacted image applied after changing legal basis 2024-07-15 15:43:34 +02:00
corinaolariu
27ac2ddcb8 RED-9608 - Comments not removed after uploading multiple files via ZIP-Archive with "Overwrite and start over" selected
- remove (soft-delete) all comments, not just the ones related to the manual redactions at overwrite
- restore back all comments (not just the ones related to the manual redactions) when file is restored
- unit tests added
2024-07-15 16:01:54 +03:00
Dominique Eifländer
bba18e40b0 Merge branch 'RED-9658' into 'release/2.465.x'
RED-9658: Fixed changed order of liquibase migrations

See merge request redactmanager/persistence-service!593
2024-07-15 13:17:10 +02:00
Dominique Eifländer
ad4179f2b4 RED-9658: Fixed changed order of liquibase migrations 2024-07-15 13:06:04 +02:00
Andrei Isvoran
fa0a16210b Merge branch 'RED-9622' into 'release/2.465.x'
RED-9622 - Introduce csv validation

See merge request redactmanager/persistence-service!591
2024-07-15 09:51:37 +02:00
Andrei Isvoran
c57ee4fa08 RED-9622 - Introduce csv validation 2024-07-15 09:51:37 +02:00
Andrei Isvoran
058d44da90 Merge branch 'RED-9606-bp' into 'release/2.465.x'
RED-9606 - Fix removal

See merge request redactmanager/persistence-service!585
2024-07-11 14:36:36 +02:00
Andrei Isvoran
73ce9cec82 RED-9606 - Fix removal 2024-07-11 15:03:02 +03:00
Kilian Schüttler
067fbbe776 Merge branch 'RED-8876-bp' into 'release/2.465.x'
Edit pending dict entry messages

See merge request redactmanager/persistence-service!584
2024-07-11 11:04:51 +02:00
Kilian Schüttler
a586770704 Edit pending dict entry messages 2024-07-11 11:04:50 +02:00
Kilian Schüttler
e8451b3f5b Merge branch 'RED-9255-bp' into 'release/2.465.x'
Fix some NPEs

See merge request redactmanager/persistence-service!575
2024-07-11 10:44:11 +02:00
Kilian Schüttler
f4af62f7bf Fix some NPEs 2024-07-11 10:44:11 +02:00
Ali Oezyetimoglu
7ab65b311d Merge branch 'RED-9573-bp' into 'release/2.465.x'
RED-9573: updated redaction-report-service version to enable updated cache mechanism

See merge request redactmanager/persistence-service!582
2024-07-11 09:55:13 +02:00
Ali Oezyetimoglu
87a6152632 RED-9573: updated redaction-report-service version to enable updated cache mechanism 2024-07-11 09:36:56 +02:00
Andrei Isvoran
f9d2a7a53d Merge branch 'RED-9604-41-bp' into 'release/2.465.x'
RED-9604 - Don't check that removed entries still have comments

See merge request redactmanager/persistence-service!581
2024-07-11 08:57:29 +02:00
Andrei Isvoran
1766c40c6e RED-9604 - Don't check that removed entries still have comments 2024-07-10 17:37:00 +03:00
Corina Olariu
b4c7ae0990 Merge branch 'RED-9515-bp' into 'release/2.465.x'
RED-9515 - When locally added annotations are locally removed while...

See merge request redactmanager/persistence-service!578
2024-07-10 13:04:10 +02:00
corinaolariu
bf6ec0897a RED-9515 - When locally added annotations are locally removed while auto-analysis is off, its skipped - bp
- fix sonar issues
2024-07-10 13:18:44 +03:00
corinaolariu
54dbd1cdff RED-9515 - When locally added annotations are locally removed while auto-analysis is off, its skipped - bp
- special case when removing local add (and other local changes to this add annotation) annotations to set the state to REMOVED instead of IGNORED.
- unit tests done
2024-07-10 13:16:03 +03:00
Andrei Isvoran
1a9523812d Merge branch 'RED-9496-fix-shutdown-bp' into 'release/2.465.x'
RED-9496 - Implement graceful shutdown

See merge request redactmanager/persistence-service!572
2024-07-05 10:57:42 +02:00
Andrei Isvoran
c747961b94 RED-9496 - Implement graceful shutdown 2024-07-04 17:06:55 +03:00
Andrei Isvoran
91e39bb8ca Merge branch 'RED-9498-bp' into 'release/2.465.x'
RED-9496 - Implement graceful shutdown

See merge request redactmanager/persistence-service!568
2024-07-04 14:11:25 +02:00
Andrei Isvoran
2118559264 RED-9496 - Implement graceful shutdown 2024-07-04 13:15:07 +03:00
Ali Oezyetimoglu
f76d55137f Merge branch 'RED-9499-bp' into 'release/2.465.x'
RED-9499: increased id size of entity fo fit typeId length of a created entity

See merge request redactmanager/persistence-service!577
2024-07-04 09:19:30 +02:00
Ali Oezyetimoglu
0fafad458d Update TypeTest.java 2024-07-03 16:17:53 +02:00
Ali Oezyetimoglu
81198cc946 RED-9499: increased id size of entity fo fit typeId length of a created entity
* catch too long names
* added JUnit test
2024-07-03 15:44:53 +02:00
Dominique Eifländer
eeb2838bcb Merge branch 'RED-9530-4.1' into 'release/2.465.x'
RED-9530: Fixed single and bulk create download endpoints in api v2

See merge request redactmanager/persistence-service!574
2024-07-03 11:39:20 +02:00
Dominique Eifländer
9b2e17f079 RED-9530: Fixed single and bulk create download endpoints in api v2 2024-07-03 11:22:39 +02:00
Dominique Eifländer
31c2d48371 Merge branch 'RED-9487-4.1' into 'release/2.465.x'
RED-9487: Fixed resizing of manual redactions when auto analysis is disabled

See merge request redactmanager/persistence-service!570
2024-07-01 12:24:40 +02:00
Dominique Eifländer
c0c2f453ab RED-9487: Fixed resizing of manual redactions when auto analysis is disabled 2024-07-01 12:09:55 +02:00
Andrei Isvoran
674ca41df3 Merge branch 'RED-9348' into 'release/2.465.x'
RED-9349 - Add check for dossier template length

See merge request redactmanager/persistence-service!568
2024-07-01 10:20:22 +02:00
Andrei Isvoran
63c3b347cc RED-9349 - Add check for dossier template length 2024-07-01 10:18:49 +03:00
Dominique Eifländer
459467e824 Merge branch 'hotfix-bp' into 'release/2.465.x'
hotfix: Fixed logging

See merge request redactmanager/persistence-service!566
2024-06-28 10:46:01 +02:00
Dominique Eifländer
a5350d048a hotfix: Fixed logging
(cherry picked from commit c4e682254b81c3a45391b88921b7d2c73f37eb56)
2024-06-28 10:31:45 +02:00
Kilian Schüttler
9afd6ab594 Merge branch 'RED-9255-bp' into 'release/2.465.x'
RED-9255: fix annotations, refactor for observability

See merge request redactmanager/persistence-service!563
2024-06-28 09:55:54 +02:00
Corina Olariu
ca3cad0270 Merge branch 'RED-9347-bp' into 'release/2.465.x'
Revert "RED-9347 - Make /rss controller unavailable"

See merge request redactmanager/persistence-service!564
2024-06-28 09:54:56 +02:00
corinaolariu
1c859184e2 Revert "RED-9347 - Make /rss controller unavailable"
This reverts commit 94ecfbd1b48d8330770f3da6bea1555a677e6e97.
2024-06-28 10:35:13 +03:00
Kilian Schuettler
b3c899e6f8 RED-9255: fix annotations, refactor for observability 2024-06-27 19:08:27 +02:00
209 changed files with 2410 additions and 941 deletions

View File

@ -8,7 +8,7 @@ plugins {
val redactionServiceVersion by rootProject.extra { "4.290.0" }
val pdftronRedactionServiceVersion by rootProject.extra { "4.48.0" }
val redactionReportServiceVersion by rootProject.extra { "4.64.0" }
val redactionReportServiceVersion by rootProject.extra { "4.73.6" }
val searchServiceVersion by rootProject.extra { "2.90.0" }
repositories {

View File

@ -381,7 +381,6 @@ public class DossierController implements DossierResource {
throw new AccessDeniedException("Can not delete dossier that is owned by a different user");
}
dossierManagementService.softDeleteDossier(dossierId);
auditPersistenceService.audit(AuditRequest.builder()
@ -450,7 +449,7 @@ public class DossierController implements DossierResource {
@PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')")
public List<Dossier> getSoftDeletedDossiers() {
var dossiers = dossierManagementService.getSoftDeletedDossiers()
var dossiers = dossierManagementService.getSoftDeletedDossiers()
.stream()
.map(dossierACLService::enhanceDossierWithACLData)
.collect(Collectors.toList());
@ -463,7 +462,7 @@ public class DossierController implements DossierResource {
@PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')")
public List<Dossier> getArchivedDossiers() {
var dossiers = dossierManagementService.getArchivedDossiers()
var dossiers = dossierManagementService.getArchivedDossiers()
.stream()
.map(dossierACLService::enhanceDossierWithACLData)
.collect(Collectors.toList());

View File

@ -37,7 +37,10 @@ public class EntityLogController implements EntityLogResource {
accessControlService.checkViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
return entityLogResponseMapper.toLogResponse(entityLogService.getEntityLog(dossierId, fileId, excludedTypes == null ? new ArrayList<>() : excludedTypes, includeUnprocessed));
return entityLogResponseMapper.toLogResponse(entityLogService.getEntityLog(dossierId,
fileId,
excludedTypes == null ? new ArrayList<>() : excludedTypes,
includeUnprocessed));
}

View File

@ -276,11 +276,7 @@ public class ManualRedactionController implements ManualRedactionResource {
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsMemberOrApprover(dossierId);
List<ManualAddResponse> responseList = manualRedactionService.addRecategorization(dossierId,
fileId,
dossier,
recategorizationRequests,
includeUnprocessed);
List<ManualAddResponse> responseList = manualRedactionService.addRecategorization(dossierId, fileId, dossier, recategorizationRequests, includeUnprocessed);
responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())

View File

@ -109,6 +109,7 @@ public class ReportTemplateController implements ReportTemplateResource {
.multiFileReport(multiFileReport)
.build();
var reportTemplate = reportTemplateService.uploadTemplate(reportTemplateUploadRequest);
reportTemplatePlaceholderClient.evictReportTemplateCache();
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(reportTemplate.getTemplateId())
@ -173,6 +174,7 @@ public class ReportTemplateController implements ReportTemplateResource {
var storageId = reportTemplatePersistenceService.find(templateId).getStorageId();
storageService.deleteObject(TenantContext.getTenantId(), storageId);
reportTemplatePersistenceService.delete(templateId);
reportTemplatePlaceholderClient.evictReportTemplateCache();
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(templateId)

View File

@ -1,5 +1,6 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.IMPORT_FILES;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.USE_SUPPORT_CONTROLLER;
import java.io.IOException;
@ -23,6 +24,8 @@ import com.iqser.red.service.persistence.management.v1.processor.service.FileSta
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusMapper;
import com.iqser.red.service.persistence.management.v1.processor.service.ReanalysisService;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.service.FileExchangeExportService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ImportResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ReanalysisSettings;
import com.iqser.red.service.persistence.service.v1.api.external.resource.SupportResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DownloadResponse;
@ -132,7 +135,8 @@ public class SupportController implements SupportResource {
@Override
public void importFiles(MultipartFile file) {
@PreAuthorize("hasAuthority('" + IMPORT_FILES + "')")
public ImportResponse importFiles(MultipartFile file) {
byte[] bytes;
try {
@ -140,7 +144,7 @@ public class SupportController implements SupportResource {
} catch (IOException e) {
throw new BadRequestException("File could not be read and is likely corrupted.", e);
}
fileExchangeImportService.importFileExchangeArchive(KeycloakSecurity.getUserId(), bytes);
return fileExchangeImportService.importFileExchangeArchive(KeycloakSecurity.getUserId(), bytes);
}
}

View File

@ -27,34 +27,37 @@ public class DownloadControllerV2 implements DownloadResource {
private final DownloadController downloadController;
private final DownloadStatusPersistenceService downloadStatusPersistenceService;
@Transactional
public DownloadStatusList getDownloadStatusList() {
var downloads = downloadStatusPersistenceService.getStatusesByUser(KeycloakSecurity.getUserId());
return new DownloadStatusList(downloads.stream().map(
status ->
DownloadStatus.builder()
.id(status.getUuid()) // This is a workaround the real id is the storageId.
.userId(status.getUserId())
.filename(status.getFilename())
.mimeType(status.getMimeType())
.errorCause(status.getErrorCause())
.status(status.getStatus())
.creationDate(status.getCreationDate())
.lastDownload(status.getLastDownload())
.fileSize(status.getFileSize())
.dossierId(status.getDossier() != null ? status.getDossier().getId() : null)
.fileIds(status.getFiles()
.stream()
.map(FileEntity::getId)
.toList())
.downloadFileTypes(status.getDownloadFileTypes()
.stream()
.toList())
.reportTemplateIds(status.getReports().stream().map(ReportTemplateEntity::getTemplateId).toList())
.build()).toList()
);
return new DownloadStatusList(downloads.stream()
.map(status -> DownloadStatus.builder()
.id(status.getUuid()) // This is a workaround the real id is the storageId.
.userId(status.getUserId())
.filename(status.getFilename())
.mimeType(status.getMimeType())
.errorCause(status.getErrorCause())
.status(status.getStatus())
.creationDate(status.getCreationDate())
.lastDownload(status.getLastDownload())
.fileSize(status.getFileSize())
.dossierId(status.getDossier() != null ? status.getDossier().getId() : null)
.fileIds(status.getFiles()
.stream()
.map(FileEntity::getId)
.toList())
.downloadFileTypes(status.getDownloadFileTypes()
.stream()
.toList())
.reportTemplateIds(status.getReports()
.stream()
.map(ReportTemplateEntity::getTemplateId)
.toList())
.build())
.toList());
}
@ -81,7 +84,10 @@ public class DownloadControllerV2 implements DownloadResource {
.downloadFileTypes(status.getDownloadFileTypes()
.stream()
.toList())
.reportTemplateIds(status.getReports().stream().map(ReportTemplateEntity::getTemplateId).toList())
.reportTemplateIds(status.getReports()
.stream()
.map(ReportTemplateEntity::getTemplateId)
.toList())
.build();
}

View File

@ -178,6 +178,8 @@ public class ExternalControllerAdviceV2 {
String message = Objects.requireNonNull(exception.getRootCause()).getMessage();
if (message.contains("uq_component_definition_technical_name_template")) {
message = "A component with the same technical name already exists in the given dossier template.";
} else if (message.contains("value too long for type character varying")) {
message = "Value is too long. Please use a shorter value.";
} else {
message = "Database error occurred.";
}

View File

@ -176,10 +176,7 @@ public class FileControllerV2 implements FileResource {
var storageId = downloadController.prepareDownload(PrepareDownloadWithOptionRequest.builder()
.dossierId(dossierId)
.fileIds(statusController.getDossierStatus(dossierId)
.stream()
.map(FileStatus::getId)
.toList())
.fileIds(bulkDownloadRequest.getFileIds())
.reportTemplateIds(bulkDownloadRequest.getReportTemplateIds())
.downloadFileTypes(bulkDownloadRequest.getDownloadFileTypes())
.redactionPreviewColor(bulkDownloadRequest.getRedactionPreviewColor())

View File

@ -34,11 +34,13 @@ public interface ComponentMapper {
List<ComponentLogEntryValue> toComponentLogEntries(List<ComponentValue> values);
Component toComponent(ComponentLogEntry entry);
List<Component> toComponents(List<ComponentLogEntry> entries);
ComponentLogEntry toComponentLogEntry(Component component);

View File

@ -49,9 +49,7 @@ public interface ComponentLogResource {
@PostMapping(value = COMPONENT_LOG_PATH + OVERRIDE_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Adds overrides for components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void addOverride(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody ComponentsOverrides componentsOverrides);
void addOverride(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody ComponentsOverrides componentsOverrides);
@ResponseBody
@ -59,8 +57,7 @@ public interface ComponentLogResource {
@GetMapping(value = COMPONENT_LOG_PATH + OVERRIDE_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets overrides for components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found")})
ComponentsOverrides getOverrides(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId);
ComponentsOverrides getOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@ResponseBody
@ -68,8 +65,6 @@ public interface ComponentLogResource {
@PostMapping(value = COMPONENT_LOG_PATH + OVERRIDE_PATH + "/revert" + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Reverts overrides for components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void revertOverrides(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody RevertOverrideRequest revertOverrideRequest);
void revertOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RevertOverrideRequest revertOverrideRequest);
}

View File

@ -55,7 +55,9 @@ public interface EntityLogResource {
@GetMapping(value = ENTITY_LOG_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE + "/pages", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets the entity log for a fileId with all entities found on the given page numbers", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The dossier / file / entity log is not found.")})
EntityLogResponse getEntityLogWithEntriesOnPages(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestParam(value = "pageNumbers", defaultValue = "") List<Integer> pageNumbers);
EntityLogResponse getEntityLogWithEntriesOnPages(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "pageNumbers", defaultValue = "") List<Integer> pageNumbers);
@GetMapping(value = ENTITY_LOG_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE + ANALYSIS_NUMBER_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)

View File

@ -13,13 +13,15 @@ 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.v1.api.shared.model.ReanalysisSettings;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DownloadResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileExchangeExportRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatusFilter;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileExchangeExportRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ImportResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ReanalysisSettings;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ -54,15 +56,15 @@ public interface SupportResource {
@PostMapping(value = REANALYSIS_REST_PATH + DOSSIER_TEMPLATE_DOSSIER_TEMPLATE_ID_PATH_VARIABLE)
@Operation(summary = "Reanalyze all files in dossier template", description = """
## Reanalyze Files Endpoint
Use this endpoint to reanalyze all files in a specified Dossier Template. The reanalysis process can be tailored using various filtering options provided in the request body.
### Parameters
- **DossierTemplateId**: Specifies the Dossier Template whose files need to be reanalyzed.
### Request Body Configuration Options
- **dossierIds**: List of dossier IDs to filter. If empty, all dossiers are selected for reanalysis.
- **fileIds**: List of file IDs to filter. If empty, all files are selected for reanalysis.
- **repeatStructureAnalysis**: Boolean. If true, layout parsing and named entity recognition will be repeated.
@ -118,16 +120,16 @@ public interface SupportResource {
+ DOSSIER_TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Exports all dossiers and files from a given Dossier Template.", description = """
## Export Files Endpoint
Use this endpoint to export a full Dossier Template, including all configurations, dossiers, and files.
The endpoint returns a String storageId, which is used to query the DownloadController for the export zip archive's status and to download the archive.
### Parameters
- **DossierTemplateId**: Specifies the Dossier Template to be exported.
### Request Body Configuration Options
- **dossierIds**: List of dossier IDs to filter. If empty, all dossiers are selected.
- **fileIds**: List of file IDs to filter. If empty, all files are selected.
- **excludeLayoutFiles**: Boolean. If true, excludes DOCUMENT_STRUCTURE/_PAGES/_TEXT/_POSITIONS, SIMPLIFIED_TEXT, and NER_ENTITIES files.
@ -139,11 +141,11 @@ public interface SupportResource {
DownloadResponse exportFiles(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody FileExchangeExportRequest exportRequest);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = FILE_EXCHANGE_REST_PATH + IMPORT, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Imports a file exchange export zip.", description = "Use this endpoint to import a full export of a given Dossier Template including all its configurations, dossiers, and files.")
@Operation(summary = "Imports a file exchange export zip.", description = "Use this endpoint to import a full export of a given Dossier Template including all its configurations, dossiers, and files. Returns the resulting dossierTemplateId.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
void importFiles(@RequestPart(name = "file") MultipartFile file);
ImportResponse importFiles(@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file);
}

View File

@ -22,4 +22,5 @@ public class BulkDownloadRequest {
private Set<DownloadFileType> downloadFileTypes = new HashSet<>();
private String redactionPreviewColor;
private List<String> fileIds = new ArrayList<>();
}

View File

@ -19,4 +19,5 @@ public class ComponentOverrideList {
String fileId;
List<Component> componentOverrides = new ArrayList<>();
}

View File

@ -14,5 +14,6 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
public class DossierAttributeDefinitionList {
private List<DossierAttributeDefinition> dossierAttributeDefinitions= new ArrayList<>();
private List<DossierAttributeDefinition> dossierAttributeDefinitions = new ArrayList<>();
}

View File

@ -15,4 +15,5 @@ import lombok.NoArgsConstructor;
public class DossierStatusDefinitionList {
private List<DossierStatusDefinition> dossierStatusDefinitions = new ArrayList<>();
}

View File

@ -89,8 +89,8 @@ public interface DossierResource {
@Operation(summary = "Update or set attributes for a specific dossier.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Dossier attributes successfully updated."), @ApiResponse(responseCode = "404", description = "Not found")})
void setDossierAttributes(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of a dossier template", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of a dossier", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestBody DossierAttributes dossierAttributes);
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of a dossier", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestBody DossierAttributes dossierAttributes);
@ResponseBody

View File

@ -415,7 +415,7 @@ 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 Requirements
@ -434,7 +434,7 @@ paths:
- The component mapping file can be utilized in component rules to relate components to existing master data.
#### Example
| search_value | mapped_value |
|--------------|--------------|
| Alice | Manager |
@ -595,9 +595,9 @@ paths:
- Component Definitions
summary: Create new component definitions
description: |
Create new component definitions for a given dossier template. The component will have a technical name which is automatically converted to snake case
that can't be updated after the creation. The rank is used to determine the order in which the components are displayed. The component's rank will
automatically be appended at the end based on the current number of components of this dossier template.
Create new component definitions for a given dossier template. The component will have a technical name which is automatically converted to snake case
that can't be updated after the creation. The rank is used to determine the order in which the components are displayed. The component's rank will
automatically be appended at the end based on the current number of components of this dossier template.
parameters:
- $ref: '#/components/parameters/dossierTemplateId'
requestBody:
@ -890,7 +890,7 @@ paths:
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.
@ -2184,7 +2184,7 @@ components:
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.
@ -2202,7 +2202,7 @@ components:
- `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
@ -3324,7 +3324,7 @@ components:
- a0044ae9-ddca-4f97-b0a1-3cb2517dbf39
- c2e33246-e50a-4c43-831c-6789a5637db8
- 6123fa16-6943-4b74-8524-54b0046a0ce6
approverIds: []
approverIds: [ ]
reportTemplateIds:
- daadea5f-917b-482a-b7d2-e65afe8f80ca
- 8130acf6-4910-4123-827c-caacd8111402
@ -3500,7 +3500,7 @@ components:
- technicalName
- displayName
ComponentDefinitionUpdateRequest:
description: |
description: |
The ComponentDefinitionUpdateRequest object represents a request to update a component definition within a dossier template.
Only the display name and description can be updated.
example:
@ -4632,10 +4632,10 @@ components:
authorizationCode:
authorizationUrl: /auth/realms/{workspaceId}/protocol/openid-connect/auth
tokenUrl: /auth/realms/{workspaceId}/protocol/openid-connect/token
scopes: {}
scopes: { }
password:
tokenUrl: /auth/realms/{workspaceId}/protocol/openid-connect/token
scopes: {}
scopes: { }
clientCredentials:
tokenUrl: /auth/realms/{workspaceId}/protocol/openid-connect/token
scopes: {}
scopes: { }

View File

@ -31,7 +31,7 @@ servers:
- url: https://app.redactmanager.com
description: RedactManager Cloud Service
security:
- FF-OAUTH: []
- FF-OAUTH: [ ]
tags:
- name: 1. Dossier Templates
description: Operations related to dossier templates.
@ -258,7 +258,7 @@ paths:
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.
@ -1309,7 +1309,7 @@ components:
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.
@ -1327,7 +1327,7 @@ components:
- `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
@ -3171,10 +3171,10 @@ components:
authorizationCode:
authorizationUrl: /auth/realms/{workspaceId}/protocol/openid-connect/auth
tokenUrl: /auth/realms/{workspaceId}/protocol/openid-connect/token
scopes: {}
scopes: { }
password:
tokenUrl: /auth/realms/{workspaceId}/protocol/openid-connect/token
scopes: {}
scopes: { }
clientCredentials:
tokenUrl: /auth/realms/{workspaceId}/protocol/openid-connect/token
scopes: {}
scopes: { }

View File

@ -8,6 +8,7 @@ import java.util.stream.Collectors;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.MutableAclService;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.stereotype.Service;
@ -110,7 +111,7 @@ public class DossierACLService extends AbstractACLService<String> {
@Transactional
public void updateDossierACL(Set<String> members, Set<String> approvers, String owner, String dossierId) {
var acl = getOrCreateACL(dossierId);
MutableAcl acl = getOrCreateACL(dossierId);
// naive clear all ace's first
while (!acl.getEntries().isEmpty()) {

View File

@ -1,6 +1,5 @@
package com.iqser.red.service.persistence.management.v1.processor.acl.custom.dossier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;

View File

@ -0,0 +1,10 @@
package com.iqser.red.service.persistence.management.v1.processor.client.redactionreportservice;
import org.springframework.cloud.openfeign.FeignClient;
import com.iqser.red.service.redaction.report.v1.api.resource.RSSResource;
@FeignClient(name = "RssReportClient", url = "${redaction-report-service.url}")
public interface RssReportClient extends RSSResource {
}

View File

@ -6,6 +6,7 @@ import static com.knecon.fforesight.service.layoutparser.internal.api.queue.Layo
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -193,6 +194,7 @@ public class MessagingConfiguration {
@Bean
@ConditionalOnProperty(prefix = "persistence-service", name = "migrateOnly", havingValue = "false")
public Queue ocrStatusUpdateResponseQueue() {
return QueueBuilder.durable(OCR_STATUS_UPDATE_RESPONSE_QUEUE)
@ -203,6 +205,7 @@ public class MessagingConfiguration {
@Bean
@ConditionalOnProperty(prefix = "persistence-service", name = "migrateOnly", havingValue = "false")
public Queue ocrStatusUpdateResponseDQL() {
return QueueBuilder.durable(OCR_STATUS_UPDATE_RESPONSE_DQL).build();
@ -239,6 +242,7 @@ public class MessagingConfiguration {
@Bean
@ConditionalOnProperty(prefix = "persistence-service", name = "migrateOnly", havingValue = "false")
public Queue ocrRequestQueue() {
return QueueBuilder.durable(OCR_REQUEST_QUEUE)
@ -249,19 +253,20 @@ public class MessagingConfiguration {
.build();
}
@Bean
@ConditionalOnProperty(prefix = "persistence-service", name = "migrateOnly", havingValue = "false")
public Queue ocrResponseQueue() {
return QueueBuilder.durable(OCR_RESPONSE_QUEUE)
.build();
return QueueBuilder.durable(OCR_RESPONSE_QUEUE).build();
}
@Bean
@ConditionalOnProperty(prefix = "persistence-service", name = "migrateOnly", havingValue = "false")
public Queue ocrDLQ() {
return QueueBuilder.durable(OCR_DLQ)
.build();
return QueueBuilder.durable(OCR_DLQ).build();
}

View File

@ -73,6 +73,7 @@ public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBro
}
private Optional<String> extractTenantId(Message<?> message) {
StompHeaderAccessor sha = StompHeaderAccessor.wrap(message);

View File

@ -0,0 +1,70 @@
package com.iqser.red.service.persistence.management.v1.processor.dataexchange.service;
import java.nio.file.Path;
import java.util.List;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.FileExchangeNames;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.utils.FileSystemBackedArchiver;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileExchangeExportRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import io.micrometer.observation.annotation.Observed;
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 DossierExportService {
DictionaryPersistenceService dictionaryPersistenceService;
FileExportService fileExportService;
FileStatusManagementService fileStatusManagementService;
EntityTypeExportService entityTypeExportService;
ObjectMapper mapper;
@SneakyThrows
@Observed(name = "FileExchangeExportService", contextualName = "export-dossier")
public void addDossierToArchive(FileSystemBackedArchiver archiver, Path folder, FileExchangeExportRequest request, Dossier dossier) {
List<FileModel> files = fileStatusManagementService.getDossierStatus(dossier.getId());
if (!request.dossierIds().contains(dossier.getId()) //
&& files.stream()
.noneMatch(fileModel -> request.fileIds().isEmpty() || request.fileIds().contains(fileModel.getId()))) {
// dossier has no files in requested files and dossier not explicitly requested -> don't export it.
return;
}
Path dossierFolder = folder.resolve(dossier.getId());
archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(dossierFolder, FileExchangeNames.DOSSIER, mapper.writeValueAsBytes(dossier)));
for (FileModel fileEntity : files) {
if (!request.fileIds().isEmpty() && !request.fileIds().contains(fileEntity.getId())) {
continue;
}
fileExportService.addFileToArchive(archiver, dossierFolder, request, fileEntity);
}
List<TypeEntity> types = dictionaryPersistenceService.getAllTypesForDossier(dossier.getId(), false);
String entitiesFolder = dossierFolder.resolve(FileExchangeNames.DOSSIER_ENTITY_FOLDER).toFile().toString();
for (TypeEntity type : types) {
entityTypeExportService.addEntityTypeToArchive(archiver, type, entitiesFolder);
}
}
}

View File

@ -0,0 +1,84 @@
package com.iqser.red.service.persistence.management.v1.processor.dataexchange.service;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.FileExchangeImportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.TemplateImportInfo;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierCreatorService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.CreateOrUpdateDossierRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
import io.micrometer.observation.annotation.Observed;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class DossierImportService {
DossierCreatorService dossierCreatorService;
FileImportService fileImportService;
EntityTypeImportService entityTypeImportService;
@Observed(name = "DossierImportService", contextualName = "import-dossier")
public void importDossier(FileExchangeImportModel fileExchangeImportModel,
String userId,
Dossier dossierToImport,
TemplateImportInfo templateImportInfo,
List<String> allReportTemplateIds) {
String dossierId = getDossierForImport(templateImportInfo, dossierToImport, userId, allReportTemplateIds);
entityTypeImportService.importEntityTypes(templateImportInfo.getDossierTemplateId(),
dossierId,
fileExchangeImportModel.getDossierTypes()
.get(dossierToImport.getId()));
List<FileExchangeImportModel.FileImport> files = fileExchangeImportModel.getFiles().getOrDefault(dossierToImport.getId(), Collections.emptyList());
fileImportService.importFilesInParallel(files, userId, templateImportInfo, dossierId);
}
private String getDossierForImport(TemplateImportInfo templateImportInfo, Dossier dossier, String userId, List<String> reportTemplateIds) {
String dossierTemplateId = templateImportInfo.getDossierTemplateId();
CreateOrUpdateDossierRequest request = CreateOrUpdateDossierRequest.builder()
.dossierName(dossier.getName())
.description(dossier.getDescription())
.downloadFileTypes(dossier.getDownloadFileTypes())
.dueDate(dossier.getDueDate())
.dossierTemplateId(dossierTemplateId)
.requestingUser(userId)
.reportTemplateIds(reportTemplateIds)
.build();
Dossier importedDossier = null;
int retries = 0;
while (importedDossier == null && retries < 10) {
try {
importedDossier = dossierCreatorService.addDossier(request, Set.of(userId), Set.of(userId), userId);
} catch (ConflictException e) {
retries++;
request.setDossierName(String.format("%s (%d)", dossier.getName(), retries));
}
}
if (importedDossier == null) {
throw new BadRequestException(String.format("Could not create dossier with name %s in %d retries", dossier.getName(), retries));
}
return importedDossier.getId();
}
}

View File

@ -123,6 +123,7 @@ public class DossierTemplateExportService {
});
}
@Observed(name = "DossierTemplateExportService", contextualName = "export-dossier-template")
public long createDownloadArchive(ExportDownloadMessage downloadJob) throws IOException {
@ -142,6 +143,7 @@ public class DossierTemplateExportService {
}
@Observed(name = "DossierTemplateExportService", contextualName = "write-dossier-template-to-archive")
public void addDossierTemplateToArchive(FileSystemBackedArchiver fileSystemBackedArchiver, String folder, DossierTemplateEntity dossierTemplate) throws IOException {

View File

@ -67,6 +67,7 @@ import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import com.knecon.fforesight.tenantcommons.TenantContext;
import io.micrometer.observation.annotation.Observed;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
@ -121,6 +122,7 @@ public class DossierTemplateImportService {
}
@Observed(name = "DossierTemplateImportService", contextualName = "import-template")
public TemplateImportInfo importDossierTemplate(ImportTemplateResult request) {
long start = System.currentTimeMillis();

View File

@ -41,6 +41,7 @@ public class EntityTypeExportService {
ObjectMapper mapper;
EntryPersistenceService entryPersistenceService;
@Observed(name = "EntityTypeExportService", contextualName = "export-entity-type")
public void addEntityTypeToArchive(FileSystemBackedArchiver fileSystemBackedArchiver, TypeEntity typeEntity, String folder) throws JsonProcessingException {

View File

@ -96,6 +96,9 @@ public class EntityTypeImportService {
@Observed(name = "EntityTypeImportService", contextualName = "import-entity-type")
public void importEntityTypes(String dossierTemplateId, String dossierId, EntityTypeImportModel importModel) {
if (importModel == null) {
return;
}
enrichObservation(importModel);
for (Type type : importModel.getTypes()) {
@ -168,7 +171,7 @@ public class EntityTypeImportService {
private void addEntries(Map<String, List<String>> entries, Map<String, List<String>> deleteEntries, String typeId, String typeName, DictionaryEntryType dictionaryType) {
if (entries != null && !entries.isEmpty() && entries.get(typeName) != null && !entries.get(typeName).isEmpty()) {
dictionaryManagementService.addEntries(typeId, entries.get(typeName), true, true, dictionaryType);
dictionaryManagementService.addEntries(typeId, entries.get(typeName), true, true, dictionaryType, true);
} else { // no entries, delete current entries
List<String> existing = entryPersistenceService.getEntries(typeId, dictionaryType, null)
.stream()

View File

@ -0,0 +1,38 @@
package com.iqser.red.service.persistence.management.v1.processor.dataexchange.service;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.FileExchangeImportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.zipreaders.FileExchangeArchiveReader;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.zipreaders.ZipEntryIterator;
import com.iqser.red.service.persistence.management.v1.processor.settings.FileManagementServiceSettings;
import io.micrometer.observation.annotation.Observed;
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 FileExchangeArchivalService {
public static final long SIZE_THRESHOLD = 10000000000L; // 10GB
FileManagementServiceSettings settings;
@SneakyThrows
@Observed(name = "FileExchangeImportService", contextualName = "read-import-archive")
public FileExchangeImportModel readFileExchangeArchive(byte[] archive, String userId) {
FileExchangeArchiveReader fileExchangeArchiveReader = new FileExchangeArchiveReader(userId);
try (ZipEntryIterator zipEntryIterator = new ZipEntryIterator(archive, settings.getCompressionThresholdRatio(), SIZE_THRESHOLD)) {
zipEntryIterator.forEachRemaining(fileExchangeArchiveReader::handleZipEntryData);
}
return fileExchangeArchiveReader.buildResult();
}
}

View File

@ -3,28 +3,17 @@ package com.iqser.red.service.persistence.management.v1.processor.dataexchange.s
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Sets;
import com.iqser.red.service.persistence.management.v1.processor.configuration.MessagingConfiguration;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.ExportDownloadMessage;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.FileExchangeNames;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentLogService;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierIdFileIdRequestValidator;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.utils.FileSystemBackedArchiver;
@ -32,15 +21,11 @@ import com.iqser.red.service.persistence.management.v1.processor.utils.StorageId
import com.iqser.red.service.persistence.service.v1.api.shared.model.DownloadResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileExchangeExportRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.knecon.fforesight.tenantcommons.TenantContext;
import io.micrometer.observation.annotation.Observed;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
@ -53,16 +38,12 @@ public class FileExchangeExportService {
DossierTemplateExportService dossierTemplateExportService;
DossierTemplatePersistenceService dossierTemplatePersistenceService;
DossierManagementService dossierManagementService;
FileStatusManagementService fileStatusManagementService;
FileManagementStorageService storageService;
DownloadStatusPersistenceService downloadStatusPersistenceService;
ManualChangesExportService manualChangesExportService;
DictionaryPersistenceService dictionaryPersistenceService;
ComponentLogService componentLogService;
ObjectMapper mapper;
RabbitTemplate rabbitTemplate;
EntityTypeExportService entityTypeExportService;
DossierIdFileIdRequestValidator requestValidator;
DossierExportService dossierExportService;
@Observed(name = "FileExchangeExportService", contextualName = "prepare-export")
@ -116,7 +97,7 @@ public class FileExchangeExportService {
continue;
}
addDossierToArchive(archiver, Path.of(""), request, dossierEntity);
dossierExportService.addDossierToArchive(archiver, Path.of(""), request, dossierEntity);
}
storeZipFile(downloadJob.getStorageId(), archiver);
@ -126,125 +107,12 @@ public class FileExchangeExportService {
}
@SneakyThrows
@Observed(name = "FileExchangeExportService", contextualName = "export-dossier")
public void addDossierToArchive(FileSystemBackedArchiver archiver, Path folder, FileExchangeExportRequest request, Dossier dossier) {
List<FileModel> files = fileStatusManagementService.getDossierStatus(dossier.getId());
if (!request.dossierIds().contains(dossier.getId()) //
&& files.stream()
.noneMatch(fileModel -> request.fileIds().isEmpty() || request.fileIds().contains(fileModel.getId()))) {
// dossier has no files in requested files and dossier not explicitly requested -> don't export it.
return;
}
Path dossierFolder = folder.resolve(dossier.getId());
archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(dossierFolder, FileExchangeNames.DOSSIER, mapper.writeValueAsBytes(dossier)));
for (FileModel fileEntity : files) {
if (!request.fileIds().isEmpty() && !request.fileIds().contains(fileEntity.getId())) {
continue;
}
addFileToArchive(archiver, dossierFolder, request, fileEntity);
}
List<TypeEntity> types = dictionaryPersistenceService.getAllTypesForDossier(dossier.getId(), false);
String entitiesFolder = dossierFolder.resolve(FileExchangeNames.DOSSIER_ENTITY_FOLDER).toFile().toString();
for (TypeEntity type : types) {
entityTypeExportService.addEntityTypeToArchive(archiver, type, entitiesFolder);
}
}
@SneakyThrows
@Observed(name = "FileExchangeExportService", contextualName = "export-file")
public void addFileToArchive(FileSystemBackedArchiver archiver, Path folder, FileExchangeExportRequest request, FileModel file) {
Path fileFolder = folder.resolve(file.getId());
if (!request.excludeAnalysisLogs()) {
archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder,
buildFileName(file, FileExchangeNames.ENTITY_LOG.getName()),
mapper.writeValueAsBytes(storageService.getEntityLog(file.getDossierId(), file.getId()))));
if (storageService.objectExists(file.getDossierId(), file.getId(), FileType.COMPONENT_LOG)) {
archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder,
buildFileName(file, FileExchangeNames.COMPONENT_LOG.getName()),
mapper.writeValueAsBytes(storageService.getComponentLog(file.getDossierId(), file.getId()))));
archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder,
buildFileName(file, FileExchangeNames.COMPONENT_OVERRIDES),
mapper.writeValueAsBytes(componentLogService.getOverrides(file.getDossierId(), file.getId()))));
}
}
archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder, buildFileName(file, FileExchangeNames.FILE_STATUS), mapper.writeValueAsBytes(file)));
if (!request.excludeLayoutFiles()) {
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.STRUCTURE);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.TEXT);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.POSITIONS);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.PAGES);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.NER_ENTITIES);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.SIMPLIFIED_TEXT);
}
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.FIGURE);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.HIGHLIGHTS);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.TABLES);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.IMAGES);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.VISUAL_LAYOUT);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.IMPORTED);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.ORIGIN);
if (!request.originFileOnly()) {
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.UNTOUCHED);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.VIEWER);
}
if (!request.excludeFileAttributes()) {
archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder,
buildFileName(file, FileExchangeNames.FILE_ATTRIBUTES),
mapper.writeValueAsBytes(file.getFileAttributes())));
}
if (!request.excludeManualRedactions()) {
archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder,
buildFileName(file, FileExchangeNames.MANUAL_REDACTIONS),
mapper.writeValueAsBytes(manualChangesExportService.export(file.getId()))));
}
}
private static String buildFileName(FileModel file, String name) {
return buildFileName(file.getId(), name);
}
public static String buildFileName(String fileId, String name) {
return fileId + "." + name;
}
@SneakyThrows
private void addArchiveModelForStorageFile(FileSystemBackedArchiver archiver, FileModel file, Path fileFolder, FileExchangeNames.Definition definition) {
String storageId = StorageIdUtils.getStorageId(file.getDossierId(), file.getId(), definition.fileType());
if (!storageService.objectExists(storageId)) {
log.debug("File {} not found in storage with id {}, skipping...", definition.fileType(), storageId);
return;
}
log.debug("Adding file {} from storage with id {} to archive.", definition.fileType(), storageId);
byte[] data = storageService.getObject(TenantContext.getTenantId(), storageId).readAllBytes();
FileSystemBackedArchiver.ArchiveModel archiveModel = new FileSystemBackedArchiver.ArchiveModel(fileFolder, buildFileName(file, definition.getName()), data);
archiver.addEntry(archiveModel);
}
@Observed(name = "FileExchangeExportService", contextualName = "store-archive")
private void storeZipFile(String storageId, FileSystemBackedArchiver fileSystemBackedArchiver) {

View File

@ -1,37 +1,23 @@
package com.iqser.red.service.persistence.management.v1.processor.dataexchange.service;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.ReportTemplateEntity;
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.dataexchange.models.FileExchangeImportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.ImportTemplateResult;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.TemplateImportInfo;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.zipreaders.FileExchangeArchiveReader;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.zipreaders.ZipEntryIterator;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentLogService;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierCreatorService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.ReportTemplateEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ReportTemplatePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.settings.FileManagementServiceSettings;
import com.iqser.red.service.persistence.management.v1.processor.utils.StorageIdUtils;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.CreateOrUpdateDossierRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ImportResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
import io.micrometer.common.KeyValue;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.annotation.Observed;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
@ -41,25 +27,19 @@ import lombok.extern.slf4j.Slf4j;
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class FileExchangeImportService {
public static final long SIZE_THRESHOLD = 10000000000L; // 10GB
FileManagementServiceSettings settings;
DossierTemplateImportService dossierTemplateImportService;
EntityTypeImportService entityTypeImportService;
DossierCreatorService dossierCreatorService;
FileImportService fileImportService;
FileManagementStorageService storageService;
ComponentLogService componentLogService;
DossierImportService dossierImportService;
FileExchangeArchivalService fileExchangeArchivalService;
ReportTemplatePersistenceService reportTemplateService;
ObservationRegistry registry;
@Observed(name = "FileExchangeImportService", contextualName = "import-files")
public String importFileExchangeArchive(String userId, byte[] archive) {
public ImportResponse importFileExchangeArchive(String userId, byte[] archive) {
long start = System.currentTimeMillis();
log.info("Starting file import for user {}", userId);
FileExchangeImportModel fileExchangeImportModel = readFileExchangeArchive(archive, userId);
FileExchangeImportModel fileExchangeImportModel = fileExchangeArchivalService.readFileExchangeArchive(archive, userId);
log.info("Parsed file import archive with {} dossiers and {} files",
fileExchangeImportModel.getDossiers().size(),
fileExchangeImportModel.getFiles().values()
@ -70,7 +50,29 @@ public class FileExchangeImportService {
enrichObservation(userId, archive, fileExchangeImportModel);
return dossierTemplateId;
return new ImportResponse(dossierTemplateId);
}
private String importFileExchangeModel(FileExchangeImportModel fileExchangeImportModel, String userId) {
ImportTemplateResult importTemplateResult = fileExchangeImportModel.getImportTemplateResult();
if (importTemplateResult == null) {
throw new BadRequestException("DossierTemplate not present in import archive.");
}
TemplateImportInfo templateImportInfo = dossierTemplateImportService.importDossierTemplate(importTemplateResult);
List<String> allReportTemplateIds = reportTemplateService.findByDossierTemplateId(templateImportInfo.getDossierTemplateId())
.stream()
.map(ReportTemplateEntity::getTemplateId)
.toList();
for (Dossier dossierToImport : fileExchangeImportModel.getDossiers()) {
dossierImportService.importDossier(fileExchangeImportModel, userId, dossierToImport, templateImportInfo, allReportTemplateIds);
}
return templateImportInfo.getDossierTemplateId();
}
@ -88,108 +90,4 @@ public class FileExchangeImportService {
}
}
@Observed(name = "FileExchangeImportService", contextualName = "write-import-data-to-storage")
private String importFileExchangeModel(FileExchangeImportModel fileExchangeImportModel, String userId) {
TemplateImportInfo templateImportInfo = getDossierTemplateForImport(fileExchangeImportModel);
List<String> allReportTemplateIds = reportTemplateService.findByDossierTemplateId(templateImportInfo.getDossierTemplateId())
.stream()
.map(ReportTemplateEntity::getTemplateId)
.toList();
for (Dossier dossierToImport : fileExchangeImportModel.getDossiers()) {
String dossierId = getDossierForImport(templateImportInfo, dossierToImport, userId, allReportTemplateIds);
entityTypeImportService.importEntityTypes(templateImportInfo.getDossierTemplateId(),
dossierId,
fileExchangeImportModel.getDossierTypes()
.get(dossierToImport.getId()));
List<FileExchangeImportModel.FileImport> files = fileExchangeImportModel.getFiles().getOrDefault(dossierToImport.getId(), Collections.emptyList());
for (FileExchangeImportModel.FileImport file : files) {
// separate service for transactions
String fileId = fileImportService.saveFileToDb(userId, file, dossierId, templateImportInfo);
file.getFilesToUpload()
.forEach(fileToUpload -> {
try (FileInputStream in = new FileInputStream(fileToUpload.file().toFile())) {
storageService.storeObject(StorageIdUtils.getStorageId(dossierId, fileId, fileToUpload.fileType()), in);
Files.deleteIfExists(fileToUpload.file());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
storageService.saveEntityLog(dossierId, fileId, file.getEntityLog());
if (file.getComponentLog() != null) {
storageService.saveComponentLog(dossierId, fileId, file.getComponentLog());
}
if (file.getOverrides() != null) {
file.getOverrides()
.forEach(componentOverride -> componentLogService.addOverride(dossierId, fileId, componentOverride));
}
}
}
return templateImportInfo.getDossierTemplateId();
}
private String getDossierForImport(TemplateImportInfo templateImportInfo, Dossier dossier, String userId, List<String> reportTemplateIds) {
String dossierTemplateId = templateImportInfo.getDossierTemplateId();
CreateOrUpdateDossierRequest request = CreateOrUpdateDossierRequest.builder()
.dossierName(dossier.getName())
.description(dossier.getDescription())
.downloadFileTypes(dossier.getDownloadFileTypes())
.dueDate(dossier.getDueDate())
.dossierTemplateId(dossierTemplateId)
.requestingUser(userId)
.reportTemplateIds(reportTemplateIds)
.build();
Dossier importedDossier = null;
int retries = 0;
while (importedDossier == null && retries < 10) {
try {
importedDossier = dossierCreatorService.addDossier(request, Set.of(userId), Set.of(userId), userId);
} catch (ConflictException e) {
retries++;
request.setDossierName(String.format("%s (%d)", dossier.getName(), retries));
}
}
if (importedDossier == null) {
throw new BadRequestException(String.format("Could not create dossier with name %s in %d retries", dossier.getName(), retries));
}
return importedDossier.getId();
}
private TemplateImportInfo getDossierTemplateForImport(FileExchangeImportModel fileExchangeImportModel) {
ImportTemplateResult importTemplateResult = fileExchangeImportModel.getImportTemplateResult();
if (importTemplateResult == null) {
throw new BadRequestException("DossierTemplate not present in import archive.");
}
return dossierTemplateImportService.importDossierTemplate(importTemplateResult);
}
@SneakyThrows
@Observed(name = "FileExchangeImportService", contextualName = "read-import-archive")
private FileExchangeImportModel readFileExchangeArchive(byte[] archive, String userId) {
FileExchangeArchiveReader fileExchangeArchiveReader = new FileExchangeArchiveReader(userId);
try (ZipEntryIterator zipEntryIterator = new ZipEntryIterator(archive, settings.getCompressionThresholdRatio(), SIZE_THRESHOLD)) {
zipEntryIterator.forEachRemaining(fileExchangeArchiveReader::handleZipEntryData);
}
return fileExchangeArchiveReader.buildResult();
}
}

View File

@ -0,0 +1,122 @@
package com.iqser.red.service.persistence.management.v1.processor.dataexchange.service;
import java.nio.file.Path;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.FileExchangeNames;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentLogService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.utils.FileSystemBackedArchiver;
import com.iqser.red.service.persistence.management.v1.processor.utils.StorageIdUtils;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileExchangeExportRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
import com.knecon.fforesight.tenantcommons.TenantContext;
import io.micrometer.observation.annotation.Observed;
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 FileExportService {
ManualChangesExportService manualChangesExportService;
ComponentLogService componentLogService;
ObjectMapper mapper;
FileManagementStorageService storageService;
@SneakyThrows
@Observed(name = "FileExchangeExportService", contextualName = "export-file")
public void addFileToArchive(FileSystemBackedArchiver archiver, Path folder, FileExchangeExportRequest request, FileModel file) {
Path fileFolder = folder.resolve(file.getId());
if (!request.excludeAnalysisLogs()) {
archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder,
buildFileName(file, FileExchangeNames.ENTITY_LOG.getName()),
mapper.writeValueAsBytes(storageService.getEntityLog(file.getDossierId(), file.getId()))));
if (storageService.objectExists(file.getDossierId(), file.getId(), FileType.COMPONENT_LOG)) {
archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder,
buildFileName(file, FileExchangeNames.COMPONENT_LOG.getName()),
mapper.writeValueAsBytes(storageService.getComponentLog(file.getDossierId(), file.getId()))));
archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder,
buildFileName(file, FileExchangeNames.COMPONENT_OVERRIDES),
mapper.writeValueAsBytes(componentLogService.getOverrides(file.getDossierId(), file.getId()))));
}
}
archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder, buildFileName(file, FileExchangeNames.FILE_STATUS), mapper.writeValueAsBytes(file)));
if (!request.excludeLayoutFiles()) {
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.STRUCTURE);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.TEXT);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.POSITIONS);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.PAGES);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.NER_ENTITIES);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.SIMPLIFIED_TEXT);
}
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.FIGURE);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.HIGHLIGHTS);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.TABLES);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.IMAGES);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.VISUAL_LAYOUT);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.IMPORTED);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.ORIGIN);
if (!request.originFileOnly()) {
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.UNTOUCHED);
addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.VIEWER);
}
if (!request.excludeFileAttributes()) {
archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder,
buildFileName(file, FileExchangeNames.FILE_ATTRIBUTES),
mapper.writeValueAsBytes(file.getFileAttributes())));
}
if (!request.excludeManualRedactions()) {
archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder,
buildFileName(file, FileExchangeNames.MANUAL_REDACTIONS),
mapper.writeValueAsBytes(manualChangesExportService.export(file.getId()))));
}
}
private static String buildFileName(FileModel file, String name) {
return buildFileName(file.getId(), name);
}
public static String buildFileName(String fileId, String name) {
return fileId + "." + name;
}
@SneakyThrows
private void addArchiveModelForStorageFile(FileSystemBackedArchiver archiver, FileModel file, Path fileFolder, FileExchangeNames.Definition definition) {
String storageId = StorageIdUtils.getStorageId(file.getDossierId(), file.getId(), definition.fileType());
if (!storageService.objectExists(storageId)) {
log.debug("File {} not found in storage with id {}, skipping...", definition.fileType(), storageId);
return;
}
log.debug("Adding file {} from storage with id {} to archive.", definition.fileType(), storageId);
byte[] data = storageService.getObject(TenantContext.getTenantId(), storageId).readAllBytes();
FileSystemBackedArchiver.ArchiveModel archiveModel = new FileSystemBackedArchiver.ArchiveModel(fileFolder, buildFileName(file, definition.getName()), data);
archiver.addEntry(archiveModel);
}
}

View File

@ -0,0 +1,66 @@
package com.iqser.red.service.persistence.management.v1.processor.dataexchange.service;
import java.util.Map;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.FileExchangeImportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.TemplateImportInfo;
import com.iqser.red.service.persistence.management.v1.processor.service.UploadService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileRepository;
import com.iqser.red.service.persistence.management.v1.processor.utils.FileEntityMapper;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import io.micrometer.observation.annotation.Observed;
import jakarta.transaction.Transactional;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
@Service
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class FileImportPersistenceService {
ManualChangesImportService manualChangesImportService;
FileRepository fileRepository;
@Transactional
@Observed(name = "FileImportPersistenceService", contextualName = "import-file-to-db")
public synchronized String saveFileToDb(String userId,
FileExchangeImportModel.FileImport file,
String dossierId,
TemplateImportInfo templateImportInfo) { // synchronized as this is being called in an async block. Might lock up DB otherwise.
String fileId = createFile(dossierId, userId, file, templateImportInfo.getIdMapping());
manualChangesImportService.importManualChanges(file.getManualChanges(), fileId, userId);
return fileId;
}
private String createFile(String dossierId, String userid, FileExchangeImportModel.FileImport file, Map<String, String> fileAttributeConfigMap) {
FileModel fileModel = file.getFileModel();
fileModel.setId(UploadService.generateFileId(fileModel.getFilename(), dossierId));
FileEntity fileEntity = MagicConverter.convert(fileModel, FileEntity.class, new FileEntityMapper(fileAttributeConfigMap));
fileEntity.setAssignee(userid);
fileEntity.setLastReviewer(userid);
fileEntity.setLastApprover(userid);
fileEntity.setUploader(userid);
fileEntity.setDossierId(dossierId);
fileRepository.save(fileEntity);
return fileEntity.getId();
}
}

View File

@ -1,57 +1,80 @@
package com.iqser.red.service.persistence.management.v1.processor.dataexchange.service;
import java.util.Map;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.FileExchangeImportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.TemplateImportInfo;
import com.iqser.red.service.persistence.management.v1.processor.service.UploadService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileRepository;
import com.iqser.red.service.persistence.management.v1.processor.utils.FileEntityMapper;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentLogService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.utils.StorageIdUtils;
import com.knecon.fforesight.tenantcommons.TenantContext;
import jakarta.transaction.Transactional;
import io.micrometer.observation.annotation.Observed;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class FileImportService {
ManualChangesImportService manualChangesImportService;
FileRepository fileRepository;
FileImportPersistenceService fileImportPersistenceService;
FileManagementStorageService storageService;
ComponentLogService componentLogService;
@Transactional
public String saveFileToDb(String userId, FileExchangeImportModel.FileImport file, String dossierId, TemplateImportInfo templateImportInfo) {
@Observed(name = "FileImportService", contextualName = "import-files-in-parallel")
public void importFilesInParallel(List<FileExchangeImportModel.FileImport> files, String userId, TemplateImportInfo templateImportInfo, String dossierId) {
String fileId = createFile(dossierId, userId, file, templateImportInfo.getIdMapping());
List<CompletableFuture<Void>> fileImportFutures = new LinkedList<>();
manualChangesImportService.importManualChanges(file.getManualChanges(), fileId);
String tenantId = TenantContext.getTenantId();
return fileId;
for (FileExchangeImportModel.FileImport file : files) {
fileImportFutures.add(CompletableFuture.supplyAsync(() -> {
TenantContext.setTenantId(tenantId);
importFile(userId, templateImportInfo, file, dossierId);
return null;
}));
}
fileImportFutures.forEach(CompletableFuture::join);
}
private String createFile(String dossierId, String userid, FileExchangeImportModel.FileImport file, Map<String, String> fileAttributeConfigMap) {
@Observed(name = "FileImportService", contextualName = "import-file")
public void importFile(String userId, TemplateImportInfo templateImportInfo, FileExchangeImportModel.FileImport file, String dossierId) {
FileModel fileModel = file.getFileModel();
// separate service for transactions
String fileId = fileImportPersistenceService.saveFileToDb(userId, file, dossierId, templateImportInfo);
fileModel.setId(UploadService.generateFileId(fileModel.getFilename(), dossierId));
FileEntity fileEntity = MagicConverter.convert(fileModel, FileEntity.class, new FileEntityMapper(fileAttributeConfigMap));
fileEntity.setDossierId(dossierId);
fileRepository.save(fileEntity);
return fileEntity.getId();
file.getFilesToUpload()
.forEach(fileToUpload -> {
try (FileInputStream in = new FileInputStream(fileToUpload.file().toFile())) {
storageService.storeObject(StorageIdUtils.getStorageId(dossierId, fileId, fileToUpload.fileType()), in);
Files.deleteIfExists(fileToUpload.file());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
if (file.getEntityLog() != null) {
storageService.saveEntityLog(dossierId, fileId, file.getEntityLog());
}
if (file.getComponentLog() != null) {
storageService.saveComponentLog(dossierId, fileId, file.getComponentLog());
}
if (file.getOverrides() != null) {
file.getOverrides()
.forEach(componentOverride -> componentLogService.addOverride(dossierId, fileId, componentOverride));
}
}
}

View File

@ -12,6 +12,7 @@ import com.iqser.red.service.persistence.management.v1.processor.service.persist
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.annotations.RemoveRedactionPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.annotations.ResizeRedactionPersistenceService;
import io.micrometer.observation.annotation.Observed;
import jakarta.transaction.Transactional;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
@ -33,33 +34,34 @@ public class ManualChangesExportService {
@Transactional
@Observed(name = "ManualChangesExportService", contextualName = "export-manual-changes")
public ManualChangesExportModel export(String fileId) {
ManualChangesExportModel exportModel = new ManualChangesExportModel();
exportModel.setManualAddExportModels(addRedactionPersistenceService.findEntriesByFileIdAndOptions(fileId, ManualChangesQueryOptions.all())
.stream()
.map(mapper::toModel)
.toList());
.stream()
.map(mapper::toModel)
.toList());
exportModel.setManualRemoveExportModels(removeRedactionPersistenceService.findEntriesByFileIdAndOptions(fileId, ManualChangesQueryOptions.all())
.stream()
.map(mapper::toModel)
.toList());
.stream()
.map(mapper::toModel)
.toList());
exportModel.setManualForceExportModels(forceRedactionPersistenceService.findEntriesByFileIdAndOptions(fileId, ManualChangesQueryOptions.all())
.stream()
.map(mapper::toModel)
.toList());
.stream()
.map(mapper::toModel)
.toList());
exportModel.setManualLegalBasisChangeExportModels(legalBasisChangePersistenceService.findEntriesByFileIdAndOptions(fileId, ManualChangesQueryOptions.all())
.stream()
.map(mapper::toModel)
.toList());
.stream()
.map(mapper::toModel)
.toList());
exportModel.setManualRecategorizationExportModels(recategorizationPersistenceService.findEntriesByFileIdAndOptions(fileId, ManualChangesQueryOptions.all())
.stream()
.map(mapper::toModel)
.toList());
.stream()
.map(mapper::toModel)
.toList());
exportModel.setManualResizeExportModels(resizeRedactionPersistenceService.findEntriesByFileIdAndOptions(fileId, ManualChangesQueryOptions.all())
.stream()
.map(mapper::toModel)
.toList());
.stream()
.map(mapper::toModel)
.toList());
return exportModel;
}

View File

@ -13,6 +13,7 @@ import com.iqser.red.service.persistence.management.v1.processor.service.persist
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.annotationentity.RemoveRedactionRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.annotationentity.ResizeRedactionRepository;
import io.micrometer.observation.annotation.Observed;
import jakarta.transaction.Transactional;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
@ -36,38 +37,39 @@ public class ManualChangesImportService {
@Transactional
public void importManualChanges(ManualChangesExportModel exportModel, String fileId) {
@Observed(name = "ManualChangesImportService", contextualName = "import-manual-changes")
public void importManualChanges(ManualChangesExportModel exportModel, String fileId, String userId) {
FileEntity fileEntity = fileRepository.findById(fileId)
.orElseThrow();
exportModel.getManualAddExportModels()
.stream()
.map(model -> mapper.toEntity(model, fileEntity))
.map(model -> mapper.toEntity(model, fileEntity, userId))
.forEach(manualRedactionRepository::saveAndFlush);
exportModel.getManualForceExportModels()
.stream()
.map(model -> mapper.toEntity(model, fileEntity))
.map(model -> mapper.toEntity(model, fileEntity, userId))
.forEach(forceRedactionRepository::saveAndFlush);
exportModel.getManualRecategorizationExportModels()
.stream()
.map(model -> mapper.toEntity(model, fileEntity))
.map(model -> mapper.toEntity(model, fileEntity, userId))
.forEach(recategorizationRepository::saveAndFlush);
exportModel.getManualRemoveExportModels()
.stream()
.map(model -> mapper.toEntity(model, fileEntity))
.map(model -> mapper.toEntity(model, fileEntity, userId))
.forEach(removeRedactionRepository::saveAndFlush);
exportModel.getManualResizeExportModels()
.stream()
.map(model -> mapper.toEntity(model, fileEntity))
.map(model -> mapper.toEntity(model, fileEntity, userId))
.forEach(resizeRedactionRepository::saveAndFlush);
exportModel.getManualLegalBasisChangeExportModels()
.stream()
.map(model -> mapper.toEntity(model, fileEntity))
.map(model -> mapper.toEntity(model, fileEntity, userId))
.forEach(legalBasisChangeRepository::saveAndFlush);
}

View File

@ -70,7 +70,7 @@ public class ZipEntryIterator implements Iterator<ZipEntryData>, AutoCloseable {
double compressionRatio = (double) totalSizeEntry / nextEntry.getCompressedSize();
if (compressionRatio > compressionThresholdRatio) {
throw new BadRequestException("ZIP-Bomb detected (compressionRatio). " + compressionRatio + "/" + compressionThresholdRatio );
throw new BadRequestException("ZIP-Bomb detected (compressionRatio). " + compressionRatio + "/" + compressionThresholdRatio);
}
}

View File

@ -4,7 +4,6 @@ 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;

View File

@ -4,7 +4,6 @@ import java.io.Serializable;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -2,6 +2,8 @@ package com.iqser.red.service.persistence.management.v1.processor.entity.annotat
import java.time.OffsetDateTime;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
@ -9,9 +11,6 @@ import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -1,7 +1,6 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.annotations;
import jakarta.persistence.Embeddable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -3,6 +3,8 @@ package com.iqser.red.service.persistence.management.v1.processor.entity.annotat
import java.io.Serializable;
import java.time.OffsetDateTime;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.EmbeddedId;
@ -11,9 +13,6 @@ import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MapsId;
import jakarta.persistence.Table;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -4,6 +4,8 @@ import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.Map;
import com.iqser.red.service.persistence.management.v1.processor.utils.JSONMapConverter;
import jakarta.persistence.Basic;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
@ -13,9 +15,6 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import com.iqser.red.service.persistence.management.v1.processor.utils.JSONMapConverter;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -4,7 +4,6 @@ import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -8,7 +8,6 @@ import jakarta.persistence.Id;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -6,7 +6,6 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -6,7 +6,6 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -1,13 +1,12 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.configuration;
import com.iqser.red.service.persistence.service.v1.api.shared.model.utils.SuppressFBWarnings;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import jakarta.persistence.Table;
import com.iqser.red.service.persistence.service.v1.api.shared.model.utils.SuppressFBWarnings;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -1,5 +1,7 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.configuration;
import com.iqser.red.service.persistence.service.v1.api.shared.model.utils.SuppressFBWarnings;
import jakarta.persistence.Basic;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@ -7,9 +9,6 @@ import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import jakarta.persistence.Table;
import com.iqser.red.service.persistence.service.v1.api.shared.model.utils.SuppressFBWarnings;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -1,15 +1,14 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.configuration;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.DigitalSignatureType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.utils.SuppressFBWarnings;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.DigitalSignatureType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.utils.SuppressFBWarnings;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -1,15 +1,14 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.configuration;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.MapsId;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -2,7 +2,6 @@ package com.iqser.red.service.persistence.management.v1.processor.entity.configu
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -3,11 +3,15 @@ package com.iqser.red.service.persistence.management.v1.processor.entity.configu
import java.util.ArrayList;
import java.util.List;
import jakarta.persistence.*;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -2,6 +2,9 @@ package com.iqser.red.service.persistence.management.v1.processor.entity.configu
import java.time.OffsetDateTime;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
@ -9,10 +12,6 @@ import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -2,6 +2,11 @@ package com.iqser.red.service.persistence.management.v1.processor.entity.configu
import java.time.OffsetDateTime;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.HorizontalTextAlignment;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.VerticalTextAlignment;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.WatermarkOrientation;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
@ -13,12 +18,6 @@ import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.HorizontalTextAlignment;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.VerticalTextAlignment;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.WatermarkOrientation;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -4,8 +4,6 @@ import java.time.OffsetDateTime;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;

View File

@ -1,5 +1,7 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.dossier;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttributeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
@ -7,9 +9,6 @@ import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttributeType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -7,7 +7,6 @@ import jakarta.persistence.Embeddable;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -6,6 +6,17 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.WatermarkEntity;
import com.iqser.red.service.persistence.management.v1.processor.utils.JSONDownloadFileTypeConverter;
import com.iqser.red.service.persistence.management.v1.processor.utils.JSONStringSetConverter;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierVisibility;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
@ -19,18 +30,6 @@ import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.WatermarkEntity;
import com.iqser.red.service.persistence.management.v1.processor.utils.JSONDownloadFileTypeConverter;
import com.iqser.red.service.persistence.management.v1.processor.utils.JSONStringSetConverter;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierVisibility;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -3,6 +3,9 @@ package com.iqser.red.service.persistence.management.v1.processor.entity.dossier
import java.util.ArrayList;
import java.util.List;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
@ -11,10 +14,6 @@ import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -1,5 +1,8 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.dossier;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileAttributeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
@ -7,10 +10,6 @@ import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileAttributeType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -7,7 +7,6 @@ import jakarta.persistence.Embeddable;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

View File

@ -4,6 +4,8 @@ import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
@ -11,9 +13,6 @@ import jakarta.persistence.Id;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -1,17 +1,21 @@
package com.iqser.red.service.persistence.management.v1.processor.entity.download;
import java.util.ArrayList;
import java.util.List;
import com.iqser.red.service.pdftron.redaction.v1.api.model.RedactionResultDetail;
import com.iqser.red.service.persistence.management.v1.processor.utils.JSONDownloadRedactionFileDetailsConverter;
import jakarta.persistence.*;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Data
@Entity
@Builder

View File

@ -2,13 +2,12 @@ package com.iqser.red.service.persistence.management.v1.processor.entity.index;
import java.time.OffsetDateTime;
import com.iqser.red.service.persistence.service.v1.api.shared.model.utils.SuppressFBWarnings;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import com.iqser.red.service.persistence.service.v1.api.shared.model.utils.SuppressFBWarnings;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -6,7 +6,6 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -2,7 +2,12 @@ package com.iqser.red.service.persistence.management.v1.processor.entity.migrati
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.SaasMigrationStatus;
import jakarta.persistence.*;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -4,6 +4,8 @@ import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.Map;
import com.iqser.red.service.persistence.management.v1.processor.utils.JSONMapConverter;
import jakarta.persistence.Basic;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
@ -12,11 +14,7 @@ import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import com.iqser.red.service.persistence.management.v1.processor.utils.JSONMapConverter;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -3,18 +3,17 @@ package com.iqser.red.service.persistence.management.v1.processor.entity.notific
import java.util.ArrayList;
import java.util.List;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import com.iqser.red.service.persistence.service.v1.api.shared.model.notification.EmailNotificationType;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import com.iqser.red.service.persistence.service.v1.api.shared.model.notification.EmailNotificationType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -7,7 +7,6 @@ import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

View File

@ -2,8 +2,6 @@ package com.iqser.red.service.persistence.management.v1.processor.jobs;
import java.text.ParseException;
import com.iqser.red.service.persistence.management.v1.processor.service.job.*;
import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
@ -13,6 +11,15 @@ import org.quartz.TriggerBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.iqser.red.service.persistence.management.v1.processor.service.job.AnalysisFlagCalculationSchedulerJob;
import com.iqser.red.service.persistence.management.v1.processor.service.job.AutomaticAnalysisJob;
import com.iqser.red.service.persistence.management.v1.processor.service.job.DeletedFilesCleanupJob;
import com.iqser.red.service.persistence.management.v1.processor.service.job.DownloadCleanupJob;
import com.iqser.red.service.persistence.management.v1.processor.service.job.DownloadReadyJob;
import com.iqser.red.service.persistence.management.v1.processor.service.job.KeyCloakUserSyncJob;
import com.iqser.red.service.persistence.management.v1.processor.service.job.SendNotificationEmailJob;
import com.iqser.red.service.persistence.management.v1.processor.service.job.SyncUserPermissionsJob;
@Configuration
public class CreateJobsConfiguration {

View File

@ -0,0 +1,57 @@
package com.iqser.red.service.persistence.management.v1.processor.lifecycle;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
import org.springframework.stereotype.Component;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class LifecycleAspect {
private final LifecycleManager lifecycleManager;
private final LifecycleProperties lifecycleProperties;
@Around("@annotation(org.springframework.amqp.rabbit.annotation.RabbitListener) || "
+ "@annotation(org.springframework.web.bind.annotation.GetMapping) || "
+ "@annotation(org.springframework.web.bind.annotation.PostMapping) || "
+ "@annotation(org.springframework.web.bind.annotation.PutMapping) || "
+ "@annotation(org.springframework.web.bind.annotation.DeleteMapping) || "
+ "@annotation(org.springframework.web.bind.annotation.RequestMapping))")
public Object checkLifecycle(ProceedingJoinPoint joinPoint) throws Throwable {
String targetClassName = joinPoint.getTarget().getClass().getPackageName();
if (!targetClassName.startsWith(lifecycleProperties.getBasePackage())) {
return joinPoint.proceed();
}
synchronized (lifecycleManager) {
if (!lifecycleManager.isRunning()) {
log.info("Application is shutting down, rejecting new messages.");
throw new AmqpRejectAndDontRequeueException("Application is shutting down, rejecting new messages.");
}
lifecycleManager.incrementAndGet();
}
try {
return joinPoint.proceed();
} finally {
int remainingTasks = lifecycleManager.decrementAndGet();
synchronized (lifecycleManager) {
if (remainingTasks == 0 && !lifecycleManager.isRunning()) {
lifecycleManager.countDown();
log.info("All tasks are done, ready for shutdown.");
}
}
}
}
}

View File

@ -0,0 +1,106 @@
package com.iqser.red.service.persistence.management.v1.processor.lifecycle;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.context.ApplicationListener;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
@RequiredArgsConstructor
public class LifecycleManager implements SmartLifecycle, ApplicationListener<ContextClosedEvent> {
private volatile boolean running; // by default initialized as false
private volatile boolean shutdownInitiated; // by default initialized as false
private final Object shutdownMonitor = new Object();
private final AtomicInteger activeTasks = new AtomicInteger(0);
private final CountDownLatch latch = new CountDownLatch(1);
public void countDown() {
latch.countDown();
}
public int incrementAndGet() {
return activeTasks.incrementAndGet();
}
public int decrementAndGet() {
return activeTasks.decrementAndGet();
}
@Override
public void start() {
synchronized (shutdownMonitor) {
running = true;
}
}
@Override
public void stop() {
synchronized (shutdownMonitor) {
running = false;
if (activeTasks.get() == 0) {
latch.countDown(); // No active tasks, release the latch immediately
}
}
}
@Override
public boolean isRunning() {
return running;
}
@Override
public boolean isAutoStartup() {
return true;
}
@Override
public int getPhase() {
return Integer.MAX_VALUE; // Start this component last and stop it first
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
synchronized (shutdownMonitor) {
if (shutdownInitiated) {
return; // Avoid multiple shutdown initiations
}
shutdownInitiated = true;
}
stop();
log.info("Context is closing, waiting for ongoing tasks to complete.");
try {
latch.await(); // Wait for the latch to count down to zero
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("Shutdown was interrupted", e);
}
}
}

View File

@ -0,0 +1,17 @@
package com.iqser.red.service.persistence.management.v1.processor.lifecycle;
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ConfigurationProperties("lifecycle")
public class LifecycleProperties {
private String basePackage;
}

View File

@ -8,6 +8,12 @@ import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.manualchanges.ManualAddExportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.manualchanges.ManualForceExportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.manualchanges.ManualLegalBasisChangeExportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.manualchanges.ManualRecategorizationExportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.manualchanges.ManualRemoveExportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.manualchanges.ManualResizeExportModel;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.AnnotationEntityId;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.IdRemovalEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualForceRedactionEntity;
@ -16,12 +22,6 @@ import com.iqser.red.service.persistence.management.v1.processor.entity.annotati
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualRedactionEntryEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualResizeRedactionEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.manualchanges.ManualAddExportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.manualchanges.ManualForceExportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.manualchanges.ManualLegalBasisChangeExportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.manualchanges.ManualRecategorizationExportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.manualchanges.ManualRemoveExportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.manualchanges.ManualResizeExportModel;
@Mapper
public interface ManualChangesExportMapper {
@ -53,79 +53,96 @@ public interface ManualChangesExportMapper {
ManualResizeExportModel toModel(ManualResizeRedactionEntity entity);
ManualLegalBasisChangeEntity toEntity(ManualLegalBasisChangeExportModel model, @Context FileEntity file);
ManualLegalBasisChangeEntity toEntity(ManualLegalBasisChangeExportModel model, @Context FileEntity file, @Context String userId);
ManualForceRedactionEntity toEntity(ManualForceExportModel model, @Context FileEntity file);
ManualForceRedactionEntity toEntity(ManualForceExportModel model, @Context FileEntity file, @Context String userId);
ManualRedactionEntryEntity toEntity(ManualAddExportModel model, @Context FileEntity file);
ManualRedactionEntryEntity toEntity(ManualAddExportModel model, @Context FileEntity file, @Context String userId);
IdRemovalEntity toEntity(ManualRemoveExportModel model, @Context FileEntity file);
IdRemovalEntity toEntity(ManualRemoveExportModel model, @Context FileEntity file, @Context String userId);
ManualRecategorizationEntity toEntity(ManualRecategorizationExportModel model, @Context FileEntity file);
ManualRecategorizationEntity toEntity(ManualRecategorizationExportModel model, @Context FileEntity file, @Context String userId);
ManualResizeRedactionEntity toEntity(ManualResizeExportModel model, @Context FileEntity file);
ManualResizeRedactionEntity toEntity(ManualResizeExportModel model, @Context FileEntity file, @Context String userId);
@AfterMapping
default void setFileStatusAndId(ManualLegalBasisChangeExportModel model,
@MappingTarget ManualLegalBasisChangeEntity.ManualLegalBasisChangeEntityBuilder entity,
@Context FileEntity file) {
@Context FileEntity file,
@Context String userId) {
AnnotationEntityId annotationEntityId = new AnnotationEntityId(model.getAnnotationId(), file.getId());
entity.id(annotationEntityId);
entity.fileStatus(file);
entity.user(userId);
}
@AfterMapping
default void setFileStatusAndId(ManualRecategorizationExportModel model,
@MappingTarget ManualRecategorizationEntity.ManualRecategorizationEntityBuilder entity,
@Context FileEntity file) {
@Context FileEntity file,
@Context String userId) {
AnnotationEntityId annotationEntityId = new AnnotationEntityId(model.getAnnotationId(), file.getId());
entity.id(annotationEntityId);
entity.fileStatus(file);
entity.user(userId);
}
@AfterMapping
default void setFileStatusAndId(ManualAddExportModel model, @MappingTarget ManualRedactionEntryEntity.ManualRedactionEntryEntityBuilder entity, @Context FileEntity file) {
default void setFileStatusAndId(ManualAddExportModel model,
@MappingTarget ManualRedactionEntryEntity.ManualRedactionEntryEntityBuilder entity,
@Context FileEntity file,
@Context String userId) {
AnnotationEntityId annotationEntityId = new AnnotationEntityId(model.getAnnotationId(), file.getId());
entity.id(annotationEntityId);
entity.fileStatus(file);
entity.user(userId);
}
@AfterMapping
default void setFileStatusAndId(ManualResizeExportModel model, @MappingTarget ManualResizeRedactionEntity.ManualResizeRedactionEntityBuilder entity, @Context FileEntity file) {
default void setFileStatusAndId(ManualResizeExportModel model,
@MappingTarget ManualResizeRedactionEntity.ManualResizeRedactionEntityBuilder entity,
@Context FileEntity file,
@Context String userId) {
AnnotationEntityId annotationEntityId = new AnnotationEntityId(model.getAnnotationId(), file.getId());
entity.id(annotationEntityId);
entity.fileStatus(file);
entity.user(userId);
}
@AfterMapping
default void setFileStatusAndId(ManualForceExportModel model, @MappingTarget ManualForceRedactionEntity.ManualForceRedactionEntityBuilder entity, @Context FileEntity file) {
default void setFileStatusAndId(ManualForceExportModel model,
@MappingTarget ManualForceRedactionEntity.ManualForceRedactionEntityBuilder entity,
@Context FileEntity file,
@Context String userId) {
AnnotationEntityId annotationEntityId = new AnnotationEntityId(model.getAnnotationId(), file.getId());
entity.id(annotationEntityId);
entity.fileStatus(file);
entity.user(userId);
}
@AfterMapping
default void setFileStatusAndId(ManualRemoveExportModel model, @MappingTarget IdRemovalEntity.IdRemovalEntityBuilder entity, @Context FileEntity file) {
default void setFileStatusAndId(ManualRemoveExportModel model, @MappingTarget IdRemovalEntity.IdRemovalEntityBuilder entity, @Context FileEntity file, @Context String userId) {
AnnotationEntityId annotationEntityId = new AnnotationEntityId(model.getAnnotationId(), file.getId());
entity.id(annotationEntityId);
entity.fileStatus(file);
entity.user(userId);
}
}

View File

@ -0,0 +1,40 @@
package com.iqser.red.service.persistence.management.v1.processor.migration;
import static com.iqser.red.service.persistence.management.v1.processor.configuration.MessagingConfiguration.OCR_STATUS_UPDATE_RESPONSE_DQL;
import static com.iqser.red.service.persistence.management.v1.processor.configuration.MessagingConfiguration.OCR_STATUS_UPDATE_RESPONSE_QUEUE;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import com.iqser.red.service.persistence.management.v1.processor.settings.FileManagementServiceSettings;
import lombok.RequiredArgsConstructor;
// This can not run with migration starter service, it needs to be an InitializingBean otherwise it would be executed after queue listeners are initialized
@Component
@RequiredArgsConstructor
public class OldQueueCleanup implements InitializingBean {
private final AmqpAdmin amqpAdmin;
private final FileManagementServiceSettings settings;
public static final String OCR_QUEUE = "ocrQueue";
public static final String OCR_DLQ = "ocrDLQ";
@Override
public void afterPropertiesSet() {
// This should only run in post upgrade hook
if (settings.isMigrateOnly()) {
amqpAdmin.deleteQueue(OCR_QUEUE);
amqpAdmin.deleteQueue(OCR_DLQ);
amqpAdmin.deleteQueue(OCR_STATUS_UPDATE_RESPONSE_QUEUE);
amqpAdmin.deleteQueue(OCR_STATUS_UPDATE_RESPONSE_DQL);
amqpAdmin.deleteQueue("ocr_status_update_response_dql");
}
}
}

View File

@ -49,9 +49,11 @@ public class StorageToMongoCopyService {
log.info("Reading dossier {} file {} from storage", dossierFile.dossierId, dossierFile.fileId);
Optional<EntityLog> entityLogFromStorage = getEntityLogFromStorageForMigration(dossierFile.dossierId, dossierFile.fileId);
if (entityLogFromStorage.isPresent()) {
log.info("File found, now saving in mongodb");
fileManagementStorageService.saveEntityLog(dossierFile.dossierId, dossierFile.fileId, entityLogFromStorage.get());
log.info("File found, now saving in mongodb");
fileManagementStorageService.deleteEntityLog(dossierFile.dossierId, dossierFile.fileId);
fileManagementStorageService.insertEntityLog(dossierFile.dossierId, dossierFile.fileId, entityLogFromStorage.get());
log.info("Deleting old file from storage");
fileManagementStorageService.storeJSONObject(dossierFile.dossierId, dossierFile.fileId, FileType.ENTITY_LOG_BAK, entityLogFromStorage.get());
fileManagementStorageService.deleteObject(dossierFile.dossierId, dossierFile.fileId, FileType.ENTITY_LOG);
}
}

View File

@ -18,4 +18,5 @@ public class ComponentOverride {
String name;
List<ComponentLogEntryValue> componentOverrideValues = new ArrayList<>();
}

View File

@ -1,11 +1,9 @@
package com.iqser.red.service.persistence.management.v1.processor.model;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.BaseAnnotation;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRedactionEntry;
import lombok.Builder;
import lombok.Getter;

View File

@ -21,7 +21,9 @@ public class AnalyseEvent {
private int numberOfPagesToOCR;
private int numberOfOCRedPages;
public AnalyseEvent(String dossierId, String fileId, AnalyseStatus analyseStatus, Integer analysisNumber, OffsetDateTime timestamp) {
this.dossierId = dossierId;
this.fileId = fileId;
this.analyseStatus = analyseStatus;

View File

@ -10,6 +10,8 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
public class DossierEvent {
private String dossierId;
private DossierEventType dossierEventType;
}

View File

@ -1,5 +1,11 @@
package com.iqser.red.service.persistence.management.v1.processor.model.websocket;
public enum DossierEventType {
CREATE, UPDATE, SOFT_DELETE, HARD_DELETE, UNDELETE, ARCHIVE, UNARCHIVE
CREATE,
UPDATE,
SOFT_DELETE,
HARD_DELETE,
UNDELETE,
ARCHIVE,
UNARCHIVE
}

View File

@ -16,4 +16,5 @@ public class DownloadEvent {
private String downloadId;
private String userId;
private DownloadStatusValue status;
}

View File

@ -15,5 +15,4 @@ public class FileEvent {
private String fileId;
private FileEventType fileEventType;
}

View File

@ -1,5 +1,9 @@
package com.iqser.red.service.persistence.management.v1.processor.model.websocket;
public enum FileEventType {
CREATE, UPDATE, SOFT_DELETE, HARD_DELETE, UNDELETE
CREATE,
UPDATE,
SOFT_DELETE,
HARD_DELETE,
UNDELETE
}

View File

@ -12,4 +12,5 @@ import lombok.NoArgsConstructor;
public class NotificationEvent {
private String userId;
}

View File

@ -174,8 +174,7 @@ public final class ActionRoles {
public static final String DEPLOYMENT_INFO = "red-deployment-info";
public static final String USE_SUPPORT_CONTROLLER = "red-use-support-controller";
public static final String IMPORT_FILES = "red-import-files";
private ActionRoles() {}

View File

@ -33,6 +33,7 @@ import static com.iqser.red.service.persistence.management.v1.processor.roles.Ac
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_REPORT_TEMPLATES;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_RSS;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_TENANTS;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.IMPORT_FILES;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.MANAGE_ACL_PERMISSIONS;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.MANAGE_USER_PREFERENCES;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.MANAGE_VIEWED_PAGES;
@ -116,12 +117,19 @@ public final class ApplicationRoles {
public static final Set<String> UNMAPPED_ACTION_ROLES = Sets.newHashSet(UNARCHIVE_DOSSIER, UPDATE_LICENSE, GET_RSS, USE_SUPPORT_CONTROLLER);
public static final Set<String> KNECON_ADMIN_ACTION_ROLES = Sets.newHashSet(READ_LICENSE, UPDATE_LICENSE, GET_TENANTS, CREATE_TENANT, READ_USERS, READ_ALL_USERS, WRITE_USERS,
READ_SMTP_CONFIGURATION,
WRITE_SMTP_CONFIGURATION,
UNARCHIVE_DOSSIER,
USE_SUPPORT_CONTROLLER);
public static final Set<String> KNECON_SUPPORT_ACTION_ROLES = Sets.newHashSet(KNECON_ADMIN_ACTION_ROLES);
public static final Set<String> KNECON_SUPPORT_ACTION_ROLES = Sets.newHashSet(READ_LICENSE,
UPDATE_LICENSE,
GET_TENANTS,
CREATE_TENANT,
READ_USERS,
READ_ALL_USERS,
WRITE_USERS,
READ_SMTP_CONFIGURATION,
WRITE_SMTP_CONFIGURATION,
UNARCHIVE_DOSSIER,
USE_SUPPORT_CONTROLLER);
public static final Set<String> KNECON_ADMIN_ACTION_ROLES = Sets.union(KNECON_SUPPORT_ACTION_ROLES, Sets.newHashSet(IMPORT_FILES));
public static final Set<String> RED_USER_ACTION_ROLES = Sets.newHashSet(ADD_COMMENT,
READ_LICENSE,

View File

@ -117,7 +117,7 @@ public class AnalysisFlagsCalculationService {
hasUpdates = true;
}
if (entry.getNumberOfComments() > 0) {
if (entry.getNumberOfComments() > 0 && !entry.getState().equals(EntryState.REMOVED)) {
hasComments = true;
}

View File

@ -113,7 +113,6 @@ public class ComponentDefinitionService {
@Transactional
public List<ComponentDefinition> restoreComponents(String dossierTemplateId, List<String> componentIds) {
List<ComponentDefinitionEntity> componentDefinitionEntities = componentDefinitionPersistenceService.restoreComponents(dossierTemplateId, componentIds);
int rank = componentDefinitionPersistenceService.countByDossierTemplateId(dossierTemplateId);
for (ComponentDefinitionEntity componentDefinitionEntity : componentDefinitionEntities) {

View File

@ -7,19 +7,17 @@ import java.time.OffsetDateTime;
import java.util.List;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
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.entity.ComponentMappingEntity;
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.ComponentMappingDownloadModel;
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 jakarta.validation.ConstraintViolationException;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;

View File

@ -238,6 +238,13 @@ public class DictionaryManagementService {
@Transactional
public void addEntries(String typeId, List<String> entries, boolean removeCurrent, boolean ignoreInvalidEntries, DictionaryEntryType dictionaryEntryType) {
addEntries(typeId, entries, removeCurrent, ignoreInvalidEntries, dictionaryEntryType, false);
}
@Transactional
public void addEntries(String typeId, List<String> entries, boolean removeCurrent, boolean ignoreInvalidEntries, DictionaryEntryType dictionaryEntryType, boolean isImport) {
checkForDossierTypeExistenceAndCreate(typeId);
Set<String> cleanEntries = entries.stream()
@ -258,7 +265,7 @@ public class DictionaryManagementService {
// To check whether the type exists, type should not be added into database implicitly by addEntry.
Type typeResult = MagicConverter.convert(dictionaryPersistenceService.getType(typeId), Type.class);
if (!typeResult.isHasDictionary()) {
if (!typeResult.isHasDictionary() && !isImport) {
throw new BadRequestException("Entity type does not have a dictionary");
}

View File

@ -132,7 +132,8 @@ public class DictionaryService {
if (entriesToDelete != null && !entriesToDelete.isEmpty()) {
deleteGlobalEntries(type, dossierTemplateId, entriesToDelete, dictionaryEntryType);
}
if (entriesToAdd != null && !entriesToAdd.isEmpty() && !entriesToAdd.stream().allMatch(String::isEmpty)) {
if (entriesToAdd != null && !entriesToAdd.isEmpty() && !entriesToAdd.stream()
.allMatch(String::isEmpty)) {
addGlobalEntries(type, dossierTemplateId, entriesToAdd, false, dictionaryEntryType);
}
}
@ -150,7 +151,8 @@ public class DictionaryService {
if (entriesToDelete != null && !entriesToDelete.isEmpty()) {
deleteDossierEntries(type, dossierTemplateId, entriesToDelete, dossierId, dictionaryEntryType);
}
if (entriesToAdd != null && !entriesToAdd.isEmpty() && !entriesToAdd.stream().allMatch(String::isEmpty)) {
if (entriesToAdd != null && !entriesToAdd.isEmpty() && !entriesToAdd.stream()
.allMatch(String::isEmpty)) {
addDossierEntries(type, dossierTemplateId, entriesToAdd, false, dossierId, dictionaryEntryType);
}
}
@ -487,6 +489,7 @@ public class DictionaryService {
return MagicConverter.convert(colorsService.getColors(dossierTemplateId), Colors.class);
}
@PreAuthorize("hasAuthority('" + ADD_UPDATE_DICTIONARY_TYPE + "')")
public void changeAddToDictionary(String type, String dossierTemplateId, String dossierId, boolean addToDictionary) {

View File

@ -6,6 +6,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.Dossier;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import io.micrometer.observation.annotation.Observed;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -24,6 +25,7 @@ public class DossierCreatorService {
@Transactional
@Observed(name = "DossierCreatorService", contextualName = "create-dossier-acl")
public Dossier addDossier(CreateOrUpdateDossierRequest dossierRequest, Set<String> members, Set<String> approvers, String ownerId) {
var dossier = dossierService.addDossier(dossierRequest);

View File

@ -23,6 +23,7 @@ public class DossierIdFileIdRequestValidator {
DossierManagementService dossierManagementService;
FileStatusManagementService fileStatusManagementService;
@Observed(name = "DossierIdFileIdRequestValidator", contextualName = "validate-request")
public void validateRequestOrThrow404(String dossierTemplateId, Set<String> dossierIds, Set<String> fileIds) {

View File

@ -64,8 +64,9 @@ public class DossierManagementService {
List<FileModel> fileStatuses = fileStatusService.getDossierStatus(dossierId);
var relevantFileIds = fileStatuses.stream()
.filter(fileStatus -> fileStatus.getDeleted() == null).map(FileModel::getId).toList();
.filter(fileStatus -> fileStatus.getDeleted() == null)
.map(FileModel::getId)
.toList();
dossierDeletionService.softDeleteDossier(dossierId, relevantFileIds, now);
fileDeletionService.reindexDeletedFiles(dossierId, relevantFileIds);
@ -119,6 +120,7 @@ public class DossierManagementService {
}
@Transactional
public Set<String> getAllDossierIdsForDossierTemplateId(String dossierTemplateId) {
@ -208,7 +210,10 @@ public class DossierManagementService {
for (String dossierId : dossierIds) {
List<String> fileIds = fileStatusService.getDossierStatus(dossierId).stream().map(FileModel::getId).collect(Collectors.toList());
List<String> fileIds = fileStatusService.getDossierStatus(dossierId)
.stream()
.map(FileModel::getId)
.collect(Collectors.toList());
dossierDeletionService.hardDeleteDossier(dossierId, fileIds);
dossierDeletionService.hardDeleteFileDataAndIndexUpdates(dossierId, fileIds);
}
@ -221,10 +226,13 @@ public class DossierManagementService {
for (String dossierId : dossierIds) {
var dossier = dossierService.getDossierById(dossierId);
List<FileModel> fileStatuses = fileStatusService.getDossierStatus(dossierId);
var relevantFileIds = fileStatuses.stream().filter(fileStatus -> fileStatus.getDeleted() != null && (fileStatus.getDeleted().equals(dossier.getSoftDeletedTime()) || fileStatus.getDeleted()
.isAfter(dossier.getSoftDeletedTime()))).map(FileModel::getId).collect(Collectors.toList());
var relevantFileIds = fileStatuses.stream()
.filter(fileStatus -> fileStatus.getDeleted() != null && (fileStatus.getDeleted().equals(dossier.getSoftDeletedTime()) || fileStatus.getDeleted()
.isAfter(dossier.getSoftDeletedTime())))
.map(FileModel::getId)
.collect(Collectors.toList());
dossierDeletionService.undeleteDossier(dossierId,relevantFileIds,dossier.getSoftDeletedTime());
dossierDeletionService.undeleteDossier(dossierId, relevantFileIds, dossier.getSoftDeletedTime());
dossierDeletionService.reindexUndeletedFiles(dossier.getDossierTemplateId(), dossierId, relevantFileIds);
}

View File

@ -54,6 +54,8 @@ public class DossierService {
throw new BadRequestException("Dossier template is not active.");
}
validateDossierName(createOrUpdateDossierRequest);
try {
return dossierPersistenceService.insert(createOrUpdateDossierRequest);
} catch (Exception e) {
@ -68,6 +70,14 @@ public class DossierService {
}
private void validateDossierName(CreateOrUpdateDossierRequest createOrUpdateDossierRequest) {
if (createOrUpdateDossierRequest.getDossierName().length() > 254) {
throw new BadRequestException("Dossier name can not be longer than 254 characters");
}
}
public DossierEntity updateDossier(CreateOrUpdateDossierRequest dossierRequest, String dossierId) {
var dossierTemplate = dossierTemplatePersistenceService.getDossierTemplate(dossierRequest.getDossierTemplateId());
@ -143,6 +153,7 @@ public class DossierService {
return dossierPersistenceService.findAllDossiersForDossierTemplateId(dossierTemplateId);
}
public Set<String> getAllDossierIdsForDossierTemplateId(String dossierTemplateId) {
return dossierPersistenceService.findAllDossierIdsForDossierTemplateId(dossierTemplateId);

Some files were not shown because too many files have changed in this diff Show More