From 9f46cd529d6341df68aa5a1c17a42dbf7a4907ea Mon Sep 17 00:00:00 2001 From: Stefano Rughetti Date: Wed, 1 Jul 2026 17:11:19 +0200 Subject: [PATCH 1/3] [PN-20528] Feat: Implementazione flusso per generazione frontespizio --- pom.xml | 39 ++++++ .../config/PnWorkflowManagerConfigs.java | 1 + .../msclient/SafeStorageApiConfigurator.java | 31 +++++ .../dto/safestorage/DocumentType.java | 5 + .../safestorage/FileCreationResponseInt.java | 12 ++ .../FileCreationWithContentRequest.java | 11 ++ .../dto/safestorage/FileDownloadInfoInt.java | 16 +++ .../safestorage/FileDownloadResponseInt.java | 19 +++ .../dto/safestorage/FileTagEnumInt.java | 29 +++++ .../UpdateFileMetadataResponseInt.java | 19 +++ .../WorkflowManagerExceptionCodes.java | 4 + .../safestorage/PnSafeStorageClient.java | 26 ++++ .../safestorage/PnSafeStorageClientImpl.java | 111 ++++++++++++++++++ .../templateengine/TemplateEngineClient.java | 3 + .../TemplateEngineClientImpl.java | 8 ++ .../service/SafeStorageService.java | 16 +++ .../service/SaveDocumentService.java | 12 ++ .../service/TemplateGeneratorService.java | 4 + .../service/impl/SafeStorageServiceImpl.java | 83 +++++++++++++ .../service/impl/SaveDocumentServiceImpl.java | 62 ++++++++++ .../impl/TemplateGeneratorServiceImpl.java | 9 ++ .../pn/workflowmanager/utils/FileUtils.java | 21 ++++ .../PnSafeStorageClientImplTest.java | 71 +++++++++++ 23 files changed, 612 insertions(+) create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/config/msclient/SafeStorageApiConfigurator.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/DocumentType.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileCreationResponseInt.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileCreationWithContentRequest.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileDownloadInfoInt.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileDownloadResponseInt.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileTagEnumInt.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/UpdateFileMetadataResponseInt.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClient.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClientImpl.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/service/SafeStorageService.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/service/SaveDocumentService.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/service/impl/SafeStorageServiceImpl.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/service/impl/SaveDocumentServiceImpl.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/utils/FileUtils.java create mode 100644 src/test/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClientImplTest.java diff --git a/pom.xml b/pom.xml index 5e93f31..8c72e1d 100644 --- a/pom.xml +++ b/pom.xml @@ -378,6 +378,45 @@ + + + generate-client-pn-safestorage + + generate + + generate-resources + + + https://raw.githubusercontent.com/pagopa/pn-ss/5fefc1970a9747d08e2ee8989900e0e90dc5532d/docs/openapi/pn-safestorage-v1.1-api.yaml + + ${project.build.directory}/dependency-resources/scripts/openapi/templates/7.4.0/client + java + resttemplate + false + false + false + removeEnumValuePrefix=false + + OffsetDateTime=java.time.Instant + + + true + true + false + false + java8 + true + none + source + false + false + true + + it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model + it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.api + + + diff --git a/src/main/java/it/pagopa/pn/workflowmanager/config/PnWorkflowManagerConfigs.java b/src/main/java/it/pagopa/pn/workflowmanager/config/PnWorkflowManagerConfigs.java index f160f35..17a5ce3 100644 --- a/src/main/java/it/pagopa/pn/workflowmanager/config/PnWorkflowManagerConfigs.java +++ b/src/main/java/it/pagopa/pn/workflowmanager/config/PnWorkflowManagerConfigs.java @@ -24,6 +24,7 @@ public class PnWorkflowManagerConfigs { private String deliveryBaseUrl; private String templateEngineBaseUrl; private String ioConnectorBaseUrl; + private String safeStorageBaseUrl; private Integer ioPollingMaxMins; diff --git a/src/main/java/it/pagopa/pn/workflowmanager/config/msclient/SafeStorageApiConfigurator.java b/src/main/java/it/pagopa/pn/workflowmanager/config/msclient/SafeStorageApiConfigurator.java new file mode 100644 index 0000000..faf1e38 --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/config/msclient/SafeStorageApiConfigurator.java @@ -0,0 +1,31 @@ +package it.pagopa.pn.workflowmanager.config.msclient; + +import it.pagopa.pn.workflowmanager.config.PnWorkflowManagerConfigs; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.ApiClient; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.api.FileDownloadApi; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.api.FileUploadApi; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class SafeStorageApiConfigurator { + @Bean + @Primary + public FileUploadApi fileUploadApi(@Qualifier("withTracing") RestTemplate restTemplate, PnWorkflowManagerConfigs cfg){ + ApiClient newApiClient = new ApiClient(restTemplate); + newApiClient.setBasePath(cfg.getSafeStorageBaseUrl()); + return new FileUploadApi( newApiClient ); + } + + @Bean + @Primary + public FileDownloadApi fileDownloadApi(@Qualifier("withTracing") RestTemplate restTemplate, PnWorkflowManagerConfigs cfg){ + ApiClient newApiClient = new ApiClient(restTemplate); + newApiClient.setBasePath(cfg.getSafeStorageBaseUrl()); + return new FileDownloadApi( newApiClient ); + } + +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/DocumentType.java b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/DocumentType.java new file mode 100644 index 0000000..3fe90ea --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/DocumentType.java @@ -0,0 +1,5 @@ +package it.pagopa.pn.workflowmanager.dto.safestorage; + +public enum DocumentType { + COVERPAGE +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileCreationResponseInt.java b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileCreationResponseInt.java new file mode 100644 index 0000000..2b658d7 --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileCreationResponseInt.java @@ -0,0 +1,12 @@ +package it.pagopa.pn.workflowmanager.dto.safestorage; +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder(toBuilder = true) +@EqualsAndHashCode +public class FileCreationResponseInt { + private String key; +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileCreationWithContentRequest.java b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileCreationWithContentRequest.java new file mode 100644 index 0000000..97608c9 --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileCreationWithContentRequest.java @@ -0,0 +1,11 @@ +package it.pagopa.pn.workflowmanager.dto.safestorage; + +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.FileCreationRequest; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class FileCreationWithContentRequest extends FileCreationRequest { + private byte[] content; +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileDownloadInfoInt.java b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileDownloadInfoInt.java new file mode 100644 index 0000000..8a1fb80 --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileDownloadInfoInt.java @@ -0,0 +1,16 @@ +package it.pagopa.pn.workflowmanager.dto.safestorage; + +import lombok.*; + +import java.math.BigDecimal; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder(toBuilder = true) +@EqualsAndHashCode +public class FileDownloadInfoInt { + private String url; + private BigDecimal retryAfter; +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileDownloadResponseInt.java b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileDownloadResponseInt.java new file mode 100644 index 0000000..08a2cfd --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileDownloadResponseInt.java @@ -0,0 +1,19 @@ +package it.pagopa.pn.workflowmanager.dto.safestorage; + +import lombok.*; + +import java.math.BigDecimal; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder(toBuilder = true) +@EqualsAndHashCode +public class FileDownloadResponseInt { + private String key; + private String checksum; + private BigDecimal contentLength; + private String contentType; + private FileDownloadInfoInt download; +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileTagEnumInt.java b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileTagEnumInt.java new file mode 100644 index 0000000..1871c46 --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileTagEnumInt.java @@ -0,0 +1,29 @@ +package it.pagopa.pn.workflowmanager.dto.safestorage; + +import lombok.Getter; + +@Getter +public enum FileTagEnumInt { + DOCUMENT("DOCUMENT"), + + ATTACHMENT_PAGOPA("ATTACHMENT_PAGOPA"), + + ATTACHMENT_F24("ATTACHMENT_F24"), + + LEGAL_FACT("LEGAL_FACT"), + + LEGAL_FACT_EXTERNAL("LEGAL_FACT_EXTERNAL"), + + AAR("AAR"); + + private final String value; + + FileTagEnumInt(String value) { + this.value = value; + } + + @Override + public String toString() { + return String.valueOf(value); + } +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/UpdateFileMetadataResponseInt.java b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/UpdateFileMetadataResponseInt.java new file mode 100644 index 0000000..836108d --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/UpdateFileMetadataResponseInt.java @@ -0,0 +1,19 @@ +package it.pagopa.pn.workflowmanager.dto.safestorage; +import lombok.*; + +import java.util.List; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder(toBuilder = true) +@EqualsAndHashCode +@ToString +public class UpdateFileMetadataResponseInt { + private String resultCode; + + private String resultDescription; + + private List errorList = null; +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/exceptions/WorkflowManagerExceptionCodes.java b/src/main/java/it/pagopa/pn/workflowmanager/exceptions/WorkflowManagerExceptionCodes.java index bb5b664..c6842c9 100644 --- a/src/main/java/it/pagopa/pn/workflowmanager/exceptions/WorkflowManagerExceptionCodes.java +++ b/src/main/java/it/pagopa/pn/workflowmanager/exceptions/WorkflowManagerExceptionCodes.java @@ -14,4 +14,8 @@ public class WorkflowManagerExceptionCodes extends PnExceptionsCodes { public static final String ERROR_CODE_WORKFLOWMANAGER_ROUTER_HANDLER_NOT_FOUND = "PN_WORKFLOWMANAGER_ROUTER_NOT_FOUND"; public static final String ERROR_CODE_WORKFLOWMANAGER_HANDLEEVENTFAILED = "PN_WORKFLOWMANAGER_HANDLEEVENTFAILED"; public static final String ERROR_CODE_DELIVERYPUSH_ACTION_CONFLICT = "PN_ACTIONMANAGER_ERROR_CODE_ACTION_CONFLICT"; + public static final String ERROR_CODE_WORKFLOWMANAGER_SAVELEGALFACTSFAILED = "PN_WORKFLOWMANAGER_SAVELEGALFACTSFAILED"; + public static final String ERROR_CODE_WORKFLOWMANAGER_UPLOADFILEERROR = "PN_WORKFLOWMANAGER_UPLOADFILEERROR"; + + } diff --git a/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClient.java b/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClient.java new file mode 100644 index 0000000..bb0fce1 --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClient.java @@ -0,0 +1,26 @@ +package it.pagopa.pn.workflowmanager.middleware.externalclient.pnclient.safestorage; + +import it.pagopa.pn.workflowmanager.dto.safestorage.FileCreationWithContentRequest; +import it.pagopa.pn.workflowmanager.dto.safestorage.UpdateFileMetadataResponseInt; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.FileCreationRequest; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.FileCreationResponse; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.FileDownloadResponse; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.UpdateFileMetadataRequest; + +public interface PnSafeStorageClient { + // TODO: String CLIENT_NAME = PnLogger.EXTERNAL_SERVICES.PN_SAFE_STORAGE; + String CLIENT_NAME = "pn-safe-storage"; + + String CREATE_FILE = "CREATE FILE"; + String GET_FILE = "GET FILE"; + String SAFE_STORAGE_URL_PREFIX = "safestorage://"; + String UPLOAD_FILE_CONTENT = "UPLOAD FILE CONTENT"; + + FileCreationResponse createFile(String checksumValue, String checksum, FileCreationRequest fileCreationRequest); + + FileDownloadResponse getFile(String fileKey, Boolean metadataOnly, Boolean tags); + + UpdateFileMetadataResponseInt updateFileMetadata(String fileKey, UpdateFileMetadataRequest updateFileMetadataRequest); + + void uploadContent(FileCreationWithContentRequest fileCreationRequest, FileCreationResponse fileCreationResponse, String sha256); +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClientImpl.java b/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClientImpl.java new file mode 100644 index 0000000..526f9cd --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClientImpl.java @@ -0,0 +1,111 @@ +package it.pagopa.pn.workflowmanager.middleware.externalclient.pnclient.safestorage; + +import it.pagopa.pn.commons.exceptions.PnInternalException; +import it.pagopa.pn.workflowmanager.config.PnWorkflowManagerConfigs; +import it.pagopa.pn.workflowmanager.dto.safestorage.FileCreationWithContentRequest; +import it.pagopa.pn.workflowmanager.dto.safestorage.UpdateFileMetadataResponseInt; +import it.pagopa.pn.workflowmanager.exceptions.WorkflowManagerExceptionCodes; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.api.FileDownloadApi; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.api.FileUploadApi; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.FileCreationRequest; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.FileCreationResponse; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.FileDownloadResponse; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.UpdateFileMetadataRequest; +import lombok.CustomLog; +import lombok.RequiredArgsConstructor; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import java.net.URI; + +@CustomLog +@RequiredArgsConstructor +@Component +public class PnSafeStorageClientImpl implements PnSafeStorageClient { + private final FileUploadApi fileUploadApi; + private final FileDownloadApi fileDownloadApi; + private final PnWorkflowManagerConfigs cfg; + private final RestTemplate restTemplate; + + @Override + public FileCreationResponse createFile(String checksumValue, String checksum, FileCreationRequest fileCreationRequest) { + log.logInvokingExternalService(CLIENT_NAME, CREATE_FILE); + return fileUploadApi.createFile(cfg.getCxId(), checksumValue, checksum, fileCreationRequest); + } + + @Override + public FileDownloadResponse getFile(String fileKey, Boolean metadataOnly, Boolean tags) { + log.logInvokingExternalService(CLIENT_NAME, GET_FILE); + return fileDownloadApi.getFile(fileKey, cfg.getCxId(), metadataOnly, tags); + } + + @Override + public UpdateFileMetadataResponseInt updateFileMetadata(String fileKey, UpdateFileMetadataRequest updateFileMetadataRequest) { + log.logInvokingExternalService(CLIENT_NAME, "UPDATE FILE METADATA"); + + HttpHeaders headers = new HttpHeaders(); + headers.add("x-pagopa-safestorage-cx-id", cfg.getCxId()); + HttpEntity requestEntity = new HttpEntity<>(updateFileMetadataRequest, headers); + String url = cfg.getSafeStorageBaseUrl() + "/safe-storage/v1/files/{fileKey}"; + + try { + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.POST, + requestEntity, + UpdateFileMetadataResponseInt.class, + fileKey + ); + if (response.getBody() == null) { + throw new PnInternalException("Empty response while updating metadata", WorkflowManagerExceptionCodes.ERROR_CODE_WORKFLOWMANAGER_UPLOADFILEERROR); + } + return response.getBody(); + } catch (RestClientException exception) { + throw new PnInternalException("Cannot update file metadata", WorkflowManagerExceptionCodes.ERROR_CODE_WORKFLOWMANAGER_UPLOADFILEERROR, exception); + } + } + + @Override + public void uploadContent(FileCreationWithContentRequest fileCreationRequest, FileCreationResponse fileCreationResponse, String sha256) { + try { + log.logInvokingAsyncExternalService(CLIENT_NAME, UPLOAD_FILE_CONTENT, fileCreationResponse.getKey()); + + MultiValueMap headers = new LinkedMultiValueMap<>(); + headers.add("Content-type", fileCreationRequest.getContentType()); + headers.add("x-amz-checksum-sha256", sha256); + headers.add("x-amz-meta-secret", fileCreationResponse.getSecret()); + + HttpEntity req = new HttpEntity<>(new ByteArrayResource(fileCreationRequest.getContent()), headers); + + URI url = URI.create(fileCreationResponse.getUploadUrl()); + HttpMethod method = fileCreationResponse.getUploadMethod() == FileCreationResponse.UploadMethodEnum.POST ? HttpMethod.POST : HttpMethod.PUT; + + ResponseEntity res = restTemplate.exchange(url, method, req, String.class); + + if (res.getStatusCode().value() != HttpStatus.OK.value()) + { + throw new PnInternalException("File upload failed", WorkflowManagerExceptionCodes.ERROR_CODE_WORKFLOWMANAGER_UPLOADFILEERROR); + } + + } catch (PnInternalException ee) + { + log.error("uploadContent PnInternalException uploading file", ee); + throw ee; + } + catch (Exception ee) + { + log.error("uploadContent Exception uploading file", ee); + throw new PnInternalException("Exception uploading file", WorkflowManagerExceptionCodes.ERROR_CODE_WORKFLOWMANAGER_UPLOADFILEERROR, ee); + } + } +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/templateengine/TemplateEngineClient.java b/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/templateengine/TemplateEngineClient.java index 3c30716..d2f50fa 100644 --- a/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/templateengine/TemplateEngineClient.java +++ b/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/templateengine/TemplateEngineClient.java @@ -3,6 +3,8 @@ import it.pagopa.pn.workflowmanager.generated.openapi.msclient.templateengine.model.InformalCommunication; import it.pagopa.pn.workflowmanager.generated.openapi.msclient.templateengine.model.LanguageEnum; +import java.io.File; + public interface TemplateEngineClient { // TODO: String CLIENT_NAME = PnLogger.EXTERNAL_SERVICES.PN_TEMPLATE_ENGINE; String CLIENT_NAME = "pn-template-engine"; @@ -10,4 +12,5 @@ public interface TemplateEngineClient { String IO_MESSAGE_TEMPLATE = "IO MESSAGE TEMPLATE"; String ioMessageTemplate(LanguageEnum language, InformalCommunication informalCommunication); + File informalAnalogCommunication(LanguageEnum xLanguage, InformalCommunication informalCommunication); } diff --git a/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/templateengine/TemplateEngineClientImpl.java b/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/templateengine/TemplateEngineClientImpl.java index fe72a3f..88ffb00 100644 --- a/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/templateengine/TemplateEngineClientImpl.java +++ b/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/templateengine/TemplateEngineClientImpl.java @@ -6,6 +6,9 @@ import lombok.CustomLog; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; +import org.springframework.web.client.RestClientException; + +import java.io.File; @CustomLog @RequiredArgsConstructor @@ -17,4 +20,9 @@ public String ioMessageTemplate(LanguageEnum language, InformalCommunication inf log.logInvokingExternalService(CLIENT_NAME, IO_MESSAGE_TEMPLATE); return templateApi.informalIoCommunication(language, informalCommunication); } + + public File informalAnalogCommunication(LanguageEnum xLanguage, InformalCommunication informalCommunication) { + log.logInvokingExternalService(CLIENT_NAME, IO_MESSAGE_TEMPLATE); + return templateApi.informalAnalogCommunication(xLanguage, informalCommunication); + } } diff --git a/src/main/java/it/pagopa/pn/workflowmanager/service/SafeStorageService.java b/src/main/java/it/pagopa/pn/workflowmanager/service/SafeStorageService.java new file mode 100644 index 0000000..e567556 --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/service/SafeStorageService.java @@ -0,0 +1,16 @@ +package it.pagopa.pn.workflowmanager.service; + +import it.pagopa.pn.workflowmanager.dto.safestorage.FileCreationResponseInt; +import it.pagopa.pn.workflowmanager.dto.safestorage.FileCreationWithContentRequest; +import it.pagopa.pn.workflowmanager.dto.safestorage.UpdateFileMetadataResponseInt; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.FileDownloadResponse; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.UpdateFileMetadataRequest; + +public interface SafeStorageService { + + FileCreationResponseInt createAndUploadContent(FileCreationWithContentRequest fileCreationRequest); + + UpdateFileMetadataResponseInt updateFileMetadata(String fileKey, UpdateFileMetadataRequest updateFileMetadataRequest); + + FileDownloadResponse getFile(String fileKey, Boolean metadataOnly, Boolean tags); +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/service/SaveDocumentService.java b/src/main/java/it/pagopa/pn/workflowmanager/service/SaveDocumentService.java new file mode 100644 index 0000000..499555a --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/service/SaveDocumentService.java @@ -0,0 +1,12 @@ +package it.pagopa.pn.workflowmanager.service; + + +import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationInt; +import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationRecipientInt; + +public interface SaveDocumentService { + String saveCoverpage( + NotificationInt notification, + NotificationRecipientInt recipient + ); +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/service/TemplateGeneratorService.java b/src/main/java/it/pagopa/pn/workflowmanager/service/TemplateGeneratorService.java index a4fc0f8..ea4ed23 100644 --- a/src/main/java/it/pagopa/pn/workflowmanager/service/TemplateGeneratorService.java +++ b/src/main/java/it/pagopa/pn/workflowmanager/service/TemplateGeneratorService.java @@ -3,6 +3,10 @@ import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationInt; import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationRecipientInt; +import java.io.File; + public interface TemplateGeneratorService { String generateIoMessageTemplate(NotificationInt notificationInt, NotificationRecipientInt notificationRecipientInt, boolean isIoUser); + File informalAnalogCommunication(NotificationInt notificationInt, NotificationRecipientInt notificationRecipientInt, boolean isIoUser); + } diff --git a/src/main/java/it/pagopa/pn/workflowmanager/service/impl/SafeStorageServiceImpl.java b/src/main/java/it/pagopa/pn/workflowmanager/service/impl/SafeStorageServiceImpl.java new file mode 100644 index 0000000..f4de46d --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/service/impl/SafeStorageServiceImpl.java @@ -0,0 +1,83 @@ +package it.pagopa.pn.workflowmanager.service.impl; + +import it.pagopa.pn.commons.exceptions.PnInternalException; +import it.pagopa.pn.commons.utils.MDCUtils; +import it.pagopa.pn.workflowmanager.dto.safestorage.FileCreationResponseInt; +import it.pagopa.pn.workflowmanager.dto.safestorage.FileCreationWithContentRequest; +import it.pagopa.pn.workflowmanager.dto.safestorage.UpdateFileMetadataResponseInt; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.FileDownloadResponse; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.UpdateFileMetadataRequest; +import it.pagopa.pn.workflowmanager.middleware.externalclient.pnclient.safestorage.PnSafeStorageClient; +import it.pagopa.pn.workflowmanager.service.SafeStorageService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.MDC; +import org.springframework.stereotype.Service; + +import java.security.MessageDigest; +import java.util.Base64; + +import static it.pagopa.pn.workflowmanager.exceptions.WorkflowManagerExceptionCodes.ERROR_CODE_WORKFLOWMANAGER_SAVELEGALFACTSFAILED; + +@Slf4j +@Service +@AllArgsConstructor +public class SafeStorageServiceImpl implements SafeStorageService { + private final PnSafeStorageClient safeStorageClient; + + @Override + public FileCreationResponseInt createAndUploadContent(FileCreationWithContentRequest fileCreationRequest) { + log.info("Start createAndUploadFile - documentType={} filesize={}", fileCreationRequest.getDocumentType(), fileCreationRequest.getContent().length); + + String sha256 = computeSha256(fileCreationRequest.getContent()); + try { + var fileCreationResponse = safeStorageClient.createFile(sha256, "SHA256", fileCreationRequest); + safeStorageClient.uploadContent(fileCreationRequest, fileCreationResponse, sha256); + + FileCreationResponseInt fileCreationResponseInt = FileCreationResponseInt.builder() + .key(fileCreationResponse.getKey()) + .build(); + log.info("createAndUploadContent file uploaded successfully key={} sha256={}", fileCreationResponseInt.getKey(), sha256); + return fileCreationResponseInt; + } catch (Exception exception) { + log.error("Cannot create or upload file", exception); + throw new PnInternalException("Cannot create file", ERROR_CODE_WORKFLOWMANAGER_SAVELEGALFACTSFAILED, exception); + } + } + + @Override + public UpdateFileMetadataResponseInt updateFileMetadata(String fileKey, UpdateFileMetadataRequest updateFileMetadataRequest) { + MDC.put(MDCUtils.MDC_PN_CTX_SAFESTORAGE_FILEKEY, fileKey); + log.debug("Start call updateFileMetadata - fileKey={} updateFileMetadataRequest={}", fileKey, updateFileMetadataRequest); + try { + UpdateFileMetadataResponseInt res = safeStorageClient.updateFileMetadata(fileKey, updateFileMetadataRequest); + log.debug("updateFileMetadata file ok key={} updateFileMetadataResponseInt={}", fileKey, res); + return res; + } catch (Exception err) { + log.error("Cannot update metadata ", err); + throw new PnInternalException("Cannot update metadata", ERROR_CODE_WORKFLOWMANAGER_SAVELEGALFACTSFAILED, err); + } finally { + MDC.remove(MDCUtils.MDC_PN_CTX_SAFESTORAGE_FILEKEY); + } + } + + @Override + public FileDownloadResponse getFile(String fileKey, Boolean metadataOnly, Boolean tags) { + return safeStorageClient.getFile(fileKey, metadataOnly, tags); + } + + private String computeSha256( byte[] content ) { + + try{ + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] encodedhash = digest.digest( content ); + return bytesToBase64( encodedhash ); + } catch (Exception exc) { + throw new PnInternalException("cannot compute sha256", ERROR_CODE_WORKFLOWMANAGER_SAVELEGALFACTSFAILED, exc); + } + } + + private static String bytesToBase64(byte[] hash) { + return Base64.getEncoder().encodeToString( hash ); + } +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/service/impl/SaveDocumentServiceImpl.java b/src/main/java/it/pagopa/pn/workflowmanager/service/impl/SaveDocumentServiceImpl.java new file mode 100644 index 0000000..eff6011 --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/service/impl/SaveDocumentServiceImpl.java @@ -0,0 +1,62 @@ +package it.pagopa.pn.workflowmanager.service.impl; + +import it.pagopa.pn.commons.exceptions.PnInternalException; +import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationInt; +import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationRecipientInt; +import it.pagopa.pn.workflowmanager.dto.safestorage.FileCreationWithContentRequest; +import it.pagopa.pn.workflowmanager.service.SafeStorageService; +import it.pagopa.pn.workflowmanager.service.SaveDocumentService; +import it.pagopa.pn.workflowmanager.service.TemplateGeneratorService; +import it.pagopa.pn.workflowmanager.utils.FileUtils; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.nio.file.Files; + +import static it.pagopa.pn.workflowmanager.exceptions.WorkflowManagerExceptionCodes.ERROR_CODE_WORKFLOWMANAGER_SAVELEGALFACTSFAILED; + + +@Slf4j +@Service +@AllArgsConstructor +public class SaveDocumentServiceImpl implements SaveDocumentService { + + public static final String SAVE_LEGAL_FACT_EXCEPTION_MESSAGE = "Generating %s legal fact for IUN=%s and recipientId=%s"; + public static final String LEGALFACTS_MEDIATYPE_STRING = "application/pdf"; + public static final String PN_LEGAL_FACTS = "PN_LEGAL_FACTS"; + public static final String SAVED = "SAVED"; + public static final String PN_AAR = "PN_AAR"; + + private final TemplateGeneratorService templateGeneratorService; + + private final SafeStorageService safeStorageService; + + public String saveLegalFact(byte[] legalFact) { + FileCreationWithContentRequest fileCreationRequest = new FileCreationWithContentRequest(); + fileCreationRequest.setContentType(LEGALFACTS_MEDIATYPE_STRING); + fileCreationRequest.setDocumentType(PN_LEGAL_FACTS); + fileCreationRequest.setStatus(SAVED); + fileCreationRequest.setContent(legalFact); + + return FileUtils.getKeyWithStoragePrefix( + safeStorageService.createAndUploadContent(fileCreationRequest).getKey() + ); + } + + public String saveCoverpage( + NotificationInt notification, + NotificationRecipientInt recipient + ) { + try { + log.debug("Start saveCoverpage - iun={}", notification.getIun()); + File legalFactFile = templateGeneratorService.informalAnalogCommunication(notification, recipient, true); + byte[] legalFact = Files.readAllBytes(legalFactFile.toPath()); + return saveLegalFact(legalFact); + } catch (Exception exc) { + String msg = String.format(SAVE_LEGAL_FACT_EXCEPTION_MESSAGE, "DIGITAL_DELIVERY", notification.getIun(), recipient.getTaxId()); + throw new PnInternalException(msg, ERROR_CODE_WORKFLOWMANAGER_SAVELEGALFACTSFAILED, exc); + } + } +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/service/impl/TemplateGeneratorServiceImpl.java b/src/main/java/it/pagopa/pn/workflowmanager/service/impl/TemplateGeneratorServiceImpl.java index 0bd6a89..6f2f1d8 100644 --- a/src/main/java/it/pagopa/pn/workflowmanager/service/impl/TemplateGeneratorServiceImpl.java +++ b/src/main/java/it/pagopa/pn/workflowmanager/service/impl/TemplateGeneratorServiceImpl.java @@ -11,6 +11,7 @@ import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; +import java.io.File; import java.util.List; import static it.pagopa.pn.workflowmanager.service.mapper.TemplateEngineMapper.mapToInformalCommunication; @@ -28,6 +29,14 @@ public String generateIoMessageTemplate(NotificationInt notificationInt, Notific return templateEngineClient.ioMessageTemplate(language, informalCommunication); } + @Override + public File informalAnalogCommunication(NotificationInt notificationInt, NotificationRecipientInt notificationRecipientInt, boolean isIoUser) { + LanguageEnum language = getLanguage(notificationRecipientInt.getAdditionalLanguages()); + InformalCommunication informalCommunication = mapToInformalCommunication(notificationInt, notificationRecipientInt, isIoUser); + return templateEngineClient.informalAnalogCommunication(language, informalCommunication); + } + + private LanguageEnum getLanguage(List additionalLanguages) { return CollectionUtils.isEmpty(additionalLanguages) ? LanguageEnum.IT : LanguageEnum.fromValue(additionalLanguages.getFirst()); diff --git a/src/main/java/it/pagopa/pn/workflowmanager/utils/FileUtils.java b/src/main/java/it/pagopa/pn/workflowmanager/utils/FileUtils.java new file mode 100644 index 0000000..d1e707b --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/utils/FileUtils.java @@ -0,0 +1,21 @@ +package it.pagopa.pn.workflowmanager.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; + +import static it.pagopa.pn.workflowmanager.middleware.externalclient.pnclient.safestorage.PnSafeStorageClient.SAFE_STORAGE_URL_PREFIX; + +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class FileUtils { + + @NotNull + public static String getKeyWithStoragePrefix(String key) { + if(key.startsWith(SAFE_STORAGE_URL_PREFIX)){ + return key; + } + return SAFE_STORAGE_URL_PREFIX + key; + } +} diff --git a/src/test/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClientImplTest.java b/src/test/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClientImplTest.java new file mode 100644 index 0000000..ca7f1c0 --- /dev/null +++ b/src/test/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClientImplTest.java @@ -0,0 +1,71 @@ +package it.pagopa.pn.workflowmanager.middleware.externalclient.pnclient.safestorage; + +import it.pagopa.pn.workflowmanager.config.PnWorkflowManagerConfigs; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.api.FileDownloadApi; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.api.FileUploadApi; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.FileCreationRequest; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.FileCreationResponse; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.FileDownloadResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.web.client.RestTemplate; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class PnSafeStorageClientImplTest { + @Mock + private FileUploadApi fileUploadApi; + @Mock + private FileDownloadApi fileDownloadApi; + @Mock + private PnWorkflowManagerConfigs cfg; + @Mock + private RestTemplate restTemplate; + @InjectMocks + private PnSafeStorageClientImpl client; + + @BeforeEach + void setUp() { + when(cfg.getCxId()).thenReturn("cx-id"); + } + + @Test + void shouldInvokeCreateFileUsingConfiguredCxId() { + FileCreationRequest request = new FileCreationRequest(); + FileCreationResponse expected = new FileCreationResponse(); + String checksumValue = "checksum-value"; + String checksum = "SHA-256"; + + when(fileUploadApi.createFile(eq("cx-id"), eq(checksumValue), eq(checksum), eq(request))) + .thenReturn(expected); + + FileCreationResponse result = client.createFile(checksumValue, checksum, request); + + assertSame(expected, result); + verify(fileUploadApi).createFile("cx-id", checksumValue, checksum, request); + } + + @Test + void shouldInvokeGetFileUsingConfiguredCxId() { + String fileKey = "safe/file-key"; + Boolean metadataOnly = Boolean.TRUE; + Boolean tags = Boolean.FALSE; + FileDownloadResponse expected = new FileDownloadResponse(); + + when(fileDownloadApi.getFile(eq(fileKey), eq("cx-id"), eq(metadataOnly), eq(tags))) + .thenReturn(expected); + + FileDownloadResponse result = client.getFile(fileKey, metadataOnly, tags); + + assertSame(expected, result); + verify(fileDownloadApi).getFile(fileKey, "cx-id", metadataOnly, tags); + } +} From 084784209f5f3860bfd59f74db09c276e02caf3e Mon Sep 17 00:00:00 2001 From: Stefano Rughetti Date: Wed, 1 Jul 2026 17:59:10 +0200 Subject: [PATCH 2/3] fixes --- .../dto/safestorage/FileTagEnumInt.java | 10 +------- .../safestorage/PnSafeStorageClient.java | 4 ++-- .../service/SaveDocumentService.java | 4 +++- .../service/impl/SaveDocumentServiceImpl.java | 23 ++++++++++++++----- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileTagEnumInt.java b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileTagEnumInt.java index 1871c46..7c607b7 100644 --- a/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileTagEnumInt.java +++ b/src/main/java/it/pagopa/pn/workflowmanager/dto/safestorage/FileTagEnumInt.java @@ -6,15 +6,7 @@ public enum FileTagEnumInt { DOCUMENT("DOCUMENT"), - ATTACHMENT_PAGOPA("ATTACHMENT_PAGOPA"), - - ATTACHMENT_F24("ATTACHMENT_F24"), - - LEGAL_FACT("LEGAL_FACT"), - - LEGAL_FACT_EXTERNAL("LEGAL_FACT_EXTERNAL"), - - AAR("AAR"); + ATTACHMENT_PAGOPA("ATTACHMENT_PAGOPA"); private final String value; diff --git a/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClient.java b/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClient.java index bb0fce1..699c097 100644 --- a/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClient.java +++ b/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/safestorage/PnSafeStorageClient.java @@ -1,5 +1,6 @@ package it.pagopa.pn.workflowmanager.middleware.externalclient.pnclient.safestorage; +import it.pagopa.pn.commons.log.PnLogger; import it.pagopa.pn.workflowmanager.dto.safestorage.FileCreationWithContentRequest; import it.pagopa.pn.workflowmanager.dto.safestorage.UpdateFileMetadataResponseInt; import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.FileCreationRequest; @@ -8,8 +9,7 @@ import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.UpdateFileMetadataRequest; public interface PnSafeStorageClient { - // TODO: String CLIENT_NAME = PnLogger.EXTERNAL_SERVICES.PN_SAFE_STORAGE; - String CLIENT_NAME = "pn-safe-storage"; + String CLIENT_NAME = PnLogger.EXTERNAL_SERVICES.PN_SAFE_STORAGE; String CREATE_FILE = "CREATE FILE"; String GET_FILE = "GET FILE"; diff --git a/src/main/java/it/pagopa/pn/workflowmanager/service/SaveDocumentService.java b/src/main/java/it/pagopa/pn/workflowmanager/service/SaveDocumentService.java index 499555a..046cb2d 100644 --- a/src/main/java/it/pagopa/pn/workflowmanager/service/SaveDocumentService.java +++ b/src/main/java/it/pagopa/pn/workflowmanager/service/SaveDocumentService.java @@ -7,6 +7,8 @@ public interface SaveDocumentService { String saveCoverpage( NotificationInt notification, - NotificationRecipientInt recipient + NotificationRecipientInt recipient, + String timelineElementId, + String recIndex ); } diff --git a/src/main/java/it/pagopa/pn/workflowmanager/service/impl/SaveDocumentServiceImpl.java b/src/main/java/it/pagopa/pn/workflowmanager/service/impl/SaveDocumentServiceImpl.java index eff6011..1340540 100644 --- a/src/main/java/it/pagopa/pn/workflowmanager/service/impl/SaveDocumentServiceImpl.java +++ b/src/main/java/it/pagopa/pn/workflowmanager/service/impl/SaveDocumentServiceImpl.java @@ -3,6 +3,7 @@ import it.pagopa.pn.commons.exceptions.PnInternalException; import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationInt; import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationRecipientInt; +import it.pagopa.pn.workflowmanager.dto.safestorage.DocumentType; import it.pagopa.pn.workflowmanager.dto.safestorage.FileCreationWithContentRequest; import it.pagopa.pn.workflowmanager.service.SafeStorageService; import it.pagopa.pn.workflowmanager.service.SaveDocumentService; @@ -14,6 +15,8 @@ import java.io.File; import java.nio.file.Files; +import java.util.List; +import java.util.Map; import static it.pagopa.pn.workflowmanager.exceptions.WorkflowManagerExceptionCodes.ERROR_CODE_WORKFLOWMANAGER_SAVELEGALFACTSFAILED; @@ -25,20 +28,20 @@ public class SaveDocumentServiceImpl implements SaveDocumentService { public static final String SAVE_LEGAL_FACT_EXCEPTION_MESSAGE = "Generating %s legal fact for IUN=%s and recipientId=%s"; public static final String LEGALFACTS_MEDIATYPE_STRING = "application/pdf"; - public static final String PN_LEGAL_FACTS = "PN_LEGAL_FACTS"; + public static final String PN_COMMUNICATIONS_COVERPAGE = "PN_COMMUNICATIONS_COVERPAGE"; public static final String SAVED = "SAVED"; - public static final String PN_AAR = "PN_AAR"; private final TemplateGeneratorService templateGeneratorService; private final SafeStorageService safeStorageService; - public String saveLegalFact(byte[] legalFact) { + public String saveLegalFact(byte[] legalFact, Map> tags) { FileCreationWithContentRequest fileCreationRequest = new FileCreationWithContentRequest(); fileCreationRequest.setContentType(LEGALFACTS_MEDIATYPE_STRING); - fileCreationRequest.setDocumentType(PN_LEGAL_FACTS); + fileCreationRequest.setDocumentType(PN_COMMUNICATIONS_COVERPAGE); fileCreationRequest.setStatus(SAVED); fileCreationRequest.setContent(legalFact); + fileCreationRequest.setTags(tags); return FileUtils.getKeyWithStoragePrefix( safeStorageService.createAndUploadContent(fileCreationRequest).getKey() @@ -47,13 +50,21 @@ public String saveLegalFact(byte[] legalFact) { public String saveCoverpage( NotificationInt notification, - NotificationRecipientInt recipient + NotificationRecipientInt recipient, + String timelineElementId, + String recIndex ) { try { log.debug("Start saveCoverpage - iun={}", notification.getIun()); File legalFactFile = templateGeneratorService.informalAnalogCommunication(notification, recipient, true); byte[] legalFact = Files.readAllBytes(legalFactFile.toPath()); - return saveLegalFact(legalFact); + Map> tags = Map.of( + "iun", List.of(notification.getIun()), + "recIndex", List.of(recIndex), + "documentType", List.of(DocumentType.COVERPAGE.name()), + "timelineElementId", List.of(timelineElementId) + ); + return saveLegalFact(legalFact, tags); } catch (Exception exc) { String msg = String.format(SAVE_LEGAL_FACT_EXCEPTION_MESSAGE, "DIGITAL_DELIVERY", notification.getIun(), recipient.getTaxId()); throw new PnInternalException(msg, ERROR_CODE_WORKFLOWMANAGER_SAVELEGALFACTSFAILED, exc); From d81f2c03d6aaafa2567c5e06e0a91cee3e59368e Mon Sep 17 00:00:00 2001 From: Stefano Rughetti Date: Thu, 2 Jul 2026 16:02:10 +0200 Subject: [PATCH 3/3] [PN-20521] Feat: Added junits --- .../impl/SafeStorageServiceImplTest.java | 47 ++++++ .../impl/SaveDocumentServiceImplTest.java | 155 ++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 src/test/java/it/pagopa/pn/workflowmanager/service/impl/SafeStorageServiceImplTest.java create mode 100644 src/test/java/it/pagopa/pn/workflowmanager/service/impl/SaveDocumentServiceImplTest.java diff --git a/src/test/java/it/pagopa/pn/workflowmanager/service/impl/SafeStorageServiceImplTest.java b/src/test/java/it/pagopa/pn/workflowmanager/service/impl/SafeStorageServiceImplTest.java new file mode 100644 index 0000000..5117c82 --- /dev/null +++ b/src/test/java/it/pagopa/pn/workflowmanager/service/impl/SafeStorageServiceImplTest.java @@ -0,0 +1,47 @@ +package it.pagopa.pn.workflowmanager.service.impl; + +import it.pagopa.pn.workflowmanager.dto.safestorage.FileCreationResponseInt; +import it.pagopa.pn.workflowmanager.dto.safestorage.FileCreationWithContentRequest; +import it.pagopa.pn.workflowmanager.generated.openapi.msclient.safestorage.model.FileCreationResponse; +import it.pagopa.pn.workflowmanager.middleware.externalclient.pnclient.safestorage.PnSafeStorageClient; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +class SafeStorageServiceImplTest { + @Mock + private PnSafeStorageClient safeStorageClient; + + private SafeStorageServiceImpl safeStorageService; + + @BeforeEach + public void init(){ + safeStorageService = new SafeStorageServiceImpl( safeStorageClient); + } + + @Test + @ExtendWith(SpringExtension.class) + void createAndUploadContent() { + //GIVEN + FileCreationWithContentRequest fileCreationWithContentRequest = new FileCreationWithContentRequest(); + fileCreationWithContentRequest.setContent("content".getBytes()); + + FileCreationResponse expectedResponse = new FileCreationResponse(); + expectedResponse.setKey("key"); + expectedResponse.setSecret("secret"); + + Mockito.when(safeStorageClient.createFile(Mockito.anyString(), Mockito.anyString(), Mockito.any(FileCreationWithContentRequest.class))) + .thenReturn(expectedResponse); + + //WHEN + FileCreationResponseInt response = safeStorageService.createAndUploadContent(fileCreationWithContentRequest); + + //THEN + Assertions.assertNotNull(response); + Assertions.assertEquals(response.getKey(), expectedResponse.getKey()); + } +} \ No newline at end of file diff --git a/src/test/java/it/pagopa/pn/workflowmanager/service/impl/SaveDocumentServiceImplTest.java b/src/test/java/it/pagopa/pn/workflowmanager/service/impl/SaveDocumentServiceImplTest.java new file mode 100644 index 0000000..6e2a405 --- /dev/null +++ b/src/test/java/it/pagopa/pn/workflowmanager/service/impl/SaveDocumentServiceImplTest.java @@ -0,0 +1,155 @@ +package it.pagopa.pn.workflowmanager.service.impl; + +import it.pagopa.pn.commons.exceptions.PnInternalException; +import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationInt; +import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationRecipientInt; +import it.pagopa.pn.workflowmanager.dto.safestorage.DocumentType; +import it.pagopa.pn.workflowmanager.dto.safestorage.FileCreationResponseInt; +import it.pagopa.pn.workflowmanager.dto.safestorage.FileCreationWithContentRequest; +import it.pagopa.pn.workflowmanager.service.SafeStorageService; +import it.pagopa.pn.workflowmanager.service.TemplateGeneratorService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; + +import static it.pagopa.pn.workflowmanager.exceptions.WorkflowManagerExceptionCodes.ERROR_CODE_WORKFLOWMANAGER_SAVELEGALFACTSFAILED; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class SaveDocumentServiceImplTest { + + @Mock + private TemplateGeneratorService templateGeneratorService; + + @Mock + private SafeStorageService safeStorageService; + + @TempDir + Path tempDir; + + private SaveDocumentServiceImpl saveDocumentService; + + @BeforeEach + void setUp() { + saveDocumentService = new SaveDocumentServiceImpl(templateGeneratorService, safeStorageService); + } + + @Test + void saveLegalFactShouldUploadPdfWithMetadataAndReturnPrefixedKey() { + byte[] content = "legal-fact".getBytes(StandardCharsets.UTF_8); + Map> tags = Map.of("iun", List.of("IUN123")); + + when(safeStorageService.createAndUploadContent(any())) + .thenReturn(new FileCreationResponseInt("generatedKey")); + + String result = saveDocumentService.saveLegalFact(content, tags); + + assertEquals("safestorage://generatedKey", result); + + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(FileCreationWithContentRequest.class); + verify(safeStorageService).createAndUploadContent(requestCaptor.capture()); + FileCreationWithContentRequest req = requestCaptor.getValue(); + assertEquals(SaveDocumentServiceImpl.LEGALFACTS_MEDIATYPE_STRING, req.getContentType()); + assertEquals(SaveDocumentServiceImpl.PN_COMMUNICATIONS_COVERPAGE, req.getDocumentType()); + assertEquals(SaveDocumentServiceImpl.SAVED, req.getStatus()); + assertArrayEquals(content, req.getContent()); + assertEquals(tags, req.getTags()); + } + + @Test + void saveLegalFactShouldReturnUnchangedKeyWhenStoragePrefixIsAlreadyPresent() { + when(safeStorageService.createAndUploadContent(any())) + .thenReturn(new FileCreationResponseInt("safestorage://generatedKey")); + + String result = saveDocumentService.saveLegalFact(new byte[0], Map.of()); + + assertEquals("safestorage://generatedKey", result); + } + + @Test + void saveCoverpageShouldGenerateAndStoreCoverpageWithExpectedTags() throws Exception { + byte[] fileBytes = "pdfcontent".getBytes(StandardCharsets.UTF_8); + Path coverpagePath = tempDir.resolve("coverpage.pdf"); + Files.write(coverpagePath, fileBytes); + File tmp = coverpagePath.toFile(); + + NotificationInt notification = NotificationInt.builder().iun("iun-123").build(); + NotificationRecipientInt recipient = NotificationRecipientInt.builder().taxId("tax-1").build(); + + when(templateGeneratorService.informalAnalogCommunication(notification, recipient, true)).thenReturn(tmp); + when(safeStorageService.createAndUploadContent(any())).thenReturn(new FileCreationResponseInt("k1")); + + String res = saveDocumentService.saveCoverpage(notification, recipient, "timeline-1", "0"); + + assertEquals("safestorage://k1", res); + + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(FileCreationWithContentRequest.class); + verify(safeStorageService).createAndUploadContent(requestCaptor.capture()); + FileCreationWithContentRequest req = requestCaptor.getValue(); + assertEquals(SaveDocumentServiceImpl.LEGALFACTS_MEDIATYPE_STRING, req.getContentType()); + assertEquals(SaveDocumentServiceImpl.PN_COMMUNICATIONS_COVERPAGE, req.getDocumentType()); + assertEquals(SaveDocumentServiceImpl.SAVED, req.getStatus()); + assertArrayEquals(fileBytes, req.getContent()); + + assertEquals(Map.of( + "iun", List.of("iun-123"), + "recIndex", List.of("0"), + "documentType", List.of(DocumentType.COVERPAGE.name()), + "timelineElementId", List.of("timeline-1") + ), req.getTags()); + } + + @Test + void saveCoverpageShouldWrapFileReadFailuresInPnInternalException() { + NotificationInt notification = NotificationInt.builder().iun("iun-999").build(); + NotificationRecipientInt recipient = NotificationRecipientInt.builder().taxId("tax-999").build(); + File missingFile = tempDir.resolve("missing.pdf").toFile(); + + when(templateGeneratorService.informalAnalogCommunication(notification, recipient, true)).thenReturn(missingFile); + + PnInternalException exc = assertThrows(PnInternalException.class, () -> + saveDocumentService.saveCoverpage(notification, recipient, "t-1", "1") + ); + + assertEquals(String.format(SaveDocumentServiceImpl.SAVE_LEGAL_FACT_EXCEPTION_MESSAGE, + "DIGITAL_DELIVERY", notification.getIun(), recipient.getTaxId()), exc.getProblem().getDetail()); + assertEquals(ERROR_CODE_WORKFLOWMANAGER_SAVELEGALFACTSFAILED, exc.getProblem().getErrors().getFirst().getCode()); + assertNotNull(exc.getCause()); + } + + @Test + void saveCoverpageShouldWrapStorageFailuresInPnInternalException() throws Exception { + byte[] fileBytes = "pdfcontent".getBytes(StandardCharsets.UTF_8); + Path coverpagePath = tempDir.resolve("coverpage-storage-failure.pdf"); + Files.write(coverpagePath, fileBytes); + NotificationInt notification = NotificationInt.builder().iun("iun-999").build(); + NotificationRecipientInt recipient = NotificationRecipientInt.builder().taxId("tax-999").build(); + RuntimeException storageException = new RuntimeException("generation failed"); + + when(templateGeneratorService.informalAnalogCommunication(notification, recipient, true)).thenReturn(coverpagePath.toFile()); + when(safeStorageService.createAndUploadContent(any())).thenThrow(storageException); + + PnInternalException exc = assertThrows(PnInternalException.class, () -> + saveDocumentService.saveCoverpage(notification, recipient, "t-1", "1") + ); + + assertEquals(String.format(SaveDocumentServiceImpl.SAVE_LEGAL_FACT_EXCEPTION_MESSAGE, + "DIGITAL_DELIVERY", notification.getIun(), recipient.getTaxId()), exc.getProblem().getDetail()); + assertEquals(ERROR_CODE_WORKFLOWMANAGER_SAVELEGALFACTSFAILED, exc.getProblem().getErrors().getFirst().getCode()); + assertSame(storageException, exc.getCause()); + } +}