From df0163ad3f1cb36d1f323161b8a4e8d3e917e1e7 Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Wed, 17 Apr 2024 17:25:49 +0200 Subject: [PATCH 01/24] PAGOPA-1695 wip --- pom.xml | 5 +++ .../config/QueueClientConfiguration.java | 33 ++++++++++++++++ .../controller/scheduler/Scheduler.java | 28 ++++++++++++++ .../payments/service/PartnerService.java | 38 +++++++++++++++++-- 4 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 src/main/java/it/gov/pagopa/payments/config/QueueClientConfiguration.java create mode 100644 src/main/java/it/gov/pagopa/payments/controller/scheduler/Scheduler.java diff --git a/pom.xml b/pom.xml index a715bfce..d30282ef 100644 --- a/pom.xml +++ b/pom.xml @@ -237,6 +237,11 @@ 2.1.2 compile + + com.azure + azure-storage-queue + 12.20.2 + diff --git a/src/main/java/it/gov/pagopa/payments/config/QueueClientConfiguration.java b/src/main/java/it/gov/pagopa/payments/config/QueueClientConfiguration.java new file mode 100644 index 00000000..eba93462 --- /dev/null +++ b/src/main/java/it/gov/pagopa/payments/config/QueueClientConfiguration.java @@ -0,0 +1,33 @@ +package it.gov.pagopa.payments.config; + +import com.azure.storage.queue.QueueClient; +import com.azure.storage.queue.QueueClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class QueueClientConfiguration { + + private static String QUEUE_NAME; + + private static String CONNECTION_STRING; + + @Value("${azure.queue.connection.string}") + public void setConnectionStringStatic(String connectionString) { + QueueClientConfiguration.CONNECTION_STRING = connectionString; + } + + @Value("${azure.queue.queueName}") + public void setTableNameStatic(String queueName) { + QueueClientConfiguration.QUEUE_NAME = queueName; + } + + @Bean + public QueueClient queueClientConfig(){ + return new QueueClientBuilder() + .queueName(QUEUE_NAME) + .connectionString(CONNECTION_STRING) + .buildClient(); + } +} diff --git a/src/main/java/it/gov/pagopa/payments/controller/scheduler/Scheduler.java b/src/main/java/it/gov/pagopa/payments/controller/scheduler/Scheduler.java new file mode 100644 index 00000000..878d36f1 --- /dev/null +++ b/src/main/java/it/gov/pagopa/payments/controller/scheduler/Scheduler.java @@ -0,0 +1,28 @@ +package it.gov.pagopa.payments.controller.scheduler; + +import it.gov.pagopa.payments.service.PartnerService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +@Slf4j +@Validated +public class Scheduler { + + @Autowired + PartnerService partnerService; + + @GetMapping( + value = "/scheduler", + produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity getFailureReceipts() { + partnerService.getAllFailuresQueue(); + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java index 9795d379..d8f8a188 100644 --- a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java +++ b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java @@ -4,6 +4,8 @@ import com.azure.data.tables.models.TableEntity; import com.azure.data.tables.models.TableErrorCode; import com.azure.data.tables.models.TableServiceException; +import com.azure.storage.queue.QueueClient; +import com.azure.storage.queue.models.QueueMessageItem; import com.microsoft.azure.storage.StorageException; import feign.FeignException; import feign.RetryableException; @@ -49,15 +51,13 @@ import java.io.StringWriter; import java.math.BigDecimal; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.time.LocalDateTime; import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Marshaller; +import javax.xml.bind.*; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.DatatypeFactory; @@ -109,6 +109,8 @@ public class PartnerService { @Autowired private TableClient tableClient; + @Autowired private QueueClient queueClient; + @Autowired private CustomizedMapper customizedModelMapper; private static final String DBERROR = "Error in organization table connection"; @@ -905,14 +907,42 @@ private PaymentOptionModelResponse getReceiptPaymentOption(String noticeNumber, throw e; } catch (RetryableException e) { log.error("[getReceiptPaymentOption] GPD Not Reachable [noticeNumber={}]", noticeNumber, e); + queueClient.sendMessage(receiptEntity.getDocument()); throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); } catch (FeignException e) { log.error("[getReceiptPaymentOption] GPD Error Response [noticeNumber={}]", noticeNumber, e); + queueClient.sendMessage(receiptEntity.getDocument()); throw new PartnerValidationException(PaaErrorEnum.PAA_SEMANTICA); } catch (Exception e) { log.error("[getReceiptPaymentOption] GPD Generic Error [noticeNumber={}]", noticeNumber, e); + queueClient.sendMessage(receiptEntity.getDocument()); throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); } return paymentOption; } + + public void getAllFailuresQueue() { + try { + QueueMessageItem queueMessageItem; + List failureBodies = new ArrayList<>(); + Unmarshaller unmarshaller = JAXBContext.newInstance(PaSendRTV2Request.class).createUnmarshaller(); + while ((queueMessageItem = queueClient.receiveMessage()) != null) { + failureBodies.add(getFailureQueue(queueMessageItem)); + } + for (String failureBody : failureBodies) { + JAXBElement request = (JAXBElement) unmarshaller.unmarshal(new ByteArrayInputStream(failureBody.getBytes(StandardCharsets.UTF_8))); + paSendRTV2(request.getValue()); + } + } catch (JAXBException e) { + log.error( + "[managePaSendRtRequest] Error in receipt unmarshalling in queue", + e); + throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); + } + } + + public String getFailureQueue(QueueMessageItem queueMessageItem){ + queueClient.deleteMessage(queueMessageItem.getMessageId(), queueMessageItem.getPopReceipt()); + return new String(queueMessageItem.getBody().toBytes(), StandardCharsets.UTF_8); + } } From 4b7da606ff52f56eb1e3b04adb14b88870f1dcd1 Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:43:54 +0200 Subject: [PATCH 02/24] PAGOPA-1695 wip handling xml --- .../payments/service/PartnerService.java | 109 ++++++++++++++---- 1 file changed, 86 insertions(+), 23 deletions(-) diff --git a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java index d8f8a188..d050d567 100644 --- a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java +++ b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java @@ -48,6 +48,7 @@ import it.gov.pagopa.payments.utils.CustomizedMapper; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.StringReader; import java.io.StringWriter; import java.math.BigDecimal; import java.net.URISyntaxException; @@ -67,6 +68,8 @@ import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLStreamException; +import javax.xml.xpath.*; + import it.gov.pagopa.payments.utils.Validator; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; @@ -76,6 +79,10 @@ import org.springframework.core.io.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; import org.xml.sax.SAXException; @Service @@ -760,11 +767,16 @@ private PaymentOptionModelResponse managePaSendRtRequest(PaSendRTReq request) { "[managePaSendRtRequest] save receipt [noticeNumber={}]", request.getReceipt().getNoticeNumber()); + String debtorIdentifier = + Optional.ofNullable(request.getReceipt().getDebtor()) + .map(CtSubject::getUniqueIdentifier) + .map(CtEntityUniqueIdentifier::getEntityUniqueIdentifierValue) + .orElse(""); ReceiptEntity receiptEntity = this.getReceiptEntity( request.getIdPA(), request.getReceipt().getCreditorReferenceId(), - request.getReceipt().getDebtor(), + debtorIdentifier, request.getReceipt().getPaymentDateTime().toString()); try { @@ -808,11 +820,16 @@ private PaymentOptionModelResponse managePaSendRtRequest(PaSendRTV2Request reque "[managePaSendRtRequest] save V2 receipt [noticeNumber={}]", request.getReceipt().getNoticeNumber()); + String debtorIdentifier = + Optional.ofNullable(request.getReceipt().getDebtor()) + .map(CtSubject::getUniqueIdentifier) + .map(CtEntityUniqueIdentifier::getEntityUniqueIdentifierValue) + .orElse(""); ReceiptEntity receiptEntity = this.getReceiptEntity( request.getIdPA(), request.getReceipt().getCreditorReferenceId(), - request.getReceipt().getDebtor(), + debtorIdentifier, request.getReceipt().getPaymentDateTime().toString()); try { receiptEntity.setDocument(this.marshalV2(request)); @@ -852,14 +869,9 @@ private PaymentOptionModelResponse managePaSendRtRequest(PaSendRTV2Request reque } private ReceiptEntity getReceiptEntity( - String idPa, String creditorReferenceId, CtSubject debtor, String paymentDateTime) { + String idPa, String creditorReferenceId, String debtor, String paymentDateTime) { ReceiptEntity receiptEntity = new ReceiptEntity(idPa, creditorReferenceId); - String debtorIdentifier = - Optional.ofNullable(debtor) - .map(CtSubject::getUniqueIdentifier) - .map(CtEntityUniqueIdentifier::getEntityUniqueIdentifierValue) - .orElse(""); - receiptEntity.setDebtor(debtorIdentifier); + receiptEntity.setDebtor(debtor); String paymentDateTimeIdentifier = Optional.ofNullable(paymentDateTime).orElse(""); receiptEntity.setPaymentDateTime(paymentDateTimeIdentifier); return receiptEntity; @@ -922,27 +934,78 @@ private PaymentOptionModelResponse getReceiptPaymentOption(String noticeNumber, } public void getAllFailuresQueue() { + QueueMessageItem queueMessageItem; + List failureBodies = new ArrayList<>(); + XPathFactory xPathfactory = XPathFactory.newInstance(); + XPath xpath = xPathfactory.newXPath(); + while ((queueMessageItem = queueClient.receiveMessage()) != null) { + failureBodies.add(getFailureQueue(queueMessageItem)); + } try { - QueueMessageItem queueMessageItem; - List failureBodies = new ArrayList<>(); - Unmarshaller unmarshaller = JAXBContext.newInstance(PaSendRTV2Request.class).createUnmarshaller(); - while ((queueMessageItem = queueClient.receiveMessage()) != null) { - failureBodies.add(getFailureQueue(queueMessageItem)); - } for (String failureBody : failureBodies) { - JAXBElement request = (JAXBElement) unmarshaller.unmarshal(new ByteArrayInputStream(failureBody.getBytes(StandardCharsets.UTF_8))); - paSendRTV2(request.getValue()); + Document xmlDoc = getXMLDocument(failureBody); + handlingXml(xmlDoc, xpath); } - } catch (JAXBException e) { - log.error( - "[managePaSendRtRequest] Error in receipt unmarshalling in queue", - e); - throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); + } catch (XPathExpressionException e) { + + } + } + + public void handlingXml (Document xmlDoc, XPath xpath) throws XPathExpressionException { + XPathExpression xPathExpr = xpath.compile('/' + xmlDoc.getFirstChild().getNodeName()); + NodeList nodes = (NodeList) xPathExpr.evaluate(xmlDoc, XPathConstants.NODESET); + + Element node = (Element) nodes.item(0); + + String idPA = node.getElementsByTagName("idPA").item(0).getTextContent(); + String creditorReferenceId = node.getElementsByTagName("creditorReferenceId").item(0).getTextContent(); + String noticeNumber = node.getElementsByTagName("noticeNumber").item(0).getTextContent(); + String paymentDateTime = node.getElementsByTagName("paymentDateTime").item(0).getTextContent(); + String receiptId = node.getElementsByTagName("receiptId").item(0).getTextContent(); + String PSPCompanyName = node.getElementsByTagName("PSPCompanyName").item(0).getTextContent(); + String paymentMethod = node.getElementsByTagName("paymentMethod").item(0).getTextContent(); + String fee = node.getElementsByTagName("fee").item(0).getTextContent(); + String entityUniqueIdentifierValue = node.getElementsByTagName("entityUniqueIdentifierValue").item(0).getTextContent(); + + ReceiptEntity receiptEntity = new ReceiptEntity(idPA, creditorReferenceId); + receiptEntity.setDebtor(entityUniqueIdentifierValue); + String paymentDateTimeIdentifier = Optional.ofNullable(paymentDateTime).orElse(""); + receiptEntity.setPaymentDateTime(paymentDateTimeIdentifier); + + LocalDateTime localPaymentDateTime = paymentDateTime != null ? LocalDateTime.parse(paymentDateTime) : null; + PaymentOptionModel body = + PaymentOptionModel.builder() + .idReceipt(receiptId) + .paymentDate(localPaymentDateTime) + .pspCompany(PSPCompanyName) + .paymentMethod(paymentMethod) + .fee(String.valueOf(getFeeInCent(new BigDecimal(fee)))) + .build(); + + try { + getReceiptPaymentOption( + noticeNumber, + idPA, + creditorReferenceId, + body, + receiptEntity); + } catch (Exception e) { + log.info("[paSendRT] Retry failed [fiscalCode={},noticeNumber={}]\",\n", idPA, noticeNumber); } } public String getFailureQueue(QueueMessageItem queueMessageItem){ - queueClient.deleteMessage(queueMessageItem.getMessageId(), queueMessageItem.getPopReceipt()); + //queueClient.deleteMessage(queueMessageItem.getMessageId(), queueMessageItem.getPopReceipt()); return new String(queueMessageItem.getBody().toBytes(), StandardCharsets.UTF_8); } + + public Document getXMLDocument(String xmlString) { + try { + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + InputSource is = new InputSource(new StringReader(xmlString)); + return builder.parse(is); + } catch (ParserConfigurationException | SAXException | IOException e) { + throw new AppException(AppError.UNKNOWN); + } + } } From 0d126f830893340b114dc8f7006621e485cc40c1 Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:05:09 +0200 Subject: [PATCH 03/24] PAGOPA-1695 wip --- .../payments/service/PartnerService.java | 24 +++++++++++-------- .../payments/service/PartnerServiceTest.java | 24 +++++++++++++++++++ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java index d050d567..b2c16139 100644 --- a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java +++ b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java @@ -1,5 +1,6 @@ package it.gov.pagopa.payments.service; +import com.azure.core.util.Context; import com.azure.data.tables.TableClient; import com.azure.data.tables.models.TableEntity; import com.azure.data.tables.models.TableErrorCode; @@ -54,6 +55,7 @@ import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; +import java.time.Duration; import java.time.LocalDateTime; import java.util.*; import java.util.function.Predicate; @@ -919,39 +921,40 @@ private PaymentOptionModelResponse getReceiptPaymentOption(String noticeNumber, throw e; } catch (RetryableException e) { log.error("[getReceiptPaymentOption] GPD Not Reachable [noticeNumber={}]", noticeNumber, e); - queueClient.sendMessage(receiptEntity.getDocument()); + queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofMinutes(10L), null, null, Context.NONE); throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); } catch (FeignException e) { log.error("[getReceiptPaymentOption] GPD Error Response [noticeNumber={}]", noticeNumber, e); - queueClient.sendMessage(receiptEntity.getDocument()); + queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofMinutes(10L), null, null, Context.NONE); throw new PartnerValidationException(PaaErrorEnum.PAA_SEMANTICA); } catch (Exception e) { log.error("[getReceiptPaymentOption] GPD Generic Error [noticeNumber={}]", noticeNumber, e); - queueClient.sendMessage(receiptEntity.getDocument()); + queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofMinutes(10L), null, null, Context.NONE); throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); } return paymentOption; } public void getAllFailuresQueue() { - QueueMessageItem queueMessageItem; List failureBodies = new ArrayList<>(); XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath(); - while ((queueMessageItem = queueClient.receiveMessage()) != null) { - failureBodies.add(getFailureQueue(queueMessageItem)); + // The message is dequeued and locked for 30 seconds by default + Optional optQueueMessageItem = queueClient.receiveMessages(1).stream().findFirst(); + while (optQueueMessageItem.isPresent()) { + failureBodies.add(getFailureQueue(optQueueMessageItem.get())); } try { for (String failureBody : failureBodies) { - Document xmlDoc = getXMLDocument(failureBody); - handlingXml(xmlDoc, xpath); + handlingXml(failureBody, xpath); } } catch (XPathExpressionException e) { } } - public void handlingXml (Document xmlDoc, XPath xpath) throws XPathExpressionException { + public void handlingXml (String failureBody, XPath xpath) throws XPathExpressionException { + Document xmlDoc = getXMLDocument(failureBody); XPathExpression xPathExpr = xpath.compile('/' + xmlDoc.getFirstChild().getNodeName()); NodeList nodes = (NodeList) xPathExpr.evaluate(xmlDoc, XPathConstants.NODESET); @@ -971,6 +974,7 @@ public void handlingXml (Document xmlDoc, XPath xpath) throws XPathExpressionExc receiptEntity.setDebtor(entityUniqueIdentifierValue); String paymentDateTimeIdentifier = Optional.ofNullable(paymentDateTime).orElse(""); receiptEntity.setPaymentDateTime(paymentDateTimeIdentifier); + receiptEntity.setDocument(failureBody); LocalDateTime localPaymentDateTime = paymentDateTime != null ? LocalDateTime.parse(paymentDateTime) : null; PaymentOptionModel body = @@ -995,7 +999,7 @@ public void handlingXml (Document xmlDoc, XPath xpath) throws XPathExpressionExc } public String getFailureQueue(QueueMessageItem queueMessageItem){ - //queueClient.deleteMessage(queueMessageItem.getMessageId(), queueMessageItem.getPopReceipt()); + queueClient.deleteMessage(queueMessageItem.getMessageId(), queueMessageItem.getPopReceipt()); return new String(queueMessageItem.getBody().toBytes(), StandardCharsets.UTF_8); } diff --git a/src/test/java/it/gov/pagopa/payments/service/PartnerServiceTest.java b/src/test/java/it/gov/pagopa/payments/service/PartnerServiceTest.java index 31c09daa..3277f198 100644 --- a/src/test/java/it/gov/pagopa/payments/service/PartnerServiceTest.java +++ b/src/test/java/it/gov/pagopa/payments/service/PartnerServiceTest.java @@ -22,6 +22,7 @@ import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLStreamException; +import com.azure.storage.queue.QueueClient; import org.junit.ClassRule; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -103,6 +104,8 @@ class PartnerServiceTest { @Mock private GpsClient gpsClient; + @Mock private QueueClient queueClient; + private String genericService = "/xsd/general-service.xsd"; ResourceLoader resourceLoader = new DefaultResourceLoader(); Resource resource = resourceLoader.getResource(genericService); @@ -438,6 +441,7 @@ void paSendRTTest() throws DatatypeConfigurationException, IOException { gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -481,6 +485,7 @@ void paSendRTTestKOConflict() throws DatatypeConfigurationException, IOException gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -523,6 +528,7 @@ void paSendRTTestKOUnknown() throws DatatypeConfigurationException, IOException gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -566,6 +572,7 @@ void paSendRTTestKOStatus(String status, String iuv) throws DatatypeConfiguratio gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -611,6 +618,7 @@ void paSendRTTestKORetryableException() throws DatatypeConfigurationException, I gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -653,6 +661,7 @@ void paSendRTTestKOFeignException() throws DatatypeConfigurationException, IOExc gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions PaSendRTReq requestBody = PaSendRTReqMock.getMock("11111111112222225"); @@ -694,6 +703,7 @@ void paSendRTTestKO() throws DatatypeConfigurationException, IOException { gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -748,6 +758,7 @@ void paDemandPaymentNoticeTest() gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -797,6 +808,7 @@ void paDemandPaymentNoticeNotFoundTest() gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -830,6 +842,7 @@ void paGetPaymentV2Test() gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -893,6 +906,7 @@ void paGetPaymentV2_TransferTypePAGOPA_Test() gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -975,6 +989,7 @@ void paGetPaymentIncompleteAddressV2Test() gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -1085,6 +1100,7 @@ void paSendRTV2Test() throws DatatypeConfigurationException, IOException { gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -1128,6 +1144,7 @@ void paSendRTV2TestKOConflict() throws DatatypeConfigurationException, IOExcepti gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -1170,6 +1187,7 @@ void paSendRTV2TestKOUnknown() throws DatatypeConfigurationException, IOExceptio gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -1213,6 +1231,7 @@ void paSendRTV2TestKOStatus(String status, String iuv) throws DatatypeConfigurat gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -1258,6 +1277,7 @@ void paSendRTV2TestKORetryableException() throws DatatypeConfigurationException, gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -1300,6 +1320,7 @@ void paSendRTV2TestKOFeignException() throws DatatypeConfigurationException, IOE gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions PaSendRTV2Request requestBody = PaSendRTReqMock.getMockV2("11111111112222235"); @@ -1341,6 +1362,7 @@ void paSendRTV2TestKO() throws DatatypeConfigurationException, IOException { gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -1383,6 +1405,7 @@ void paSendRTV2ReceiptConflict() throws DatatypeConfigurationException, IOExcept gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions @@ -1426,6 +1449,7 @@ void paSendRTReceiptConflict() throws DatatypeConfigurationException, IOExceptio gpdClient, gpsClient, tableClientConfiguration(), + queueClient, customizedModelMapper)); // Test preconditions From 805681c39bedca053a0fd2ea502bbfa9ee0475de Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Tue, 23 Apr 2024 11:35:48 +0200 Subject: [PATCH 04/24] PAGOPA-1695 wip fixing logic and junit --- .../payments/service/PartnerService.java | 249 ++++++++++-------- .../payments/service/PartnerServiceTest.java | 102 +++++-- src/test/resources/application.properties | 3 + 3 files changed, 216 insertions(+), 138 deletions(-) diff --git a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java index b2c16139..26259a3f 100644 --- a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java +++ b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java @@ -765,109 +765,137 @@ private PaymentsModelResponse manageGetPaymentRequest(String idPa, CtQrCode qrCo } private PaymentOptionModelResponse managePaSendRtRequest(PaSendRTReq request) { - log.debug( - "[managePaSendRtRequest] save receipt [noticeNumber={}]", - request.getReceipt().getNoticeNumber()); + log.debug( + "[managePaSendRtRequest] save receipt [noticeNumber={}]", + request.getReceipt().getNoticeNumber()); - String debtorIdentifier = + String debtorIdentifier = Optional.ofNullable(request.getReceipt().getDebtor()) .map(CtSubject::getUniqueIdentifier) .map(CtEntityUniqueIdentifier::getEntityUniqueIdentifierValue) .orElse(""); - ReceiptEntity receiptEntity = - this.getReceiptEntity( - request.getIdPA(), - request.getReceipt().getCreditorReferenceId(), - debtorIdentifier, - request.getReceipt().getPaymentDateTime().toString()); - - try { - receiptEntity.setDocument(this.marshal(request)); - } catch (JAXBException e) { - log.error( - "[managePaSendRtRequest] Error in receipt marshalling [noticeNumber={}]", - request.getReceipt().getNoticeNumber(), - e); - throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); - } - - LocalDateTime paymentDateTime = - request.getReceipt().getPaymentDateTime() != null - ? request - .getReceipt() - .getPaymentDateTime() - .toGregorianCalendar() - .toZonedDateTime() - .toLocalDateTime() - : null; - PaymentOptionModel body = - PaymentOptionModel.builder() - .idReceipt(request.getReceipt().getReceiptId()) - .paymentDate(paymentDateTime) - .pspCompany(request.getReceipt().getPSPCompanyName()) - .paymentMethod(request.getReceipt().getPaymentMethod()) - .fee(String.valueOf(this.getFeeInCent(request.getReceipt().getFee()))) - .build(); - - return this.getReceiptPaymentOption( - request.getReceipt().getNoticeNumber(), - request.getIdPA(), - request.getReceipt().getCreditorReferenceId(), - body, - receiptEntity); + ReceiptEntity receiptEntity = + this.getReceiptEntity( + request.getIdPA(), + request.getReceipt().getCreditorReferenceId(), + debtorIdentifier, + request.getReceipt().getPaymentDateTime().toString()); + + try { + receiptEntity.setDocument(this.marshal(request)); + } catch (JAXBException e) { + log.error( + "[managePaSendRtRequest] Error in receipt marshalling [noticeNumber={}]", + request.getReceipt().getNoticeNumber(), + e); + throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); + } + + LocalDateTime paymentDateTime = + request.getReceipt().getPaymentDateTime() != null + ? request + .getReceipt() + .getPaymentDateTime() + .toGregorianCalendar() + .toZonedDateTime() + .toLocalDateTime() + : null; + PaymentOptionModel body = + PaymentOptionModel.builder() + .idReceipt(request.getReceipt().getReceiptId()) + .paymentDate(paymentDateTime) + .pspCompany(request.getReceipt().getPSPCompanyName()) + .paymentMethod(request.getReceipt().getPaymentMethod()) + .fee(String.valueOf(this.getFeeInCent(request.getReceipt().getFee()))) + .build(); + try { + return this.getReceiptPaymentOption( + request.getReceipt().getNoticeNumber(), + request.getIdPA(), + request.getReceipt().getCreditorReferenceId(), + body, + receiptEntity); + } catch (RetryableException e) { + log.error("[getReceiptPaymentOption] GPD Not Reachable [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); + queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofMinutes(10L), null, null, Context.NONE); + throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); + } catch (FeignException e) { + log.error("[getReceiptPaymentOption] GPD Error Response [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); + queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofMinutes(10L), null, null, Context.NONE); + throw new PartnerValidationException(PaaErrorEnum.PAA_SEMANTICA); + } catch (PartnerValidationException e) { + throw e; + } catch (Exception e) { + log.error("[getReceiptPaymentOption] GPD Generic Error [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); + throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); + } } private PaymentOptionModelResponse managePaSendRtRequest(PaSendRTV2Request request) { - log.debug( - "[managePaSendRtRequest] save V2 receipt [noticeNumber={}]", - request.getReceipt().getNoticeNumber()); + log.debug( + "[managePaSendRtRequest] save V2 receipt [noticeNumber={}]", + request.getReceipt().getNoticeNumber()); - String debtorIdentifier = + String debtorIdentifier = Optional.ofNullable(request.getReceipt().getDebtor()) .map(CtSubject::getUniqueIdentifier) .map(CtEntityUniqueIdentifier::getEntityUniqueIdentifierValue) .orElse(""); - ReceiptEntity receiptEntity = - this.getReceiptEntity( - request.getIdPA(), - request.getReceipt().getCreditorReferenceId(), - debtorIdentifier, - request.getReceipt().getPaymentDateTime().toString()); - try { - receiptEntity.setDocument(this.marshalV2(request)); - } catch (JAXBException e) { - log.error( - "[managePaSendRtRequest] Error in receipt marshalling [noticeNumber={}]", - request.getReceipt().getNoticeNumber(), - e); - throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); - } - - LocalDateTime paymentDateTime = - request.getReceipt().getPaymentDateTime() != null - ? request - .getReceipt() - .getPaymentDateTime() - .toGregorianCalendar() - .toZonedDateTime() - .toLocalDateTime() - : null; - - PaymentOptionModel body = - PaymentOptionModel.builder() - .idReceipt(request.getReceipt().getReceiptId()) - .paymentDate(paymentDateTime) - .pspCompany(request.getReceipt().getPSPCompanyName()) - .paymentMethod(request.getReceipt().getPaymentMethod()) - .fee(String.valueOf(this.getFeeInCent(request.getReceipt().getFee()))) - .build(); - - return this.getReceiptPaymentOption( - request.getReceipt().getNoticeNumber(), - request.getIdPA(), - request.getReceipt().getCreditorReferenceId(), - body, - receiptEntity); + ReceiptEntity receiptEntity = + this.getReceiptEntity( + request.getIdPA(), + request.getReceipt().getCreditorReferenceId(), + debtorIdentifier, + request.getReceipt().getPaymentDateTime().toString()); + try { + receiptEntity.setDocument(this.marshalV2(request)); + } catch (JAXBException e) { + log.error( + "[managePaSendRtRequest] Error in receipt marshalling [noticeNumber={}]", + request.getReceipt().getNoticeNumber(), + e); + throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); + } + + LocalDateTime paymentDateTime = + request.getReceipt().getPaymentDateTime() != null + ? request + .getReceipt() + .getPaymentDateTime() + .toGregorianCalendar() + .toZonedDateTime() + .toLocalDateTime() + : null; + + PaymentOptionModel body = + PaymentOptionModel.builder() + .idReceipt(request.getReceipt().getReceiptId()) + .paymentDate(paymentDateTime) + .pspCompany(request.getReceipt().getPSPCompanyName()) + .paymentMethod(request.getReceipt().getPaymentMethod()) + .fee(String.valueOf(this.getFeeInCent(request.getReceipt().getFee()))) + .build(); + try { + return this.getReceiptPaymentOption( + request.getReceipt().getNoticeNumber(), + request.getIdPA(), + request.getReceipt().getCreditorReferenceId(), + body, + receiptEntity); + } catch (RetryableException e) { + log.error("[getReceiptPaymentOption] GPD Not Reachable [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); + queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofMinutes(10L), null, null, Context.NONE); + throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); + } catch (FeignException e) { + log.error("[getReceiptPaymentOption] GPD Error Response [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); + queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofMinutes(10L), null, null, Context.NONE); + throw new PartnerValidationException(PaaErrorEnum.PAA_SEMANTICA); + } catch (PartnerValidationException e) { + throw e; + } catch (Exception e) { + log.error("[getReceiptPaymentOption] GPD Generic Error [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); + throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); + } } private ReceiptEntity getReceiptEntity( @@ -883,7 +911,7 @@ private PaymentOptionModelResponse getReceiptPaymentOption(String noticeNumber, String idPa, String creditorReferenceId, PaymentOptionModel body, - ReceiptEntity receiptEntity) { + ReceiptEntity receiptEntity) throws FeignException, URISyntaxException, InvalidKeyException, StorageException{ PaymentOptionModelResponse paymentOption = new PaymentOptionModelResponse(); try { paymentOption = gpdClient.receiptPaymentOption(idPa, noticeNumber, body); @@ -919,41 +947,29 @@ private PaymentOptionModelResponse getReceiptPaymentOption(String noticeNumber, throw new PartnerValidationException(PaaErrorEnum.PAA_PAGAMENTO_SCONOSCIUTO); } catch (PartnerValidationException e) { throw e; - } catch (RetryableException e) { - log.error("[getReceiptPaymentOption] GPD Not Reachable [noticeNumber={}]", noticeNumber, e); - queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofMinutes(10L), null, null, Context.NONE); - throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); - } catch (FeignException e) { - log.error("[getReceiptPaymentOption] GPD Error Response [noticeNumber={}]", noticeNumber, e); - queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofMinutes(10L), null, null, Context.NONE); - throw new PartnerValidationException(PaaErrorEnum.PAA_SEMANTICA); - } catch (Exception e) { - log.error("[getReceiptPaymentOption] GPD Generic Error [noticeNumber={}]", noticeNumber, e); - queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofMinutes(10L), null, null, Context.NONE); - throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); } return paymentOption; } public void getAllFailuresQueue() { - List failureBodies = new ArrayList<>(); XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath(); - // The message is dequeued and locked for 30 seconds by default - Optional optQueueMessageItem = queueClient.receiveMessages(1).stream().findFirst(); - while (optQueueMessageItem.isPresent()) { - failureBodies.add(getFailureQueue(optQueueMessageItem.get())); - } try { - for (String failureBody : failureBodies) { - handlingXml(failureBody, xpath); + // The message is dequeued and locked for 30 seconds by default + List queueList = queueClient.receiveMessages(10, Duration.ofMinutes(5L), null, Context.NONE).stream().toList(); + for(QueueMessageItem message: queueList) { + if(checkQueueCountValidity(message)) { + handlingXml(getFailureQueue(message), xpath, message); + } else { + queueClient.deleteMessage(message.getMessageId(), message.getPopReceipt()); + } } } catch (XPathExpressionException e) { } } - public void handlingXml (String failureBody, XPath xpath) throws XPathExpressionException { + public void handlingXml (String failureBody, XPath xpath, QueueMessageItem queueMessageItem) throws XPathExpressionException { Document xmlDoc = getXMLDocument(failureBody); XPathExpression xPathExpr = xpath.compile('/' + xmlDoc.getFirstChild().getNodeName()); NodeList nodes = (NodeList) xPathExpr.evaluate(xmlDoc, XPathConstants.NODESET); @@ -993,16 +1009,19 @@ public void handlingXml (String failureBody, XPath xpath) throws XPathExpression creditorReferenceId, body, receiptEntity); - } catch (Exception e) { + } catch (FeignException | URISyntaxException | InvalidKeyException | StorageException e) { log.info("[paSendRT] Retry failed [fiscalCode={},noticeNumber={}]\",\n", idPA, noticeNumber); + queueClient.updateMessageWithResponse(queueMessageItem.getMessageId(), queueMessageItem.getPopReceipt(), receiptEntity.getDocument(), Duration.ofMinutes(10L), null, Context.NONE); } } + public boolean checkQueueCountValidity(QueueMessageItem message) { + return message.getDequeueCount() <= 5; //TODO parametrize this variable + } + public String getFailureQueue(QueueMessageItem queueMessageItem){ - queueClient.deleteMessage(queueMessageItem.getMessageId(), queueMessageItem.getPopReceipt()); return new String(queueMessageItem.getBody().toBytes(), StandardCharsets.UTF_8); } - public Document getXMLDocument(String xmlString) { try { DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); diff --git a/src/test/java/it/gov/pagopa/payments/service/PartnerServiceTest.java b/src/test/java/it/gov/pagopa/payments/service/PartnerServiceTest.java index 3277f198..353490c9 100644 --- a/src/test/java/it/gov/pagopa/payments/service/PartnerServiceTest.java +++ b/src/test/java/it/gov/pagopa/payments/service/PartnerServiceTest.java @@ -23,6 +23,9 @@ import javax.xml.stream.XMLStreamException; import com.azure.storage.queue.QueueClient; +import com.azure.storage.queue.QueueClientBuilder; +import com.microsoft.azure.storage.queue.CloudQueue; +import com.microsoft.azure.storage.queue.CloudQueueClient; import org.junit.ClassRule; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -104,8 +107,6 @@ class PartnerServiceTest { @Mock private GpsClient gpsClient; - @Mock private QueueClient queueClient; - private String genericService = "/xsd/general-service.xsd"; ResourceLoader resourceLoader = new DefaultResourceLoader(); Resource resource = resourceLoader.getResource(genericService); @@ -441,7 +442,7 @@ void paSendRTTest() throws DatatypeConfigurationException, IOException { gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -462,6 +463,9 @@ void paSendRTTest() throws DatatypeConfigurationException, IOException { cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception e) { log.info("Error during table creation", e); } @@ -485,7 +489,7 @@ void paSendRTTestKOConflict() throws DatatypeConfigurationException, IOException gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -503,6 +507,9 @@ void paSendRTTestKOConflict() throws DatatypeConfigurationException, IOException cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception ex) { log.info("Error during table creation", e); } @@ -528,7 +535,7 @@ void paSendRTTestKOUnknown() throws DatatypeConfigurationException, IOException gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -546,6 +553,9 @@ void paSendRTTestKOUnknown() throws DatatypeConfigurationException, IOException cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception ex) { log.info("Error during table creation", e); } @@ -572,7 +582,7 @@ void paSendRTTestKOStatus(String status, String iuv) throws DatatypeConfiguratio gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -593,6 +603,9 @@ void paSendRTTestKOStatus(String status, String iuv) throws DatatypeConfiguratio cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception ex) { log.info("Error during table creation", ex); } @@ -618,7 +631,7 @@ void paSendRTTestKORetryableException() throws DatatypeConfigurationException, I gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -636,6 +649,9 @@ void paSendRTTestKORetryableException() throws DatatypeConfigurationException, I cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception ex) { log.info("Error during table creation", e); } @@ -661,7 +677,7 @@ void paSendRTTestKOFeignException() throws DatatypeConfigurationException, IOExc gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions PaSendRTReq requestBody = PaSendRTReqMock.getMock("11111111112222225"); @@ -678,6 +694,9 @@ void paSendRTTestKOFeignException() throws DatatypeConfigurationException, IOExc cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception ex) { log.info("Error during table creation", e); } @@ -703,7 +722,7 @@ void paSendRTTestKO() throws DatatypeConfigurationException, IOException { gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -721,6 +740,9 @@ void paSendRTTestKO() throws DatatypeConfigurationException, IOException { cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception ex) { log.info("Error during table creation", e); } @@ -758,7 +780,7 @@ void paDemandPaymentNoticeTest() gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -808,7 +830,7 @@ void paDemandPaymentNoticeNotFoundTest() gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -842,7 +864,7 @@ void paGetPaymentV2Test() gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -906,7 +928,7 @@ void paGetPaymentV2_TransferTypePAGOPA_Test() gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -989,7 +1011,7 @@ void paGetPaymentIncompleteAddressV2Test() gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -1100,7 +1122,7 @@ void paSendRTV2Test() throws DatatypeConfigurationException, IOException { gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -1121,6 +1143,9 @@ void paSendRTV2Test() throws DatatypeConfigurationException, IOException { cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception e) { log.info("Error during table creation", e); } @@ -1144,7 +1169,7 @@ void paSendRTV2TestKOConflict() throws DatatypeConfigurationException, IOExcepti gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -1162,6 +1187,9 @@ void paSendRTV2TestKOConflict() throws DatatypeConfigurationException, IOExcepti cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception ex) { log.info("Error during table creation", e); } @@ -1187,7 +1215,7 @@ void paSendRTV2TestKOUnknown() throws DatatypeConfigurationException, IOExceptio gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -1205,6 +1233,9 @@ void paSendRTV2TestKOUnknown() throws DatatypeConfigurationException, IOExceptio cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception ex) { log.info("Error during table creation", e); } @@ -1231,7 +1262,7 @@ void paSendRTV2TestKOStatus(String status, String iuv) throws DatatypeConfigurat gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -1252,6 +1283,9 @@ void paSendRTV2TestKOStatus(String status, String iuv) throws DatatypeConfigurat cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception ex) { log.info("Error during table creation", ex); } @@ -1277,7 +1311,7 @@ void paSendRTV2TestKORetryableException() throws DatatypeConfigurationException, gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -1295,6 +1329,9 @@ void paSendRTV2TestKORetryableException() throws DatatypeConfigurationException, cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception ex) { log.info("Error during table creation", e); } @@ -1320,7 +1357,7 @@ void paSendRTV2TestKOFeignException() throws DatatypeConfigurationException, IOE gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions PaSendRTV2Request requestBody = PaSendRTReqMock.getMockV2("11111111112222235"); @@ -1337,6 +1374,9 @@ void paSendRTV2TestKOFeignException() throws DatatypeConfigurationException, IOE cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception ex) { log.info("Error during table creation", e); } @@ -1362,7 +1402,7 @@ void paSendRTV2TestKO() throws DatatypeConfigurationException, IOException { gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -1380,6 +1420,9 @@ void paSendRTV2TestKO() throws DatatypeConfigurationException, IOException { cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception ex) { log.info("Error during table creation", e); } @@ -1405,7 +1448,7 @@ void paSendRTV2ReceiptConflict() throws DatatypeConfigurationException, IOExcept gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -1426,6 +1469,9 @@ void paSendRTV2ReceiptConflict() throws DatatypeConfigurationException, IOExcept cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception e) { log.info("Error during table creation", e); } @@ -1449,7 +1495,7 @@ void paSendRTReceiptConflict() throws DatatypeConfigurationException, IOExceptio gpdClient, gpsClient, tableClientConfiguration(), - queueClient, + queueClientConfiguration(), customizedModelMapper)); // Test preconditions @@ -1470,6 +1516,9 @@ void paSendRTReceiptConflict() throws DatatypeConfigurationException, IOExceptio cloudTableClient.setDefaultRequestOptions(tableRequestOptions); CloudTable table = cloudTableClient.getTableReference("receiptsTable"); table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); } catch (Exception e) { log.info("Error during table creation", e); } @@ -1488,4 +1537,11 @@ private TableClient tableClientConfiguration() { .tableName("receiptsTable") .buildClient(); } + + private QueueClient queueClientConfiguration() { + return new QueueClientBuilder() + .connectionString(storageConnectionString) + .queueName("testqueue") + .buildClient(); + } } diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index 2ce15b5c..e9dcf4e0 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -32,3 +32,6 @@ retry.maxDelay=200 xsd.generic-service=payments/src/main/resources/xsd/general-service.xsd + +azure.queue.connection.string=DefaultEndpointsProtocol=http;AccountName=localhost;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==;QueueEndpoint=http://localhost:8902/; +azure.queue.queueName=testqueue From 6cc83a0abcedc2283046ac93095bd14543df64ee Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Tue, 23 Apr 2024 13:00:51 +0200 Subject: [PATCH 05/24] PAGOPA-1695 adding scheduler class --- .../pagopa/payments/scheduler/Scheduler.java | 36 +++++ .../SchedulerTest.java} | 4 +- .../payments/service/PartnerService.java | 4 +- .../payments/service/SchedulerService.java | 129 ++++++++++++++++++ 4 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java rename src/main/java/it/gov/pagopa/payments/{controller/scheduler/Scheduler.java => scheduler/SchedulerTest.java} (91%) create mode 100644 src/main/java/it/gov/pagopa/payments/service/SchedulerService.java diff --git a/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java b/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java new file mode 100644 index 00000000..d70d2782 --- /dev/null +++ b/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java @@ -0,0 +1,36 @@ +package it.gov.pagopa.payments.scheduler; + +import it.gov.pagopa.payments.service.SchedulerService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +@Component +@Slf4j +@ConditionalOnProperty(name = "cron.job.schedule.retry.enabled", matchIfMissing = true) +public class Scheduler { + + private static final String LOG_BASE_HEADER_INFO = "[OperationType: %s] - [ClassMethod: %s] - [MethodParamsToLog: %s]"; + private static final String CRON_JOB = "CRON JOB"; + private Thread threadOfExecution; + + @Autowired + SchedulerService schedulerService; + + @Scheduled(cron = "${cron.job.schedule.expression.retry.status}") + @Async + @Transactional + public void changeDebtPositionStatusToValid() { + log.info(String.format(LOG_BASE_HEADER_INFO, CRON_JOB, "retry sendRT", "Running at " + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()))); + schedulerService.getAllFailuresQueue(); + this.threadOfExecution = Thread.currentThread(); + } +} + diff --git a/src/main/java/it/gov/pagopa/payments/controller/scheduler/Scheduler.java b/src/main/java/it/gov/pagopa/payments/scheduler/SchedulerTest.java similarity index 91% rename from src/main/java/it/gov/pagopa/payments/controller/scheduler/Scheduler.java rename to src/main/java/it/gov/pagopa/payments/scheduler/SchedulerTest.java index 878d36f1..14b5c407 100644 --- a/src/main/java/it/gov/pagopa/payments/controller/scheduler/Scheduler.java +++ b/src/main/java/it/gov/pagopa/payments/scheduler/SchedulerTest.java @@ -1,4 +1,4 @@ -package it.gov.pagopa.payments.controller.scheduler; +package it.gov.pagopa.payments.scheduler; import it.gov.pagopa.payments.service.PartnerService; import lombok.extern.slf4j.Slf4j; @@ -13,7 +13,7 @@ @Controller @Slf4j @Validated -public class Scheduler { +public class SchedulerTest { @Autowired PartnerService partnerService; diff --git a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java index 26259a3f..9e9f467f 100644 --- a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java +++ b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java @@ -694,7 +694,7 @@ private ReceiptEntity getReceipt(String organizationFiscalCode, String iuv) } } - private long getFeeInCent(BigDecimal fee) { + public long getFeeInCent(BigDecimal fee) { long feeInCent = 0; if (null != fee) { feeInCent = fee.multiply(BigDecimal.valueOf(100)).longValue(); @@ -907,7 +907,7 @@ private ReceiptEntity getReceiptEntity( return receiptEntity; } - private PaymentOptionModelResponse getReceiptPaymentOption(String noticeNumber, + public PaymentOptionModelResponse getReceiptPaymentOption(String noticeNumber, String idPa, String creditorReferenceId, PaymentOptionModel body, diff --git a/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java new file mode 100644 index 00000000..a803c48f --- /dev/null +++ b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java @@ -0,0 +1,129 @@ +package it.gov.pagopa.payments.service; + +import com.azure.core.util.Context; +import com.azure.storage.queue.QueueClient; +import com.azure.storage.queue.models.QueueMessageItem; +import com.microsoft.azure.storage.StorageException; +import feign.FeignException; +import it.gov.pagopa.payments.entity.ReceiptEntity; +import it.gov.pagopa.payments.exception.AppError; +import it.gov.pagopa.payments.exception.AppException; +import it.gov.pagopa.payments.model.PaymentOptionModel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.*; +import java.io.IOException; +import java.io.StringReader; +import java.math.BigDecimal; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@Service +@Slf4j +public class SchedulerService { + + @Value(value = "${azure.queue.dequeue.limit}") + private Integer dequeueLimit; + @Autowired + QueueClient queueClient; + + @Autowired + PartnerService partnerService; + + public void getAllFailuresQueue() { + XPathFactory xPathfactory = XPathFactory.newInstance(); + XPath xpath = xPathfactory.newXPath(); + try { + // The message is dequeued and locked for 30 seconds by default + List queueList = queueClient.receiveMessages(10, Duration.ofMinutes(5L), null, Context.NONE).stream().toList(); + for(QueueMessageItem message: queueList) { + if(checkQueueCountValidity(message)) { + handlingXml(getFailureQueue(message), xpath, message); + } else { + queueClient.deleteMessage(message.getMessageId(), message.getPopReceipt()); + } + } + } catch (XPathExpressionException e) { + + } + } + + public void handlingXml (String failureBody, XPath xpath, QueueMessageItem queueMessageItem) throws XPathExpressionException { + Document xmlDoc = getXMLDocument(failureBody); + XPathExpression xPathExpr = xpath.compile('/' + xmlDoc.getFirstChild().getNodeName()); + NodeList nodes = (NodeList) xPathExpr.evaluate(xmlDoc, XPathConstants.NODESET); + + Element node = (Element) nodes.item(0); + + String idPA = node.getElementsByTagName("idPA").item(0).getTextContent(); + String creditorReferenceId = node.getElementsByTagName("creditorReferenceId").item(0).getTextContent(); + String noticeNumber = node.getElementsByTagName("noticeNumber").item(0).getTextContent(); + String paymentDateTime = node.getElementsByTagName("paymentDateTime").item(0).getTextContent(); + String receiptId = node.getElementsByTagName("receiptId").item(0).getTextContent(); + String PSPCompanyName = node.getElementsByTagName("PSPCompanyName").item(0).getTextContent(); + String paymentMethod = node.getElementsByTagName("paymentMethod").item(0).getTextContent(); + String fee = node.getElementsByTagName("fee").item(0).getTextContent(); + String entityUniqueIdentifierValue = node.getElementsByTagName("entityUniqueIdentifierValue").item(0).getTextContent(); + + ReceiptEntity receiptEntity = new ReceiptEntity(idPA, creditorReferenceId); + receiptEntity.setDebtor(entityUniqueIdentifierValue); + String paymentDateTimeIdentifier = Optional.ofNullable(paymentDateTime).orElse(""); + receiptEntity.setPaymentDateTime(paymentDateTimeIdentifier); + receiptEntity.setDocument(failureBody); + + LocalDateTime localPaymentDateTime = paymentDateTime != null ? LocalDateTime.parse(paymentDateTime) : null; + PaymentOptionModel body = + PaymentOptionModel.builder() + .idReceipt(receiptId) + .paymentDate(localPaymentDateTime) + .pspCompany(PSPCompanyName) + .paymentMethod(paymentMethod) + .fee(String.valueOf(partnerService.getFeeInCent(new BigDecimal(fee)))) + .build(); + + try { + partnerService.getReceiptPaymentOption( + noticeNumber, + idPA, + creditorReferenceId, + body, + receiptEntity); + } catch (FeignException | URISyntaxException | InvalidKeyException | StorageException e) { + log.info("[paSendRT] Retry failed [fiscalCode={},noticeNumber={}]\",\n", idPA, noticeNumber); + queueClient.updateMessageWithResponse(queueMessageItem.getMessageId(), queueMessageItem.getPopReceipt(), receiptEntity.getDocument(), Duration.ofMinutes(10L), null, Context.NONE); + } + } + + public boolean checkQueueCountValidity(QueueMessageItem message) { + return message.getDequeueCount() <= dequeueLimit; + } + + public String getFailureQueue(QueueMessageItem queueMessageItem){ + return new String(queueMessageItem.getBody().toBytes(), StandardCharsets.UTF_8); + } + public Document getXMLDocument(String xmlString) { + try { + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + InputSource is = new InputSource(new StringReader(xmlString)); + return builder.parse(is); + } catch (ParserConfigurationException | SAXException | IOException e) { + throw new AppException(AppError.UNKNOWN); + } + } +} From 579fb63a76896bf0b904558b39151c33fa37b910 Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:10:51 +0200 Subject: [PATCH 06/24] PAGOPA-1695 updating logic and inserting junit tests --- openapi/openapi.json | 813 +++++++++--------- .../payments/scheduler/SchedulerTest.java | 28 - .../payments/service/PartnerService.java | 116 +-- .../payments/service/SchedulerService.java | 45 +- .../pagopa/payments/mock/PaSendRTReqMock.java | 36 +- .../payments/service/PartnerServiceTest.java | 86 +- .../service/SchedulerServiceTest.java | 309 +++++++ src/test/resources/application.properties | 7 + 8 files changed, 870 insertions(+), 570 deletions(-) delete mode 100644 src/main/java/it/gov/pagopa/payments/scheduler/SchedulerTest.java create mode 100644 src/test/java/it/gov/pagopa/payments/service/SchedulerServiceTest.java diff --git a/openapi/openapi.json b/openapi/openapi.json index c2fcb9dd..0caf8f6e 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -1,554 +1,515 @@ { - "openapi": "3.0.1", - "info": { - "title": "PagoPA API Payments", - "description": "Payments", - "termsOfService": "https://www.pagopa.gov.it/", - "version": "0.12.20" + "openapi" : "3.0.1", + "info" : { + "title" : "PagoPA API Payments", + "description" : "Payments", + "termsOfService" : "https://www.pagopa.gov.it/", + "version" : "0.12.20" }, - "servers": [ - { - "url": "http://localhost", - "description": "Generated server url" - } - ], - "tags": [ - { - "name": "Payments receipts API" - } - ], - "paths": { - "/info": { - "get": { - "tags": [ - "Home" - ], - "summary": "health check", - "description": "Return OK if application is started", - "operationId": "healthCheck", - "responses": { - "200": { - "description": "OK", - "headers": { - "X-Request-Id": { - "description": "This header identifies the call", - "schema": { - "type": "string" + "servers" : [ { + "url" : "http://localhost", + "description" : "Generated server url" + } ], + "tags" : [ { + "name" : "Payments receipts API" + } ], + "paths" : { + "/info" : { + "get" : { + "tags" : [ "Home" ], + "summary" : "health check", + "description" : "Return OK if application is started", + "operationId" : "healthCheck", + "responses" : { + "200" : { + "description" : "OK", + "headers" : { + "X-Request-Id" : { + "description" : "This header identifies the call", + "schema" : { + "type" : "string" } } }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AppInfo" + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AppInfo" } } } }, - "400": { - "description": "Bad Request", - "headers": { - "X-Request-Id": { - "description": "This header identifies the call", - "schema": { - "type": "string" + "400" : { + "description" : "Bad Request", + "headers" : { + "X-Request-Id" : { + "description" : "This header identifies the call", + "schema" : { + "type" : "string" } } }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemJson" + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ProblemJson" } } } }, - "401": { - "description": "Unauthorized", - "headers": { - "X-Request-Id": { - "description": "This header identifies the call", - "schema": { - "type": "string" + "401" : { + "description" : "Unauthorized", + "headers" : { + "X-Request-Id" : { + "description" : "This header identifies the call", + "schema" : { + "type" : "string" } } } }, - "403": { - "description": "Forbidden", - "headers": { - "X-Request-Id": { - "description": "This header identifies the call", - "schema": { - "type": "string" + "403" : { + "description" : "Forbidden", + "headers" : { + "X-Request-Id" : { + "description" : "This header identifies the call", + "schema" : { + "type" : "string" } } } }, - "429": { - "description": "Too many requests", - "headers": { - "X-Request-Id": { - "description": "This header identifies the call", - "schema": { - "type": "string" + "429" : { + "description" : "Too many requests", + "headers" : { + "X-Request-Id" : { + "description" : "This header identifies the call", + "schema" : { + "type" : "string" } } } }, - "500": { - "description": "Service unavailable", - "headers": { - "X-Request-Id": { - "description": "This header identifies the call", - "schema": { - "type": "string" + "500" : { + "description" : "Service unavailable", + "headers" : { + "X-Request-Id" : { + "description" : "This header identifies the call", + "schema" : { + "type" : "string" } } }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemJson" + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ProblemJson" } } } } }, - "security": [ - { - "ApiKey": [] - }, - { - "Authorization": [] - } - ] + "security" : [ { + "ApiKey" : [ ] + }, { + "Authorization" : [ ] + } ] }, - "parameters": [ - { - "name": "X-Request-Id", - "in": "header", - "description": "This header identifies the call, if not passed it is self-generated. This ID is returned in the response.", - "schema": { - "type": "string" - } + "parameters" : [ { + "name" : "X-Request-Id", + "in" : "header", + "description" : "This header identifies the call, if not passed it is self-generated. This ID is returned in the response.", + "schema" : { + "type" : "string" } - ] + } ] }, - "/payments/{organizationfiscalcode}/receipts": { - "get": { - "tags": [ - "Payments receipts API" - ], - "summary": "Return the list of the organization receipts.", - "operationId": "getOrganizationReceipts", - "parameters": [ - { - "name": "organizationfiscalcode", - "in": "path", - "description": "Organization fiscal code, the fiscal code of the Organization.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "pageNum", - "in": "query", - "description": "Page number, starts from 0", - "required": false, - "schema": { - "minimum": 0, - "type": "integer", - "format": "int32", - "default": 0 - } - }, - { - "name": "pageSize", - "in": "query", - "description": "Number of elements per page. Default = 20", - "required": false, - "schema": { - "maximum": 100, - "type": "integer", - "format": "int32", - "default": 20 - } - }, - { - "name": "debtor", - "in": "query", - "description": "Filter by debtor", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "service", - "in": "query", - "description": "Filter by service", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "from", - "in": "query", - "description": "Filter by date, from this date", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "to", - "in": "query", - "description": "Filter by date, to this date", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "segregationCodes", - "in": "query", - "description": "Segregation codes for which broker is authorized", - "required": false, - "schema": { - "pattern": "\\d{2}(,\\d{2})*", - "type": "string" - } - }, - { - "name": "debtorOrIuv", - "in": "query", - "description": "Filter start of debtor or IUV", - "required": false, - "schema": { - "type": "string" - } + "/payments/{organizationfiscalcode}/receipts" : { + "get" : { + "tags" : [ "Payments receipts API" ], + "summary" : "Return the list of the organization receipts.", + "operationId" : "getOrganizationReceipts", + "parameters" : [ { + "name" : "organizationfiscalcode", + "in" : "path", + "description" : "Organization fiscal code, the fiscal code of the Organization.", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "pageNum", + "in" : "query", + "description" : "Page number, starts from 0", + "required" : false, + "schema" : { + "minimum" : 0, + "type" : "integer", + "format" : "int32", + "default" : 0 } - ], - "responses": { - "200": { - "description": "Obtained all organization payment positions.", - "headers": { - "X-Request-Id": { - "description": "This header identifies the call", - "schema": { - "type": "string" + }, { + "name" : "pageSize", + "in" : "query", + "description" : "Number of elements per page. Default = 20", + "required" : false, + "schema" : { + "maximum" : 100, + "type" : "integer", + "format" : "int32", + "default" : 20 + } + }, { + "name" : "debtor", + "in" : "query", + "description" : "Filter by debtor", + "required" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "service", + "in" : "query", + "description" : "Filter by service", + "required" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "from", + "in" : "query", + "description" : "Filter by date, from this date", + "required" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "to", + "in" : "query", + "description" : "Filter by date, to this date", + "required" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "segregationCodes", + "in" : "query", + "description" : "Segregation codes for which broker is authorized", + "required" : false, + "schema" : { + "pattern" : "\\d{2}(,\\d{2})*", + "type" : "string" + } + }, { + "name" : "debtorOrIuv", + "in" : "query", + "description" : "Filter start of debtor or IUV", + "required" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "Obtained all organization payment positions.", + "headers" : { + "X-Request-Id" : { + "description" : "This header identifies the call", + "schema" : { + "type" : "string" } } }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PaymentsResult" + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PaymentsResult" } } } }, - "401": { - "description": "Wrong or missing function key.", - "headers": { - "X-Request-Id": { - "description": "This header identifies the call", - "schema": { - "type": "string" + "401" : { + "description" : "Wrong or missing function key.", + "headers" : { + "X-Request-Id" : { + "description" : "This header identifies the call", + "schema" : { + "type" : "string" } } } }, - "404": { - "description": "No receipts found.", - "headers": { - "X-Request-Id": { - "description": "This header identifies the call", - "schema": { - "type": "string" + "404" : { + "description" : "No receipts found.", + "headers" : { + "X-Request-Id" : { + "description" : "This header identifies the call", + "schema" : { + "type" : "string" } } }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemJson" + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ProblemJson" } } } }, - "500": { - "description": "Service unavailable.", - "headers": { - "X-Request-Id": { - "description": "This header identifies the call", - "schema": { - "type": "string" + "500" : { + "description" : "Service unavailable.", + "headers" : { + "X-Request-Id" : { + "description" : "This header identifies the call", + "schema" : { + "type" : "string" } } }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemJson" + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ProblemJson" } } } } }, - "security": [ - { - "ApiKey": [] - }, - { - "Authorization": [] - } - ] + "security" : [ { + "ApiKey" : [ ] + }, { + "Authorization" : [ ] + } ] }, - "parameters": [ - { - "name": "X-Request-Id", - "in": "header", - "description": "This header identifies the call, if not passed it is self-generated. This ID is returned in the response.", - "schema": { - "type": "string" - } + "parameters" : [ { + "name" : "X-Request-Id", + "in" : "header", + "description" : "This header identifies the call, if not passed it is self-generated. This ID is returned in the response.", + "schema" : { + "type" : "string" } - ] + } ] }, - "/payments/{organizationfiscalcode}/receipts/{iuv}": { - "get": { - "tags": [ - "Payments receipts API" - ], - "summary": "Return the details of a specific receipt.", - "operationId": "getReceiptByIUV", - "parameters": [ - { - "name": "organizationfiscalcode", - "in": "path", - "description": "Organization fiscal code, the fiscal code of the Organization.", - "required": true, - "schema": { - "type": "string" - }, - "example": 12345 - }, - { - "name": "iuv", - "in": "path", - "description": "IUV (Unique Payment Identification). Alphanumeric code that uniquely associates and identifies three key elements of a payment: reason, payer, amount", - "required": true, - "schema": { - "type": "string" - }, - "example": "ABC123" - }, - { - "name": "segregationCodes", - "in": "query", - "description": "Segregation codes for which broker is authorized", - "required": false, - "schema": { - "pattern": "\\d{2}(,\\d{2})*", - "type": "string" - } + "/payments/{organizationfiscalcode}/receipts/{iuv}" : { + "get" : { + "tags" : [ "Payments receipts API" ], + "summary" : "Return the details of a specific receipt.", + "operationId" : "getReceiptByIUV", + "parameters" : [ { + "name" : "organizationfiscalcode", + "in" : "path", + "description" : "Organization fiscal code, the fiscal code of the Organization.", + "required" : true, + "schema" : { + "type" : "string" + }, + "example" : 12345 + }, { + "name" : "iuv", + "in" : "path", + "description" : "IUV (Unique Payment Identification). Alphanumeric code that uniquely associates and identifies three key elements of a payment: reason, payer, amount", + "required" : true, + "schema" : { + "type" : "string" + }, + "example" : "ABC123" + }, { + "name" : "segregationCodes", + "in" : "query", + "description" : "Segregation codes for which broker is authorized", + "required" : false, + "schema" : { + "pattern" : "\\d{2}(,\\d{2})*", + "type" : "string" } - ], - "responses": { - "200": { - "description": "Obtained receipt details.", - "headers": { - "X-Request-Id": { - "description": "This header identifies the call", - "schema": { - "type": "string" + } ], + "responses" : { + "200" : { + "description" : "Obtained receipt details.", + "headers" : { + "X-Request-Id" : { + "description" : "This header identifies the call", + "schema" : { + "type" : "string" } } }, - "content": { - "application/xml": { - "schema": { - "type": "string" + "content" : { + "application/xml" : { + "schema" : { + "type" : "string" } } } }, - "401": { - "description": "Wrong or missing function key.", - "headers": { - "X-Request-Id": { - "description": "This header identifies the call", - "schema": { - "type": "string" + "401" : { + "description" : "Wrong or missing function key.", + "headers" : { + "X-Request-Id" : { + "description" : "This header identifies the call", + "schema" : { + "type" : "string" } } } }, - "404": { - "description": "No receipt found.", - "headers": { - "X-Request-Id": { - "description": "This header identifies the call", - "schema": { - "type": "string" + "404" : { + "description" : "No receipt found.", + "headers" : { + "X-Request-Id" : { + "description" : "This header identifies the call", + "schema" : { + "type" : "string" } } }, - "content": { - "application/xml": { - "schema": { - "$ref": "#/components/schemas/ProblemJson" + "content" : { + "application/xml" : { + "schema" : { + "$ref" : "#/components/schemas/ProblemJson" } }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemJson" + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ProblemJson" } } } }, - "422": { - "description": "Unable to process the request.", - "headers": { - "X-Request-Id": { - "description": "This header identifies the call", - "schema": { - "type": "string" + "422" : { + "description" : "Unable to process the request.", + "headers" : { + "X-Request-Id" : { + "description" : "This header identifies the call", + "schema" : { + "type" : "string" } } }, - "content": { - "application/xml": { - "schema": { - "$ref": "#/components/schemas/ProblemJson" + "content" : { + "application/xml" : { + "schema" : { + "$ref" : "#/components/schemas/ProblemJson" } }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemJson" + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ProblemJson" } } } }, - "500": { - "description": "Service unavailable.", - "headers": { - "X-Request-Id": { - "description": "This header identifies the call", - "schema": { - "type": "string" + "500" : { + "description" : "Service unavailable.", + "headers" : { + "X-Request-Id" : { + "description" : "This header identifies the call", + "schema" : { + "type" : "string" } } }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemJson" + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ProblemJson" } } } } }, - "security": [ - { - "ApiKey": [] - }, - { - "Authorization": [] - } - ] + "security" : [ { + "ApiKey" : [ ] + }, { + "Authorization" : [ ] + } ] }, - "parameters": [ - { - "name": "X-Request-Id", - "in": "header", - "description": "This header identifies the call, if not passed it is self-generated. This ID is returned in the response.", - "schema": { - "type": "string" - } + "parameters" : [ { + "name" : "X-Request-Id", + "in" : "header", + "description" : "This header identifies the call, if not passed it is self-generated. This ID is returned in the response.", + "schema" : { + "type" : "string" } - ] + } ] } }, - "components": { - "schemas": { - "ProblemJson": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "A short, summary of the problem type. Written in english and readable for engineers (usually not suited for non technical stakeholders and not localized); example: Service Unavailable" - }, - "status": { - "maximum": 600, - "minimum": 100, - "type": "integer", - "description": "The HTTP status code generated by the origin server for this occurrence of the problem.", - "format": "int32", - "example": 200 - }, - "detail": { - "type": "string", - "description": "A human readable explanation specific to this occurrence of the problem.", - "example": "There was an error processing the request" + "components" : { + "schemas" : { + "ProblemJson" : { + "type" : "object", + "properties" : { + "title" : { + "type" : "string", + "description" : "A short, summary of the problem type. Written in english and readable for engineers (usually not suited for non technical stakeholders and not localized); example: Service Unavailable" + }, + "status" : { + "maximum" : 600, + "minimum" : 100, + "type" : "integer", + "description" : "The HTTP status code generated by the origin server for this occurrence of the problem.", + "format" : "int32", + "example" : 200 + }, + "detail" : { + "type" : "string", + "description" : "A human readable explanation specific to this occurrence of the problem.", + "example" : "There was an error processing the request" } } }, - "PaymentsResult": { - "type": "object", - "properties": { - "currentPageNumber": { - "type": "integer", - "format": "int32" - }, - "length": { - "type": "integer", - "format": "int32" - }, - "totalPages": { - "type": "integer", - "format": "int32" - }, - "results": { - "type": "array", - "items": { - "type": "object" + "PaymentsResult" : { + "type" : "object", + "properties" : { + "currentPageNumber" : { + "type" : "integer", + "format" : "int32" + }, + "length" : { + "type" : "integer", + "format" : "int32" + }, + "totalPages" : { + "type" : "integer", + "format" : "int32" + }, + "results" : { + "type" : "array", + "items" : { + "type" : "object" } } } }, - "AppInfo": { - "type": "object", - "properties": { - "name": { - "type": "string" + "AppInfo" : { + "type" : "object", + "properties" : { + "name" : { + "type" : "string" }, - "version": { - "type": "string" + "version" : { + "type" : "string" }, - "environment": { - "type": "string" + "environment" : { + "type" : "string" } } } }, - "securitySchemes": { - "ApiKey": { - "type": "apiKey", - "description": "The API key to access this function app.", - "name": "Ocp-Apim-Subscription-Key", - "in": "header" + "securitySchemes" : { + "ApiKey" : { + "type" : "apiKey", + "description" : "The API key to access this function app.", + "name" : "Ocp-Apim-Subscription-Key", + "in" : "header" }, - "Authorization": { - "type": "http", - "description": "JWT token get after Azure Login", - "scheme": "bearer", - "bearerFormat": "JWT" + "Authorization" : { + "type" : "http", + "description" : "JWT token get after Azure Login", + "scheme" : "bearer", + "bearerFormat" : "JWT" } } } -} +} \ No newline at end of file diff --git a/src/main/java/it/gov/pagopa/payments/scheduler/SchedulerTest.java b/src/main/java/it/gov/pagopa/payments/scheduler/SchedulerTest.java deleted file mode 100644 index 14b5c407..00000000 --- a/src/main/java/it/gov/pagopa/payments/scheduler/SchedulerTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package it.gov.pagopa.payments.scheduler; - -import it.gov.pagopa.payments.service.PartnerService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; - -@Controller -@Slf4j -@Validated -public class SchedulerTest { - - @Autowired - PartnerService partnerService; - - @GetMapping( - value = "/scheduler", - produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity getFailureReceipts() { - partnerService.getAllFailuresQueue(); - return new ResponseEntity<>(HttpStatus.OK); - } -} diff --git a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java index 9e9f467f..198eab5e 100644 --- a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java +++ b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java @@ -6,7 +6,6 @@ import com.azure.data.tables.models.TableErrorCode; import com.azure.data.tables.models.TableServiceException; import com.azure.storage.queue.QueueClient; -import com.azure.storage.queue.models.QueueMessageItem; import com.microsoft.azure.storage.StorageException; import feign.FeignException; import feign.RetryableException; @@ -49,11 +48,9 @@ import it.gov.pagopa.payments.utils.CustomizedMapper; import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.StringReader; import java.io.StringWriter; import java.math.BigDecimal; import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.time.Duration; import java.time.LocalDateTime; @@ -70,7 +67,6 @@ import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLStreamException; -import javax.xml.xpath.*; import it.gov.pagopa.payments.utils.Validator; import lombok.AllArgsConstructor; @@ -81,10 +77,6 @@ import org.springframework.core.io.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; import org.xml.sax.SAXException; @Service @@ -110,6 +102,9 @@ public class PartnerService { @Value(value = "${xsd.generic-service}") private Resource xsdGenericService; + @Value(value = "${azure.queue.send.invisibilityTime}") + private Long queueSendInvisibilityTime; + @Autowired private ObjectFactory factory; @Autowired private GpdClient gpdClient; @@ -817,12 +812,16 @@ private PaymentOptionModelResponse managePaSendRtRequest(PaSendRTReq request) { receiptEntity); } catch (RetryableException e) { log.error("[getReceiptPaymentOption] GPD Not Reachable [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); - queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofMinutes(10L), null, null, Context.NONE); + queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofSeconds(queueSendInvisibilityTime), null, null, Context.NONE); throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); } catch (FeignException e) { log.error("[getReceiptPaymentOption] GPD Error Response [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); - queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofMinutes(10L), null, null, Context.NONE); + queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofSeconds(queueSendInvisibilityTime), null, null, Context.NONE); throw new PartnerValidationException(PaaErrorEnum.PAA_SEMANTICA); + } catch (StorageException e) { + log.error("[getReceiptPaymentOption] Storage exception [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); + queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofSeconds(queueSendInvisibilityTime), null, null, Context.NONE); + throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); } catch (PartnerValidationException e) { throw e; } catch (Exception e) { @@ -884,12 +883,16 @@ private PaymentOptionModelResponse managePaSendRtRequest(PaSendRTV2Request reque receiptEntity); } catch (RetryableException e) { log.error("[getReceiptPaymentOption] GPD Not Reachable [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); - queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofMinutes(10L), null, null, Context.NONE); + queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofSeconds(queueSendInvisibilityTime), null, null, Context.NONE); throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); } catch (FeignException e) { log.error("[getReceiptPaymentOption] GPD Error Response [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); - queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofMinutes(10L), null, null, Context.NONE); + queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofSeconds(queueSendInvisibilityTime), null, null, Context.NONE); throw new PartnerValidationException(PaaErrorEnum.PAA_SEMANTICA); + } catch (StorageException e) { + log.error("[getReceiptPaymentOption] Storage exception [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); + queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofSeconds(queueSendInvisibilityTime), null, null, Context.NONE); + throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); } catch (PartnerValidationException e) { throw e; } catch (Exception e) { @@ -907,11 +910,11 @@ private ReceiptEntity getReceiptEntity( return receiptEntity; } - public PaymentOptionModelResponse getReceiptPaymentOption(String noticeNumber, + private PaymentOptionModelResponse getReceiptPaymentOption(String noticeNumber, String idPa, String creditorReferenceId, PaymentOptionModel body, - ReceiptEntity receiptEntity) throws FeignException, URISyntaxException, InvalidKeyException, StorageException{ + ReceiptEntity receiptEntity) throws FeignException, URISyntaxException, InvalidKeyException, StorageException { PaymentOptionModelResponse paymentOption = new PaymentOptionModelResponse(); try { paymentOption = gpdClient.receiptPaymentOption(idPa, noticeNumber, body); @@ -951,84 +954,11 @@ public PaymentOptionModelResponse getReceiptPaymentOption(String noticeNumber, return paymentOption; } - public void getAllFailuresQueue() { - XPathFactory xPathfactory = XPathFactory.newInstance(); - XPath xpath = xPathfactory.newXPath(); - try { - // The message is dequeued and locked for 30 seconds by default - List queueList = queueClient.receiveMessages(10, Duration.ofMinutes(5L), null, Context.NONE).stream().toList(); - for(QueueMessageItem message: queueList) { - if(checkQueueCountValidity(message)) { - handlingXml(getFailureQueue(message), xpath, message); - } else { - queueClient.deleteMessage(message.getMessageId(), message.getPopReceipt()); - } - } - } catch (XPathExpressionException e) { - - } - } - - public void handlingXml (String failureBody, XPath xpath, QueueMessageItem queueMessageItem) throws XPathExpressionException { - Document xmlDoc = getXMLDocument(failureBody); - XPathExpression xPathExpr = xpath.compile('/' + xmlDoc.getFirstChild().getNodeName()); - NodeList nodes = (NodeList) xPathExpr.evaluate(xmlDoc, XPathConstants.NODESET); - - Element node = (Element) nodes.item(0); - - String idPA = node.getElementsByTagName("idPA").item(0).getTextContent(); - String creditorReferenceId = node.getElementsByTagName("creditorReferenceId").item(0).getTextContent(); - String noticeNumber = node.getElementsByTagName("noticeNumber").item(0).getTextContent(); - String paymentDateTime = node.getElementsByTagName("paymentDateTime").item(0).getTextContent(); - String receiptId = node.getElementsByTagName("receiptId").item(0).getTextContent(); - String PSPCompanyName = node.getElementsByTagName("PSPCompanyName").item(0).getTextContent(); - String paymentMethod = node.getElementsByTagName("paymentMethod").item(0).getTextContent(); - String fee = node.getElementsByTagName("fee").item(0).getTextContent(); - String entityUniqueIdentifierValue = node.getElementsByTagName("entityUniqueIdentifierValue").item(0).getTextContent(); - - ReceiptEntity receiptEntity = new ReceiptEntity(idPA, creditorReferenceId); - receiptEntity.setDebtor(entityUniqueIdentifierValue); - String paymentDateTimeIdentifier = Optional.ofNullable(paymentDateTime).orElse(""); - receiptEntity.setPaymentDateTime(paymentDateTimeIdentifier); - receiptEntity.setDocument(failureBody); - - LocalDateTime localPaymentDateTime = paymentDateTime != null ? LocalDateTime.parse(paymentDateTime) : null; - PaymentOptionModel body = - PaymentOptionModel.builder() - .idReceipt(receiptId) - .paymentDate(localPaymentDateTime) - .pspCompany(PSPCompanyName) - .paymentMethod(paymentMethod) - .fee(String.valueOf(getFeeInCent(new BigDecimal(fee)))) - .build(); - - try { - getReceiptPaymentOption( - noticeNumber, - idPA, - creditorReferenceId, - body, - receiptEntity); - } catch (FeignException | URISyntaxException | InvalidKeyException | StorageException e) { - log.info("[paSendRT] Retry failed [fiscalCode={},noticeNumber={}]\",\n", idPA, noticeNumber); - queueClient.updateMessageWithResponse(queueMessageItem.getMessageId(), queueMessageItem.getPopReceipt(), receiptEntity.getDocument(), Duration.ofMinutes(10L), null, Context.NONE); - } - } - - public boolean checkQueueCountValidity(QueueMessageItem message) { - return message.getDequeueCount() <= 5; //TODO parametrize this variable - } - - public String getFailureQueue(QueueMessageItem queueMessageItem){ - return new String(queueMessageItem.getBody().toBytes(), StandardCharsets.UTF_8); - } - public Document getXMLDocument(String xmlString) { - try { - DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - InputSource is = new InputSource(new StringReader(xmlString)); - return builder.parse(is); - } catch (ParserConfigurationException | SAXException | IOException e) { - throw new AppException(AppError.UNKNOWN); - } + public PaymentOptionModelResponse getReceiptPaymentOptionScheduler(String noticeNumber, + String idPa, + String creditorReferenceId, + PaymentOptionModel body, + ReceiptEntity receiptEntity) throws FeignException, URISyntaxException, InvalidKeyException, StorageException { + return getReceiptPaymentOption(noticeNumber, idPa, creditorReferenceId, body, receiptEntity); } } diff --git a/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java index a803c48f..46d5fca8 100644 --- a/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java +++ b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java @@ -9,6 +9,8 @@ import it.gov.pagopa.payments.exception.AppError; import it.gov.pagopa.payments.exception.AppException; import it.gov.pagopa.payments.model.PaymentOptionModel; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -36,10 +38,19 @@ @Service @Slf4j +@NoArgsConstructor +@AllArgsConstructor public class SchedulerService { @Value(value = "${azure.queue.dequeue.limit}") private Integer dequeueLimit; + + @Value(value = "${azure.queue.receive.invisibilityTime}") + private Long queueReceiveInvisibilityTime; + + @Value(value = "${azure.queue.send.invisibilityTime}") + private Long queueUpdateInvisibilityTime; + @Autowired QueueClient queueClient; @@ -49,18 +60,23 @@ public class SchedulerService { public void getAllFailuresQueue() { XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath(); - try { - // The message is dequeued and locked for 30 seconds by default - List queueList = queueClient.receiveMessages(10, Duration.ofMinutes(5L), null, Context.NONE).stream().toList(); - for(QueueMessageItem message: queueList) { - if(checkQueueCountValidity(message)) { + // The message is dequeued and locked for a timeout equal to seconds + List queueList = queueClient.receiveMessages( + 10, + Duration.ofSeconds(queueReceiveInvisibilityTime), + null, + Context.NONE) + .stream().toList(); + for(QueueMessageItem message: queueList) { + if(checkQueueCountValidity(message)) { + try{ handlingXml(getFailureQueue(message), xpath, message); - } else { - queueClient.deleteMessage(message.getMessageId(), message.getPopReceipt()); + } catch (XPathExpressionException e) { + log.error("[paSendRT] XML error during retry process [messageId={},popReceipt={}]\",\n", message.getMessageId() , message.getPopReceipt()); } + } else { + queueClient.deleteMessage(message.getMessageId(), message.getPopReceipt()); } - } catch (XPathExpressionException e) { - } } @@ -98,15 +114,22 @@ public void handlingXml (String failureBody, XPath xpath, QueueMessageItem queue .build(); try { - partnerService.getReceiptPaymentOption( + partnerService.getReceiptPaymentOptionScheduler( noticeNumber, idPA, creditorReferenceId, body, receiptEntity); + queueClient.deleteMessage(queueMessageItem.getMessageId(), queueMessageItem.getPopReceipt()); } catch (FeignException | URISyntaxException | InvalidKeyException | StorageException e) { log.info("[paSendRT] Retry failed [fiscalCode={},noticeNumber={}]\",\n", idPA, noticeNumber); - queueClient.updateMessageWithResponse(queueMessageItem.getMessageId(), queueMessageItem.getPopReceipt(), receiptEntity.getDocument(), Duration.ofMinutes(10L), null, Context.NONE); + queueClient.updateMessageWithResponse( + queueMessageItem.getMessageId(), + queueMessageItem.getPopReceipt(), + receiptEntity.getDocument(), + Duration.ofSeconds(queueUpdateInvisibilityTime), + null, + Context.NONE); } } diff --git a/src/test/java/it/gov/pagopa/payments/mock/PaSendRTReqMock.java b/src/test/java/it/gov/pagopa/payments/mock/PaSendRTReqMock.java index 2767d88e..06b6050e 100644 --- a/src/test/java/it/gov/pagopa/payments/mock/PaSendRTReqMock.java +++ b/src/test/java/it/gov/pagopa/payments/mock/PaSendRTReqMock.java @@ -1,10 +1,7 @@ package it.gov.pagopa.payments.mock; -import it.gov.pagopa.payments.model.partner.CtReceipt; -import it.gov.pagopa.payments.model.partner.CtReceiptV2; -import it.gov.pagopa.payments.model.partner.PaSendRTReq; -import it.gov.pagopa.payments.model.partner.PaSendRTV2Request; -import it.gov.pagopa.payments.model.partner.StOutcome; +import it.gov.pagopa.payments.model.partner.*; + import java.math.BigDecimal; import java.time.LocalDateTime; import javax.xml.datatype.DatatypeConfigurationException; @@ -61,4 +58,33 @@ public static PaSendRTV2Request getMockV2(String iuv) throws DatatypeConfigurati return mock; } + + public static PaSendRTReq getMockDebtor(String iuv) throws DatatypeConfigurationException { + CtEntityUniqueIdentifier debtorCode = new CtEntityUniqueIdentifier(); + debtorCode.setEntityUniqueIdentifierValue("debtorCode"); + + CtSubject debtor = new CtSubject(); + debtor.setUniqueIdentifier(debtorCode); + + CtReceipt receipt = new CtReceipt(); + receipt.setReceiptId("c110729d258c4ab1b765fe902aae41d6"); + receipt.setNoticeNumber("3" + iuv); + receipt.setFiscalCode("77777777777"); + receipt.setOutcome(StOutcome.OK); + receipt.setCreditorReferenceId(iuv); + receipt.setPaymentMethod("creditCard"); + receipt.setPSPCompanyName("Intesa San Paolo"); + receipt.setFee(BigDecimal.valueOf(2)); + receipt.setDebtor(debtor); + receipt.setPaymentDateTime( + DatatypeFactory.newInstance().newXMLGregorianCalendar(LocalDateTime.now().toString())); + + PaSendRTReq mock = new PaSendRTReq(); + mock.setIdBrokerPA("77777777777"); + mock.setIdPA("77777777777"); + mock.setIdStation("77777777777_01"); + mock.setReceipt(receipt); + + return mock; + } } diff --git a/src/test/java/it/gov/pagopa/payments/service/PartnerServiceTest.java b/src/test/java/it/gov/pagopa/payments/service/PartnerServiceTest.java index 353490c9..b6c52fdd 100644 --- a/src/test/java/it/gov/pagopa/payments/service/PartnerServiceTest.java +++ b/src/test/java/it/gov/pagopa/payments/service/PartnerServiceTest.java @@ -5,8 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -38,6 +37,7 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; @@ -111,6 +111,9 @@ class PartnerServiceTest { ResourceLoader resourceLoader = new DefaultResourceLoader(); Resource resource = resourceLoader.getResource(genericService); + @Value(value = "${azure.queue.send.invisibilityTime}") + private Long queueSendInvisibilityTime; + private final ObjectFactory factoryUtil = new ObjectFactory(); @Autowired private CustomizedMapper customizedModelMapper; @@ -438,6 +441,7 @@ void paSendRTTest() throws DatatypeConfigurationException, IOException { spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -485,6 +489,7 @@ void paSendRTTestKOConflict() throws DatatypeConfigurationException, IOException spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -531,6 +536,7 @@ void paSendRTTestKOUnknown() throws DatatypeConfigurationException, IOException spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -578,6 +584,7 @@ void paSendRTTestKOStatus(String status, String iuv) throws DatatypeConfiguratio spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -627,6 +634,7 @@ void paSendRTTestKORetryableException() throws DatatypeConfigurationException, I spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -673,6 +681,7 @@ void paSendRTTestKOFeignException() throws DatatypeConfigurationException, IOExc spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -718,6 +727,7 @@ void paSendRTTestKO() throws DatatypeConfigurationException, IOException { spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -776,6 +786,7 @@ void paDemandPaymentNoticeTest() spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -826,6 +837,7 @@ void paDemandPaymentNoticeNotFoundTest() spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -860,6 +872,7 @@ void paGetPaymentV2Test() spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -924,6 +937,7 @@ void paGetPaymentV2_TransferTypePAGOPA_Test() spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -1007,6 +1021,7 @@ void paGetPaymentIncompleteAddressV2Test() spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -1118,6 +1133,7 @@ void paSendRTV2Test() throws DatatypeConfigurationException, IOException { spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -1165,6 +1181,7 @@ void paSendRTV2TestKOConflict() throws DatatypeConfigurationException, IOExcepti spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -1211,6 +1228,7 @@ void paSendRTV2TestKOUnknown() throws DatatypeConfigurationException, IOExceptio spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -1258,6 +1276,7 @@ void paSendRTV2TestKOStatus(String status, String iuv) throws DatatypeConfigurat spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -1307,6 +1326,7 @@ void paSendRTV2TestKORetryableException() throws DatatypeConfigurationException, spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -1353,6 +1373,7 @@ void paSendRTV2TestKOFeignException() throws DatatypeConfigurationException, IOE spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -1398,6 +1419,7 @@ void paSendRTV2TestKO() throws DatatypeConfigurationException, IOException { spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -1444,6 +1466,7 @@ void paSendRTV2ReceiptConflict() throws DatatypeConfigurationException, IOExcept spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -1491,6 +1514,7 @@ void paSendRTReceiptConflict() throws DatatypeConfigurationException, IOExceptio spy( new PartnerService( resource, + queueSendInvisibilityTime, factory, gpdClient, gpsClient, @@ -1531,12 +1555,60 @@ void paSendRTReceiptConflict() throws DatatypeConfigurationException, IOExceptio assertEquals("L'id del pagamento ricevuto e' duplicato", e.getMessage()); } - private TableClient tableClientConfiguration() { - return new TableClientBuilder() - .connectionString(storageConnectionString) - .tableName("receiptsTable") - .buildClient(); + @Test + void paSendRTQueueInsertTest() throws DatatypeConfigurationException, IOException { + + var pService = + spy( + new PartnerService( + resource, + queueSendInvisibilityTime, + factory, + gpdClient, + gpsClient, + tableClientConfiguration(), + queueClientConfiguration(), + customizedModelMapper)); + // Test preconditions + PaSendRTReq requestBody = PaSendRTReqMock.getMock("11111111112222225"); + + var e = Mockito.mock(FeignException.class); + when(gpdClient.receiptPaymentOption(anyString(), anyString(), any(PaymentOptionModel.class))) + .thenThrow(e); + + try { + CloudStorageAccount cloudStorageAccount = CloudStorageAccount.parse(storageConnectionString); + CloudTableClient cloudTableClient = cloudStorageAccount.createCloudTableClient(); + TableRequestOptions tableRequestOptions = new TableRequestOptions(); + tableRequestOptions.setRetryPolicyFactory(RetryNoRetry.getInstance()); + cloudTableClient.setDefaultRequestOptions(tableRequestOptions); + CloudTable table = cloudTableClient.getTableReference("receiptsTable"); + table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.createIfNotExists(); + } catch (Exception ex) { + log.info("Error during table creation", e); + } + + try { + // Test execution + assertEquals(0, queueClientConfiguration().receiveMessages(10).stream().toList().size()); + pService.paSendRT(requestBody); + fail(); + } catch (PartnerValidationException ex) { + // Test post condition + assertEquals(PaaErrorEnum.PAA_SEMANTICA, ex.getError()); + assertEquals(1, queueClientConfiguration().receiveMessages(10).stream().toList().size()); } + } + + private TableClient tableClientConfiguration() { + return new TableClientBuilder() + .connectionString(storageConnectionString) + .tableName("receiptsTable") + .buildClient(); + } private QueueClient queueClientConfiguration() { return new QueueClientBuilder() diff --git a/src/test/java/it/gov/pagopa/payments/service/SchedulerServiceTest.java b/src/test/java/it/gov/pagopa/payments/service/SchedulerServiceTest.java new file mode 100644 index 00000000..0e2bd6b1 --- /dev/null +++ b/src/test/java/it/gov/pagopa/payments/service/SchedulerServiceTest.java @@ -0,0 +1,309 @@ +package it.gov.pagopa.payments.service; + +import com.azure.core.util.Context; +import com.azure.data.tables.TableClient; +import com.azure.data.tables.TableClientBuilder; +import com.azure.storage.queue.QueueClient; +import com.azure.storage.queue.QueueClientBuilder; +import com.azure.storage.queue.models.QueueMessageItem; +import com.microsoft.azure.storage.CloudStorageAccount; +import com.microsoft.azure.storage.RetryNoRetry; +import com.microsoft.azure.storage.StorageException; +import com.microsoft.azure.storage.queue.CloudQueue; +import com.microsoft.azure.storage.queue.CloudQueueClient; +import com.microsoft.azure.storage.table.CloudTable; +import com.microsoft.azure.storage.table.CloudTableClient; +import com.microsoft.azure.storage.table.TableRequestOptions; +import feign.FeignException; +import it.gov.pagopa.payments.endpoints.validation.exceptions.PartnerValidationException; +import it.gov.pagopa.payments.entity.ReceiptEntity; +import it.gov.pagopa.payments.mock.*; +import it.gov.pagopa.payments.model.*; +import it.gov.pagopa.payments.model.partner.*; +import it.gov.pagopa.payments.utils.CustomizedMapper; +import lombok.extern.slf4j.Slf4j; +import org.junit.ClassRule; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import javax.xml.datatype.*; +import java.io.IOException; +import java.net.URISyntaxException; +import java.security.InvalidKeyException; +import java.time.Duration; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.testcontainers.shaded.org.awaitility.Awaitility.await; + +@Testcontainers +@ExtendWith(MockitoExtension.class) +@Slf4j +@SpringBootTest +class SchedulerServiceTest { + + @Autowired private SchedulerService schedulerService; + + @InjectMocks private PartnerService partnerService; + + @Mock private ObjectFactory factory; + + @Mock private GpdClient gpdClient; + + @Mock private GpsClient gpsClient; + + private String genericService = "/xsd/general-service.xsd"; + ResourceLoader resourceLoader = new DefaultResourceLoader(); + Resource resource = resourceLoader.getResource(genericService); + + @Value(value = "${azure.queue.send.invisibilityTime}") + private Long queueSendInvisibilityTime; + private final ObjectFactory factoryUtil = new ObjectFactory(); + + @Autowired private CustomizedMapper customizedModelMapper; + + @ClassRule @Container + public static GenericContainer azurite = + new GenericContainer<>( + DockerImageName.parse("mcr.microsoft.com/azure-storage/azurite:latest")) + .withExposedPorts(10001, 10002, 10000); + + String storageConnectionString = + String.format( + "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;TableEndpoint=http://%s:%s/devstoreaccount1;QueueEndpoint=http://%s:%s/devstoreaccount1;BlobEndpoint=http://%s:%s/devstoreaccount1", + azurite.getContainerIpAddress(), + azurite.getMappedPort(10002), + azurite.getContainerIpAddress(), + azurite.getMappedPort(10001), + azurite.getContainerIpAddress(), + azurite.getMappedPort(10000)); + + @Test + void paSendRTQueueReceiveTestOk() throws DatatypeConfigurationException, IOException, URISyntaxException, InvalidKeyException, StorageException { + + var pService = + spy( + new PartnerService( + resource, + queueSendInvisibilityTime, + factory, + gpdClient, + gpsClient, + tableClientConfiguration(), + queueClientConfiguration(), + customizedModelMapper)); + + var schedService = + spy( + new SchedulerService( + 5, + 1L, + 1L, + queueClientConfiguration(), + pService)); + + // Test preconditions + PaSendRTReq requestBody = PaSendRTReqMock.getMockDebtor("11111111112222225"); + + var e = Mockito.mock(FeignException.class); + when(gpdClient.receiptPaymentOption(anyString(), anyString(), nullable(PaymentOptionModel.class))) + .thenThrow(e); + doReturn(MockUtil.readModelFromFile("gpd/receiptPaymentOption.json", PaymentOptionModelResponse.class)) + .when(pService) + .getReceiptPaymentOptionScheduler(anyString(), anyString(), anyString(), any(PaymentOptionModel.class), any(ReceiptEntity.class)); + + try { + CloudStorageAccount cloudStorageAccount = CloudStorageAccount.parse(storageConnectionString); + CloudTableClient cloudTableClient = cloudStorageAccount.createCloudTableClient(); + TableRequestOptions tableRequestOptions = new TableRequestOptions(); + tableRequestOptions.setRetryPolicyFactory(RetryNoRetry.getInstance()); + cloudTableClient.setDefaultRequestOptions(tableRequestOptions); + CloudTable table = cloudTableClient.getTableReference("receiptsTable"); + table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.create(); + } catch (Exception ex) { + log.info("Error during table creation", e); + } + + try { + // Test execution + queueClientConfiguration().clearMessages(); + assertEquals(0, queueClientConfiguration().receiveMessages(10).stream().toList().size()); + pService.paSendRT(requestBody); + fail(); + } catch (PartnerValidationException ex) { + // Test post condition + assertEquals(1, queueClientConfiguration().peekMessages(10, null, Context.NONE).stream().toList().size()); + assertEquals(PaaErrorEnum.PAA_SEMANTICA, ex.getError()); + schedService.getAllFailuresQueue(); + await().pollDelay(Duration.ofSeconds(2L)).until(() -> true); + assertEquals(0, queueClientConfiguration().peekMessages(10, null, Context.NONE).stream().toList().size()); + } + } + + @Test + void paSendRTQueueReceiveTestKo() throws DatatypeConfigurationException, IOException, URISyntaxException, InvalidKeyException, StorageException { + + var pService = + spy( + new PartnerService( + resource, + queueSendInvisibilityTime, + factory, + gpdClient, + gpsClient, + tableClientConfiguration(), + queueClientConfiguration(), + customizedModelMapper)); + + var schedService = + spy( + new SchedulerService( + 5, + 1L, + 1L, + queueClientConfiguration(), + pService)); + + // Test preconditions + PaSendRTReq requestBody = PaSendRTReqMock.getMockDebtor("11111111112222225"); + + var e = Mockito.mock(FeignException.class); + when(gpdClient.receiptPaymentOption(anyString(), anyString(), nullable(PaymentOptionModel.class))) + .thenThrow(e); + doThrow(FeignException.class) + .when(pService) + .getReceiptPaymentOptionScheduler(anyString(), anyString(), anyString(), any(PaymentOptionModel.class), any(ReceiptEntity.class)); + + try { + CloudStorageAccount cloudStorageAccount = CloudStorageAccount.parse(storageConnectionString); + CloudTableClient cloudTableClient = cloudStorageAccount.createCloudTableClient(); + TableRequestOptions tableRequestOptions = new TableRequestOptions(); + tableRequestOptions.setRetryPolicyFactory(RetryNoRetry.getInstance()); + cloudTableClient.setDefaultRequestOptions(tableRequestOptions); + CloudTable table = cloudTableClient.getTableReference("receiptsTable"); + table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.create(); + } catch (Exception ex) { + log.info("Error during table creation", e); + } + + try { + // Test execution + queueClientConfiguration().clearMessages(); + assertEquals(0, queueClientConfiguration().receiveMessages(10).stream().toList().size()); + pService.paSendRT(requestBody); + fail(); + } catch (PartnerValidationException ex) { + // Test post condition + assertEquals(1, queueClientConfiguration().peekMessages(10, null, Context.NONE).stream().toList().size()); + assertEquals(PaaErrorEnum.PAA_SEMANTICA, ex.getError()); + schedService.getAllFailuresQueue(); + await().pollDelay(Duration.ofSeconds(2L)).until(() -> true); + assertEquals(1, queueClientConfiguration().peekMessages(10, null, Context.NONE).stream().toList().size()); + } + } + + @Test + void paSendRTQueueReceiveTestDequeueCountKo() throws DatatypeConfigurationException, IOException, URISyntaxException, InvalidKeyException, StorageException { + + var pService = + spy( + new PartnerService( + resource, + queueSendInvisibilityTime, + factory, + gpdClient, + gpsClient, + tableClientConfiguration(), + queueClientConfiguration(), + customizedModelMapper)); + + var schedService = + spy( + new SchedulerService( + 5, + 1L, + 1L, + queueClientConfiguration(), + pService)); + + // Test preconditions + PaSendRTReq requestBody = PaSendRTReqMock.getMockDebtor("11111111112222225"); + + var e = Mockito.mock(FeignException.class); + when(gpdClient.receiptPaymentOption(anyString(), anyString(), nullable(PaymentOptionModel.class))) + .thenThrow(e); + + try { + CloudStorageAccount cloudStorageAccount = CloudStorageAccount.parse(storageConnectionString); + CloudTableClient cloudTableClient = cloudStorageAccount.createCloudTableClient(); + TableRequestOptions tableRequestOptions = new TableRequestOptions(); + tableRequestOptions.setRetryPolicyFactory(RetryNoRetry.getInstance()); + cloudTableClient.setDefaultRequestOptions(tableRequestOptions); + CloudTable table = cloudTableClient.getTableReference("receiptsTable"); + table.createIfNotExists(); + CloudQueueClient cloudQueueClient = cloudStorageAccount.createCloudQueueClient(); + CloudQueue queue = cloudQueueClient.getQueueReference("testqueue"); + queue.create(); + } catch (Exception ex) { + log.info("Error during table creation", e); + } + + try { + // Test execution + queueClientConfiguration().clearMessages(); + assertEquals(0, queueClientConfiguration().receiveMessages(10).stream().toList().size()); + pService.paSendRT(requestBody); + fail(); + } catch (PartnerValidationException ex) { + // Test post condition + assertEquals(1, queueClientConfiguration().peekMessages(10, null, Context.NONE).stream().toList().size()); + assertEquals(PaaErrorEnum.PAA_SEMANTICA, ex.getError()); + for(int i = 0; i <= 4; i++) { + QueueMessageItem receptionMessage = queueClientConfiguration().receiveMessage(); + queueClientConfiguration().updateMessage( + receptionMessage.getMessageId(), + receptionMessage.getPopReceipt(), + "text", + null + ); + } + schedService.getAllFailuresQueue(); + assertEquals(0, queueClientConfiguration().peekMessages(10, null, Context.NONE).stream().toList().size()); + } + } + + private TableClient tableClientConfiguration() { + return new TableClientBuilder() + .connectionString(storageConnectionString) + .tableName("receiptsTable") + .buildClient(); + } + + private QueueClient queueClientConfiguration() { + return new QueueClientBuilder() + .connectionString(storageConnectionString) + .queueName("testqueue") + .buildClient(); + } +} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index e9dcf4e0..54bb5e3d 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -35,3 +35,10 @@ xsd.generic-service=payments/src/main/resources/xsd/general-service.xsd azure.queue.connection.string=DefaultEndpointsProtocol=http;AccountName=localhost;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==;QueueEndpoint=http://localhost:8902/; azure.queue.queueName=testqueue +azure.queue.dequeue.limit=5 +azure.queue.send.invisibilityTime=0 +azure.queue.receive.invisibilityTime=0 + +# cron configuration +cron.job.schedule.enabled=false +cron.job.schedule.expression.retry.status=*/35 * * * * * From d6c62244efa8476c43d099809ee1b8fb65209b5a Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Mon, 6 May 2024 16:19:36 +0200 Subject: [PATCH 07/24] PAGOPA-1695 solving some issues --- .../config/QueueClientConfiguration.java | 4 +- .../payments/service/PartnerService.java | 60 +++++++++---------- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/src/main/java/it/gov/pagopa/payments/config/QueueClientConfiguration.java b/src/main/java/it/gov/pagopa/payments/config/QueueClientConfiguration.java index eba93462..be234179 100644 --- a/src/main/java/it/gov/pagopa/payments/config/QueueClientConfiguration.java +++ b/src/main/java/it/gov/pagopa/payments/config/QueueClientConfiguration.java @@ -14,12 +14,12 @@ public class QueueClientConfiguration { private static String CONNECTION_STRING; @Value("${azure.queue.connection.string}") - public void setConnectionStringStatic(String connectionString) { + public static void setConnectionStringStatic(String connectionString) { QueueClientConfiguration.CONNECTION_STRING = connectionString; } @Value("${azure.queue.queueName}") - public void setTableNameStatic(String queueName) { + public static void setTableNameStatic(String queueName) { QueueClientConfiguration.QUEUE_NAME = queueName; } diff --git a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java index 198eab5e..0c1ce3e5 100644 --- a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java +++ b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java @@ -803,31 +803,13 @@ private PaymentOptionModelResponse managePaSendRtRequest(PaSendRTReq request) { .paymentMethod(request.getReceipt().getPaymentMethod()) .fee(String.valueOf(this.getFeeInCent(request.getReceipt().getFee()))) .build(); - try { - return this.getReceiptPaymentOption( - request.getReceipt().getNoticeNumber(), - request.getIdPA(), - request.getReceipt().getCreditorReferenceId(), - body, - receiptEntity); - } catch (RetryableException e) { - log.error("[getReceiptPaymentOption] GPD Not Reachable [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); - queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofSeconds(queueSendInvisibilityTime), null, null, Context.NONE); - throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); - } catch (FeignException e) { - log.error("[getReceiptPaymentOption] GPD Error Response [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); - queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofSeconds(queueSendInvisibilityTime), null, null, Context.NONE); - throw new PartnerValidationException(PaaErrorEnum.PAA_SEMANTICA); - } catch (StorageException e) { - log.error("[getReceiptPaymentOption] Storage exception [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); - queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofSeconds(queueSendInvisibilityTime), null, null, Context.NONE); - throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); - } catch (PartnerValidationException e) { - throw e; - } catch (Exception e) { - log.error("[getReceiptPaymentOption] GPD Generic Error [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); - throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); - } + + return this.getReceiptExceptionHandling( + request.getReceipt().getNoticeNumber(), + request.getIdPA(), + request.getReceipt().getCreditorReferenceId(), + body, + receiptEntity); } private PaymentOptionModelResponse managePaSendRtRequest(PaSendRTV2Request request) { @@ -874,29 +856,43 @@ private PaymentOptionModelResponse managePaSendRtRequest(PaSendRTV2Request reque .paymentMethod(request.getReceipt().getPaymentMethod()) .fee(String.valueOf(this.getFeeInCent(request.getReceipt().getFee()))) .build(); + + return this.getReceiptExceptionHandling( + request.getReceipt().getNoticeNumber(), + request.getIdPA(), + request.getReceipt().getCreditorReferenceId(), + body, + receiptEntity); + } + + private PaymentOptionModelResponse getReceiptExceptionHandling(String noticeNumber, + String idPa, + String creditorReferenceId, + PaymentOptionModel body, + ReceiptEntity receiptEntity ) { try { return this.getReceiptPaymentOption( - request.getReceipt().getNoticeNumber(), - request.getIdPA(), - request.getReceipt().getCreditorReferenceId(), + noticeNumber, + idPa, + creditorReferenceId, body, receiptEntity); } catch (RetryableException e) { - log.error("[getReceiptPaymentOption] GPD Not Reachable [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); + log.error("[getReceiptPaymentOption] GPD Not Reachable [noticeNumber={}]", noticeNumber, e); queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofSeconds(queueSendInvisibilityTime), null, null, Context.NONE); throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); } catch (FeignException e) { - log.error("[getReceiptPaymentOption] GPD Error Response [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); + log.error("[getReceiptPaymentOption] GPD Error Response [noticeNumber={}]", noticeNumber, e); queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofSeconds(queueSendInvisibilityTime), null, null, Context.NONE); throw new PartnerValidationException(PaaErrorEnum.PAA_SEMANTICA); } catch (StorageException e) { - log.error("[getReceiptPaymentOption] Storage exception [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); + log.error("[getReceiptPaymentOption] Storage exception [noticeNumber={}]", noticeNumber, e); queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofSeconds(queueSendInvisibilityTime), null, null, Context.NONE); throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); } catch (PartnerValidationException e) { throw e; } catch (Exception e) { - log.error("[getReceiptPaymentOption] GPD Generic Error [noticeNumber={}]", request.getReceipt().getNoticeNumber(), e); + log.error("[getReceiptPaymentOption] GPD Generic Error [noticeNumber={}]", noticeNumber, e); throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); } } From 429d5d19dcee2ef66793ea338cd3f3d83e924102 Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Mon, 6 May 2024 16:25:05 +0200 Subject: [PATCH 08/24] PAGOPA-1695 fixing junit --- .../gov/pagopa/payments/config/QueueClientConfiguration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/it/gov/pagopa/payments/config/QueueClientConfiguration.java b/src/main/java/it/gov/pagopa/payments/config/QueueClientConfiguration.java index be234179..eba93462 100644 --- a/src/main/java/it/gov/pagopa/payments/config/QueueClientConfiguration.java +++ b/src/main/java/it/gov/pagopa/payments/config/QueueClientConfiguration.java @@ -14,12 +14,12 @@ public class QueueClientConfiguration { private static String CONNECTION_STRING; @Value("${azure.queue.connection.string}") - public static void setConnectionStringStatic(String connectionString) { + public void setConnectionStringStatic(String connectionString) { QueueClientConfiguration.CONNECTION_STRING = connectionString; } @Value("${azure.queue.queueName}") - public static void setTableNameStatic(String queueName) { + public void setTableNameStatic(String queueName) { QueueClientConfiguration.QUEUE_NAME = queueName; } From 7dbd3f4005177d46ce56a7b3b8f4515bd0d066c2 Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Tue, 7 May 2024 10:02:31 +0200 Subject: [PATCH 09/24] PAGOPA-1695 adding xml validation --- .../it/gov/pagopa/payments/service/SchedulerService.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java index 46d5fca8..66714dad 100644 --- a/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java +++ b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java @@ -21,6 +21,7 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; +import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -142,7 +143,10 @@ public String getFailureQueue(QueueMessageItem queueMessageItem){ } public Document getXMLDocument(String xmlString) { try { - DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + DocumentBuilderFactory x = DocumentBuilderFactory.newInstance(); + x.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + x.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + DocumentBuilder builder = x.newDocumentBuilder(); InputSource is = new InputSource(new StringReader(xmlString)); return builder.parse(is); } catch (ParserConfigurationException | SAXException | IOException e) { From 91079473632c88b2bbee0458d310ed0b55f2f357 Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Tue, 7 May 2024 10:21:22 +0200 Subject: [PATCH 10/24] PAGOPA-1695 testing security --- .../it/gov/pagopa/payments/service/SchedulerService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java index 66714dad..50a90fd7 100644 --- a/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java +++ b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java @@ -143,10 +143,10 @@ public String getFailureQueue(QueueMessageItem queueMessageItem){ } public Document getXMLDocument(String xmlString) { try { - DocumentBuilderFactory x = DocumentBuilderFactory.newInstance(); - x.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - x.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); - DocumentBuilder builder = x.newDocumentBuilder(); + DocumentBuilderFactory factory = DocumentBuilderFactory.newDefaultNSInstance(); + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + DocumentBuilder builder = factory.newDocumentBuilder(); InputSource is = new InputSource(new StringReader(xmlString)); return builder.parse(is); } catch (ParserConfigurationException | SAXException | IOException e) { From 1e178fa0bf0bb3f460b428d9867c42622966e9a0 Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Tue, 7 May 2024 11:43:30 +0200 Subject: [PATCH 11/24] PAGOPA-1695 changing documentbuilderfactory --- .../java/it/gov/pagopa/payments/service/SchedulerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java index 50a90fd7..7a0dffee 100644 --- a/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java +++ b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java @@ -143,7 +143,7 @@ public String getFailureQueue(QueueMessageItem queueMessageItem){ } public Document getXMLDocument(String xmlString) { try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newDefaultNSInstance(); + DocumentBuilderFactory factory = DocumentBuilderFactory.newDefaultInstance(); factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); DocumentBuilder builder = factory.newDocumentBuilder(); From 2818c440b988278e880faa7e3026d0b4baeeb801 Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Tue, 7 May 2024 12:26:31 +0200 Subject: [PATCH 12/24] PAGOPA-1695 adding values for kubernetes --- helm/values-dev.yaml | 7 +++++++ helm/values-prod.yaml | 7 +++++++ helm/values-uat.yaml | 7 +++++++ .../it/gov/pagopa/payments/scheduler/Scheduler.java | 2 +- src/main/resources/application.properties | 11 +++++++++++ 5 files changed, 33 insertions(+), 1 deletion(-) diff --git a/helm/values-dev.yaml b/helm/values-dev.yaml index 7114f62f..219cf606 100644 --- a/helm/values-dev.yaml +++ b/helm/values-dev.yaml @@ -75,6 +75,12 @@ microservice-chart: API_CONFIG_HOST: "https://api.dev.platform.pagopa.it/apiconfig/auth/api/v1" GPS_HOST: "https://api.dev.platform.pagopa.it/gps/spontaneous-payments-service/v1" GPD_HOST: "https://api.dev.platform.pagopa.it/gpd/api/v1" + QUEUE_NAME: "gpd-receipt-poison-queue" + DEQUEUE_LIMIT: "5" + QUEUE_SEND_INVISIBILITY_TIME: "120" + QUEUE_RECEIVE_INVISIBILITY_TIME: "300" + CRON_JOB_SCHEDULE_RETRY_ENABLED: "false" + CRON_JOB_SCHEDULE_HISTORY_TRIGGER: "0 0 13 * * *" envSecret: # required APPLICATIONINSIGHTS_CONNECTION_STRING: 'ai-d-connection-string' @@ -82,6 +88,7 @@ microservice-chart: GPD_SUBSCRIPTION_KEY: "gpd-d-gpd-subscription-key" GPS_SUBSCRIPTION_KEY: "gpd-d-gps-subscription-key" AZURE_TABLES_CONNECTION_STRING: "gpd-payments-d-cosmos-connection-string" + QUEUE_CONNECTION_STRING: "gpd-payments-d-queue-connection-string" keyvault: name: "pagopa-d-gps-kv" tenantId: "7788edaf-0346-4068-9d79-c868aed15b3d" diff --git a/helm/values-prod.yaml b/helm/values-prod.yaml index 82823dce..046a09f5 100644 --- a/helm/values-prod.yaml +++ b/helm/values-prod.yaml @@ -75,6 +75,12 @@ microservice-chart: API_CONFIG_HOST: "https://api.platform.pagopa.it/apiconfig/auth/api/v1" GPS_HOST: "https://api.platform.pagopa.it/gps/spontaneous-payments-service/v1" GPD_HOST: "https://api.platform.pagopa.it/gpd/api/v1" + QUEUE_NAME: "gpd-receipt-poison-queue" + DEQUEUE_LIMIT: "5" + QUEUE_SEND_INVISIBILITY_TIME: "120" + QUEUE_RECEIVE_INVISIBILITY_TIME: "300" + CRON_JOB_SCHEDULE_RETRY_ENABLED: "false" + CRON_JOB_SCHEDULE_HISTORY_TRIGGER: "0 0 13 * * *" envSecret: # required APPLICATIONINSIGHTS_CONNECTION_STRING: 'ai-p-connection-string' @@ -82,6 +88,7 @@ microservice-chart: GPD_SUBSCRIPTION_KEY: "gpd-p-gpd-subscription-key" GPS_SUBSCRIPTION_KEY: "gpd-p-gps-subscription-key" AZURE_TABLES_CONNECTION_STRING: "gpd-payments-p-cosmos-connection-string" + QUEUE_CONNECTION_STRING: "gpd-payments-p-queue-connection-string" keyvault: name: "pagopa-p-gps-kv" tenantId: "7788edaf-0346-4068-9d79-c868aed15b3d" diff --git a/helm/values-uat.yaml b/helm/values-uat.yaml index e5c46c43..e6204d0d 100644 --- a/helm/values-uat.yaml +++ b/helm/values-uat.yaml @@ -75,6 +75,12 @@ microservice-chart: API_CONFIG_HOST: "https://api.uat.platform.pagopa.it/apiconfig/auth/api/v1" GPS_HOST: "https://api.uat.platform.pagopa.it/gps/spontaneous-payments-service/v1" GPD_HOST: "https://api.uat.platform.pagopa.it/gpd/api/v1" + QUEUE_NAME: "gpd-receipt-poison-queue" + DEQUEUE_LIMIT: "5" + QUEUE_SEND_INVISIBILITY_TIME: "120" + QUEUE_RECEIVE_INVISIBILITY_TIME: "300" + CRON_JOB_SCHEDULE_RETRY_ENABLED: "false" + CRON_JOB_SCHEDULE_HISTORY_TRIGGER: "0 0 13 * * *" envSecret: # required APPLICATIONINSIGHTS_CONNECTION_STRING: 'ai-u-connection-string' @@ -82,6 +88,7 @@ microservice-chart: GPD_SUBSCRIPTION_KEY: "gpd-u-gpd-subscription-key" GPS_SUBSCRIPTION_KEY: "gpd-u-gps-subscription-key" AZURE_TABLES_CONNECTION_STRING: "gpd-payments-u-cosmos-connection-string" + QUEUE_CONNECTION_STRING: "gpd-payments-u-queue-connection-string" keyvault: name: "pagopa-u-gps-kv" tenantId: "7788edaf-0346-4068-9d79-c868aed15b3d" diff --git a/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java b/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java index d70d2782..4e6e9d9c 100644 --- a/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java +++ b/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java @@ -24,7 +24,7 @@ public class Scheduler { @Autowired SchedulerService schedulerService; - @Scheduled(cron = "${cron.job.schedule.expression.retry.status}") + @Scheduled(cron = "${cron.job.schedule.expression.retry.trigger}") @Async @Transactional public void changeDebtPositionStatusToValid() { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 634077ee..3fabcd75 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -45,3 +45,14 @@ spring.cache.type=caffeine spring.cache.caffeine.spec=maximumSize=${CACHE_SIZE}, expireAfterAccess=${CACHE_EXPIRATION_TIME} xsd.generic-service=classpath:/xsd/general-service.xsd + +# queue configuration +azure.queue.connection.string=${QUEUE_CONNECTION_STRING} +azure.queue.queueName=${QUEUE_NAME} +azure.queue.dequeue.limit=${DEQUEUE_LIMIT} +azure.queue.send.invisibilityTime=${QUEUE_SEND_INVISIBILITY_TIME} +azure.queue.receive.invisibilityTime=${QUEUE_RECEIVE_INVISIBILITY_TIME} + +# cron configuration +cron.job.schedule.retry.enabled=${CRON_JOB_SCHEDULE_RETRY_ENABLED} +cron.job.schedule.expression.retry.status=${CRON_JOB_SCHEDULE_HISTORY_TRIGGER} From 1560c355f9d4b91bf0f541cb3b6ffc6ed6aa80f5 Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Tue, 7 May 2024 12:56:08 +0200 Subject: [PATCH 13/24] PAGOPA-1695 update local properties file --- src/main/resources/application-local.properties | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/resources/application-local.properties b/src/main/resources/application-local.properties index f6c5b80f..f904a89d 100644 --- a/src/main/resources/application-local.properties +++ b/src/main/resources/application-local.properties @@ -32,4 +32,14 @@ retry.maxDelay=500 retry.multiplier=2 # cache configuration -spring.cache.caffeine.spec=maximumSize=100, expireAfterAccess=10s \ No newline at end of file +spring.cache.caffeine.spec=maximumSize=100, expireAfterAccess=10s + +azure.queue.connection.string=${QUEUE_CONNECTION_STRING} +azure.queue.queueName=testqueue +azure.queue.dequeue.limit=5 +azure.queue.send.invisibilityTime=0 +azure.queue.receive.invisibilityTime=0 + +# cron configuration +cron.job.schedule.enabled=false +cron.job.schedule.expression.retry.status=*/35 * * * * * \ No newline at end of file From 0f2e5dba1b0166848745609cac7848644f7321d4 Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Tue, 7 May 2024 14:23:12 +0200 Subject: [PATCH 14/24] PAGOPA-1695 update helm value --- helm/values-dev.yaml | 4 ++-- helm/values-prod.yaml | 4 ++-- helm/values-uat.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/helm/values-dev.yaml b/helm/values-dev.yaml index a623ca59..ede26422 100644 --- a/helm/values-dev.yaml +++ b/helm/values-dev.yaml @@ -81,8 +81,8 @@ microservice-chart: DEQUEUE_LIMIT: "5" QUEUE_SEND_INVISIBILITY_TIME: "120" QUEUE_RECEIVE_INVISIBILITY_TIME: "300" - CRON_JOB_SCHEDULE_RETRY_ENABLED: "false" - CRON_JOB_SCHEDULE_HISTORY_TRIGGER: "0 0 13 * * *" + CRON_JOB_SCHEDULE_RETRY_ENABLED: "true" + CRON_JOB_SCHEDULE_RETRY_TRIGGER: "0 0 13 * * *" envSecret: # required APPLICATIONINSIGHTS_CONNECTION_STRING: 'ai-d-connection-string' diff --git a/helm/values-prod.yaml b/helm/values-prod.yaml index 68a1e7f0..9117b1cf 100644 --- a/helm/values-prod.yaml +++ b/helm/values-prod.yaml @@ -81,8 +81,8 @@ microservice-chart: DEQUEUE_LIMIT: "5" QUEUE_SEND_INVISIBILITY_TIME: "120" QUEUE_RECEIVE_INVISIBILITY_TIME: "300" - CRON_JOB_SCHEDULE_RETRY_ENABLED: "false" - CRON_JOB_SCHEDULE_HISTORY_TRIGGER: "0 0 13 * * *" + CRON_JOB_SCHEDULE_RETRY_ENABLED: "true" + CRON_JOB_SCHEDULE_RETRY_TRIGGER: "0 0 13 * * *" envSecret: # required APPLICATIONINSIGHTS_CONNECTION_STRING: 'ai-p-connection-string' diff --git a/helm/values-uat.yaml b/helm/values-uat.yaml index bf61090a..b82a438e 100644 --- a/helm/values-uat.yaml +++ b/helm/values-uat.yaml @@ -82,7 +82,7 @@ microservice-chart: QUEUE_SEND_INVISIBILITY_TIME: "120" QUEUE_RECEIVE_INVISIBILITY_TIME: "300" CRON_JOB_SCHEDULE_RETRY_ENABLED: "false" - CRON_JOB_SCHEDULE_HISTORY_TRIGGER: "0 0 13 * * *" + CRON_JOB_SCHEDULE_RETRY_TRIGGER: "0 0 13 * * *" envSecret: # required APPLICATIONINSIGHTS_CONNECTION_STRING: 'ai-u-connection-string' From db4cb6b1d8ec132aa10b633dd9181e1b47f5299c Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Tue, 7 May 2024 14:26:12 +0200 Subject: [PATCH 15/24] PAGOPA-1695 update properties file --- src/main/resources/application-local.properties | 2 +- src/main/resources/application.properties | 2 +- src/test/resources/application.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/application-local.properties b/src/main/resources/application-local.properties index f904a89d..49f41dca 100644 --- a/src/main/resources/application-local.properties +++ b/src/main/resources/application-local.properties @@ -42,4 +42,4 @@ azure.queue.receive.invisibilityTime=0 # cron configuration cron.job.schedule.enabled=false -cron.job.schedule.expression.retry.status=*/35 * * * * * \ No newline at end of file +cron.job.schedule.expression.retry.trigger=*/35 * * * * * \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index d6374bb6..f1c24da2 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -59,4 +59,4 @@ azure.queue.receive.invisibilityTime=${QUEUE_RECEIVE_INVISIBILITY_TIME} # cron configuration cron.job.schedule.retry.enabled=${CRON_JOB_SCHEDULE_RETRY_ENABLED} -cron.job.schedule.expression.retry.status=${CRON_JOB_SCHEDULE_HISTORY_TRIGGER} +cron.job.schedule.expression.retry.trigger=${CRON_JOB_SCHEDULE_RETRY_TRIGGER} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index 54bb5e3d..5384fb34 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -41,4 +41,4 @@ azure.queue.receive.invisibilityTime=0 # cron configuration cron.job.schedule.enabled=false -cron.job.schedule.expression.retry.status=*/35 * * * * * +cron.job.schedule.expression.retry.trigger=*/35 * * * * * From 4f22564f22e39dc6b33aa27e3d81ed06bc69feae Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Tue, 7 May 2024 14:28:29 +0200 Subject: [PATCH 16/24] PAGOPA-1695 minor fix --- helm/values-uat.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm/values-uat.yaml b/helm/values-uat.yaml index b82a438e..277132f7 100644 --- a/helm/values-uat.yaml +++ b/helm/values-uat.yaml @@ -81,7 +81,7 @@ microservice-chart: DEQUEUE_LIMIT: "5" QUEUE_SEND_INVISIBILITY_TIME: "120" QUEUE_RECEIVE_INVISIBILITY_TIME: "300" - CRON_JOB_SCHEDULE_RETRY_ENABLED: "false" + CRON_JOB_SCHEDULE_RETRY_ENABLED: "true" CRON_JOB_SCHEDULE_RETRY_TRIGGER: "0 0 13 * * *" envSecret: # required From 384034b5123cabb2919bc70b6812038afe88e3ad Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Tue, 7 May 2024 14:34:44 +0200 Subject: [PATCH 17/24] PAGOPA-1695 minor fix --- src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java b/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java index 4e6e9d9c..5779f8ab 100644 --- a/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java +++ b/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java @@ -26,8 +26,7 @@ public class Scheduler { @Scheduled(cron = "${cron.job.schedule.expression.retry.trigger}") @Async - @Transactional - public void changeDebtPositionStatusToValid() { + public void retryPaSendRT() { log.info(String.format(LOG_BASE_HEADER_INFO, CRON_JOB, "retry sendRT", "Running at " + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()))); schedulerService.getAllFailuresQueue(); this.threadOfExecution = Thread.currentThread(); From 1dccd1fd6774af551fac054cc77cf9beb16e4b65 Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Tue, 7 May 2024 14:36:08 +0200 Subject: [PATCH 18/24] PAGOPA-1695 minor fix --- .../java/it/gov/pagopa/payments/scheduler/Scheduler.java | 3 +-- .../it/gov/pagopa/payments/service/SchedulerService.java | 2 +- .../gov/pagopa/payments/service/SchedulerServiceTest.java | 6 +++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java b/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java index 5779f8ab..cfa77db8 100644 --- a/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java +++ b/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java @@ -7,7 +7,6 @@ import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -28,7 +27,7 @@ public class Scheduler { @Async public void retryPaSendRT() { log.info(String.format(LOG_BASE_HEADER_INFO, CRON_JOB, "retry sendRT", "Running at " + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()))); - schedulerService.getAllFailuresQueue(); + schedulerService.retryFailedPaSendRT(); this.threadOfExecution = Thread.currentThread(); } } diff --git a/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java index 7a0dffee..f5125122 100644 --- a/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java +++ b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java @@ -58,7 +58,7 @@ public class SchedulerService { @Autowired PartnerService partnerService; - public void getAllFailuresQueue() { + public void retryFailedPaSendRT() { XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath(); // The message is dequeued and locked for a timeout equal to seconds diff --git a/src/test/java/it/gov/pagopa/payments/service/SchedulerServiceTest.java b/src/test/java/it/gov/pagopa/payments/service/SchedulerServiceTest.java index 0e2bd6b1..81f1ba9c 100644 --- a/src/test/java/it/gov/pagopa/payments/service/SchedulerServiceTest.java +++ b/src/test/java/it/gov/pagopa/payments/service/SchedulerServiceTest.java @@ -152,7 +152,7 @@ void paSendRTQueueReceiveTestOk() throws DatatypeConfigurationException, IOExcep // Test post condition assertEquals(1, queueClientConfiguration().peekMessages(10, null, Context.NONE).stream().toList().size()); assertEquals(PaaErrorEnum.PAA_SEMANTICA, ex.getError()); - schedService.getAllFailuresQueue(); + schedService.retryFailedPaSendRT(); await().pollDelay(Duration.ofSeconds(2L)).until(() -> true); assertEquals(0, queueClientConfiguration().peekMessages(10, null, Context.NONE).stream().toList().size()); } @@ -217,7 +217,7 @@ void paSendRTQueueReceiveTestKo() throws DatatypeConfigurationException, IOExcep // Test post condition assertEquals(1, queueClientConfiguration().peekMessages(10, null, Context.NONE).stream().toList().size()); assertEquals(PaaErrorEnum.PAA_SEMANTICA, ex.getError()); - schedService.getAllFailuresQueue(); + schedService.retryFailedPaSendRT(); await().pollDelay(Duration.ofSeconds(2L)).until(() -> true); assertEquals(1, queueClientConfiguration().peekMessages(10, null, Context.NONE).stream().toList().size()); } @@ -288,7 +288,7 @@ void paSendRTQueueReceiveTestDequeueCountKo() throws DatatypeConfigurationExcept null ); } - schedService.getAllFailuresQueue(); + schedService.retryFailedPaSendRT(); assertEquals(0, queueClientConfiguration().peekMessages(10, null, Context.NONE).stream().toList().size()); } } From d42ef655fe53e5ed64a5f7c1367ca0383d5ca41a Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Tue, 7 May 2024 16:31:59 +0200 Subject: [PATCH 19/24] PAGOPA-1695 fixing comments --- helm/values-dev.yaml | 2 +- helm/values-prod.yaml | 2 +- helm/values-uat.yaml | 2 +- .../it/gov/pagopa/payments/service/SchedulerService.java | 9 +++++++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/helm/values-dev.yaml b/helm/values-dev.yaml index ede26422..872fff9c 100644 --- a/helm/values-dev.yaml +++ b/helm/values-dev.yaml @@ -82,7 +82,7 @@ microservice-chart: QUEUE_SEND_INVISIBILITY_TIME: "120" QUEUE_RECEIVE_INVISIBILITY_TIME: "300" CRON_JOB_SCHEDULE_RETRY_ENABLED: "true" - CRON_JOB_SCHEDULE_RETRY_TRIGGER: "0 0 13 * * *" + CRON_JOB_SCHEDULE_RETRY_TRIGGER: "0 0 0,6,12,18 * * *" envSecret: # required APPLICATIONINSIGHTS_CONNECTION_STRING: 'ai-d-connection-string' diff --git a/helm/values-prod.yaml b/helm/values-prod.yaml index 9117b1cf..75081871 100644 --- a/helm/values-prod.yaml +++ b/helm/values-prod.yaml @@ -82,7 +82,7 @@ microservice-chart: QUEUE_SEND_INVISIBILITY_TIME: "120" QUEUE_RECEIVE_INVISIBILITY_TIME: "300" CRON_JOB_SCHEDULE_RETRY_ENABLED: "true" - CRON_JOB_SCHEDULE_RETRY_TRIGGER: "0 0 13 * * *" + CRON_JOB_SCHEDULE_RETRY_TRIGGER: "0 0 0,6,12,18 * * *" envSecret: # required APPLICATIONINSIGHTS_CONNECTION_STRING: 'ai-p-connection-string' diff --git a/helm/values-uat.yaml b/helm/values-uat.yaml index 277132f7..2d4f964b 100644 --- a/helm/values-uat.yaml +++ b/helm/values-uat.yaml @@ -82,7 +82,7 @@ microservice-chart: QUEUE_SEND_INVISIBILITY_TIME: "120" QUEUE_RECEIVE_INVISIBILITY_TIME: "300" CRON_JOB_SCHEDULE_RETRY_ENABLED: "true" - CRON_JOB_SCHEDULE_RETRY_TRIGGER: "0 0 13 * * *" + CRON_JOB_SCHEDULE_RETRY_TRIGGER: "0 0 0,6,12,18 * * *" envSecret: # required APPLICATIONINSIGHTS_CONNECTION_STRING: 'ai-u-connection-string' diff --git a/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java index f5125122..d7816896 100644 --- a/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java +++ b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java @@ -71,7 +71,7 @@ public void retryFailedPaSendRT() { for(QueueMessageItem message: queueList) { if(checkQueueCountValidity(message)) { try{ - handlingXml(getFailureQueue(message), xpath, message); + handlingXml(getMessageContent(message), xpath, message); } catch (XPathExpressionException e) { log.error("[paSendRT] XML error during retry process [messageId={},popReceipt={}]\",\n", message.getMessageId() , message.getPopReceipt()); } @@ -81,6 +81,10 @@ public void retryFailedPaSendRT() { } } + /* + This method is used to parse the xml string and extract the necessary values to create a payment option body. + After this, the payment process is triggered with the newly created body. + */ public void handlingXml (String failureBody, XPath xpath, QueueMessageItem queueMessageItem) throws XPathExpressionException { Document xmlDoc = getXMLDocument(failureBody); XPathExpression xPathExpr = xpath.compile('/' + xmlDoc.getFirstChild().getNodeName()); @@ -138,9 +142,10 @@ public boolean checkQueueCountValidity(QueueMessageItem message) { return message.getDequeueCount() <= dequeueLimit; } - public String getFailureQueue(QueueMessageItem queueMessageItem){ + public String getMessageContent(QueueMessageItem queueMessageItem){ return new String(queueMessageItem.getBody().toBytes(), StandardCharsets.UTF_8); } + public Document getXMLDocument(String xmlString) { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newDefaultInstance(); From 7740bcd0c0350797dc459ff29869705f9a9004e1 Mon Sep 17 00:00:00 2001 From: AngeloCaporaso Date: Wed, 8 May 2024 14:07:34 +0200 Subject: [PATCH 20/24] [PAGOPA-1695] chore: Update log --- .../pagopa/payments/service/PartnerService.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java index 0c1ce3e5..dda9d552 100644 --- a/src/main/java/it/gov/pagopa/payments/service/PartnerService.java +++ b/src/main/java/it/gov/pagopa/payments/service/PartnerService.java @@ -878,21 +878,23 @@ private PaymentOptionModelResponse getReceiptExceptionHandling(String noticeNumb body, receiptEntity); } catch (RetryableException e) { - log.error("[getReceiptPaymentOption] GPD Not Reachable [noticeNumber={}]", noticeNumber, e); + log.error("[getReceiptPaymentOption] PAA_SYSTEM_ERROR: GPD Not Reachable [noticeNumber={}]", noticeNumber, e); queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofSeconds(queueSendInvisibilityTime), null, null, Context.NONE); throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); } catch (FeignException e) { - log.error("[getReceiptPaymentOption] GPD Error Response [noticeNumber={}]", noticeNumber, e); + log.error("[getReceiptPaymentOption] PAA_SEMANTICA: GPD Error Response [noticeNumber={}]", noticeNumber, e); queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofSeconds(queueSendInvisibilityTime), null, null, Context.NONE); throw new PartnerValidationException(PaaErrorEnum.PAA_SEMANTICA); } catch (StorageException e) { - log.error("[getReceiptPaymentOption] Storage exception [noticeNumber={}]", noticeNumber, e); + log.error("[getReceiptPaymentOption] PAA_SYSTEM_ERROR: Storage exception [noticeNumber={}]", noticeNumber, e); queueClient.sendMessageWithResponse(receiptEntity.getDocument(), Duration.ofSeconds(queueSendInvisibilityTime), null, null, Context.NONE); throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); } catch (PartnerValidationException e) { + // { PAA_RECEIPT_DUPLICATA, PAA_PAGAMENTO_SCONOSCIUTO } throw e; } catch (Exception e) { - log.error("[getReceiptPaymentOption] GPD Generic Error [noticeNumber={}]", noticeNumber, e); + // no retry because the long-term retry is enabled only when there is a gpd-core error response or a storage communication failure + log.error("[getReceiptPaymentOption] PAA_SYSTEM_ERROR: GPD Generic Error [noticeNumber={}]", noticeNumber, e); throw new PartnerValidationException(PaaErrorEnum.PAA_SYSTEM_ERROR); } } @@ -922,7 +924,7 @@ private PaymentOptionModelResponse getReceiptPaymentOption(String noticeNumber, // if PO is already paid on GPD --> checks and in case creates the receipt in PAID status try { log.error( - "[getReceiptPaymentOption] GPD Conflict Error Response [noticeNumber={}]", + "[getReceiptPaymentOption] PAA_RECEIPT_DUPLICATA: GPD Conflict Error Response [noticeNumber={}]", noticeNumber, e); ReceiptEntity receiptEntityToCreate = this.getReceipt(idPa, creditorReferenceId); @@ -940,7 +942,7 @@ private PaymentOptionModelResponse getReceiptPaymentOption(String noticeNumber, throw new PartnerValidationException(PaaErrorEnum.PAA_RECEIPT_DUPLICATA); } catch (FeignException.NotFound e) { log.error( - "[getReceiptPaymentOption] GPD Not Found Error Response [noticeNumber={}]", + "[getReceiptPaymentOption] PAA_PAGAMENTO_SCONOSCIUTO: GPD Not Found Error Response [noticeNumber={}]", noticeNumber, e); throw new PartnerValidationException(PaaErrorEnum.PAA_PAGAMENTO_SCONOSCIUTO); From 8c59dbcfd9a29ed7b874dfc015b0d8e4f76ed220 Mon Sep 17 00:00:00 2001 From: pagopa-github-bot Date: Wed, 8 May 2024 12:09:46 +0000 Subject: [PATCH 21/24] Bump to version 0.12.21-1-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability [skip ci] --- helm/Chart.yaml | 4 +- helm/values-dev.yaml | 2 +- helm/values-prod.yaml | 2 +- helm/values-uat.yaml | 2 +- openapi/openapi.json | 813 ++++++++++++++++++++++-------------------- pom.xml | 2 +- 6 files changed, 432 insertions(+), 393 deletions(-) diff --git a/helm/Chart.yaml b/helm/Chart.yaml index bddb3380..e18100a3 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: pagopa-gpd-payments description: Microservice that exposes API for payment receipts retrieving and other operations type: application -version: 0.93.0 -appVersion: 0.12.21 +version: 0.94.0 +appVersion: 0.12.21-1-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability dependencies: - name: microservice-chart version: 2.4.0 diff --git a/helm/values-dev.yaml b/helm/values-dev.yaml index 872fff9c..77cc5dbc 100644 --- a/helm/values-dev.yaml +++ b/helm/values-dev.yaml @@ -4,7 +4,7 @@ microservice-chart: fullnameOverride: "" image: repository: ghcr.io/pagopa/pagopa-gpd-payments - tag: "0.12.21" + tag: "0.12.21-1-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability" pullPolicy: Always livenessProbe: httpGet: diff --git a/helm/values-prod.yaml b/helm/values-prod.yaml index 75081871..5fadde4c 100644 --- a/helm/values-prod.yaml +++ b/helm/values-prod.yaml @@ -4,7 +4,7 @@ microservice-chart: fullnameOverride: "" image: repository: ghcr.io/pagopa/pagopa-gpd-payments - tag: "0.12.21" + tag: "0.12.21-1-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability" pullPolicy: Always livenessProbe: httpGet: diff --git a/helm/values-uat.yaml b/helm/values-uat.yaml index 2d4f964b..16330493 100644 --- a/helm/values-uat.yaml +++ b/helm/values-uat.yaml @@ -4,7 +4,7 @@ microservice-chart: fullnameOverride: "" image: repository: ghcr.io/pagopa/pagopa-gpd-payments - tag: "0.12.21" + tag: "0.12.21-1-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability" pullPolicy: Always livenessProbe: httpGet: diff --git a/openapi/openapi.json b/openapi/openapi.json index 0caf8f6e..e1ab4b1e 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -1,515 +1,554 @@ { - "openapi" : "3.0.1", - "info" : { - "title" : "PagoPA API Payments", - "description" : "Payments", - "termsOfService" : "https://www.pagopa.gov.it/", - "version" : "0.12.20" + "openapi": "3.0.1", + "info": { + "title": "PagoPA API Payments", + "description": "Payments", + "termsOfService": "https://www.pagopa.gov.it/", + "version": "0.12.21-1-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability" }, - "servers" : [ { - "url" : "http://localhost", - "description" : "Generated server url" - } ], - "tags" : [ { - "name" : "Payments receipts API" - } ], - "paths" : { - "/info" : { - "get" : { - "tags" : [ "Home" ], - "summary" : "health check", - "description" : "Return OK if application is started", - "operationId" : "healthCheck", - "responses" : { - "200" : { - "description" : "OK", - "headers" : { - "X-Request-Id" : { - "description" : "This header identifies the call", - "schema" : { - "type" : "string" + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "tags": [ + { + "name": "Payments receipts API" + } + ], + "paths": { + "/info": { + "get": { + "tags": [ + "Home" + ], + "summary": "health check", + "description": "Return OK if application is started", + "operationId": "healthCheck", + "responses": { + "200": { + "description": "OK", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" } } }, - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/AppInfo" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AppInfo" } } } }, - "400" : { - "description" : "Bad Request", - "headers" : { - "X-Request-Id" : { - "description" : "This header identifies the call", - "schema" : { - "type" : "string" + "400": { + "description": "Bad Request", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" } } }, - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ProblemJson" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemJson" } } } }, - "401" : { - "description" : "Unauthorized", - "headers" : { - "X-Request-Id" : { - "description" : "This header identifies the call", - "schema" : { - "type" : "string" + "401": { + "description": "Unauthorized", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" } } } }, - "403" : { - "description" : "Forbidden", - "headers" : { - "X-Request-Id" : { - "description" : "This header identifies the call", - "schema" : { - "type" : "string" + "403": { + "description": "Forbidden", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" } } } }, - "429" : { - "description" : "Too many requests", - "headers" : { - "X-Request-Id" : { - "description" : "This header identifies the call", - "schema" : { - "type" : "string" + "429": { + "description": "Too many requests", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" } } } }, - "500" : { - "description" : "Service unavailable", - "headers" : { - "X-Request-Id" : { - "description" : "This header identifies the call", - "schema" : { - "type" : "string" + "500": { + "description": "Service unavailable", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" } } }, - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ProblemJson" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemJson" } } } } }, - "security" : [ { - "ApiKey" : [ ] - }, { - "Authorization" : [ ] - } ] + "security": [ + { + "ApiKey": [] + }, + { + "Authorization": [] + } + ] }, - "parameters" : [ { - "name" : "X-Request-Id", - "in" : "header", - "description" : "This header identifies the call, if not passed it is self-generated. This ID is returned in the response.", - "schema" : { - "type" : "string" + "parameters": [ + { + "name": "X-Request-Id", + "in": "header", + "description": "This header identifies the call, if not passed it is self-generated. This ID is returned in the response.", + "schema": { + "type": "string" + } } - } ] + ] }, - "/payments/{organizationfiscalcode}/receipts" : { - "get" : { - "tags" : [ "Payments receipts API" ], - "summary" : "Return the list of the organization receipts.", - "operationId" : "getOrganizationReceipts", - "parameters" : [ { - "name" : "organizationfiscalcode", - "in" : "path", - "description" : "Organization fiscal code, the fiscal code of the Organization.", - "required" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "pageNum", - "in" : "query", - "description" : "Page number, starts from 0", - "required" : false, - "schema" : { - "minimum" : 0, - "type" : "integer", - "format" : "int32", - "default" : 0 - } - }, { - "name" : "pageSize", - "in" : "query", - "description" : "Number of elements per page. Default = 20", - "required" : false, - "schema" : { - "maximum" : 100, - "type" : "integer", - "format" : "int32", - "default" : 20 - } - }, { - "name" : "debtor", - "in" : "query", - "description" : "Filter by debtor", - "required" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "service", - "in" : "query", - "description" : "Filter by service", - "required" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "from", - "in" : "query", - "description" : "Filter by date, from this date", - "required" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "to", - "in" : "query", - "description" : "Filter by date, to this date", - "required" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "segregationCodes", - "in" : "query", - "description" : "Segregation codes for which broker is authorized", - "required" : false, - "schema" : { - "pattern" : "\\d{2}(,\\d{2})*", - "type" : "string" - } - }, { - "name" : "debtorOrIuv", - "in" : "query", - "description" : "Filter start of debtor or IUV", - "required" : false, - "schema" : { - "type" : "string" + "/payments/{organizationfiscalcode}/receipts": { + "get": { + "tags": [ + "Payments receipts API" + ], + "summary": "Return the list of the organization receipts.", + "operationId": "getOrganizationReceipts", + "parameters": [ + { + "name": "organizationfiscalcode", + "in": "path", + "description": "Organization fiscal code, the fiscal code of the Organization.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "pageNum", + "in": "query", + "description": "Page number, starts from 0", + "required": false, + "schema": { + "minimum": 0, + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "pageSize", + "in": "query", + "description": "Number of elements per page. Default = 20", + "required": false, + "schema": { + "maximum": 100, + "type": "integer", + "format": "int32", + "default": 20 + } + }, + { + "name": "debtor", + "in": "query", + "description": "Filter by debtor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "service", + "in": "query", + "description": "Filter by service", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "from", + "in": "query", + "description": "Filter by date, from this date", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "to", + "in": "query", + "description": "Filter by date, to this date", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "segregationCodes", + "in": "query", + "description": "Segregation codes for which broker is authorized", + "required": false, + "schema": { + "pattern": "\\d{2}(,\\d{2})*", + "type": "string" + } + }, + { + "name": "debtorOrIuv", + "in": "query", + "description": "Filter start of debtor or IUV", + "required": false, + "schema": { + "type": "string" + } } - } ], - "responses" : { - "200" : { - "description" : "Obtained all organization payment positions.", - "headers" : { - "X-Request-Id" : { - "description" : "This header identifies the call", - "schema" : { - "type" : "string" + ], + "responses": { + "200": { + "description": "Obtained all organization payment positions.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" } } }, - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/PaymentsResult" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentsResult" } } } }, - "401" : { - "description" : "Wrong or missing function key.", - "headers" : { - "X-Request-Id" : { - "description" : "This header identifies the call", - "schema" : { - "type" : "string" + "401": { + "description": "Wrong or missing function key.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" } } } }, - "404" : { - "description" : "No receipts found.", - "headers" : { - "X-Request-Id" : { - "description" : "This header identifies the call", - "schema" : { - "type" : "string" + "404": { + "description": "No receipts found.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" } } }, - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ProblemJson" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemJson" } } } }, - "500" : { - "description" : "Service unavailable.", - "headers" : { - "X-Request-Id" : { - "description" : "This header identifies the call", - "schema" : { - "type" : "string" + "500": { + "description": "Service unavailable.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" } } }, - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ProblemJson" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemJson" } } } } }, - "security" : [ { - "ApiKey" : [ ] - }, { - "Authorization" : [ ] - } ] + "security": [ + { + "ApiKey": [] + }, + { + "Authorization": [] + } + ] }, - "parameters" : [ { - "name" : "X-Request-Id", - "in" : "header", - "description" : "This header identifies the call, if not passed it is self-generated. This ID is returned in the response.", - "schema" : { - "type" : "string" + "parameters": [ + { + "name": "X-Request-Id", + "in": "header", + "description": "This header identifies the call, if not passed it is self-generated. This ID is returned in the response.", + "schema": { + "type": "string" + } } - } ] + ] }, - "/payments/{organizationfiscalcode}/receipts/{iuv}" : { - "get" : { - "tags" : [ "Payments receipts API" ], - "summary" : "Return the details of a specific receipt.", - "operationId" : "getReceiptByIUV", - "parameters" : [ { - "name" : "organizationfiscalcode", - "in" : "path", - "description" : "Organization fiscal code, the fiscal code of the Organization.", - "required" : true, - "schema" : { - "type" : "string" - }, - "example" : 12345 - }, { - "name" : "iuv", - "in" : "path", - "description" : "IUV (Unique Payment Identification). Alphanumeric code that uniquely associates and identifies three key elements of a payment: reason, payer, amount", - "required" : true, - "schema" : { - "type" : "string" - }, - "example" : "ABC123" - }, { - "name" : "segregationCodes", - "in" : "query", - "description" : "Segregation codes for which broker is authorized", - "required" : false, - "schema" : { - "pattern" : "\\d{2}(,\\d{2})*", - "type" : "string" + "/payments/{organizationfiscalcode}/receipts/{iuv}": { + "get": { + "tags": [ + "Payments receipts API" + ], + "summary": "Return the details of a specific receipt.", + "operationId": "getReceiptByIUV", + "parameters": [ + { + "name": "organizationfiscalcode", + "in": "path", + "description": "Organization fiscal code, the fiscal code of the Organization.", + "required": true, + "schema": { + "type": "string" + }, + "example": 12345 + }, + { + "name": "iuv", + "in": "path", + "description": "IUV (Unique Payment Identification). Alphanumeric code that uniquely associates and identifies three key elements of a payment: reason, payer, amount", + "required": true, + "schema": { + "type": "string" + }, + "example": "ABC123" + }, + { + "name": "segregationCodes", + "in": "query", + "description": "Segregation codes for which broker is authorized", + "required": false, + "schema": { + "pattern": "\\d{2}(,\\d{2})*", + "type": "string" + } } - } ], - "responses" : { - "200" : { - "description" : "Obtained receipt details.", - "headers" : { - "X-Request-Id" : { - "description" : "This header identifies the call", - "schema" : { - "type" : "string" + ], + "responses": { + "200": { + "description": "Obtained receipt details.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" } } }, - "content" : { - "application/xml" : { - "schema" : { - "type" : "string" + "content": { + "application/xml": { + "schema": { + "type": "string" } } } }, - "401" : { - "description" : "Wrong or missing function key.", - "headers" : { - "X-Request-Id" : { - "description" : "This header identifies the call", - "schema" : { - "type" : "string" + "401": { + "description": "Wrong or missing function key.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" } } } }, - "404" : { - "description" : "No receipt found.", - "headers" : { - "X-Request-Id" : { - "description" : "This header identifies the call", - "schema" : { - "type" : "string" + "404": { + "description": "No receipt found.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" } } }, - "content" : { - "application/xml" : { - "schema" : { - "$ref" : "#/components/schemas/ProblemJson" + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/ProblemJson" } }, - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ProblemJson" + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemJson" } } } }, - "422" : { - "description" : "Unable to process the request.", - "headers" : { - "X-Request-Id" : { - "description" : "This header identifies the call", - "schema" : { - "type" : "string" + "422": { + "description": "Unable to process the request.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" } } }, - "content" : { - "application/xml" : { - "schema" : { - "$ref" : "#/components/schemas/ProblemJson" + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/ProblemJson" } }, - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ProblemJson" + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemJson" } } } }, - "500" : { - "description" : "Service unavailable.", - "headers" : { - "X-Request-Id" : { - "description" : "This header identifies the call", - "schema" : { - "type" : "string" + "500": { + "description": "Service unavailable.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" } } }, - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ProblemJson" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemJson" } } } } }, - "security" : [ { - "ApiKey" : [ ] - }, { - "Authorization" : [ ] - } ] + "security": [ + { + "ApiKey": [] + }, + { + "Authorization": [] + } + ] }, - "parameters" : [ { - "name" : "X-Request-Id", - "in" : "header", - "description" : "This header identifies the call, if not passed it is self-generated. This ID is returned in the response.", - "schema" : { - "type" : "string" + "parameters": [ + { + "name": "X-Request-Id", + "in": "header", + "description": "This header identifies the call, if not passed it is self-generated. This ID is returned in the response.", + "schema": { + "type": "string" + } } - } ] + ] } }, - "components" : { - "schemas" : { - "ProblemJson" : { - "type" : "object", - "properties" : { - "title" : { - "type" : "string", - "description" : "A short, summary of the problem type. Written in english and readable for engineers (usually not suited for non technical stakeholders and not localized); example: Service Unavailable" - }, - "status" : { - "maximum" : 600, - "minimum" : 100, - "type" : "integer", - "description" : "The HTTP status code generated by the origin server for this occurrence of the problem.", - "format" : "int32", - "example" : 200 - }, - "detail" : { - "type" : "string", - "description" : "A human readable explanation specific to this occurrence of the problem.", - "example" : "There was an error processing the request" + "components": { + "schemas": { + "ProblemJson": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "A short, summary of the problem type. Written in english and readable for engineers (usually not suited for non technical stakeholders and not localized); example: Service Unavailable" + }, + "status": { + "maximum": 600, + "minimum": 100, + "type": "integer", + "description": "The HTTP status code generated by the origin server for this occurrence of the problem.", + "format": "int32", + "example": 200 + }, + "detail": { + "type": "string", + "description": "A human readable explanation specific to this occurrence of the problem.", + "example": "There was an error processing the request" } } }, - "PaymentsResult" : { - "type" : "object", - "properties" : { - "currentPageNumber" : { - "type" : "integer", - "format" : "int32" - }, - "length" : { - "type" : "integer", - "format" : "int32" - }, - "totalPages" : { - "type" : "integer", - "format" : "int32" - }, - "results" : { - "type" : "array", - "items" : { - "type" : "object" + "PaymentsResult": { + "type": "object", + "properties": { + "currentPageNumber": { + "type": "integer", + "format": "int32" + }, + "length": { + "type": "integer", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "format": "int32" + }, + "results": { + "type": "array", + "items": { + "type": "object" } } } }, - "AppInfo" : { - "type" : "object", - "properties" : { - "name" : { - "type" : "string" + "AppInfo": { + "type": "object", + "properties": { + "name": { + "type": "string" }, - "version" : { - "type" : "string" + "version": { + "type": "string" }, - "environment" : { - "type" : "string" + "environment": { + "type": "string" } } } }, - "securitySchemes" : { - "ApiKey" : { - "type" : "apiKey", - "description" : "The API key to access this function app.", - "name" : "Ocp-Apim-Subscription-Key", - "in" : "header" + "securitySchemes": { + "ApiKey": { + "type": "apiKey", + "description": "The API key to access this function app.", + "name": "Ocp-Apim-Subscription-Key", + "in": "header" }, - "Authorization" : { - "type" : "http", - "description" : "JWT token get after Azure Login", - "scheme" : "bearer", - "bearerFormat" : "JWT" + "Authorization": { + "type": "http", + "description": "JWT token get after Azure Login", + "scheme": "bearer", + "bearerFormat": "JWT" } } } -} \ No newline at end of file +} diff --git a/pom.xml b/pom.xml index 48bc0cfe..5a2d5a11 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ it.gov.pagopa payments - 0.12.21 + 0.12.21-1-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability Payments Payments From 518a98e743d95ce8265bae4e512be4adcb9f822f Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Wed, 8 May 2024 14:57:22 +0200 Subject: [PATCH 22/24] PAGOPA-1695 fixing scheduler job --- .../java/it/gov/pagopa/payments/scheduler/Scheduler.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java b/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java index cfa77db8..fed08bfb 100644 --- a/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java +++ b/src/main/java/it/gov/pagopa/payments/scheduler/Scheduler.java @@ -5,6 +5,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -13,6 +14,7 @@ @Component @Slf4j +@EnableScheduling @ConditionalOnProperty(name = "cron.job.schedule.retry.enabled", matchIfMissing = true) public class Scheduler { @@ -30,5 +32,9 @@ public void retryPaSendRT() { schedulerService.retryFailedPaSendRT(); this.threadOfExecution = Thread.currentThread(); } + + public Thread getThreadOfExecution() { + return this.threadOfExecution; + } } From a599a7a32f103b91cc2fcdd0d99ae6e8420929d1 Mon Sep 17 00:00:00 2001 From: pagopa-github-bot Date: Wed, 8 May 2024 13:10:44 +0000 Subject: [PATCH 23/24] Bump to version 0.12.21-2-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability [skip ci] --- helm/Chart.yaml | 4 ++-- helm/values-dev.yaml | 2 +- helm/values-prod.yaml | 2 +- helm/values-uat.yaml | 2 +- openapi/openapi.json | 2 +- pom.xml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/helm/Chart.yaml b/helm/Chart.yaml index e18100a3..8a3d3bca 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: pagopa-gpd-payments description: Microservice that exposes API for payment receipts retrieving and other operations type: application -version: 0.94.0 -appVersion: 0.12.21-1-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability +version: 0.95.0 +appVersion: 0.12.21-2-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability dependencies: - name: microservice-chart version: 2.4.0 diff --git a/helm/values-dev.yaml b/helm/values-dev.yaml index 77cc5dbc..2a820f64 100644 --- a/helm/values-dev.yaml +++ b/helm/values-dev.yaml @@ -4,7 +4,7 @@ microservice-chart: fullnameOverride: "" image: repository: ghcr.io/pagopa/pagopa-gpd-payments - tag: "0.12.21-1-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability" + tag: "0.12.21-2-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability" pullPolicy: Always livenessProbe: httpGet: diff --git a/helm/values-prod.yaml b/helm/values-prod.yaml index 5fadde4c..d5ac8c8f 100644 --- a/helm/values-prod.yaml +++ b/helm/values-prod.yaml @@ -4,7 +4,7 @@ microservice-chart: fullnameOverride: "" image: repository: ghcr.io/pagopa/pagopa-gpd-payments - tag: "0.12.21-1-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability" + tag: "0.12.21-2-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability" pullPolicy: Always livenessProbe: httpGet: diff --git a/helm/values-uat.yaml b/helm/values-uat.yaml index 16330493..9f5c2e83 100644 --- a/helm/values-uat.yaml +++ b/helm/values-uat.yaml @@ -4,7 +4,7 @@ microservice-chart: fullnameOverride: "" image: repository: ghcr.io/pagopa/pagopa-gpd-payments - tag: "0.12.21-1-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability" + tag: "0.12.21-2-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability" pullPolicy: Always livenessProbe: httpGet: diff --git a/openapi/openapi.json b/openapi/openapi.json index e1ab4b1e..134dfb18 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -4,7 +4,7 @@ "title": "PagoPA API Payments", "description": "Payments", "termsOfService": "https://www.pagopa.gov.it/", - "version": "0.12.21-1-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability" + "version": "0.12.21-2-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability" }, "servers": [ { diff --git a/pom.xml b/pom.xml index 5a2d5a11..76868fcd 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ it.gov.pagopa payments - 0.12.21-1-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability + 0.12.21-2-PAGOPA-1695-sviluppo-pa-send-rt-long-term-reliability Payments Payments From d653c330453e554b5b0db433e876f13c0b4b7a2b Mon Sep 17 00:00:00 2001 From: FedericoRuzzier <49512050+FedericoRuzzier@users.noreply.github.com> Date: Wed, 8 May 2024 16:13:08 +0200 Subject: [PATCH 24/24] PAGOPA-1695 adding catch in scheduler --- .../it/gov/pagopa/payments/service/SchedulerService.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java index d7816896..cb8cfa75 100644 --- a/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java +++ b/src/main/java/it/gov/pagopa/payments/service/SchedulerService.java @@ -5,6 +5,7 @@ import com.azure.storage.queue.models.QueueMessageItem; import com.microsoft.azure.storage.StorageException; import feign.FeignException; +import it.gov.pagopa.payments.endpoints.validation.exceptions.PartnerValidationException; import it.gov.pagopa.payments.entity.ReceiptEntity; import it.gov.pagopa.payments.exception.AppError; import it.gov.pagopa.payments.exception.AppException; @@ -135,6 +136,10 @@ public void handlingXml (String failureBody, XPath xpath, QueueMessageItem queue Duration.ofSeconds(queueUpdateInvisibilityTime), null, Context.NONE); + } catch (PartnerValidationException e) { + // { PAA_RECEIPT_DUPLICATA, PAA_PAGAMENTO_SCONOSCIUTO } + log.info("[paSendRT] Retry failed {} [fiscalCode={},noticeNumber={}]\",\n", e.getMessage(), idPA, noticeNumber); + queueClient.deleteMessage(queueMessageItem.getMessageId(), queueMessageItem.getPopReceipt()); } }