From d6efa613e553e1ca716ce29e4d31b23ed950ce67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominique=20Eifl=C3=A4nder?= Date: Tue, 28 May 2024 15:44:25 +0200 Subject: [PATCH] RED-9225: Implemented new endpoints for api v2 --- .../impl/controller/DossierController.java | 273 +++++++++++------- .../impl/controller/DossierControllerV2.java | 92 +++++- .../DossierTemplateControllerV2.java | 73 ++++- .../impl/controller/DownloadControllerV2.java | 102 +++++++ .../api/impl/controller/FileControllerV2.java | 68 ++++- .../impl/controller/LicenseControllerV2.java | 2 +- .../external/model/BulkDownloadRequest.java | 25 ++ .../model/DossierAttributeDefinition.java | 21 ++ .../model/DossierAttributeDefinitionList.java | 18 ++ .../model/DossierStatusDefinition.java | 21 ++ .../model/DossierStatusDefinitionList.java | 18 ++ .../api/external/model/DownloadRequest.java | 25 ++ .../v2/api/external/model/DownloadStatus.java | 35 +++ .../external/model/DownloadStatusList.java | 19 ++ .../external/resource/DossierResource.java | 25 ++ .../resource/DossierTemplateResource.java | 67 +++-- .../external/resource/DownloadResource.java | 55 ++++ .../api/external/resource/FileResource.java | 28 +- .../entity/download/DownloadStatusEntity.java | 2 + .../DownloadStatusPersistenceService.java | 10 + .../repository/DownloadStatusRepository.java | 4 + .../db/changelog/db.changelog-tenant.yaml | 2 + .../126-add-uuid-to-download-status.yaml | 11 + .../dossiertemplate/dossier/Dossier.java | 1 + .../dossier/DossierAttributes.java | 17 ++ 25 files changed, 851 insertions(+), 163 deletions(-) create mode 100644 persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DownloadControllerV2.java create mode 100644 persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/BulkDownloadRequest.java create mode 100644 persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierAttributeDefinition.java create mode 100644 persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierAttributeDefinitionList.java create mode 100644 persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierStatusDefinition.java create mode 100644 persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierStatusDefinitionList.java create mode 100644 persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DownloadRequest.java create mode 100644 persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DownloadStatus.java create mode 100644 persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DownloadStatusList.java create mode 100644 persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DownloadResource.java create mode 100644 persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/126-add-uuid-to-download-status.yaml create mode 100644 persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/DossierAttributes.java diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierController.java index bbece0f07..57792bb1e 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierController.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierController.java @@ -10,6 +10,7 @@ import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -18,6 +19,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierAttributeEntity; import com.iqser.red.service.persistence.management.v1.processor.service.DossierCreatorService; import org.apache.commons.lang3.StringUtils; @@ -42,6 +44,7 @@ import com.iqser.red.service.persistence.management.v1.processor.service.AccessC import com.iqser.red.service.persistence.management.v1.processor.service.DossierManagementService; import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusManagementService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributePersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.users.UserService; import com.iqser.red.service.persistence.management.v1.processor.service.users.model.User; @@ -55,6 +58,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.Audit import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive; 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 com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttributes; import com.iqser.red.service.persistence.service.v1.api.shared.model.notification.NotificationType; import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity; @@ -76,6 +80,7 @@ public class DossierController implements DossierResource { private final AccessControlService accessControlService; private final DossierACLService dossierACLService; private final DossierCreatorService dossierCreatorService; + private final DossierAttributePersistenceService dossierAttributePersistenceService; @Override @@ -112,8 +117,8 @@ public class DossierController implements DossierResource { Set approvers = getAndValidateMembers(ownerId, dossierRequest.getApproverIds()); members.addAll(approvers); - if ((dossierRequest.getDownloadFileTypes() == null || dossierRequest.getDownloadFileTypes() - .isEmpty()) && (dossierRequest.getReportTemplateIds() == null || dossierRequest.getReportTemplateIds().isEmpty())) { + if ((dossierRequest.getDownloadFileTypes() == null || dossierRequest.getDownloadFileTypes().isEmpty()) && (dossierRequest.getReportTemplateIds() == null + || dossierRequest.getReportTemplateIds().isEmpty())) { throw new BadRequestException("Download and report types cannot both be empty"); } @@ -128,16 +133,16 @@ public class DossierController implements DossierResource { // update using data from request and computed owner/members/approvers Dossier updatedDossier = dossierManagementService.updateDossier(CreateOrUpdateDossierRequest.builder() - .dossierName(dossierRequest.getDossierName()) - .description(dossierRequest.getDescription()) - .dossierTemplateId(dossierRequest.getDossierTemplateId()) - .downloadFileTypes(dossierRequest.getDownloadFileTypes()) - .dueDate(dossierRequest.getDueDate()) - .reportTemplateIds(new ArrayList<>(dossierRequest.getReportTemplateIds())) - .watermarkId(dossierRequest.getWatermarkId()) - .previewWatermarkId(dossierRequest.getPreviewWatermarkId()) - .dossierStatusId(dossierRequest.getDossierStatusId()) - .build(), existingDossier.getId()); + .dossierName(dossierRequest.getDossierName()) + .description(dossierRequest.getDescription()) + .dossierTemplateId(dossierRequest.getDossierTemplateId()) + .downloadFileTypes(dossierRequest.getDownloadFileTypes()) + .dueDate(dossierRequest.getDueDate()) + .reportTemplateIds(new ArrayList<>(dossierRequest.getReportTemplateIds())) + .watermarkId(dossierRequest.getWatermarkId()) + .previewWatermarkId(dossierRequest.getPreviewWatermarkId()) + .dossierStatusId(dossierRequest.getDossierStatusId()) + .build(), existingDossier.getId()); dossierACLService.updateDossierACL(members, approvers, ownerId, updatedDossier.getId()); dossierACLService.enhanceDossierWithACLData(updatedDossier); @@ -145,28 +150,28 @@ public class DossierController implements DossierResource { updateFileStatusForDossierFiles(updatedDossier.getId(), members); auditPersistenceService.audit(AuditRequest.builder() - .userId(KeycloakSecurity.getUserId()) - .objectId(updatedDossier.getId()) - .category(AuditCategory.DOSSIER.name()) - .message("Dossier has been updated.") - .build()); + .userId(KeycloakSecurity.getUserId()) + .objectId(updatedDossier.getId()) + .category(AuditCategory.DOSSIER.name()) + .message("Dossier has been updated.") + .build()); if (existingDossier.getOwnerId() == null || !existingDossier.getOwnerId().equals(ownerId)) { if (ownerId != null && !ownerId.equals(KeycloakSecurity.getUserId())) { notificationPersistenceService.insertNotification(AddNotificationRequest.builder() - .userId(ownerId) - .issuerId(KeycloakSecurity.getUserId()) - .notificationType(NotificationType.DOSSIER_OWNER_SET.name()) - .target(Map.of("dossierId", dossierRequest.getDossierId())) - .build()); + .userId(ownerId) + .issuerId(KeycloakSecurity.getUserId()) + .notificationType(NotificationType.DOSSIER_OWNER_SET.name()) + .target(Map.of("dossierId", dossierRequest.getDossierId())) + .build()); } if (existingDossier.getOwnerId() != null && !existingDossier.getOwnerId().equals(KeycloakSecurity.getUserId())) { notificationPersistenceService.insertNotification(AddNotificationRequest.builder() - .userId(existingDossier.getOwnerId()) - .issuerId(KeycloakSecurity.getUserId()) - .notificationType(NotificationType.DOSSIER_OWNER_REMOVED.name()) - .target(Map.of("dossierId", dossierRequest.getDossierId())) - .build()); + .userId(existingDossier.getOwnerId()) + .issuerId(KeycloakSecurity.getUserId()) + .notificationType(NotificationType.DOSSIER_OWNER_REMOVED.name()) + .target(Map.of("dossierId", dossierRequest.getDossierId())) + .build()); } } @@ -174,46 +179,50 @@ public class DossierController implements DossierResource { uniqueMembers.addAll(approvers); uniqueMembers.stream() - .filter(member -> !member.equals(ownerId) && !member.equals(KeycloakSecurity.getUserId()) && (existingDossier.getMemberIds() == null || !existingDossier.getMemberIds() - .contains(member))) + .filter(member -> !member.equals(ownerId) && !member.equals(KeycloakSecurity.getUserId()) && (existingDossier.getMemberIds() == null + || !existingDossier.getMemberIds().contains(member))) .forEach(member -> notificationPersistenceService.insertNotification(AddNotificationRequest.builder() - .userId(member) - .issuerId(KeycloakSecurity.getUserId()) - .notificationType(NotificationType.USER_BECOMES_DOSSIER_MEMBER.name()) - .target(Map.of("dossierId", dossierRequest.getDossierId())) - .build())); + .userId(member) + .issuerId(KeycloakSecurity.getUserId()) + .notificationType(NotificationType.USER_BECOMES_DOSSIER_MEMBER.name()) + .target(Map.of("dossierId", dossierRequest.getDossierId())) + .build())); if (existingDossier.getMemberIds() != null) { existingDossier.getMemberIds() .stream() .filter(member -> !members.contains(member) && !approvers.contains(member) && !member.equals(KeycloakSecurity.getUserId())) .forEach(member -> notificationPersistenceService.insertNotification(AddNotificationRequest.builder() - .userId(member) - .issuerId(KeycloakSecurity.getUserId()) - .notificationType(NotificationType.USER_REMOVED_AS_DOSSIER_MEMBER.name()) - .target(Map.of("dossierId", dossierRequest.getDossierId())) - .build())); + .userId(member) + .issuerId(KeycloakSecurity.getUserId()) + .notificationType(NotificationType.USER_REMOVED_AS_DOSSIER_MEMBER.name()) + .target(Map.of("dossierId", dossierRequest.getDossierId())) + .build())); } approvers.stream() .filter(approver -> !KeycloakSecurity.getUserId().equals(approver) && existingDossier.getMemberIds() != null && existingDossier.getMemberIds() .contains(approver) && (existingDossier.getApproverIds() == null || !existingDossier.getApproverIds().contains(approver))) .forEach(approver -> notificationPersistenceService.insertNotification(AddNotificationRequest.builder() - .userId(approver) - .issuerId(KeycloakSecurity.getUserId()) - .notificationType(NotificationType.USER_PROMOTED_TO_APPROVER.name()) - .target(Map.of("dossierId", dossierRequest.getDossierId())) - .build())); + .userId(approver) + .issuerId(KeycloakSecurity.getUserId()) + .notificationType(NotificationType.USER_PROMOTED_TO_APPROVER.name()) + .target(Map.of("dossierId", dossierRequest.getDossierId())) + .build())); members.stream() - .filter(member -> !member.equals(KeycloakSecurity.getUserId()) && existingDossier.getApproverIds() != null && existingDossier.getApproverIds() - .contains(member) && !approvers.contains(member)) + .filter(member -> !member.equals(KeycloakSecurity.getUserId()) + && existingDossier.getApproverIds() != null + && existingDossier.getApproverIds().contains(member) + && !approvers.contains(member)) .forEach(member -> notificationPersistenceService.insertNotification(AddNotificationRequest.builder() - .userId(member) - .issuerId(KeycloakSecurity.getUserId()) - .notificationType(NotificationType.USER_DEGRADED_TO_REVIEWER.name()) - .target(Map.of("dossierId", dossierRequest.getDossierId())) - .build())); + .userId(member) + .issuerId(KeycloakSecurity.getUserId()) + .notificationType(NotificationType.USER_DEGRADED_TO_REVIEWER.name()) + .target(Map.of("dossierId", dossierRequest.getDossierId())) + .build())); + + updatedDossier.setDossierAttributes(convertDossierAttributes(dossierAttributePersistenceService.getDossierAttributes(updatedDossier.getId()))); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_JSON); @@ -223,11 +232,11 @@ public class DossierController implements DossierResource { Dossier created = createNewDossier(dossierRequest, ownerId, members, approvers); auditPersistenceService.audit(AuditRequest.builder() - .userId(KeycloakSecurity.getUserId()) - .objectId(created.getId()) - .category(AuditCategory.DOSSIER.name()) - .message("Dossier has been created.") - .build()); + .userId(KeycloakSecurity.getUserId()) + .objectId(created.getId()) + .category(AuditCategory.DOSSIER.name()) + .message("Dossier has been created.") + .build()); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_JSON); @@ -254,7 +263,9 @@ public class DossierController implements DossierResource { } // check he has a manager role, thus he can be the owner - if (user.isPresent() && user.get().getRoles().stream().noneMatch(ApplicationRoles.RED_MANAGER_ROLE::equals)) { + if (user.isPresent() && user.get().getRoles() + .stream() + .noneMatch(ApplicationRoles.RED_MANAGER_ROLE::equals)) { throw new BadRequestException("Make sure provided user id has the manager role."); } @@ -266,7 +277,8 @@ public class DossierController implements DossierResource { private Set getAndValidateMembers(String ownerId, Set memberIds) { Set actualMemberIds = memberIds == null ? new TreeSet<>() : memberIds; - if (actualMemberIds.stream().anyMatch(Objects::isNull)) { + if (actualMemberIds.stream() + .anyMatch(Objects::isNull)) { throw new BadRequestException("Member IDs cannot be null"); } @@ -280,7 +292,10 @@ public class DossierController implements DossierResource { Set deletedUserIds = userService.removeDeletedUsers(actualMemberIds); actualMemberIds.removeAll(deletedUserIds); } - if (users.stream().anyMatch(u -> u.getRoles().stream().noneMatch(VALID_MEMBER_ROLES::contains))) { + if (users.stream() + .anyMatch(u -> u.getRoles() + .stream() + .noneMatch(VALID_MEMBER_ROLES::contains))) { throw new BadRequestException("Make sure each provided member id has the permission to be a member of a dossier."); } return actualMemberIds; @@ -299,12 +314,15 @@ public class DossierController implements DossierResource { private void updateFileStatusForDossierFiles(String dossierId, Collection members) { - fileStatusManagementService.getDossierStatus(dossierId).stream().filter(fileStatus -> !fileStatus.isSoftOrHardDeleted()).forEach(f -> { + fileStatusManagementService.getDossierStatus(dossierId) + .stream() + .filter(fileStatus -> !fileStatus.isSoftOrHardDeleted()) + .forEach(f -> { - if (f.getAssignee() != null && !members.contains(f.getAssignee())) { - fileStatusManagementService.setCurrentFileAssignee(dossierId, f.getId(), null); - } - }); + if (f.getAssignee() != null && !members.contains(f.getAssignee())) { + fileStatusManagementService.setCurrentFileAssignee(dossierId, f.getId(), null); + } + }); } @@ -312,18 +330,20 @@ public class DossierController implements DossierResource { private Dossier createNewDossier(DossierRequest dossier, String ownerId, Set members, Set approvers) { Dossier newDossier = dossierCreatorService.addDossier(CreateOrUpdateDossierRequest.builder() - .dossierName(dossier.getDossierName().trim()) - .description(dossier.getDescription()) - .dossierTemplateId(dossier.getDossierTemplateId()) - .downloadFileTypes(dossier.getDownloadFileTypes()) - .dueDate(dossier.getDueDate()) - .reportTemplateIds(dossier.getReportTemplateIds() != null ? new ArrayList<>(dossier.getReportTemplateIds()) : Lists.newArrayList()) - .watermarkId(dossier.getWatermarkId()) - .previewWatermarkId(dossier.getPreviewWatermarkId()) - .dossierStatusId(dossier.getDossierStatusId()) - .build(), members, approvers, ownerId); + .dossierName(dossier.getDossierName().trim()) + .description(dossier.getDescription()) + .dossierTemplateId(dossier.getDossierTemplateId()) + .downloadFileTypes(dossier.getDownloadFileTypes()) + .dueDate(dossier.getDueDate()) + .reportTemplateIds(dossier.getReportTemplateIds() + != null ? new ArrayList<>(dossier.getReportTemplateIds()) : Lists.newArrayList()) + .watermarkId(dossier.getWatermarkId()) + .previewWatermarkId(dossier.getPreviewWatermarkId()) + .dossierStatusId(dossier.getDossierStatusId()) + .build(), members, approvers, ownerId); dossierACLService.enhanceDossierWithACLData(newDossier); + newDossier.setDossierAttributes(new DossierAttributes()); return newDossier; @@ -358,21 +378,21 @@ public class DossierController implements DossierResource { dossierManagementService.delete(dossierId); auditPersistenceService.audit(AuditRequest.builder() - .userId(KeycloakSecurity.getUserId()) - .objectId(dossierId) - .category(AuditCategory.DOSSIER.name()) - .message("Dossier moved to trash.") - .build()); + .userId(KeycloakSecurity.getUserId()) + .objectId(dossierId) + .category(AuditCategory.DOSSIER.name()) + .message("Dossier moved to trash.") + .build()); dossier.getMemberIds() .stream() .filter(m -> !KeycloakSecurity.getUserId().equals(m)) .forEach(member -> notificationPersistenceService.insertNotification(AddNotificationRequest.builder() - .userId(member) - .issuerId(KeycloakSecurity.getUserId()) - .notificationType(NotificationType.DOSSIER_DELETED.name()) - .target(Map.of("dossierId", dossierId, "dossierName", dossier.getDossierName())) - .build())); + .userId(member) + .issuerId(KeycloakSecurity.getUserId()) + .notificationType(NotificationType.DOSSIER_DELETED.name()) + .target(Map.of("dossierId", dossierId, "dossierName", dossier.getDossierName())) + .build())); } @@ -383,8 +403,9 @@ public class DossierController implements DossierResource { @RequestParam(name = INCLUDE_DELETED_PARAM, defaultValue = "false", required = false) boolean includeDeleted) { accessControlService.checkViewPermissionsToDossier(dossierId); - - return dossierACLService.enhanceDossierWithACLData(dossierManagementService.getDossierById(dossierId, includeArchived, includeDeleted)); + var dossier = dossierManagementService.getDossierById(dossierId, includeArchived, includeDeleted); + dossier.setDossierAttributes(convertDossierAttributes(dossierAttributePersistenceService.getDossierAttributes(dossierId))); + return dossierACLService.enhanceDossierWithACLData(dossier); } @@ -393,7 +414,12 @@ public class DossierController implements DossierResource { public List getDossiers(@RequestParam(name = INCLUDE_ARCHIVED_PARAM, defaultValue = "false", required = false) boolean includeArchived, @RequestParam(name = INCLUDE_DELETED_PARAM, defaultValue = "false", required = false) boolean includeDeleted) { - return dossierManagementService.getAllDossiers(includeArchived, includeDeleted).stream().map(dossierACLService::enhanceDossierWithACLData).collect(Collectors.toList()); + var dossiers = dossierManagementService.getAllDossiers(includeArchived, includeDeleted) + .stream() + .map(dossierACLService::enhanceDossierWithACLData) + .collect(Collectors.toList()); + dossiers.forEach(dossier -> dossier.setDossierAttributes(convertDossierAttributes(dossierAttributePersistenceService.getDossierAttributes(dossier.getId())))); + return dossiers; } @@ -403,10 +429,12 @@ public class DossierController implements DossierResource { @RequestParam(name = INCLUDE_ARCHIVED_PARAM, defaultValue = "false", required = false) boolean includeArchived, @RequestParam(name = INCLUDE_DELETED_PARAM, defaultValue = "false", required = false) boolean includeDeleted) { - return dossierManagementService.getAllDossiersForDossierTemplateId(dossierTemplateId, includeArchived, includeDeleted) + var dossiers = dossierManagementService.getAllDossiersForDossierTemplateId(dossierTemplateId, includeArchived, includeDeleted) .stream() .map(dossierACLService::enhanceDossierWithACLData) .collect(Collectors.toList()); + dossiers.forEach(dossier -> dossier.setDossierAttributes(convertDossierAttributes(dossierAttributePersistenceService.getDossierAttributes(dossier.getId())))); + return dossiers; } @@ -414,8 +442,12 @@ public class DossierController implements DossierResource { @PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')") public List getSoftDeletedDossiers() { - return dossierManagementService.getSoftDeletedDossiers().stream().map(dossierACLService::enhanceDossierWithACLData).collect(Collectors.toList()); - + var dossiers = dossierManagementService.getSoftDeletedDossiers() + .stream() + .map(dossierACLService::enhanceDossierWithACLData) + .collect(Collectors.toList()); + dossiers.forEach(dossier -> dossier.setDossierAttributes(convertDossierAttributes(dossierAttributePersistenceService.getDossierAttributes(dossier.getId())))); + return dossiers; } @@ -423,7 +455,12 @@ public class DossierController implements DossierResource { @PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')") public List getArchivedDossiers() { - return dossierManagementService.getArchivedDossiers().stream().map(dossierACLService::enhanceDossierWithACLData).collect(Collectors.toList()); + var dossiers = dossierManagementService.getArchivedDossiers() + .stream() + .map(dossierACLService::enhanceDossierWithACLData) + .collect(Collectors.toList()); + dossiers.forEach(dossier -> dossier.setDossierAttributes(convertDossierAttributes(dossierAttributePersistenceService.getDossierAttributes(dossier.getId())))); + return dossiers; } @@ -431,10 +468,12 @@ public class DossierController implements DossierResource { @PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')") public List getArchivedDossiersForDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) { - return dossierManagementService.getArchivedDossiersForDossierTemplateId(dossierTemplateId) + var dossiers = dossierManagementService.getArchivedDossiersForDossierTemplateId(dossierTemplateId) .stream() .map(dossierACLService::enhanceDossierWithACLData) .collect(Collectors.toList()); + dossiers.forEach(dossier -> dossier.setDossierAttributes(convertDossierAttributes(dossierAttributePersistenceService.getDossierAttributes(dossier.getId())))); + return dossiers; } @@ -449,11 +488,11 @@ public class DossierController implements DossierResource { for (String dossierId : dossierIds) { auditPersistenceService.audit(AuditRequest.builder() - .userId(KeycloakSecurity.getUserId()) - .objectId(dossierId) - .category(AuditCategory.DOSSIER.name()) - .message("Dossier archived.") - .build()); + .userId(KeycloakSecurity.getUserId()) + .objectId(dossierId) + .category(AuditCategory.DOSSIER.name()) + .message("Dossier archived.") + .build()); } } @@ -466,11 +505,11 @@ public class DossierController implements DossierResource { for (String dossierId : dossierIds) { auditPersistenceService.audit(AuditRequest.builder() - .userId(KeycloakSecurity.getUserId()) - .objectId(dossierId) - .category(AuditCategory.DOSSIER.name()) - .message("Dossier restored from archive.") - .build()); + .userId(KeycloakSecurity.getUserId()) + .objectId(dossierId) + .category(AuditCategory.DOSSIER.name()) + .message("Dossier restored from archive.") + .build()); } } @@ -486,11 +525,11 @@ public class DossierController implements DossierResource { for (String dossierId : filteredDossierIds) { auditPersistenceService.audit(AuditRequest.builder() - .userId(KeycloakSecurity.getUserId()) - .objectId(dossierId) - .category(AuditCategory.DOSSIER.name()) - .message("Dossier permanently deleted.") - .build()); + .userId(KeycloakSecurity.getUserId()) + .objectId(dossierId) + .category(AuditCategory.DOSSIER.name()) + .message("Dossier permanently deleted.") + .build()); } } @@ -506,11 +545,11 @@ public class DossierController implements DossierResource { for (String dossierId : filteredDossierIds) { auditPersistenceService.audit(AuditRequest.builder() - .userId(KeycloakSecurity.getUserId()) - .objectId(dossierId) - .category(AuditCategory.DOSSIER.name()) - .message("Dossier restored from trash.") - .build()); + .userId(KeycloakSecurity.getUserId()) + .objectId(dossierId) + .category(AuditCategory.DOSSIER.name()) + .message("Dossier restored from trash.") + .build()); } } @@ -526,5 +565,15 @@ public class DossierController implements DossierResource { .collect(Collectors.toSet()); } + + private DossierAttributes convertDossierAttributes(List dossierAttributeEntities) { + + Map attributeIdToValue = new HashMap<>(); + for (DossierAttributeEntity dossierAttributeEntity : dossierAttributeEntities) { + attributeIdToValue.put(dossierAttributeEntity.getId().getDossierAttributeConfigId(), dossierAttributeEntity.getValue()); + } + return new DossierAttributes(attributeIdToValue); + } + } diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierControllerV2.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierControllerV2.java index 90380c93c..0b3de8bda 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierControllerV2.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierControllerV2.java @@ -1,10 +1,12 @@ package com.iqser.red.persistence.service.v2.external.api.impl.controller; +import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_DOSSIER_ATTRIBUTES; import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource.DOSSIER_TEMPLATE_ID_PARAM; import java.util.Set; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; @@ -12,12 +14,28 @@ import org.springframework.web.bind.annotation.RestController; import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierController; import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierTemplateController; +import com.iqser.red.persistence.service.v1.external.api.impl.controller.DownloadController; +import com.iqser.red.persistence.service.v1.external.api.impl.controller.StatusController; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity; +import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService; +import com.iqser.red.service.persistence.management.v1.processor.service.DossierAttributesManagementService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService; +import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory; import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierRequest; +import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus; +import com.iqser.red.service.persistence.service.v1.api.shared.model.PrepareDownloadWithOptionRequest; +import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier; +import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttribute; import com.iqser.red.service.persistence.service.v2.api.external.model.DocuMineDossierRequest; +import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttributes; import com.iqser.red.service.persistence.service.v2.api.external.model.DossierList; +import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadRequest; +import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatus; import com.iqser.red.service.persistence.service.v2.api.external.resource.DossierResource; +import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -30,6 +48,12 @@ public class DossierControllerV2 implements DossierResource { private final DossierTemplateController dossierTemplateController; private final DossierController dossierController; + private final AccessControlService accessControlService; + private final DossierAttributesManagementService dossierAttributesManagementService; + private final AuditPersistenceService auditPersistenceService; + private final DownloadController downloadController; + private final StatusController statusController; + private final DownloadStatusPersistenceService downloadStatusPersistenceService; public DossierList getDossiers(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @@ -42,7 +66,9 @@ public class DossierControllerV2 implements DossierResource { var dossiers = dossierController.getDossiersForDossierTemplate(dossierTemplateId, includeArchived, includeSoftDeleted); if (!includeActive) { - return new DossierList(dossiers.stream().filter(dossier -> dossier.getSoftDeletedTime() != null || dossier.getArchivedTime() != null).toList()); + return new DossierList(dossiers.stream() + .filter(dossier -> dossier.getSoftDeletedTime() != null || dossier.getArchivedTime() != null) + .toList()); } return new DossierList(dossiers); @@ -77,6 +103,70 @@ public class DossierControllerV2 implements DossierResource { } + @PreAuthorize("hasAuthority('" + WRITE_DOSSIER_ATTRIBUTES + "')") + public void setDossierAttributes(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, + @PathVariable(DOSSIER_ID_PARAM) String dossierId, + @RequestBody DossierAttributes dossierAttributes) { + + accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId); + accessControlService.verifyUserIsDossierOwner(dossierId); + + var dossierAttributeList = dossierAttributes.getAttributeIdToValue().entrySet() + .stream() + .map(entry -> new DossierAttribute(dossierId, entry.getKey(), entry.getValue())) + .toList(); + + dossierAttributesManagementService.setDossierAttributes(dossierId, dossierAttributeList); + auditPersistenceService.insertRecord(AuditRequest.builder() + .userId(KeycloakSecurity.getUserId()) + .objectId(dossierId) + .category(AuditCategory.DOSSIER.name()) + .message("Changed dossier attributes.") + .build()); + } + + + public DownloadStatus prepareDossierDownload(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, + @PathVariable(DOSSIER_ID_PARAM) String dossierId, + @RequestBody DownloadRequest downloadRequest) { + + var storageId = downloadController.prepareDownload(PrepareDownloadWithOptionRequest.builder() + .dossierId(dossierId) + .fileIds(statusController.getDossierStatus(dossierId) + .stream() + .map(FileStatus::getId) + .toList()) + .reportTemplateIds(downloadRequest.getReportTemplateIds()) + .downloadFileTypes(downloadRequest.getDownloadFileTypes()) + .redactionPreviewColor(downloadRequest.getRedactionPreviewColor()) + .includeUnprocessed(false) + .build()).getStorageId(); + + var status = downloadStatusPersistenceService.getStatus(storageId); + + return DownloadStatus.builder() + .id(status.getUuid()) // This is a workaround the real 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().getId()) + .fileIds(status.getFiles() + .stream() + .map(FileEntity::getId) + .toList()) + .downloadFileTypes(status.getDownloadFileTypes() + .stream() + .toList()) + .reportTemplateIds(downloadRequest.getReportTemplateIds()) + .build(); + } + + private static DossierRequest mapToDossierRequest(String dossierTemplateId, DocuMineDossierRequest dossier) { return DossierRequest.builder() diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java index d666b8909..a8fe8ac5f 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DossierTemplateControllerV2.java @@ -1,6 +1,8 @@ package com.iqser.red.persistence.service.v2.external.api.impl.controller; import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.EXPERIMENTAL; +import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_DOSSIER_ATTRIBUTES_CONFIG; +import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_DOSSIER_STATUS; import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_FILE_ATTRIBUTES_CONFIG; import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_RULES; import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_RULES; @@ -28,6 +30,8 @@ import com.iqser.red.persistence.service.v1.external.api.impl.controller.FileAtt import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException; import com.iqser.red.service.persistence.management.v1.processor.service.RulesValidationService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributeConfigPersistenceService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierStatusPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.RulesPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils; import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory; @@ -35,6 +39,10 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemp import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType; import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesUploadRequest; +import com.iqser.red.service.persistence.service.v2.api.external.model.DossierAttributeDefinition; +import com.iqser.red.service.persistence.service.v2.api.external.model.DossierAttributeDefinitionList; +import com.iqser.red.service.persistence.service.v2.api.external.model.DossierStatusDefinition; +import com.iqser.red.service.persistence.service.v2.api.external.model.DossierStatusDefinitionList; import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinition; import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinitionList; import com.iqser.red.service.persistence.service.v2.api.external.model.RulesValidationMessage; @@ -62,6 +70,8 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { private final RulesValidationService rulesValidationService; private final AuditPersistenceService auditPersistenceService; private final FileAttributesController fileAttributesController; + private final DossierStatusPersistenceService dossierStatusPersistenceService; + private final DossierAttributeConfigPersistenceService dossierAttributeConfigPersistenceService; public List getAllDossierTemplates() { @@ -134,7 +144,8 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { .filenameMappingCsvColumnHeader(fileAttributeConfigs.getFilenameMappingColumnHeaderName()) .build(); - var fileAttributeDefinitions = fileAttributeConfigs.getFileAttributeConfigs().stream() + var fileAttributeDefinitions = fileAttributeConfigs.getFileAttributeConfigs() + .stream() .map(fileAttributeConfig -> FileAttributeDefinition.builder() .id(fileAttributeConfig.getId()) .name(fileAttributeConfig.getLabel()) @@ -142,11 +153,11 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { .mappedCsvColumnHeader(fileAttributeConfig.getCsvColumnHeader()) .reportingPlaceholder(fileAttributeConfig.getPlaceholder()) .displaySettings(FileAttributeDefinition.DisplaySettings.builder() - .primaryAttribute(fileAttributeConfig.isPrimaryAttribute()) - .editable(fileAttributeConfig.isEditable()) - .filterable(fileAttributeConfig.isFilterable()) - .displayedInFileList(fileAttributeConfig.isDisplayedInFileList()) - .build()) + .primaryAttribute(fileAttributeConfig.isPrimaryAttribute()) + .editable(fileAttributeConfig.isEditable()) + .filterable(fileAttributeConfig.isFilterable()) + .displayedInFileList(fileAttributeConfig.isDisplayedInFileList()) + .build()) .build()) .toList(); @@ -154,6 +165,43 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { } + @PreAuthorize("hasAuthority('" + READ_DOSSIER_STATUS + "')") + public DossierStatusDefinitionList getDossierStatusDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) { + + getDossierTemplate(dossierTemplateId); + + return new DossierStatusDefinitionList(dossierStatusPersistenceService.getAllDossierStatusForTemplate(dossierTemplateId) + .stream() + .map(dossierStatusInfo -> DossierStatusDefinition.builder() + .id(dossierStatusInfo.getId()) + .name(dossierStatusInfo.getName()) + .description(dossierStatusInfo.getDescription()) + .rank(dossierStatusInfo.getRank()) + .color(dossierStatusInfo.getColor()) + .dossierCount(dossierStatusInfo.getDossierCount() != null ? dossierStatusInfo.getDossierCount() : 0) + .build()) + .toList()); + } + + + @PreAuthorize("hasAuthority('" + READ_DOSSIER_ATTRIBUTES_CONFIG + "')") + public DossierAttributeDefinitionList getDossierAttributeDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) { + + getDossierTemplate(dossierTemplateId); + + return new DossierAttributeDefinitionList(dossierAttributeConfigPersistenceService.getDossierAttributes(dossierTemplateId) + .stream() + .map(config -> DossierAttributeDefinition.builder() + + .id(config.getId()) + .name(config.getLabel()) + .type(config.getType()) + .reportingPlaceholder(config.getPlaceholder()) + .build()) + .toList()); + } + + @SneakyThrows private ResponseEntity uploadRules(String dossierTemplateId, RuleFileType ruleFileType, MultipartFile file, boolean dryRun) { @@ -188,11 +236,11 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { } auditPersistenceService.audit(AuditRequest.builder() - .userId(KeycloakSecurity.getUserId()) - .objectId(rulesUploadRequest.getDossierTemplateId()) - .category(AuditCategory.DOSSIER_TEMPLATE.name()) - .message(String.format("%s rules have been %s", rulesUploadRequest.getRuleFileType(), dryRun ? "validated" : "updated")) - .build()); + .userId(KeycloakSecurity.getUserId()) + .objectId(rulesUploadRequest.getDossierTemplateId()) + .category(AuditCategory.DOSSIER_TEMPLATE.name()) + .message(String.format("%s rules have been %s", rulesUploadRequest.getRuleFileType(), dryRun ? "validated" : "updated")) + .build()); // TODO Add warning and deprecations to response return new ResponseEntity<>(RulesValidationResponse.builder().build(), HttpStatus.OK); @@ -208,7 +256,8 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.TEXT_PLAIN); - httpHeaders.add("Content-Disposition", "attachment" + "; filename*=utf-8''" + StringEncodingUtils.urlEncode(ruleFileType.name().toLowerCase(Locale.ROOT) + RULES_DOWNLOAD_FILE_NAME_SUFFIX)); + httpHeaders.add("Content-Disposition", + "attachment" + "; filename*=utf-8''" + StringEncodingUtils.urlEncode(ruleFileType.name().toLowerCase(Locale.ROOT) + RULES_DOWNLOAD_FILE_NAME_SUFFIX)); InputStream is = new ByteArrayInputStream(data); diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DownloadControllerV2.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DownloadControllerV2.java new file mode 100644 index 000000000..85d08c24b --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/DownloadControllerV2.java @@ -0,0 +1,102 @@ +package com.iqser.red.persistence.service.v2.external.api.impl.controller; + +import java.util.List; + +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import com.iqser.red.persistence.service.v1.external.api.impl.controller.DownloadController; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.ReportTemplateEntity; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService; +import com.iqser.red.service.persistence.service.v1.api.shared.model.RemoveDownloadRequest; +import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatus; +import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatusList; +import com.iqser.red.service.persistence.service.v2.api.external.resource.DownloadResource; +import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity; + +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@Tag(name = "5. Downloads", description = "Operations related to download packages.") +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().getId()) + .fileIds(status.getFiles() + .stream() + .map(FileEntity::getId) + .toList()) + .downloadFileTypes(status.getDownloadFileTypes() + .stream() + .toList()) + .reportTemplateIds(status.getReports().stream().map(ReportTemplateEntity::getTemplateId).toList()) + .build()).toList() + ); + } + + + @Transactional + public DownloadStatus getDownloadStatus(@PathVariable(DOWNLOAD_ID_PARAM) String downloadId) { + + var status = downloadStatusPersistenceService.getStatusesByUuid(downloadId); + + return 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().getId()) + .fileIds(status.getFiles() + .stream() + .map(FileEntity::getId) + .toList()) + .downloadFileTypes(status.getDownloadFileTypes() + .stream() + .toList()) + .reportTemplateIds(status.getReports().stream().map(ReportTemplateEntity::getTemplateId).toList()) + .build(); + } + + + public void deleteDownload(@PathVariable(DOWNLOAD_ID_PARAM) String downloadId) { + + var status = downloadStatusPersistenceService.getStatusesByUuid(downloadId); + downloadController.deleteDownloadStatus(new RemoveDownloadRequest(List.of(status.getStorageId()))); + } + + + public void download(@PathVariable(DOWNLOAD_ID_PARAM) String downloadId) { + + var status = downloadStatusPersistenceService.getStatusesByUuid(downloadId); + downloadController.downloadFile(status.getStorageId()); + } + +} diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/FileControllerV2.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/FileControllerV2.java index 97c3ec5c8..eae54a546 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/FileControllerV2.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/FileControllerV2.java @@ -7,9 +7,7 @@ import static com.iqser.red.service.persistence.service.v2.api.external.resource import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource.DOSSIER_TEMPLATE_ID_PARAM; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; @@ -20,15 +18,22 @@ import org.springframework.web.multipart.MultipartFile; import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierController; import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierTemplateController; +import com.iqser.red.persistence.service.v1.external.api.impl.controller.DownloadController; import com.iqser.red.persistence.service.v1.external.api.impl.controller.FileAttributesController; import com.iqser.red.persistence.service.v1.external.api.impl.controller.FileManagementController; import com.iqser.red.persistence.service.v1.external.api.impl.controller.StatusController; import com.iqser.red.persistence.service.v1.external.api.impl.controller.UploadController; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity; import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusManagementService; import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusMapper; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService; import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttributes; import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus; import com.iqser.red.service.persistence.service.v1.api.shared.model.FileUploadResult; +import com.iqser.red.service.persistence.service.v1.api.shared.model.PrepareDownloadWithOptionRequest; +import com.iqser.red.service.persistence.service.v2.api.external.model.BulkDownloadRequest; +import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadRequest; +import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatus; import com.iqser.red.service.persistence.service.v2.api.external.model.FileDeleteRequest; import com.iqser.red.service.persistence.service.v2.api.external.model.FileStatusList; import com.iqser.red.service.persistence.service.v2.api.external.resource.FileResource; @@ -48,6 +53,8 @@ public class FileControllerV2 implements FileResource { private final FileStatusManagementService fileStatusManagementService; private final FileAttributesController fileAttributesController; private final DossierTemplateController dossierTemplateController; + private final DownloadController downloadController; + private final DownloadStatusPersistenceService downloadStatusPersistenceService; public FileUploadResult upload(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @@ -141,4 +148,61 @@ public class FileControllerV2 implements FileResource { fileAttributesController.setFileAttributes(dossierId, fileId, fileAttributes); } + + public DownloadStatus prepareFileDownload(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, + @PathVariable(DOSSIER_ID_PARAM) String dossierId, + @PathVariable(FILE_ID_PARAM) String fileId, + @RequestBody DownloadRequest downloadRequest) { + + return prepareBulkDownload(dossierTemplateId, + dossierId, + BulkDownloadRequest.builder() + .reportTemplateIds(downloadRequest.getReportTemplateIds()) + .downloadFileTypes(downloadRequest.getDownloadFileTypes()) + .redactionPreviewColor(downloadRequest.getRedactionPreviewColor()) + .fileIds(List.of(fileId)) + .build()); + } + + + public DownloadStatus prepareBulkDownload(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, + @PathVariable(DOSSIER_ID_PARAM) String dossierId, + @RequestBody BulkDownloadRequest bulkDownloadRequest) { + + var storageId = downloadController.prepareDownload(PrepareDownloadWithOptionRequest.builder() + .dossierId(dossierId) + .fileIds(statusController.getDossierStatus(dossierId) + .stream() + .map(FileStatus::getId) + .toList()) + .reportTemplateIds(bulkDownloadRequest.getReportTemplateIds()) + .downloadFileTypes(bulkDownloadRequest.getDownloadFileTypes()) + .redactionPreviewColor(bulkDownloadRequest.getRedactionPreviewColor()) + .includeUnprocessed(false) + .build()).getStorageId(); + + var status = downloadStatusPersistenceService.getStatus(storageId); + + return DownloadStatus.builder() + .id(status.getUuid()) // This is a workaround the real 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().getId()) + .fileIds(status.getFiles() + .stream() + .map(FileEntity::getId) + .toList()) + .downloadFileTypes(status.getDownloadFileTypes() + .stream() + .toList()) + .reportTemplateIds(bulkDownloadRequest.getReportTemplateIds()) + .build(); + } + } diff --git a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/LicenseControllerV2.java b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/LicenseControllerV2.java index e04a32fb8..7ec94f01d 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/LicenseControllerV2.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v2/src/main/java/com/iqser/red/persistence/service/v2/external/api/impl/controller/LicenseControllerV2.java @@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequiredArgsConstructor -@Tag(name = "5. License endpoints", description = "Provides operations related to the license") +@Tag(name = "7. License", description = "Operations related to license information and usage metrics.") public class LicenseControllerV2 implements LicenseResource { private final LicenseReportController licenseReportController; diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/BulkDownloadRequest.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/BulkDownloadRequest.java new file mode 100644 index 000000000..ace05e4fe --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/BulkDownloadRequest.java @@ -0,0 +1,25 @@ +package com.iqser.red.service.persistence.service.v2.api.external.model; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BulkDownloadRequest { + + private List reportTemplateIds = new ArrayList<>(); + private Set downloadFileTypes = new HashSet<>(); + private String redactionPreviewColor; + private List fileIds = new ArrayList<>(); +} diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierAttributeDefinition.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierAttributeDefinition.java new file mode 100644 index 000000000..fb214d6d7 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierAttributeDefinition.java @@ -0,0 +1,21 @@ +package com.iqser.red.service.persistence.service.v2.api.external.model; + +import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttributeType; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DossierAttributeDefinition { + + private String id; + private String name; + private DossierAttributeType type; + private String reportingPlaceholder; + +} diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierAttributeDefinitionList.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierAttributeDefinitionList.java new file mode 100644 index 000000000..a8ec38b8d --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierAttributeDefinitionList.java @@ -0,0 +1,18 @@ +package com.iqser.red.service.persistence.service.v2.api.external.model; + +import java.util.ArrayList; +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DossierAttributeDefinitionList { + + private List dossierAttributeDefinitions= new ArrayList<>(); +} diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierStatusDefinition.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierStatusDefinition.java new file mode 100644 index 000000000..f90a8b013 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierStatusDefinition.java @@ -0,0 +1,21 @@ +package com.iqser.red.service.persistence.service.v2.api.external.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DossierStatusDefinition { + + private String id; + private String name; + private String description; + private int rank; + private String color; + private long dossierCount; + +} diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierStatusDefinitionList.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierStatusDefinitionList.java new file mode 100644 index 000000000..20d62ae92 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DossierStatusDefinitionList.java @@ -0,0 +1,18 @@ +package com.iqser.red.service.persistence.service.v2.api.external.model; + +import java.util.ArrayList; +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DossierStatusDefinitionList { + + private List dossierStatusDefinitions = new ArrayList<>(); +} diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DownloadRequest.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DownloadRequest.java new file mode 100644 index 000000000..960c530f0 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DownloadRequest.java @@ -0,0 +1,25 @@ +package com.iqser.red.service.persistence.service.v2.api.external.model; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DownloadRequest { + + private List reportTemplateIds = new ArrayList<>(); + private Set downloadFileTypes = new HashSet<>(); + private String redactionPreviewColor; + +} diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DownloadStatus.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DownloadStatus.java new file mode 100644 index 000000000..6ce41e2e1 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DownloadStatus.java @@ -0,0 +1,35 @@ +package com.iqser.red.service.persistence.service.v2.api.external.model; + +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; + +import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType; +import com.iqser.red.service.persistence.service.v1.api.shared.model.download.DownloadStatusValue; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DownloadStatus { + + private String id; + private String userId; + private String filename; + private String mimeType; + private String errorCause; + private DownloadStatusValue status; + private OffsetDateTime creationDate; + private OffsetDateTime lastDownload; + private long fileSize; + private String dossierId; + private List fileIds = new ArrayList<>(); + private List downloadFileTypes = new ArrayList<>(); + private List reportTemplateIds = new ArrayList<>(); + +} diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DownloadStatusList.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DownloadStatusList.java new file mode 100644 index 000000000..d42d92d2d --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/model/DownloadStatusList.java @@ -0,0 +1,19 @@ +package com.iqser.red.service.persistence.service.v2.api.external.model; + +import java.util.ArrayList; +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DownloadStatusList { + + List downloadStatus = new ArrayList<>(); + +} diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierResource.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierResource.java index 0887c8ff2..106b4dc3d 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierResource.java +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierResource.java @@ -18,7 +18,10 @@ import org.springframework.web.bind.annotation.ResponseStatus; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier; import com.iqser.red.service.persistence.service.v2.api.external.model.DocuMineDossierRequest; +import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttributes; import com.iqser.red.service.persistence.service.v2.api.external.model.DossierList; +import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadRequest; +import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatus; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -32,6 +35,8 @@ public interface DossierResource { String PATH = ExternalApiConstants.BASE_PATH + DOSSIER_TEMPLATE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DOSSIER_PATH; String DOSSIER_ID_PARAM = "dossierId"; String DOSSIER_ID_PATH_PARAM = "/{" + DOSSIER_ID_PARAM + "}"; + String ATTRIBUTES_PATH = "/attributes"; + String CREATE_DOWNLOAD_PATH = "/create-download"; String INCLUDE_ACTIVE_PARAM = "includeActive"; String INCLUDE_ARCHIVED_PARAM = "includeArchived"; @@ -74,4 +79,24 @@ public interface DossierResource { void deleteDossier(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier to retrieve.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId); + + @ResponseStatus(value = HttpStatus.NO_CONTENT) + @ResponseBody + @PostMapping(value = PATH + DOSSIER_ID_PATH_PARAM + ATTRIBUTES_PATH, consumes = MediaType.APPLICATION_JSON_VALUE) + @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); + + + @ResponseBody + @ResponseStatus(value = HttpStatus.OK) + @PostMapping(value = PATH + CREATE_DOWNLOAD_PATH + DOSSIER_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Initiate the creation of a download package for all files of a dossier.") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully started the creation of the download package for all files of the requested dossier."), @ApiResponse(responseCode = "404", description = "Not found")}) + DownloadStatus prepareDossierDownload(@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 DownloadRequest downloadRequest); + } diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java index 91daba819..b7ae42a91 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DossierTemplateResource.java @@ -2,6 +2,8 @@ package com.iqser.red.service.persistence.service.v2.api.external.resource; import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel; import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType; +import com.iqser.red.service.persistence.service.v2.api.external.model.DossierAttributeDefinitionList; +import com.iqser.red.service.persistence.service.v2.api.external.model.DossierStatusDefinitionList; import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinitionList; import com.iqser.red.service.persistence.service.v2.api.external.model.RulesValidationResponse; @@ -36,13 +38,13 @@ public interface DossierTemplateResource { String ENTITY_RULES_PATH = "/entity-rules"; String COMPONENT_RULES_PATH = "/component-rules"; + String DOSSIER_STATUS_DEFINITIONS_PATH = "/dossier-status-definitions"; + String DOSSIER_ATTRIBUTE_DEFINITION_PATH = "/dossier-attribute-definitions"; String FILE_ATTRIBUTE_DEFINITIONS_PATH = "/file-attribute-definitions"; String DOSSIER_TEMPLATE_ID_PARAM = "dossierTemplateId"; String DOSSIER_TEMPLATE_ID_PATH_VARIABLE = "/{" + DOSSIER_TEMPLATE_ID_PARAM + "}"; - String RULE_FILE_TYPE_PARAMETER_NAME = "ruleFileType"; - String DRY_RUN_PARAM = "dryRun"; @@ -54,64 +56,61 @@ public interface DossierTemplateResource { @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Get a specific DossierTemplate by its identifier.", description = "None") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Get a specific DossierTemplate by its identifier."), - @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.") - }) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Get a specific DossierTemplate by its identifier."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) DossierTemplateModel getDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId); @ResponseStatus(value = HttpStatus.NO_CONTENT) @PostMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + ENTITY_RULES_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Upload a component or entity rules file in drools format for a specific DossierTemplate.") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Rules upload successful."), - @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found."), - @ApiResponse(responseCode = "422", description = "Uploaded rules could not be verified.") - }) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found."), @ApiResponse(responseCode = "422", description = "Uploaded rules could not be verified.")}) ResponseEntity uploadEntityRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, - @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, - @Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false ,defaultValue = "false") boolean dryRun); + @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, + @Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun); + @ResponseBody @ResponseStatus(value = HttpStatus.OK) @Operation(summary = "Returns file containing the currently used entity rules.") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "OK"), - @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.") - }) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + ENTITY_RULES_PATH) ResponseEntity downloadEntityRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId); - @ResponseStatus(value = HttpStatus.NO_CONTENT) - @PostMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_RULES_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = PATH + + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + + COMPONENT_RULES_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Upload a component or entity rules file in drools format for a specific DossierTemplate.") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Rules upload successful."), - @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found."), - @ApiResponse(responseCode = "422", description = "Uploaded rules could not be verified.") - }) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found."), @ApiResponse(responseCode = "422", description = "Uploaded rules could not be verified.")}) ResponseEntity uploadComponentRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, - @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, - @Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false ,defaultValue = "false") boolean dryRun); + @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file, + @Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun); + @ResponseBody @ResponseStatus(value = HttpStatus.OK) @Operation(summary = "Returns file containing the currently used component rules.") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "OK"), - @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.") - }) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_RULES_PATH) ResponseEntity downloadComponentRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId); + @Operation(summary = "Get the file attribute definitions of a DossierTemplate.", description = "None") @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + FILE_ATTRIBUTE_DEFINITIONS_PATH, produces = MediaType.APPLICATION_JSON_VALUE) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "File attribute definitions returned successfully."), - @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.") - }) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "File attribute definitions returned successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) FileAttributeDefinitionList getFileAttributeDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId); + + + @Operation(summary = "Returns the list of all existing dossier status definitions.", description = "None") + @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DOSSIER_STATUS_DEFINITIONS_PATH, produces = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully returned the dossier status definitions for the specified dossier template."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) + DossierStatusDefinitionList getDossierStatusDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId); + + + @Operation(summary = "Returns the list of all existing dossier attribute definitions.", description = "None") + @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DOSSIER_ATTRIBUTE_DEFINITION_PATH, produces = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully returned the dossier attribute definitions for the specified dossier template."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")}) + DossierAttributeDefinitionList getDossierAttributeDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId); + } diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DownloadResource.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DownloadResource.java new file mode 100644 index 000000000..28da06059 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/DownloadResource.java @@ -0,0 +1,55 @@ +package com.iqser.red.service.persistence.service.v2.api.external.resource; + +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatus; +import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatusList; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; + +@ApiResponses(value = {@ApiResponse(responseCode = "429", description = "Too many requests.")}) +@ResponseStatus(HttpStatus.OK) +public interface DownloadResource { + + String DOWNLOAD_PATH = "/download"; + String PATH = ExternalApiConstants.BASE_PATH + DOWNLOAD_PATH; + String DOWNLOAD_ID_PARAM = "downloadId"; + String DOWNLOAD_ID_PATH_PARAM = "/{" + DOWNLOAD_ID_PARAM + "}"; + + + @GetMapping(value = PATH, produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Get the list of downloads for the current user", description = "None") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "List of all downloads of the current users successfully retrieved.")}) + DownloadStatusList getDownloadStatusList(); + + + @GetMapping(value = PATH + DOWNLOAD_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Get the status for a specific download of the current user", description = "None") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Status of the download successfully retrieved.")}) + DownloadStatus getDownloadStatus(@Parameter(name = DOWNLOAD_ID_PARAM, description = "The identifier for the file to download.", required = true) @PathVariable(DOWNLOAD_ID_PARAM) String downloadId); + + + @ResponseStatus(HttpStatus.NO_CONTENT) + @DeleteMapping(value = PATH + DOWNLOAD_ID_PATH_PARAM) + @Operation(summary = "Deletes a specific download.", description = "None") + @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Download deletion successful. This confirms the absence of the specified download, irrespective of its previous existence.")}) + void deleteDownload(@Parameter(name = DOWNLOAD_ID_PARAM, description = "The identifier for the file to download.", required = true) @PathVariable(DOWNLOAD_ID_PARAM) String downloadId); + + + @ResponseBody + @ResponseStatus(value = HttpStatus.OK) + @Operation(summary = "Download the download package.") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully downloaded the requested file."), @ApiResponse(responseCode = "404", description = "Download not found. This happens if the requested download does not exist for the current user.")}) + @GetMapping(value = PATH + DOWNLOAD_ID_PATH_PARAM + DOWNLOAD_PATH) + void download(@Parameter(name = DOWNLOAD_ID_PARAM, description = "The identifier for the file to download.", required = true) @PathVariable(DOWNLOAD_ID_PARAM) String downloadId); + +} diff --git a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/FileResource.java b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/FileResource.java index dd5f07b7b..4f05b90c3 100644 --- a/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/FileResource.java +++ b/persistence-service-v1/persistence-service-external-api-v2/src/main/java/com/iqser/red/service/persistence/service/v2/api/external/resource/FileResource.java @@ -26,6 +26,9 @@ import org.springframework.web.multipart.MultipartFile; import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttributes; import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus; import com.iqser.red.service.persistence.service.v1.api.shared.model.FileUploadResult; +import com.iqser.red.service.persistence.service.v2.api.external.model.BulkDownloadRequest; +import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadRequest; +import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatus; import com.iqser.red.service.persistence.service.v2.api.external.model.FileDeleteRequest; import com.iqser.red.service.persistence.service.v2.api.external.model.FileStatusList; @@ -42,6 +45,8 @@ public interface FileResource { String PATH = ExternalApiConstants.BASE_PATH + DOSSIER_TEMPLATE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DOSSIER_PATH + DOSSIER_ID_PATH_PARAM + FILE_PATH; String BULK_DELETE_PATH = "/bulk/delete"; String FILE_ATTRIBUTES_PATH = "/attributes"; + String CREATE_DOWNLOAD_PATH = "/create-download"; + String BULK_CREATE_DOWNLOAD_PATH = "/bulk/create-download"; String KEEP_MANUAL_CHANGES_PARAM = "keepManualChanges"; String DELETE_PERMANENTLY_PARAM = "deletePermanently"; String FILE_PARAM = "file"; @@ -104,11 +109,32 @@ public interface FileResource { @Operation(summary = "Set file attributes to an existing file", description = "None") - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")}) + @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")}) @PostMapping(value = PATH + FILE_ID_PATH_VARIABLE + FILE_ATTRIBUTES_PATH, consumes = MediaType.APPLICATION_JSON_VALUE) void setFileAttributes(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier that contains the file.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId, @Parameter(name = FILE_ID_PARAM, description = "The identifier of the file of which the file attributes are set.", required = true) @PathVariable(FILE_ID_PARAM) String fileId, @RequestBody FileAttributes fileAttributes); + + @ResponseBody + @ResponseStatus(value = HttpStatus.OK) + @PostMapping(value = PATH + FILE_ID_PATH_VARIABLE + CREATE_DOWNLOAD_PATH, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Initiate the creation of a download package for a single file of a dossier.") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully started the creation of the download package for the requested file."), @ApiResponse(responseCode = "404", description = "Not found")}) + DownloadStatus prepareFileDownload(@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, + @Parameter(name = FILE_ID_PARAM, description = "The identifier of a file", required = true) @PathVariable(FILE_ID_PARAM) String fileId, + @RequestBody DownloadRequest downloadRequest); + + + @ResponseBody + @ResponseStatus(value = HttpStatus.OK) + @PostMapping(value = PATH + BULK_CREATE_DOWNLOAD_PATH, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Initiate the creation of a download package for specific files within a dossier.") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully started the creation of the download package for the requested files."), @ApiResponse(responseCode = "404", description = "Not found")}) + DownloadStatus prepareBulkDownload(@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 BulkDownloadRequest bulkDownloadRequest); + } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/download/DownloadStatusEntity.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/download/DownloadStatusEntity.java index 098e1d2c5..4556f02a1 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/download/DownloadStatusEntity.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/download/DownloadStatusEntity.java @@ -45,6 +45,8 @@ public class DownloadStatusEntity { @Id String storageId; @Column + String uuid; + @Column String userId; @Column String filename; diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/DownloadStatusPersistenceService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/DownloadStatusPersistenceService.java index 1d0c6ac1c..36a8136f0 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/DownloadStatusPersistenceService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/DownloadStatusPersistenceService.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.UUID; import org.springframework.stereotype.Service; @@ -50,6 +51,7 @@ public class DownloadStatusPersistenceService { DownloadStatusEntity downloadStatus = new DownloadStatusEntity(); downloadStatus.setUserId(userId); downloadStatus.setStorageId(storageId); + downloadStatus.setUuid(UUID.randomUUID().toString()); downloadStatus.setFilename(filename); downloadStatus.setMimeType(mimeType); downloadStatus.setDossier(dossier); @@ -105,6 +107,7 @@ public class DownloadStatusPersistenceService { } + @Transactional public DownloadStatusEntity getStatus(String storageId) { return downloadStatusRepository.findById(storageId).orElseThrow(() -> new NotFoundException(String.format("DownloadStatus not found for storageId: %s", storageId))); @@ -125,6 +128,13 @@ public class DownloadStatusPersistenceService { } + @Transactional + public DownloadStatusEntity getStatusesByUuid(String uuid) { + + return downloadStatusRepository.findByUuid(uuid).orElseThrow(() -> new NotFoundException(String.format("DownloadStatus not found for uuid: %s", uuid))); + } + + public List getStatus() { return downloadStatusRepository.findAll(); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/DownloadStatusRepository.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/DownloadStatusRepository.java index 1a0af6eec..4b2424bb6 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/DownloadStatusRepository.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/DownloadStatusRepository.java @@ -2,6 +2,7 @@ package com.iqser.red.service.persistence.management.v1.processor.service.persis import java.time.OffsetDateTime; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; @@ -16,6 +17,9 @@ public interface DownloadStatusRepository extends JpaRepository findAllByUserId(String userId); + // The real id is currently the storageId, this is a quick fix fpr the new customer api + // because i have no time to rewrite the complete logic that uses the storageId everywhere. + Optional findByUuid(String uuid); @Modifying @Query("update DownloadStatusEntity ds set ds.status = :status where ds.storageId = :storageId") diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml b/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml index d6e3fb607..5d72ae197 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml @@ -199,3 +199,5 @@ databaseChangeLog: file: db/changelog/tenant/sql/207-acl-migration-cleanup.sql - include: file: db/changelog/tenant/125-drop-not-null-for-legal-basis-in-manual-recategorization.yaml + - include: + file: db/changelog/tenant/126-add-uuid-to-download-status.yaml diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/126-add-uuid-to-download-status.yaml b/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/126-add-uuid-to-download-status.yaml new file mode 100644 index 000000000..80203c9a3 --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/tenant/126-add-uuid-to-download-status.yaml @@ -0,0 +1,11 @@ +databaseChangeLog: + - changeSet: + id: add-uuid-to-download-status + author: dom + changes: + - addColumn: + columns: + - column: + name: uuid + type: VARCHAR(255) + tableName: download_status \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/Dossier.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/Dossier.java index be9c3cc99..c2273f08a 100644 --- a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/Dossier.java +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/Dossier.java @@ -38,6 +38,7 @@ public class Dossier { private String dossierTemplateId; private String dossierStatusId; private DossierVisibility visibility; + private DossierAttributes dossierAttributes; // TODO: The following getters and setter ensure backwards compatibility. Remove them as soon as UI does not use them anymore diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/DossierAttributes.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/DossierAttributes.java new file mode 100644 index 000000000..00294650e --- /dev/null +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/dossiertemplate/dossier/DossierAttributes.java @@ -0,0 +1,17 @@ +package com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier; + +import java.util.HashMap; +import java.util.Map; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class DossierAttributes { + + Map attributeIdToValue = new HashMap<>(); + +}