From 9e73b6597663b7577812741da3cf516bd0469b6e Mon Sep 17 00:00:00 2001 From: Stefano Rughetti Date: Wed, 1 Jul 2026 12:15:52 +0200 Subject: [PATCH] [PN-20521] Feat: Implementazione client verso paper-channel --- scripts/aws/cfn/microservice.yml | 1 + .../config/PnWorkflowManagerConfigs.java | 1 + .../PaperMessagesApiConfigurator.java | 21 ++ .../dto/address/PhysicalAddressInt.java | 6 + .../PaperChannelPrepareRequest.java | 25 ++ .../paperchannel/PaperChannelSendRequest.java | 26 ++ .../PnPaperChannelChangedCostException.java | 18 ++ .../WorkflowManagerExceptionCodes.java | 2 + .../paperchannel/PaperMessagesClient.java | 16 ++ .../paperchannel/PaperMessagesClientImpl.java | 114 +++++++++ .../PaperChannelSendClientImplTestIT.java | 232 ++++++++++++++++++ .../NotificationRecipientTestBuilder.java | 97 ++++++++ .../utils/NotificationTestBuilder.java | 174 +++++++++++++ .../utils/PhysicalAddressBuilder.java | 56 +++++ .../pn/workflowmanager/utils/TestUtils.java | 33 +++ 15 files changed, 822 insertions(+) create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/config/msclient/PaperMessagesApiConfigurator.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/dto/ext/paperchannel/PaperChannelPrepareRequest.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/dto/ext/paperchannel/PaperChannelSendRequest.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/exceptions/PnPaperChannelChangedCostException.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/paperchannel/PaperMessagesClient.java create mode 100644 src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/paperchannel/PaperMessagesClientImpl.java create mode 100644 src/test/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/paperchannel/PaperChannelSendClientImplTestIT.java create mode 100644 src/test/java/it/pagopa/pn/workflowmanager/utils/NotificationRecipientTestBuilder.java create mode 100644 src/test/java/it/pagopa/pn/workflowmanager/utils/NotificationTestBuilder.java create mode 100644 src/test/java/it/pagopa/pn/workflowmanager/utils/PhysicalAddressBuilder.java create mode 100644 src/test/java/it/pagopa/pn/workflowmanager/utils/TestUtils.java diff --git a/scripts/aws/cfn/microservice.yml b/scripts/aws/cfn/microservice.yml index 5f89d51..09fadf6 100644 --- a/scripts/aws/cfn/microservice.yml +++ b/scripts/aws/cfn/microservice.yml @@ -318,6 +318,7 @@ Resources: ContainerEnvEntry11: !Sub 'PN_WORKFLOWMANAGER_ACTIONMANAGERBASEURL=http://${ApplicationLoadBalancerDomain}:8080' ContainerEnvEntry12: !Sub 'PN_WORKFLOWMANAGER_IOCONNECTORBASEURL=http://${ApplicationLoadBalancerDomain}:8080' ContainerEnvEntry13: !Sub 'PN_WORKFLOWMANAGER_TEMPLATEENGINEBASEURL=http://${ApplicationLoadBalancerDomain}:8080' + ContainerEnvEntry14: !Sub 'PN_WORKFLOWMANAGER_PAPERMESSAGESCLIENTBASEURL=http://${ApplicationLoadBalancerDomain}:8080' ApplicativeEnvFileChecksum: !Ref ApplicativeEnvFileChecksum JavaToolOptions: '-Dreactor.netty.ioWorkerCount=50' MappedPaths: '/workflow-manager-not-available' 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..58aeebc 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 paperMessagesClientBaseUrl; private Integer ioPollingMaxMins; diff --git a/src/main/java/it/pagopa/pn/workflowmanager/config/msclient/PaperMessagesApiConfigurator.java b/src/main/java/it/pagopa/pn/workflowmanager/config/msclient/PaperMessagesApiConfigurator.java new file mode 100644 index 0000000..c4fc6d3 --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/config/msclient/PaperMessagesApiConfigurator.java @@ -0,0 +1,21 @@ +package it.pagopa.pn.workflowmanager.config.msclient; + +import it.pagopa.pn.deliverypushworkflow.generated.openapi.msclient.paperchannel.ApiClient; +import it.pagopa.pn.deliverypushworkflow.generated.openapi.msclient.paperchannel.api.PaperMessagesApi; +import it.pagopa.pn.workflowmanager.config.PnWorkflowManagerConfigs; +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 PaperMessagesApiConfigurator { + @Bean + @Primary + public PaperMessagesApi paperMessagesApi(@Qualifier("withTracing") RestTemplate restTemplate, PnWorkflowManagerConfigs cfg){ + ApiClient newApiClient = new ApiClient(restTemplate); + newApiClient.setBasePath(cfg.getPaperMessagesClientBaseUrl()); + return new PaperMessagesApi( newApiClient ); + } +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/dto/address/PhysicalAddressInt.java b/src/main/java/it/pagopa/pn/workflowmanager/dto/address/PhysicalAddressInt.java index 3432efa..e9f7d30 100644 --- a/src/main/java/it/pagopa/pn/workflowmanager/dto/address/PhysicalAddressInt.java +++ b/src/main/java/it/pagopa/pn/workflowmanager/dto/address/PhysicalAddressInt.java @@ -20,4 +20,10 @@ public class PhysicalAddressInt { private String municipalityDetails; private String province; private String foreignState; + + public enum ANALOG_TYPE{ + REGISTERED_LETTER_890, + SIMPLE_REGISTERED_LETTER, + AR_REGISTERED_LETTER + } } diff --git a/src/main/java/it/pagopa/pn/workflowmanager/dto/ext/paperchannel/PaperChannelPrepareRequest.java b/src/main/java/it/pagopa/pn/workflowmanager/dto/ext/paperchannel/PaperChannelPrepareRequest.java new file mode 100644 index 0000000..cdd260c --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/dto/ext/paperchannel/PaperChannelPrepareRequest.java @@ -0,0 +1,25 @@ +package it.pagopa.pn.workflowmanager.dto.ext.paperchannel; + +import it.pagopa.pn.workflowmanager.dto.address.PhysicalAddressInt; +import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationInt; +import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationRecipientInt; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +@Data +@Builder(toBuilder = true) +@EqualsAndHashCode +@AllArgsConstructor +public class PaperChannelPrepareRequest { + private final NotificationInt notificationInt; + private final NotificationRecipientInt recipientInt; + private final PhysicalAddressInt paAddress; + private final String requestId; + private final PhysicalAddressInt.ANALOG_TYPE analogType; + private final List attachments; + private final String relatedRequestId; +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/dto/ext/paperchannel/PaperChannelSendRequest.java b/src/main/java/it/pagopa/pn/workflowmanager/dto/ext/paperchannel/PaperChannelSendRequest.java new file mode 100644 index 0000000..77edad3 --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/dto/ext/paperchannel/PaperChannelSendRequest.java @@ -0,0 +1,26 @@ +package it.pagopa.pn.workflowmanager.dto.ext.paperchannel; + +import it.pagopa.pn.workflowmanager.dto.address.PhysicalAddressInt; +import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationInt; +import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationRecipientInt; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +@Data +@AllArgsConstructor +@Builder +@EqualsAndHashCode +public class PaperChannelSendRequest { + private final NotificationInt notificationInt; + private final NotificationRecipientInt recipientInt; + private final PhysicalAddressInt receiverAddress; + private final String requestId; + private final String productType; + private final List attachments; + private final PhysicalAddressInt arAddress; + private final PhysicalAddressInt senderAddress; +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/pn/workflowmanager/exceptions/PnPaperChannelChangedCostException.java b/src/main/java/it/pagopa/pn/workflowmanager/exceptions/PnPaperChannelChangedCostException.java new file mode 100644 index 0000000..b0978f8 --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/exceptions/PnPaperChannelChangedCostException.java @@ -0,0 +1,18 @@ +package it.pagopa.pn.workflowmanager.exceptions; + +import it.pagopa.pn.commons.exceptions.PnRuntimeException; +import org.springframework.http.HttpStatus; + +import static it.pagopa.pn.workflowmanager.exceptions.WorkflowManagerExceptionCodes.ERROR_CODE_WORKFLOWMANAGER_PAPERCHANNELSENDCOSTCHANGED; + +public class PnPaperChannelChangedCostException extends PnRuntimeException { + + public PnPaperChannelChangedCostException() { + this(null); + } + + public PnPaperChannelChangedCostException(Throwable ex) { + super("Send cost is different from prepare, need to redo prepare", "Send cost is different from prepare, need to redo prepare", HttpStatus.UNPROCESSABLE_ENTITY.value(), ERROR_CODE_WORKFLOWMANAGER_PAPERCHANNELSENDCOSTCHANGED, null, null, ex); + } + +} 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..b2f2382 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,6 @@ 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_PAPERCHANNELSENDCOSTCHANGED = "PN_WORKFLOWMANAGER_PAPERCHANNELSENDCOSTCHANGED"; + } diff --git a/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/paperchannel/PaperMessagesClient.java b/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/paperchannel/PaperMessagesClient.java new file mode 100644 index 0000000..95bb91e --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/paperchannel/PaperMessagesClient.java @@ -0,0 +1,16 @@ +package it.pagopa.pn.workflowmanager.middleware.externalclient.pnclient.paperchannel; + +import it.pagopa.pn.deliverypushworkflow.generated.openapi.msclient.paperchannel.model.SendResponse; +import it.pagopa.pn.workflowmanager.dto.ext.paperchannel.PaperChannelPrepareRequest; +import it.pagopa.pn.workflowmanager.dto.ext.paperchannel.PaperChannelSendRequest; + +public interface PaperMessagesClient { + String CLIENT_ID = "pn-workflow-manager"; + String CLIENT_NAME = "pn-paper-messages"; + String PREPARE_ANALOG_NOTIFICATION = "PREPARE ANALOG NOTIFICATION"; + String SEND_ANALOG_NOTIFICATION = "SEND ANALOG NOTIFICATION"; + String PRINT_TYPE_BN_FRONTE_RETRO = "BN_FRONTE_RETRO"; + + void prepare(PaperChannelPrepareRequest paperChannelPrepareRequest); + SendResponse send(PaperChannelSendRequest paperChannelSendRequest); +} diff --git a/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/paperchannel/PaperMessagesClientImpl.java b/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/paperchannel/PaperMessagesClientImpl.java new file mode 100644 index 0000000..1f19b55 --- /dev/null +++ b/src/main/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/paperchannel/PaperMessagesClientImpl.java @@ -0,0 +1,114 @@ +package it.pagopa.pn.workflowmanager.middleware.externalclient.pnclient.paperchannel; + +import it.pagopa.pn.commons.exceptions.PnHttpResponseException; +import it.pagopa.pn.commons.utils.LogUtils; +import it.pagopa.pn.deliverypushworkflow.generated.openapi.msclient.paperchannel.api.PaperMessagesApi; +import it.pagopa.pn.deliverypushworkflow.generated.openapi.msclient.paperchannel.model.*; +import it.pagopa.pn.workflowmanager.config.PnWorkflowManagerConfigs; +import it.pagopa.pn.workflowmanager.dto.address.PhysicalAddressInt; +import it.pagopa.pn.workflowmanager.dto.ext.paperchannel.PaperChannelPrepareRequest; +import it.pagopa.pn.workflowmanager.dto.ext.paperchannel.PaperChannelSendRequest; +import it.pagopa.pn.workflowmanager.exceptions.PnPaperChannelChangedCostException; +import lombok.CustomLog; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; + +import java.time.Instant; + +@CustomLog +@RequiredArgsConstructor +@Component +public class PaperMessagesClientImpl implements PaperMessagesClient { + private final PaperMessagesApi paperMessagesApi; + private final PnWorkflowManagerConfigs cfg; + + @Override + public void prepare(PaperChannelPrepareRequest paperChannelPrepareRequest) { + log.logInvokingAsyncExternalService(CLIENT_NAME, PREPARE_ANALOG_NOTIFICATION, paperChannelPrepareRequest.getRequestId()); + log.debug("[enter] prepare iun={} address={} recipient={} requestId={} attachments={} relatedRequestId={}", paperChannelPrepareRequest.getNotificationInt().getIun(), LogUtils.maskGeneric(paperChannelPrepareRequest.getPaAddress()==null?"null":paperChannelPrepareRequest.getPaAddress().getAddress()), LogUtils.maskGeneric(paperChannelPrepareRequest.getRecipientInt().getDenomination()), paperChannelPrepareRequest.getRequestId(), paperChannelPrepareRequest.getAttachments(), paperChannelPrepareRequest.getRelatedRequestId()); + + //TODO rivedere campi necessari per notifiche bonarie + PrepareRequest prepareRequest = new PrepareRequest(); + prepareRequest.setRequestId(paperChannelPrepareRequest.getRequestId()); + prepareRequest.setIun(paperChannelPrepareRequest.getNotificationInt().getIun()); + prepareRequest.setPrintType(PRINT_TYPE_BN_FRONTE_RETRO); + prepareRequest.setProposalProductType(getProductType(paperChannelPrepareRequest.getAnalogType())); + prepareRequest.setReceiverAddress(mapInternalToExternal(paperChannelPrepareRequest.getPaAddress())); + prepareRequest.setAttachmentUrls(paperChannelPrepareRequest.getAttachments()); + prepareRequest.setReceiverFiscalCode(paperChannelPrepareRequest.getRecipientInt().getTaxId()); + prepareRequest.setReceiverType(paperChannelPrepareRequest.getRecipientInt().getRecipientType().getValue()); + prepareRequest.setNotificationSentAt(paperChannelPrepareRequest.getNotificationInt().getSentAt()); + prepareRequest.setSenderPaId(paperChannelPrepareRequest.getNotificationInt().getSender().getPaId()); + //prepareRequest.setSenderPriority(paperChannelPrepareRequest.getNotificationInt().getSender().getPhysicalCommunicationPriority()); + prepareRequest.setRelatedRequestId(paperChannelPrepareRequest.getRelatedRequestId()); + //prepareRequest.setDiscoveredAddress(mapInternalToExternal(paperChannelPrepareRequest.getDiscoveredAddress())); + //prepareRequest.setAarWithRadd(paperChannelPrepareRequest.getAarWithRadd()); + + paperMessagesApi.sendPaperPrepareRequest(paperChannelPrepareRequest.getRequestId(), prepareRequest, CLIENT_ID); + + log.debug("[exit] prepare iun={} address={} recipient={} requestId={} attachments={} relatedRequestId={}", + paperChannelPrepareRequest.getNotificationInt().getIun(), LogUtils.maskGeneric(paperChannelPrepareRequest.getPaAddress()==null?"null":paperChannelPrepareRequest.getPaAddress().getAddress()), LogUtils.maskGeneric(paperChannelPrepareRequest.getRecipientInt().getDenomination()), paperChannelPrepareRequest.getRequestId(), paperChannelPrepareRequest.getAttachments(), paperChannelPrepareRequest.getRelatedRequestId()); + } + + @Override + public SendResponse send(PaperChannelSendRequest paperChannelSendRequest) { + try { + log.logInvokingAsyncExternalService(CLIENT_NAME, SEND_ANALOG_NOTIFICATION, paperChannelSendRequest.getRequestId()); + log.debug("[enter] send iun={} address={} recipient={} requestId={} attachments={}", paperChannelSendRequest.getNotificationInt().getIun(), LogUtils.maskGeneric(paperChannelSendRequest.getReceiverAddress().getAddress()), LogUtils.maskGeneric(paperChannelSendRequest.getRecipientInt().getDenomination()), paperChannelSendRequest.getRequestId(), paperChannelSendRequest.getAttachments()); + + //TODO rivedere campi necessari per notifiche bonarie + SendRequest sendRequest = new SendRequest(); + sendRequest.setIun(paperChannelSendRequest.getNotificationInt().getIun()); + sendRequest.setRequestId(paperChannelSendRequest.getRequestId()); + sendRequest.setPrintType(PRINT_TYPE_BN_FRONTE_RETRO); + sendRequest.setProductType(ProductTypeEnum.fromValue(paperChannelSendRequest.getProductType())); + sendRequest.setReceiverAddress(mapInternalToExternal(paperChannelSendRequest.getReceiverAddress())); + sendRequest.setAttachmentUrls(paperChannelSendRequest.getAttachments()); + sendRequest.setReceiverFiscalCode(paperChannelSendRequest.getRecipientInt().getTaxId()); + sendRequest.setReceiverType(paperChannelSendRequest.getRecipientInt().getRecipientType().getValue()); + sendRequest.setArAddress(mapInternalToExternal(paperChannelSendRequest.getArAddress())); + sendRequest.setSenderAddress(mapInternalToExternal(paperChannelSendRequest.getSenderAddress())); + sendRequest.setRequestPaId(paperChannelSendRequest.getNotificationInt().getSender().getPaTaxId()); + sendRequest.setClientRequestTimeStamp(Instant.now()); + + SendResponse response = paperMessagesApi.sendPaperSendRequest(paperChannelSendRequest.getRequestId(), sendRequest); + log.debug("[exit] send iun={} address={} recipient={} requestId={} attachments={} amount={}", paperChannelSendRequest.getNotificationInt().getIun(), LogUtils.maskGeneric(paperChannelSendRequest.getReceiverAddress().getAddress()), LogUtils.maskGeneric(paperChannelSendRequest.getRecipientInt().getDenomination()), paperChannelSendRequest.getRequestId(), paperChannelSendRequest.getAttachments(), response.getAmount()); + return response; + } catch (PnHttpResponseException e) { + if (e.getStatusCode() == HttpStatus.UNPROCESSABLE_ENTITY.value()) { + log.error("received unprocessable from paper-channel, it means that send cost is different from prepare, and need to recompute prepare", e); + throw new PnPaperChannelChangedCostException(e); + } else { + throw e; + } + } + } + + private AnalogAddress mapInternalToExternal(PhysicalAddressInt physicalAddress){ + if (physicalAddress == null) + return null; + + AnalogAddress analogAddress = new AnalogAddress(); + analogAddress.setFullname(physicalAddress.getFullname()); + analogAddress.setNameRow2(physicalAddress.getAt()); + analogAddress.setAddress(physicalAddress.getAddress()); + analogAddress.setAddressRow2(physicalAddress.getAddressDetails()); + analogAddress.setCap(physicalAddress.getZip()); + analogAddress.setCity(physicalAddress.getMunicipality()); + analogAddress.setCity2(physicalAddress.getMunicipalityDetails()); + analogAddress.setCountry(physicalAddress.getForeignState()); + analogAddress.setPr(physicalAddress.getProvince()); + return analogAddress; + } + + private ProposalTypeEnum getProductType(PhysicalAddressInt.ANALOG_TYPE serviceLevelType) + { + return switch (serviceLevelType) { + case REGISTERED_LETTER_890 -> ProposalTypeEnum._890; + case AR_REGISTERED_LETTER -> ProposalTypeEnum.AR; + case SIMPLE_REGISTERED_LETTER -> ProposalTypeEnum.RS; + }; + } + +} diff --git a/src/test/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/paperchannel/PaperChannelSendClientImplTestIT.java b/src/test/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/paperchannel/PaperChannelSendClientImplTestIT.java new file mode 100644 index 0000000..a2a2006 --- /dev/null +++ b/src/test/java/it/pagopa/pn/workflowmanager/middleware/externalclient/pnclient/paperchannel/PaperChannelSendClientImplTestIT.java @@ -0,0 +1,232 @@ +package it.pagopa.pn.workflowmanager.middleware.externalclient.pnclient.paperchannel; + +import it.pagopa.pn.commons.exceptions.PnHttpResponseException; +import it.pagopa.pn.deliverypushworkflow.generated.openapi.msclient.paperchannel.api.PaperMessagesApi; +import it.pagopa.pn.deliverypushworkflow.generated.openapi.msclient.paperchannel.model.PrepareRequest; +import it.pagopa.pn.deliverypushworkflow.generated.openapi.msclient.paperchannel.model.ProposalTypeEnum; +import it.pagopa.pn.deliverypushworkflow.generated.openapi.msclient.paperchannel.model.ProductTypeEnum; +import it.pagopa.pn.deliverypushworkflow.generated.openapi.msclient.paperchannel.model.SendRequest; +import it.pagopa.pn.deliverypushworkflow.generated.openapi.msclient.paperchannel.model.SendResponse; +import it.pagopa.pn.workflowmanager.config.PnWorkflowManagerConfigs; +import it.pagopa.pn.workflowmanager.dto.address.PhysicalAddressInt; +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.ext.paperchannel.PaperChannelPrepareRequest; +import it.pagopa.pn.workflowmanager.dto.ext.paperchannel.PaperChannelSendRequest; +import it.pagopa.pn.workflowmanager.exceptions.PnPaperChannelChangedCostException; +import it.pagopa.pn.workflowmanager.utils.NotificationRecipientTestBuilder; +import it.pagopa.pn.workflowmanager.utils.NotificationTestBuilder; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.Instant; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class PaperChannelSendClientImplTestIT { + @Mock + private PnWorkflowManagerConfigs cfg; + @Mock + private PaperMessagesApi paperMessagesApi; + @InjectMocks + private PaperMessagesClientImpl client; + + @Test + void shouldBuildPrepareRequestFor890() { + String requestId = "requestId"; + NotificationRecipientInt recipient = NotificationRecipientTestBuilder.builder().build(); + NotificationInt notification = NotificationTestBuilder.builder().build(); + PaperChannelPrepareRequest paperChannelPrepareRequest = buildPrepareRequest( + requestId, + PhysicalAddressInt.ANALOG_TYPE.REGISTERED_LETTER_890, + notification, + recipient, + null + ); + + client.prepare(paperChannelPrepareRequest); + + ArgumentCaptor captor = ArgumentCaptor.forClass(PrepareRequest.class); + verify(paperMessagesApi).sendPaperPrepareRequest(eq(requestId), captor.capture(), anyString()); + + PrepareRequest sent = captor.getValue(); + assertEquals(requestId, sent.getRequestId()); + assertEquals(notification.getIun(), sent.getIun()); + assertEquals(ProposalTypeEnum._890, sent.getProposalProductType()); + assertEquals(recipient.getTaxId(), sent.getReceiverFiscalCode()); + assertEquals(recipient.getRecipientType().getValue(), sent.getReceiverType()); + assertEquals("test", sent.getReceiverAddress().getAddress()); + assertEquals(List.of("Att"), sent.getAttachmentUrls()); + } + + @Test + void shouldBuildPrepareRequestForAR() { + String requestId = "requestId"; + PaperChannelPrepareRequest paperChannelPrepareRequest = buildPrepareRequest( + requestId, + PhysicalAddressInt.ANALOG_TYPE.AR_REGISTERED_LETTER, + NotificationTestBuilder.builder().build(), + NotificationRecipientTestBuilder.builder().build(), + null + ); + + client.prepare(paperChannelPrepareRequest); + + ArgumentCaptor captor = ArgumentCaptor.forClass(PrepareRequest.class); + verify(paperMessagesApi).sendPaperPrepareRequest(eq(requestId), captor.capture(), anyString()); + assertEquals(ProposalTypeEnum.AR, captor.getValue().getProposalProductType()); + } + + + @Test + void shouldBuildPrepareRequestForARSecondRequest() { + String requestId = "requestId"; + String relatedRequestId = "requestId_0"; + PaperChannelPrepareRequest paperChannelPrepareRequest = buildPrepareRequest( + requestId, + PhysicalAddressInt.ANALOG_TYPE.AR_REGISTERED_LETTER, + NotificationTestBuilder.builder().build(), + NotificationRecipientTestBuilder.builder().build(), + relatedRequestId + ); + + client.prepare(paperChannelPrepareRequest); + + ArgumentCaptor captor = ArgumentCaptor.forClass(PrepareRequest.class); + verify(paperMessagesApi).sendPaperPrepareRequest(eq(requestId), captor.capture(), anyString()); + assertEquals(relatedRequestId, captor.getValue().getRelatedRequestId()); + } + + @Test + void shouldBuildPrepareRequestForSimpleRegisteredLetterWithNotificationSentAt() { + String requestId = "requestId"; + NotificationInt notificationInt = NotificationTestBuilder.builder() + .withSentAt(Instant.EPOCH.plusMillis(57)) + .withIun("iun_12345") + .build(); + + NotificationRecipientInt recipient = NotificationRecipientTestBuilder.builder() + .withTaxId("GeneratedTaxId_9ce24c59-862c-4024-aa75-40d888e6acac") + .build(); + PaperChannelPrepareRequest paperChannelPrepareRequest = buildPrepareRequest( + requestId, + PhysicalAddressInt.ANALOG_TYPE.SIMPLE_REGISTERED_LETTER, + notificationInt, + recipient, + null + ); + + client.prepare(paperChannelPrepareRequest); + + ArgumentCaptor captor = ArgumentCaptor.forClass(PrepareRequest.class); + verify(paperMessagesApi).sendPaperPrepareRequest(eq(requestId), captor.capture(), anyString()); + + PrepareRequest sent = captor.getValue(); + assertEquals(ProposalTypeEnum.RS, sent.getProposalProductType()); + assertEquals(Instant.EPOCH.plusMillis(57), sent.getNotificationSentAt()); + assertEquals("iun_12345", sent.getIun()); + assertEquals("GeneratedTaxId_9ce24c59-862c-4024-aa75-40d888e6acac", sent.getReceiverFiscalCode()); + } + + @Test + void shouldSendAndReturnResponseAmount() { + String requestId = "requestId"; + + SendResponse response = new SendResponse(); + int notificationCostExpected = 100; + response.setAmount(notificationCostExpected); + when(paperMessagesApi.sendPaperSendRequest(eq(requestId), any(SendRequest.class))).thenReturn(response); + + PaperChannelSendRequest paperChannelSendRequest = buildSendRequest(requestId); + + SendResponse sendResponse = client.send(paperChannelSendRequest); + assertEquals(notificationCostExpected, sendResponse.getAmount()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(SendRequest.class); + verify(paperMessagesApi).sendPaperSendRequest(eq(requestId), captor.capture()); + SendRequest sent = captor.getValue(); + assertEquals(requestId, sent.getRequestId()); + assertEquals(ProductTypeEnum._890, sent.getProductType()); + assertEquals("test", sent.getArAddress().getAddress()); + assertEquals("test2", sent.getReceiverAddress().getAddress()); + assertEquals(List.of("Att"), sent.getAttachmentUrls()); + assertNotNull(sent.getClientRequestTimeStamp()); + } + + + + @Test + void shouldThrowChangedCostExceptionWhenUnprocessableErrorOccurs() { + String requestId = "requestId1"; + PaperChannelSendRequest paperChannelSendRequest = buildSendRequest(requestId); + PnHttpResponseException exception = new PnHttpResponseException("unprocessable", HttpStatus.SC_UNPROCESSABLE_ENTITY); + + when(paperMessagesApi.sendPaperSendRequest(eq(requestId), any(SendRequest.class))) + .thenThrow(exception); + + assertThrows(PnPaperChannelChangedCostException.class, () -> client.send(paperChannelSendRequest)); + } + + + @Test + void shouldRethrowPnHttpResponseExceptionWhenGenericErrorOccurs() { + String requestId = "requestId2"; + PaperChannelSendRequest paperChannelSendRequest = buildSendRequest(requestId); + PnHttpResponseException exception = new PnHttpResponseException("generic error", HttpStatus.SC_INTERNAL_SERVER_ERROR); + + when(paperMessagesApi.sendPaperSendRequest(eq(requestId), any(SendRequest.class))) + .thenThrow(exception); + + PnHttpResponseException thrown = assertThrows(PnHttpResponseException.class, () -> client.send(paperChannelSendRequest)); + assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, thrown.getStatusCode()); + } + + private PaperChannelPrepareRequest buildPrepareRequest( + String requestId, + PhysicalAddressInt.ANALOG_TYPE analogType, + NotificationInt notificationInt, + NotificationRecipientInt recipient, + String relatedRequestId + ) { + return PaperChannelPrepareRequest.builder() + .analogType(analogType) + .requestId(requestId) + .relatedRequestId(relatedRequestId) + .paAddress(PhysicalAddressInt.builder() + .address("test") + .build()) + .recipientInt(recipient) + .notificationInt(notificationInt) + .attachments(List.of("Att")) + .build(); + } + + private PaperChannelSendRequest buildSendRequest(String requestId) { + return PaperChannelSendRequest.builder() + .requestId(requestId) + .productType(ProductTypeEnum._890.getValue()) + .arAddress(PhysicalAddressInt.builder() + .address("test") + .build()) + .receiverAddress(PhysicalAddressInt.builder() + .address("test2") + .build()) + .recipientInt(NotificationRecipientTestBuilder.builder().build()) + .notificationInt(NotificationTestBuilder.builder().build()) + .attachments(List.of("Att")) + .build(); + } +} \ No newline at end of file diff --git a/src/test/java/it/pagopa/pn/workflowmanager/utils/NotificationRecipientTestBuilder.java b/src/test/java/it/pagopa/pn/workflowmanager/utils/NotificationRecipientTestBuilder.java new file mode 100644 index 0000000..b3cc2f1 --- /dev/null +++ b/src/test/java/it/pagopa/pn/workflowmanager/utils/NotificationRecipientTestBuilder.java @@ -0,0 +1,97 @@ +package it.pagopa.pn.workflowmanager.utils; + + +import it.pagopa.pn.workflowmanager.dto.address.LegalDigitalAddressInt; +import it.pagopa.pn.workflowmanager.dto.address.PhysicalAddressInt; +import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationPaymentInfoInt; +import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.NotificationRecipientInt; +import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.RecipientTypeInt; + +import java.util.List; +import java.util.UUID; + + +public class NotificationRecipientTestBuilder { + private String taxId; + private PhysicalAddressInt physicalAddress; + private String internalId; + private LegalDigitalAddressInt digitalDomicile; + private List payments; + String denomination; + + public static NotificationRecipientTestBuilder builder() { + return new NotificationRecipientTestBuilder(); + } + + public NotificationRecipientTestBuilder withTaxId(String taxId) { + this.taxId = taxId; + return this; + } + + public NotificationRecipientTestBuilder withPhysicalAddress(PhysicalAddressInt physicalAddress) { + this.physicalAddress = physicalAddress; + return this; + } + + public NotificationRecipientTestBuilder withInternalId(String internalId) { + this.internalId = internalId; + return this; + } + + public NotificationRecipientTestBuilder withDigitalDomicile(LegalDigitalAddressInt digitalDomicile) { + this.digitalDomicile = digitalDomicile; + return this; + } + + + + public NotificationRecipientTestBuilder withPayments(List payments) { + this.payments = payments; + return this; + } + + public NotificationRecipientTestBuilder withDenomination(String denomination) { + this.denomination = denomination; + return this; + } + + public NotificationRecipientInt build() { + if(taxId == null){ + taxId = "GeneratedTaxId_" +UUID.randomUUID(); + } + + if(internalId == null){ + internalId = "ANON_"+taxId; + } + + if(physicalAddress == null){ + physicalAddress = PhysicalAddressInt.builder() + .address("Test.address") + .at("Test.at") + .zip("Test.zip") + .foreignState("Test.foreignState") + .municipality("Test.municipality") + .addressDetails("Test.addressDetails") + .municipalityDetails("Test.municipalityDetails") + .province("Test.province") + .foreignState("Test.foreignState") + .build(); + } + + String denominationPerson = "Name_and_surname_of_" + taxId; + if(physicalAddress != null){ + physicalAddress.setFullname(denominationPerson); + } + + return NotificationRecipientInt.builder() + .recipientType(RecipientTypeInt.PF) + .taxId(taxId) + .internalId(internalId) + .denomination(denominationPerson) + .physicalAddress(physicalAddress) + .digitalDomicile(digitalDomicile) + .payments(payments) + .build(); + } + +} diff --git a/src/test/java/it/pagopa/pn/workflowmanager/utils/NotificationTestBuilder.java b/src/test/java/it/pagopa/pn/workflowmanager/utils/NotificationTestBuilder.java new file mode 100644 index 0000000..4929a2e --- /dev/null +++ b/src/test/java/it/pagopa/pn/workflowmanager/utils/NotificationTestBuilder.java @@ -0,0 +1,174 @@ +package it.pagopa.pn.workflowmanager.utils; + +import it.pagopa.pn.commons.utils.DateFormatUtils; +import it.pagopa.pn.deliverypushworkflow.generated.openapi.msclient.delivery.model.NotificationFeePolicy; +import it.pagopa.pn.workflowmanager.dto.address.LegalDigitalAddressInt; +import it.pagopa.pn.workflowmanager.dto.ext.delivery.notification.*; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collections; +import java.util.List; + +public class NotificationTestBuilder { + private String iun; + private String paId; + private List recipients; + private NotificationFeePolicy notificationFeePolicy; + private Instant sentAt; + private List notificationDocument; + private PagoPaIntMode pagoPaIntMode; + private Integer paFee; + + private String group; + + private UsedServicesInt usedServices; + + public NotificationTestBuilder() { + sentAt = Instant.now(); + recipients = Collections.emptyList(); + notificationDocument = Collections.emptyList(); + } + + public static NotificationTestBuilder builder() { + return new NotificationTestBuilder(); + } + + public NotificationTestBuilder withIun(String iun) { + this.iun = iun; + return this; + } + + public NotificationTestBuilder withPaId(String paId) { + this.paId = paId; + return this; + } + + public NotificationTestBuilder withNotificationFeePolicy(NotificationFeePolicy notificationFeePolicy) { + this.notificationFeePolicy = notificationFeePolicy; + return this; + } + + public NotificationTestBuilder withNotificationRecipient(NotificationRecipientInt recipient) { + this.recipients = Collections.singletonList( + recipient + ); + return this; + } + + public NotificationTestBuilder withNotificationRecipients(List recipientCollections) { + this.recipients = recipientCollections; + return this; + } + + public NotificationTestBuilder withSentAt(Instant sentAt) { + this.sentAt = sentAt; + return this; + } + + public NotificationTestBuilder withNotificationDocuments(List documents) { + this.notificationDocument = documents; + return this; + } + + public NotificationTestBuilder withPagoPaIntMode(PagoPaIntMode pagoPaIntMode) { + this.pagoPaIntMode = pagoPaIntMode; + return this; + } + + public NotificationTestBuilder withPaFee(int paFee) { + this.paFee = paFee; + return this; + } + + public NotificationTestBuilder withGroup(String group1) { + this.group = group1; + return this; + } + + public NotificationTestBuilder withUsedServices(UsedServicesInt usedServices) { + this.usedServices = usedServices; + return this; + } + + public NotificationInt build() { + if(iun == null){ + iun = TestUtils.getRandomIun(4); + } + + if(paId == null){ + paId = "generatedPaId"; + } + + if( notificationDocument.isEmpty() ){ + String fileDoc = "sha256_doc00"; + + notificationDocument = List.of( + NotificationDocumentInt.builder() + .ref(NotificationDocumentInt.Ref.builder() + .key(Base64.getEncoder().encodeToString(fileDoc.getBytes())) + .versionToken("v01_doc00") + .build() + ) + .digests(NotificationDocumentInt.Digests.builder() + .sha256(Base64.getEncoder().encodeToString(fileDoc.getBytes())) + .build() + ) + .build() + ); + } + + if(notificationFeePolicy == null) { + notificationFeePolicy = NotificationFeePolicy.FLAT_RATE; + } + + if(pagoPaIntMode == null) { + pagoPaIntMode = PagoPaIntMode.SYNC; + } + + if(recipients.isEmpty()){ + recipients = new ArrayList<>(); + recipients.add(NotificationRecipientTestBuilder.builder() + .withTaxId("testTaxId") + .withInternalId("ANON_testTaxId") + .withDigitalDomicile(LegalDigitalAddressInt.builder() + .address("digitalDomicile@works") + .type(LegalDigitalAddressInt.LEGAL_DIGITAL_ADDRESS_TYPE.PEC) + .build() + ) + .withPhysicalAddress( + PhysicalAddressBuilder.builder() + .withAddress("OK_Via Nuova") + .build() + ) + .build() + ); + } + + return NotificationInt.builder() + .iun(iun) + .paProtocolNumber("protocol_01") + .subject("subject not very long but not too short") + .sentAt(Instant.now()) + .amount(18) + .paymentExpirationDate(DateFormatUtils.parseDate("2002-08-12").toInstant()) + .physicalCommunicationType(ServiceLevelTypeInt.AR_REGISTERED_LETTER) + .sender(NotificationSenderInt.builder() + .paId(paId) + .paDenomination("Denominazione pa con id " + paId) + .paTaxId("CFPA-" + paId) + .build() + ) + .notificationFeePolicy(notificationFeePolicy) + .sentAt( sentAt ) + .recipients(recipients) + .documents(notificationDocument) + .pagoPaIntMode(pagoPaIntMode) + .paFee(paFee) + .group(group) + .usedServices(usedServices) + .build(); + } + +} diff --git a/src/test/java/it/pagopa/pn/workflowmanager/utils/PhysicalAddressBuilder.java b/src/test/java/it/pagopa/pn/workflowmanager/utils/PhysicalAddressBuilder.java new file mode 100644 index 0000000..6358759 --- /dev/null +++ b/src/test/java/it/pagopa/pn/workflowmanager/utils/PhysicalAddressBuilder.java @@ -0,0 +1,56 @@ +package it.pagopa.pn.workflowmanager.utils; + + +import it.pagopa.pn.workflowmanager.dto.address.PhysicalAddressInt; + +public class PhysicalAddressBuilder { + private String address; + private String fullName; + private String zip; + private String foreignState; + + public static PhysicalAddressBuilder builder() { + return new PhysicalAddressBuilder(); + } + + public PhysicalAddressBuilder withAddress(String address) { + this.address = address; + return this; + } + + public PhysicalAddressBuilder withFullName(String fullName) { + this.fullName = fullName; + return this; + } + + public PhysicalAddressBuilder withZip(String zip) { + this.zip = zip; + return this; + } + + public PhysicalAddressBuilder withForeignState(String foreignState) { + this.foreignState = foreignState; + return this; + } + + public PhysicalAddressInt build() { + if(zip == null){ + zip = "00100"; + } + + if(foreignState == null){ + foreignState = "IT"; + } + + return PhysicalAddressInt.builder() + .fullname(fullName) + .at("Presso") + .address(address) + .zip(zip) + .municipality("Roma") + .province("RM") + .foreignState("IT") + .addressDetails("Scala A") + .build(); + } +} diff --git a/src/test/java/it/pagopa/pn/workflowmanager/utils/TestUtils.java b/src/test/java/it/pagopa/pn/workflowmanager/utils/TestUtils.java new file mode 100644 index 0000000..9424063 --- /dev/null +++ b/src/test/java/it/pagopa/pn/workflowmanager/utils/TestUtils.java @@ -0,0 +1,33 @@ +package it.pagopa.pn.workflowmanager.utils; + +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; + +import java.util.Random; + +@Slf4j +public class TestUtils { + public static String getMethodName(final int depth) { + final StackTraceElement[] ste = Thread.currentThread().getStackTrace(); + return ste[depth].getMethodName(); + } + + + public static String getRandomIun(int level) { + String callerMethod = getMethodName(level); + return getIun(callerMethod); + } + + public static String getRandomIun() { + String callerMethod = getMethodName(3); + return getIun(callerMethod); + } + + @NotNull + private static String getIun(String callerMethod) { + Random rand = new Random(); + int upperbound = 10000; + int intRandom = rand.nextInt(upperbound); + return "iun-" + callerMethod + "_" + intRandom; + } +} \ No newline at end of file