Skip to content

Commit

Permalink
[PAGOPA-2216] feat: refactored receipt recovery endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
andrea-deri committed Oct 3, 2024
1 parent 5f46ab1 commit 28d9146
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@
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 it.gov.pagopa.wispconverter.util.Trace;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.http.MediaType;
Expand All @@ -35,7 +34,6 @@

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

import java.io.IOException;
import java.io.StringReader;
import java.util.List;
Expand All @@ -48,9 +46,9 @@
@Slf4j
public class ReceiptController {

private static final String BP_RECEIPT_OK = "receipt-ok";
private static final String BP_RECEIPT_KO = "receipt-ko";
private static final String BP_RECEIPT_RETRIEVE = "receipt-retrieve";
public static final String BP_RECEIPT_OK = "receipt-ok";
public static final String BP_RECEIPT_KO = "receipt-ko";
public static final String BP_RECEIPT_RETRIEVE = "receipt-retrieve";

private final ReceiptService receiptService;

Expand All @@ -59,9 +57,9 @@ 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"})
Expand All @@ -75,7 +73,7 @@ public class ReceiptController {
public ResponseEntity<String> receiptRetrieve(@QueryParam("ci") String ci, @QueryParam("ccp") String ccp, @QueryParam("iuv") String iuv) {
try {
log.info("Invoking API operation receiptRetrieve - args: {}", ci, ccp, iuv);
if(rtReceiptCosmosService.receiptRtExist(ci, iuv, ccp))
if (rtReceiptCosmosService.receiptRtExist(ci, iuv, ccp))
return ResponseEntity.ok("");
else return ResponseEntity.notFound().build();
} catch (Exception ex) {
Expand Down Expand Up @@ -144,24 +142,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);
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;

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,8 +6,8 @@
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.RecoveryReceiptReportResponse;
import it.gov.pagopa.wispconverter.controller.model.RecoveryReceiptRequest;
import it.gov.pagopa.wispconverter.controller.model.RecoveryReceiptResponse;
import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum;
import it.gov.pagopa.wispconverter.exception.AppException;
Expand Down Expand Up @@ -72,7 +72,7 @@ public ResponseEntity<RecoveryReceiptResponse> recoverReceiptKOForCreditorInstit
log.info("Invoking API operation recoverReceiptKOForCreditorInstitution - args: {} {} {} {}", ci, iuv, dateFrom, dateTo);

RecoveryReceiptResponse recoveryReceiptResponse = recoveryService.recoverReceiptKOByIUV(ci, iuv, dateFrom, dateTo);
if(recoveryReceiptResponse != null) {
if (recoveryReceiptResponse != null) {
return ResponseEntity.ok(recoveryReceiptResponse);
} else {
// RPT with CI and IUV could not be recovered via API
Expand All @@ -94,17 +94,17 @@ public ResponseEntity<RecoveryReceiptResponse> recoverReceiptKOForCreditorInstit
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Reconciliation completed")
})
@PostMapping(value = "/proxy")
public ResponseEntity<RecoveryProxyReceiptResponse> recoverReceiptToBeSentByProxy(@RequestBody RecoveryProxyReceiptRequest request) {
@PostMapping(value = "/receipts")
public ResponseEntity<RecoveryReceiptReportResponse> recoverReceiptToBeReSent(@RequestBody RecoveryReceiptRequest request) {
try {
log.info("Invoking API operation recoverReceiptToBeSentByProxy - args: {}", request);
return ResponseEntity.ok(recoveryService.recoverReceiptToBeSentByProxy(request));
log.info("Invoking API operation recoverReceiptToBeReSent - args: {}", request);

Check failure

Code scanning / CodeQL

Log Injection High

This log entry depends on a
user-provided value
.
return ResponseEntity.ok(recoveryService.recoverReceiptToBeReSent(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);
log.error("Failed API operation recoverReceiptToBeReSent - error: {}", errorResponse);
throw ex;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@AllArgsConstructor
@ToString
@JsonIgnoreProperties(ignoreUnknown = true)
public class RecoveryProxyReceiptResponse {
public class RecoveryReceiptReportResponse {

private List<Pair<String, String>> receiptStatus;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
@AllArgsConstructor
@ToString
@JsonIgnoreProperties(ignoreUnknown = true)
public class RecoveryProxyReceiptRequest {
public class RecoveryReceiptRequest {

private List<String> receiptIds;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,26 @@
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
@Qualifier("secondaryCosmosTemplate")
public interface ReEventRepositorySecondary extends CosmosRepository<ReEventEntity, String> {

@Query("SELECT * FROM c " +
"WHERE (c.partitionKey >= @dateFrom AND c.partitionKey <= @dateTo) " +
"AND c.iuv = @iuv " +
"AND c.domainId = @organizationId")
"WHERE (c.partitionKey >= @dateFrom AND c.partitionKey <= @dateTo) " +
"AND c.iuv = @iuv " +
"AND c.domainId = @organizationId")
List<ReEventEntity> findByIuvAndOrganizationId(@Param("dateFrom") String dateFrom,
@Param("dateTo") String dateTo,
@Param("iuv") String iuv,
@Param("organizationId") String organizationId);


@Query("SELECT * FROM c " +
"WHERE (c.partitionKey >= @dateFrom AND c.partitionKey <= @dateTo) " +
"AND c.sessionId = @sessionId " +
"AND c.status = @status ORDER BY c._ts OFFSET 0 LIMIT @limit")
"WHERE (c.partitionKey >= @dateFrom AND c.partitionKey <= @dateTo) " +
"AND c.sessionId = @sessionId " +
"AND c.status = @status ORDER BY c._ts OFFSET 0 LIMIT @limit")
List<ReEventEntity> findBySessionIdAndStatus(@Param("dateFrom") String dateFrom,
@Param("dateTo") String dateTo,
@Param("sessionId") String sessionId,
Expand All @@ -53,4 +54,15 @@ List<ReEventEntity> findBySessionIdAndStatus(@Param("dateFrom") String dateFrom,
"WHERE wispSession.occurrences = 1"
)
List<SessionIdEntity> findSessionWithoutRedirect(@Param("dateFrom") String dateFrom, @Param("dateTo") String dateTo);

@Query("SELECT * FROM c " +
"WHERE (c.partitionKey >= @dateFrom AND c.partitionKey <= @dateTo) " +
"AND c.eventCategory = 'INTERFACE' AND c.eventSubcategory = 'REQ' " +
"AND c.businessProcess = @businessProcess " +
"AND c.operationId = @operationId " +
"AND c.status = @status ORDER BY c._ts OFFSET 0 LIMIT 1")
Optional<ReEventEntity> findFirstInterfaceRequest(@Param("dateFrom") String dateFrom,
@Param("dateTo") String dateTo,
@Param("businessProcess") String businessProcess,
@Param("operationId") String operationId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ public class ReceiptService {
@Value("${wisp-converter.rt-send.scheduling-time-in-minutes:60}")
private Integer schedulingTimeInMinutes;

public static IntestazionePPT generateHeader(String creditorInstitutionId, String iuv, String ccp, String brokerId, String stationId) {

gov.telematici.pagamenti.ws.nodoperpa.ppthead.ObjectFactory objectFactoryHead = new gov.telematici.pagamenti.ws.nodoperpa.ppthead.ObjectFactory();
IntestazionePPT header = objectFactoryHead.createIntestazionePPT();
header.setIdentificativoDominio(creditorInstitutionId);
header.setIdentificativoUnivocoVersamento(iuv);
header.setCodiceContestoPagamento(ccp);
header.setIdentificativoIntermediarioPA(brokerId);
header.setIdentificativoStazioneIntermediarioPA(stationId);
return header;
}

/**
* @param payload a list of {@link ReceiptDto} elements
Expand All @@ -117,7 +128,6 @@ public void sendKoPaaInviaRtToCreditorInstitution(String payload) {
sendKoPaaInviaRtToCreditorInstitution(receipts);
}


/**
* send a paaInviaRT with a KO. The body is generated from the list of receipts.
*
Expand Down Expand Up @@ -202,7 +212,6 @@ public void sendKoPaaInviaRtToCreditorInstitution(List<ReceiptDto> receipts) {
}
}


public void sendOkPaaInviaRtToCreditorInstitution(String payload) {

try {
Expand Down Expand Up @@ -258,13 +267,7 @@ public void sendOkPaaInviaRtToCreditorInstitution(String payload) {
);

// Generating the paaInviaRT payload from the RPT
JAXBElement<CtRicevutaTelematica> generatedReceipt = new it.gov.digitpa.schemas._2011.pagamenti.ObjectFactory()
.createRT(generateRTContentForOkReceipt(rpt, deepCopySendRTV2));
String rawGeneratedReceipt = jaxbElementUtil.objectToString(generatedReceipt);
String paaInviaRtPayload = generatePayloadAsRawString(intestazionePPT, commonFields.getSignatureType(), rawGeneratedReceipt, objectFactory);

// save receipt-rt
rtReceiptCosmosService.saveRTEntity(sessionData.getCommonFields().getSessionId(), rpt, ReceiptStatusEnum.SENDING, rawGeneratedReceipt, ReceiptTypeEnum.OK);
String paaInviaRtPayload = generateOkRtFromSessionData(rpt, deepCopySendRTV2, intestazionePPT, commonFields, objectFactory, ReceiptStatusEnum.SENDING);

// send receipt to the creditor institution and, if not correctly sent, add to queue for retry
sendReceiptToCreditorInstitution(sessionData, rpt, paaInviaRtPayload, receipt, rpt.getIuv(), noticeNumber, station, false);
Expand Down Expand Up @@ -305,7 +308,7 @@ private PaSendRTV2Request extractDataFromPaSendRT(String payload, RPTContentDTO

public String generateKoRtFromSessionData(String creditorInstitutionId, String iuv, RPTContentDTO rpt,
CommonFieldsDTO commonFields, gov.telematici.pagamenti.ws.papernodo.ObjectFactory objectFactory,
Map<String, ConfigurationKeyDto> configurations) {
Map<String, ConfigurationKeyDto> configurations, ReceiptStatusEnum receiptStatus) {
// generate the header for the paaInviaRT SOAP request. This object is common for each generated request
IntestazionePPT header = generateHeader(
creditorInstitutionId,
Expand All @@ -322,7 +325,21 @@ public String generateKoRtFromSessionData(String creditorInstitutionId, String i
String paaInviaRtPayload = generatePayloadAsRawString(header, null, rawGeneratedReceipt, objectFactory);

// save receipt-rt
rtReceiptCosmosService.saveRTEntity(commonFields.getSessionId(), rpt, ReceiptStatusEnum.SENDING, rawGeneratedReceipt, ReceiptTypeEnum.KO);
rtReceiptCosmosService.saveRTEntity(commonFields.getSessionId(), rpt, receiptStatus, rawGeneratedReceipt, ReceiptTypeEnum.KO);

return paaInviaRtPayload;
}

public String generateOkRtFromSessionData(RPTContentDTO rpt, PaSendRTV2Request paSendRTV2,
IntestazionePPT intestazionePPT, CommonFieldsDTO commonFields,
gov.telematici.pagamenti.ws.papernodo.ObjectFactory objectFactory, ReceiptStatusEnum receiptStatus) {
JAXBElement<CtRicevutaTelematica> generatedReceipt = new it.gov.digitpa.schemas._2011.pagamenti.ObjectFactory()
.createRT(generateRTContentForOkReceipt(rpt, paSendRTV2));
String rawGeneratedReceipt = jaxbElementUtil.objectToString(generatedReceipt);
String paaInviaRtPayload = generatePayloadAsRawString(intestazionePPT, commonFields.getSignatureType(), rawGeneratedReceipt, objectFactory);

// save receipt-rt
rtReceiptCosmosService.saveRTEntity(commonFields.getSessionId(), rpt, receiptStatus, rawGeneratedReceipt, ReceiptTypeEnum.OK);

return paaInviaRtPayload;
}
Expand Down Expand Up @@ -516,18 +533,6 @@ private CtRicevutaTelematica generateRTContentForOkReceipt(RPTContentDTO rpt, Pa
return ctRicevutaTelematica;
}

private IntestazionePPT generateHeader(String creditorInstitutionId, String iuv, String ccp, String brokerId, String stationId) {

gov.telematici.pagamenti.ws.nodoperpa.ppthead.ObjectFactory objectFactoryHead = new gov.telematici.pagamenti.ws.nodoperpa.ppthead.ObjectFactory();
IntestazionePPT header = objectFactoryHead.createIntestazionePPT();
header.setIdentificativoDominio(creditorInstitutionId);
header.setIdentificativoUnivocoVersamento(iuv);
header.setCodiceContestoPagamento(ccp);
header.setIdentificativoIntermediarioPA(brokerId);
header.setIdentificativoStazioneIntermediarioPA(stationId);
return header;
}

private String generatePayloadAsRawString(IntestazionePPT header, String signatureType, String receiptContent, gov.telematici.pagamenti.ws.papernodo.ObjectFactory objectFactory) {

// Generate paaInviaRT object, as JAXB element, with the RT in base64 format
Expand Down Expand Up @@ -741,7 +746,8 @@ public void sendRTKoFromSessionId(String sessionId, InternalStepStatus internalS
rpt,
sessionDataDTO.getCommonFields(),
objectFactory,
configurations);
configurations,
ReceiptStatusEnum.SENDING);
StationDto station = stations.get(sessionDataDTO.getCommonFields().getStationId());
ConnectionDto stationConnection = station.getConnection();
URI uri = CommonUtility.constructUrl(
Expand Down
Loading

0 comments on commit 28d9146

Please sign in to comment.