Skip to content

Commit

Permalink
[PAGOPA-2154] Merge branch 'main' into PAGOPA-2154-bug-richieste-con-…
Browse files Browse the repository at this point in the history
…anagrafica-pagatore-blank # Conflicts: # helm/Chart.yaml # helm/values-dev.yaml # helm/values-prod.yaml # helm/values-uat.yaml # openapi/openapi.json # openapi/openapi_redirect.json # pom.xml
  • Loading branch information
jacopocarlini committed Oct 8, 2024
2 parents 7864bca + 26984f1 commit 14d6bf1
Show file tree
Hide file tree
Showing 62 changed files with 1,793 additions and 365 deletions.
4 changes: 2 additions & 2 deletions helm/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ apiVersion: v2
name: pagopa-wisp-converter
description: A service that permits to handle nodoInviaRPT and nodoInviaCarrelloRPT request from WISP, interfacing them with GPD system
type: application
version: 0.200.0
appVersion: 0.3.3-1-PAGOPA-2154-bug-richieste-con-anagrafica-pagatore-blank
version: 0.222.0
appVersion: 0.4.4
dependencies:
- name: microservice-chart
version: 3.0.0
Expand Down
10 changes: 8 additions & 2 deletions helm/values-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ microservice-chart:
fullnameOverride: ""
image:
repository: ghcr.io/pagopa/pagopa-wisp-converter
tag: "0.3.3-1-PAGOPA-2154-bug-richieste-con-anagrafica-pagatore-blank"
tag: "0.4.4"
pullPolicy: Always
livenessProbe:
httpGet:
Expand Down Expand Up @@ -77,10 +77,12 @@ microservice-chart:
OTEL_TRACES_SAMPLER: "always_on"
REDIS_PORT: "6380"
COSMOS_URI: "https://pagopa-d-weu-nodo-wispconv-cosmos-account.documents.azure.com:443/"
COSMOS_READ_REGION: "West Europe"
PAAINVIART_SERVICE_BUS_NAME: "nodo_wisp_paainviart_queue"
RECEIPT_TIMER_QUEUE_NAME: "nodo_wisp_payment_timeout_queue"
ECOMMERCE_HANG_TIMER_QUEUE_NAME: "nodo_wisp_ecommerce_hang_timeout_queue"
ECOMMERCE_HANG_TIMEOUT: '1800'
APIM_PATH: 'api.dev.platform.pagopa.it'
CACHED_REQUESTID_MAPPING_TTL_MINUTES: "1440"
CACHE_REFRESH_CRON: "0 */60 * * * *"
CLIENT_GPD_READ_TIMEOUT: '5000'
Expand All @@ -98,7 +100,11 @@ microservice-chart:
RT_SEND_SCHEDULING_TIME_IN_MINUTES: '1'
RT_SEND_AVOID_SCHEDULING_ON_STATES: 'PAA_RT_DUPLICATA'
RPT_TIMER_QUEUE_NAME: "nodo_wisp_rpt_timeout_queue"
RPT_TIMEOUT: '120' # 2 minutes
CRON_JOB_SCHEDULE_RECOVERY_ENABLED: 'true'
CRON_JOB_SCHEDULE_RECOVERY_RECEIPT_KO_ENABLED: '0 0 * * * *' # top of every hour of every
CRON_JOB_SCHEDULE_RECOVERY_FROM_HOURS_AGO: '2' # from 2 hours ago
CRON_JOB_SCHEDULE_RECOVERY_UNTIL_HOURS_AGO: '1' # until 1 hours ago
RPT_TIMEOUT: '60' # 1 minute
RE_TRACING_INTERFACE_IUVGENERATOR_ENABLED: 'true'
RE_TRACING_INTERFACE_PAYMENTPOSITIONANALYSIS_ENABLED: 'true'
RE_TRACING_INTERFACE_DECOUPLERCACHING_ENABLED: 'true'
Expand Down
8 changes: 7 additions & 1 deletion helm/values-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ microservice-chart:
fullnameOverride: ""
image:
repository: ghcr.io/pagopa/pagopa-wisp-converter
tag: "0.3.3-1-PAGOPA-2154-bug-richieste-con-anagrafica-pagatore-blank"
tag: "0.4.4"
pullPolicy: Always
livenessProbe:
httpGet:
Expand Down Expand Up @@ -96,10 +96,12 @@ microservice-chart:
OTEL_TRACES_SAMPLER: "always_on"
REDIS_PORT: "6380"
COSMOS_URI: "https://pagopa-p-weu-nodo-wispconv-cosmos-account.documents.azure.com:443/"
COSMOS_READ_REGION: "North Europe"
PAAINVIART_SERVICE_BUS_NAME: "nodo_wisp_paainviart_queue"
RECEIPT_TIMER_QUEUE_NAME: "nodo_wisp_payment_timeout_queue"
ECOMMERCE_HANG_TIMER_QUEUE_NAME: "nodo_wisp_ecommerce_hang_timeout_queue"
ECOMMERCE_HANG_TIMEOUT: '1800'
APIM_PATH: 'api.platform.pagopa.it'
CACHED_REQUESTID_MAPPING_TTL_MINUTES: "1440"
CACHE_REFRESH_CRON: "0 */60 * * * *"
CLIENT_GPD_READ_TIMEOUT: '5000'
Expand All @@ -117,6 +119,10 @@ microservice-chart:
RT_SEND_SCHEDULING_TIME_IN_MINUTES: '60'
RT_SEND_AVOID_SCHEDULING_ON_STATES: 'PAA_RT_DUPLICATA'
RPT_TIMER_QUEUE_NAME: "nodo_wisp_rpt_timeout_queue"
CRON_JOB_SCHEDULE_RECOVERY_ENABLED: 'false'
CRON_JOB_SCHEDULE_RECOVERY_RECEIPT_KO_ENABLED: '0 0 * * * *' # top of every hour of every day
CRON_JOB_SCHEDULE_RECOVERY_FROM_HOURS_AGO: '2' # from 2 hours ago
CRON_JOB_SCHEDULE_RECOVERY_UNTIL_HOURS_AGO: '1' # until 1 hours ago
RPT_TIMEOUT: '300' # 5 minutes
RE_TRACING_INTERFACE_IUVGENERATOR_ENABLED: 'true'
RE_TRACING_INTERFACE_PAYMENTPOSITIONANALYSIS_ENABLED: 'true'
Expand Down
10 changes: 8 additions & 2 deletions helm/values-uat.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ microservice-chart:
fullnameOverride: ""
image:
repository: ghcr.io/pagopa/pagopa-wisp-converter
tag: "0.3.3-1-PAGOPA-2154-bug-richieste-con-anagrafica-pagatore-blank"
tag: "0.4.4"
pullPolicy: Always
livenessProbe:
httpGet:
Expand Down Expand Up @@ -77,10 +77,12 @@ microservice-chart:
OTEL_TRACES_SAMPLER: "always_on"
REDIS_PORT: "6380"
COSMOS_URI: "https://pagopa-u-weu-nodo-wispconv-cosmos-account.documents.azure.com:443/"
COSMOS_READ_REGION: "West Europe"
PAAINVIART_SERVICE_BUS_NAME: "nodo_wisp_paainviart_queue"
RECEIPT_TIMER_QUEUE_NAME: "nodo_wisp_payment_timeout_queue"
ECOMMERCE_HANG_TIMER_QUEUE_NAME: "nodo_wisp_ecommerce_hang_timeout_queue"
ECOMMERCE_HANG_TIMEOUT: '1800'
APIM_PATH: 'api.uat.platform.pagopa.it'
CACHED_REQUESTID_MAPPING_TTL_MINUTES: "1440"
CACHE_REFRESH_CRON: "0 */60 * * * *"
CLIENT_GPD_READ_TIMEOUT: '5000'
Expand All @@ -98,7 +100,11 @@ microservice-chart:
RT_SEND_SCHEDULING_TIME_IN_MINUTES: '60'
RT_SEND_AVOID_SCHEDULING_ON_STATES: 'PAA_RT_DUPLICATA'
RPT_TIMER_QUEUE_NAME: "nodo_wisp_rpt_timeout_queue"
RPT_TIMEOUT: '300' # 5 minutes
CRON_JOB_SCHEDULE_RECOVERY_ENABLED: 'true'
CRON_JOB_SCHEDULE_RECOVERY_RECEIPT_KO_ENABLED: '0 0 * * * *' # top of every hour of every day
CRON_JOB_SCHEDULE_RECOVERY_FROM_HOURS_AGO: '2' # from 2 hours ago
CRON_JOB_SCHEDULE_RECOVERY_UNTIL_HOURS_AGO: '1' # until 1 hours ago
RPT_TIMEOUT: '60' # 1 minute (set for integration tests)
RE_TRACING_INTERFACE_IUVGENERATOR_ENABLED: 'true'
RE_TRACING_INTERFACE_PAYMENTPOSITIONANALYSIS_ENABLED: 'true'
RE_TRACING_INTERFACE_DECOUPLERCACHING_ENABLED: 'true'
Expand Down
178 changes: 176 additions & 2 deletions openapi/openapi.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion openapi/openapi_redirect.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"openapi": "3.0.1",
"info": {
"title": "WISP-Converter-redirect",
"version": "0.3.3-1-PAGOPA-2154-bug-richieste-con-anagrafica-pagatore-blank"
"version": "0.4.4"
},
"servers": [
{
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<groupId>it.gov.pagopa</groupId>
<artifactId>wisp-converter</artifactId>
<version>0.3.3-1-PAGOPA-2154-bug-richieste-con-anagrafica-pagatore-blank</version>
<version>0.4.4</version>
<name>pagoPA WISP Converter</name>
<description>A service that permits to handle nodoInviaRPT and nodoInviaCarrelloRPT request from WISP, converting them in NMU payments.</description>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package it.gov.pagopa.wispconverter.config;

import com.azure.cosmos.CosmosAsyncClient;
import com.azure.cosmos.CosmosClientBuilder;
import com.azure.spring.data.cosmos.config.AbstractCosmosConfiguration;
import com.azure.spring.data.cosmos.config.CosmosConfig;
import com.azure.spring.data.cosmos.core.CosmosTemplate;
import com.azure.spring.data.cosmos.core.convert.MappingCosmosConverter;
import com.azure.spring.data.cosmos.repository.config.EnableCosmosRepositories;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Configuration
@EnableCosmosRepositories(basePackages = "it.gov.pagopa.wispconverter.secondary")
@EnableConfigurationProperties
@ConditionalOnExpression("'${info.properties.environment}'!='test'")
@Slf4j
public class SecondaryCosmosConfig extends AbstractCosmosConfiguration {

@Value("${azure.cosmos.uri}")
private String cosmosUri;

@Value("${azure.cosmos.key}")
private String cosmosKey;

@Value("${azure.cosmos.database}")
private String cosmosDatabase;

@Value("${azure.cosmos.read.region}")
private String readRegion;

@Value("${azure.cosmos.populate-query-metrics}")
private Boolean cosmosQueryMetrics;

@Bean(name = "secondaryCosmosClient")
public CosmosAsyncClient getCosmosAsyncClient() {
return new CosmosClientBuilder()
.key(cosmosKey)
.endpoint(cosmosUri)
.preferredRegions(List.of(readRegion))
.buildAsyncClient();
}

@Bean(name = "secondaryCosmosTemplate")
public CosmosTemplate secondaryCosmosTemplate(@Qualifier("secondaryCosmosClient") CosmosAsyncClient client,
MappingCosmosConverter mappingCosmosConverter) {
return new CosmosTemplate(client, cosmosDatabase, cosmosConfig(), mappingCosmosConverter);
}

@Override
public CosmosConfig cosmosConfig() {
return CosmosConfig.builder()
.enableQueryMetrics(cosmosQueryMetrics)
.build();
}

@Override
protected String getDatabaseName() {
return cosmosDatabase;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,28 @@
import it.gov.pagopa.wispconverter.service.model.ReceiptDto;
import it.gov.pagopa.wispconverter.util.Constants;
import it.gov.pagopa.wispconverter.util.ErrorUtil;
import it.gov.pagopa.wispconverter.util.ReceiptRequestHandler;
import it.gov.pagopa.wispconverter.util.Trace;
import it.gov.pagopa.wispconverter.util.ReceiptRequestHandler.PaSendRTV2Request;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.ErrorResponse;
import org.springframework.web.bind.annotation.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;

import javax.net.ssl.SSLSession;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpHeaders;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.io.StringReader;
import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/receipt")
Expand All @@ -56,6 +59,10 @@ public class ReceiptController {
private final ObjectMapper mapper;

private final ErrorUtil errorUtil;

private final SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();

private final ReceiptRequestHandler receiptRequestHandler;

@Operation(summary = "", description = "", security = {@SecurityRequirement(name = "ApiKey")}, tags = {"Receipt"})
@ApiResponses(value = {
Expand Down Expand Up @@ -124,7 +131,7 @@ public void receiptKo(@RequestBody String request) throws Exception {
public void receiptOk(@RequestBody ReceiptRequest request) throws IOException {

try {
log.info("Invoking API operation receiptOk - args: {}", request.toString());
log.info("Invoking API operation receiptOk - args: {}", this.getReceiptRequestInfoToLog(request.getContent()));
receiptService.sendOkPaaInviaRtToCreditorInstitution(request.getContent());
log.info("Successful API operation receiptOk");
} catch (Exception ex) {
Expand All @@ -137,4 +144,24 @@ public void receiptOk(@RequestBody ReceiptRequest request) throws IOException {
throw ex;
}
}

private String getReceiptRequestInfoToLog(String xml) {
String args = "n/a";
try {
if (StringUtils.isNotEmpty(xml)) {
// fix for sonar issue XML external entity in user-controlled data
saxParserFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
saxParserFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
saxParserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

saxParserFactory.newSAXParser().parse(new InputSource(new StringReader(xml)), receiptRequestHandler);

PaSendRTV2Request result = receiptRequestHandler.getPaSendRTV2Request();
args = "noticeNumber="+result.getNoticeNumber()+", fiscalCode="+result.getFiscalCode()+", creditorReferenceId="+result.getCreditorReferenceId();
}
} catch (SAXException | IOException | ParserConfigurationException e) {
return args;
}
return args;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import it.gov.pagopa.wispconverter.controller.model.RecoveryProxyReceiptRequest;
import it.gov.pagopa.wispconverter.controller.model.RecoveryProxyReceiptResponse;
import it.gov.pagopa.wispconverter.controller.model.RecoveryReceiptResponse;
import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum;
import it.gov.pagopa.wispconverter.exception.AppException;
import it.gov.pagopa.wispconverter.service.RecoveryService;
import it.gov.pagopa.wispconverter.util.Constants;
import it.gov.pagopa.wispconverter.util.ErrorUtil;
import jakarta.validation.constraints.Pattern;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.ErrorResponse;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/recovery")
Expand All @@ -43,7 +43,7 @@ public class RecoveryController {
public ResponseEntity<RecoveryReceiptResponse> recoverReceiptKOForCreditorInstitution(@PathVariable("creditor_institution") String ci, @QueryParam("date_from") String dateFrom, @QueryParam("date_to") String dateTo) {
try {
log.info("Invoking API operation recoverReceiptKOForCreditorInstitution - args: {} {} {}", ci, dateFrom, dateTo);
RecoveryReceiptResponse response = recoveryService.recoverReceiptKOForCreditorInstitution(ci, dateFrom, dateTo);
RecoveryReceiptResponse response = recoveryService.recoverReceiptKOByCI(ci, dateFrom, dateTo);
return ResponseEntity.ok(response);
} catch (Exception ex) {
String operationId = MDC.get(Constants.MDC_OPERATION_ID);
Expand All @@ -57,4 +57,55 @@ public ResponseEntity<RecoveryReceiptResponse> recoverReceiptKOForCreditorInstit
}
}


@Operation(summary = "Execute IUV reconciliation for certain creditor institution.", description = "Execute reconciliation of all IUVs for certain creditor institution, sending RT for close payment.", security = {@SecurityRequirement(name = "ApiKey")}, tags = {"Recovery"})
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Completed IUV reconciliation with explicit RT submission"),
@ApiResponse(responseCode = "400", description = "It is not possible to complete reconciliation (with explicit RT submission) for the submitted UIV")
})
@PostMapping(value = "/{creditor_institution}/rpt/{iuv}/receipt-ko")
public ResponseEntity<RecoveryReceiptResponse> recoverReceiptKOForCreditorInstitutionAndIUV(@Pattern(regexp = "[a-zA-Z0-9_-]{1,100}") @PathVariable("creditor_institution") String ci,
@Pattern(regexp = "[a-zA-Z0-9_-]{1,100}") @PathVariable("iuv") String iuv,
@Pattern(regexp = "[a-zA-Z0-9_-]{1,10}") @QueryParam("date_from") String dateFrom,
@Pattern(regexp = "[a-zA-Z0-9_-]{1,10}") @QueryParam("date_to") String dateTo) {
try {
log.info("Invoking API operation recoverReceiptKOForCreditorInstitution - args: {} {} {} {}", ci, iuv, dateFrom, dateTo);

RecoveryReceiptResponse recoveryReceiptResponse = recoveryService.recoverReceiptKOByIUV(ci, iuv, dateFrom, dateTo);
if(recoveryReceiptResponse != null) {
return ResponseEntity.ok(recoveryReceiptResponse);
} else {
// RPT with CI and IUV could not be recovered via API
return ResponseEntity.badRequest().build();
}
} catch (Exception ex) {
String operationId = MDC.get(Constants.MDC_OPERATION_ID);
log.error(String.format("GenericException: operation-id=[%s]", operationId != null ? operationId : "n/a"), ex);
AppException appException = new AppException(ex, AppErrorCodeMessageEnum.ERROR, ex.getMessage());
ErrorResponse errorResponse = errorUtil.forAppException(appException);
log.error("Failed API operation recoverReceiptKOForCreditorInstitution - error: {}", errorResponse);
throw ex;
} finally {
log.info("Successful API operation recoverReceiptKOForCreditorInstitution");
}
}

@Operation(summary = "Execute reconciliation for passed receipts.", description = "Execute reconciliation of all receipts in the request, searching by passed identifier", security = {@SecurityRequirement(name = "ApiKey")}, tags = {"Recovery"})
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Reconciliation completed")
})
@PostMapping(value = "/proxy")
public ResponseEntity<RecoveryProxyReceiptResponse> recoverReceiptToBeSentByProxy(@RequestBody RecoveryProxyReceiptRequest request) {
try {
log.info("Invoking API operation recoverReceiptToBeSentByProxy - args: {}", request);
return ResponseEntity.ok(recoveryService.recoverReceiptToBeSentByProxy(request));
} catch (Exception ex) {
String operationId = MDC.get(Constants.MDC_OPERATION_ID);
log.error(String.format("GenericException: operation-id=[%s]", operationId != null ? operationId : "n/a"), ex);
AppException appException = new AppException(ex, AppErrorCodeMessageEnum.ERROR, ex.getMessage());
ErrorResponse errorResponse = errorUtil.forAppException(appException);
log.error("Failed API operation recoverReceiptToBeSentByProxy - error: {}", errorResponse);
throw ex;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package it.gov.pagopa.wispconverter.controller.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.*;

import java.util.List;

@Data
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
@ToString
@JsonIgnoreProperties(ignoreUnknown = true)
public class RecoveryProxyReceiptRequest {

private List<String> receiptIds;
}
Loading

0 comments on commit 14d6bf1

Please sign in to comment.