Skip to content

Commit

Permalink
Merge pull request #3 from pagopa/PPANTT-81-feat-add-ec-rest-client
Browse files Browse the repository at this point in the history
[PPANTT-81] feat: add ec rest client
  • Loading branch information
alessio-cialini authored Sep 24, 2024
2 parents 70465b7 + 5488238 commit 714bb63
Show file tree
Hide file tree
Showing 36 changed files with 985 additions and 41 deletions.
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.14.2</quarkus.platform.version>
<surefire-plugin.version>3.0.0-M7</surefire-plugin.version>
<wiremock.version>3.9.1</wiremock.version>
</properties>

<dependencyManagement>
Expand Down Expand Up @@ -104,6 +105,12 @@
<version>5.13.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
<scope>test</scope>
<version>${wiremock.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jacoco</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package it.gov.pagopa.payment.options.clients;

import it.gov.pagopa.payment.options.clients.model.ConfigDataV1;
import it.gov.pagopa.payment.options.models.clients.cache.ConfigDataV1;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package it.gov.pagopa.payment.options.clients;

import com.fasterxml.jackson.databind.ObjectMapper;
import it.gov.pagopa.payment.options.exception.CreditorInstitutionException;
import it.gov.pagopa.payment.options.exception.PaymentOptionsException;
import it.gov.pagopa.payment.options.models.ErrorResponse;
import it.gov.pagopa.payment.options.models.clients.creditorInstitution.PaymentOptionsResponse;
import it.gov.pagopa.payment.options.models.enums.AppErrorCodeEnum;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.rest.client.RestClientBuilder;
import org.jboss.resteasy.reactive.ClientWebApplicationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.MalformedURLException;
import java.net.URL;

/**
* Rest Client for Creditor Institution services
*/
@ApplicationScoped
public class CreditorInstitutionRestClient {

private final Logger logger = LoggerFactory.getLogger(CreditorInstitutionRestClient.class);

@Inject
ObjectMapper objectMapper;

/**
*
* @param endpoint endpoint to use for the call (should be equivalent to the forwader(
* @param proxyHost proxy host, optional
* @param proxyPort proxy port, optional
* @param targetHost verify service host
* @param targetPort verify service port
* @param targetPath verify service path
* @param fiscalCode fiscal code to be used as input for the call
* @param noticeNumber notice number to be used as input for the call
* @return PaymentOptionResponse
* @throws MalformedURLException
* @throws PaymentOptionsException
*/
public PaymentOptionsResponse callEcPaymentOptionsVerify(
String endpoint, String proxyHost, Long proxyPort,
String targetHost, Long targetPort, String targetPath,
// String idPA, String idBrokerPA, String idStazione,
String fiscalCode, String noticeNumber)
throws MalformedURLException {

RestClientBuilder builder =
RestClientBuilder.newBuilder().baseUrl(new URL(endpoint));
if (proxyHost != null && proxyPort != null) {
builder = builder.proxyAddress(proxyHost, proxyPort.intValue());
}
CreditorInstitutionRestClientInterface ecRestClientInterface = builder.build(
CreditorInstitutionRestClientInterface.class);

try (Response response = ecRestClientInterface.verifyPaymentOptions(
fiscalCode, noticeNumber,
// idPA, idBrokerPA, idStazione,
targetHost, targetPort.intValue(),
targetPath)) {

return objectMapper.readValue(
response.readEntity(String.class), PaymentOptionsResponse.class);

} catch (ClientWebApplicationException clientWebApplicationException) {
logger.error("[Payment Options] Encountered REST client exception: {}",
clientWebApplicationException.getMessage());
manageErrorResponse(clientWebApplicationException.getResponse());
return null;
} catch (Exception e) {
logger.error("[Payment Options] Unable to call the station due to error: {}",
e.getMessage());
throw new PaymentOptionsException(
AppErrorCodeEnum.ODP_STAZIONE_INT_PA_IRRAGGIUNGIBILE, e.getMessage());
}

}

private void manageErrorResponse(Response response) {
try {
ErrorResponse errorResponse = objectMapper.readValue(
response.readEntity(String.class), ErrorResponse.class);
throw new CreditorInstitutionException(errorResponse,
"[Payment Options] Encountered a managed error calling the station REST endpoint");
} catch (CreditorInstitutionException creditorInstitutionException) {
throw creditorInstitutionException;
} catch (Exception e) {
logger.error("[Payment Options] Unable to call the station due to error: {}",
e.getMessage());
throw new PaymentOptionsException(
AppErrorCodeEnum.ODP_STAZIONE_INT_PA_IRRAGGIUNGIBILE, e.getMessage());
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package it.gov.pagopa.payment.options.clients;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam;

/**
* Template for the creditor institution REST client
*/
public interface CreditorInstitutionRestClientInterface {

@GET
@Path("/payment-options/organizations/{fiscal-code}/notices/{notice-number}")
@ClientHeaderParam(name = "Ocp-Apim-Subscription-Key", value = "${CreditorInstitutionRestClient.ocpSubKey}")
Response verifyPaymentOptions(
@PathParam("fiscal-code") String fiscalCode,
@PathParam("notice-number") String noticeNumber,
// @QueryParam("idPA") String idPA,
// @QueryParam("idBrokerPA") String idBrokerPA,
// @QueryParam("idStation") String idStation,
@HeaderParam("X-Host-Url") String hostUrl,
@HeaderParam("X-Host-Port") Integer hostPort,
@HeaderParam("X-Host-Path") String hostPath
);

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package it.gov.pagopa.payment.options.consumers;

import it.gov.pagopa.payment.options.models.CacheUpdateEvent;
import it.gov.pagopa.payment.options.models.events.CacheUpdateEvent;
import it.gov.pagopa.payment.options.services.ConfigCacheService;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import org.eclipse.microprofile.reactive.messaging.Incoming;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package it.gov.pagopa.payment.options.exception;

import it.gov.pagopa.payment.options.models.ErrorResponse;
import it.gov.pagopa.payment.options.models.enums.AppErrorCodeEnum;
import jakarta.ws.rs.core.Response;
import lombok.Getter;
import java.util.Objects;

@Getter
public class CreditorInstitutionException extends RuntimeException {

private ErrorResponse errorResponse;

/**
* Constructs new exception with provided error code and message
*
* @param errorResponse Error Response
* @param message Detail message
*/
public CreditorInstitutionException(ErrorResponse errorResponse, String message) {
super(message);
this.errorResponse = Objects.requireNonNull(errorResponse);
}

/**
* Constructs new exception with provided error response, message and cause
*
* @param errorResponse Error Response
* @param message Detail message
* @param cause Exception causing the constructed one
*/
public CreditorInstitutionException(ErrorResponse errorResponse, String message, Throwable cause) {
super(message, cause);
this.errorResponse = Objects.requireNonNull(errorResponse);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package it.gov.pagopa.payment.options.exception;

import it.gov.pagopa.payment.options.models.enums.AppErrorCodeEnum;
import jakarta.ws.rs.core.Response;
import lombok.Getter;
import java.util.Objects;

/**
* Base exception for PaymentOptions exceptions
*/
@Getter
public class PaymentOptionsException extends RuntimeException {

/**
* Error code of this exception
* -- GETTER --
* Returns error code
*
* @return Error code of this exception
*/
private final AppErrorCodeEnum errorCode;

/**
* Constructs new exception with provided error code and message
*
* @param errorCode Error code
* @param message Detail message
*/
public PaymentOptionsException(AppErrorCodeEnum errorCode, String message) {
super(message);
this.errorCode = Objects.requireNonNull(errorCode);
}

/**
* Constructs new exception with provided error code, message and cause
*
* @param errorCode Error code
* @param message Detail message
* @param cause Exception causing the constructed one
*/
public PaymentOptionsException(AppErrorCodeEnum errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = Objects.requireNonNull(errorCode);
}

public static Response.Status getHttpStatus(PaymentOptionsException e) {
return e.getErrorCode().getStatus();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package it.gov.pagopa.payment.options.exception.mapper;

import static it.gov.pagopa.payment.options.exception.PaymentOptionsException.getHttpStatus;
import static org.jboss.resteasy.reactive.RestResponse.Status.NOT_FOUND;
import static org.jboss.resteasy.reactive.RestResponse.StatusCode.INTERNAL_SERVER_ERROR;

import io.smallrye.mutiny.CompositeException;
import it.gov.pagopa.payment.options.exception.CreditorInstitutionException;
import it.gov.pagopa.payment.options.exception.PaymentOptionsException;
import it.gov.pagopa.payment.options.models.ErrorResponse;
import it.gov.pagopa.payment.options.models.enums.AppErrorCodeEnum;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import java.time.Instant;
import java.util.List;
import org.jboss.resteasy.reactive.server.ServerExceptionMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ExceptionMapper {

Logger logger = LoggerFactory.getLogger(ExceptionMapper.class);

private ErrorResponse buildErrorResponse(Response.Status status, AppErrorCodeEnum errorCode, String message) {
return ErrorResponse.builder()
.httpStatusCode(status.getStatusCode())
.httpStatusDescription(status.getReasonPhrase())
.appErrorCode(errorCode.getErrorCode())
.errorMessage(errorCode.getErrorMessage())
.timestamp(Instant.now().getEpochSecond())
.build();
}

@ServerExceptionMapper
public Response mapCompositeException(CompositeException exception) {
logger.error(exception.getMessage(), exception);
Exception composedException;
List<Throwable> causes = exception.getCauses();
composedException = (Exception) causes.get(causes.size() - 1);

if(composedException instanceof NotFoundException ex) {
return mapNotFoundException(ex);
} else if(composedException instanceof PaymentOptionsException paymentNoticeException) {
return mapPaymentNoticeException(paymentNoticeException);
} else if(composedException instanceof CreditorInstitutionException creditorInstitutionException) {
return mapCreditorInstitutionException(creditorInstitutionException);
} else {
return mapGenericException(exception);
}

}

@ServerExceptionMapper
public Response mapCreditorInstitutionException(CreditorInstitutionException creditorInstitutionException) {
logger.error(creditorInstitutionException.getMessage(), creditorInstitutionException);
return Response.status(creditorInstitutionException.getErrorResponse().getHttpStatusCode())
.entity(creditorInstitutionException.getErrorResponse()).build();
}

@ServerExceptionMapper
public Response mapNotFoundException(NotFoundException exception) {
logger.error(exception.getMessage(), exception);
return Response.status(NOT_FOUND).entity(
buildErrorResponse(Status.NOT_FOUND, AppErrorCodeEnum.ODP_SINTASSI,
"Invalid parameters on request")).build();
}

@ServerExceptionMapper
public Response mapPaymentNoticeException(PaymentOptionsException exception) {
logger.error(exception.getMessage(), exception);
Response.Status status = getHttpStatus(exception);
return Response.status(status).entity(buildErrorResponse(status,
exception.getErrorCode(), exception.getMessage())).build();
}

@ServerExceptionMapper
public Response mapGenericException(Exception exception) {
logger.error(exception.getMessage(), exception);
return Response.status(INTERNAL_SERVER_ERROR)
.entity(buildErrorResponse(
Response.Status.INTERNAL_SERVER_ERROR,
AppErrorCodeEnum.ODP_SYSTEM_ERROR,
"Unexpected Error"))
.build();
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package it.gov.pagopa.payment.options.models;

import it.gov.pagopa.payment.options.clients.model.ConfigDataV1;
import it.gov.pagopa.payment.options.models.clients.cache.ConfigDataV1;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package it.gov.pagopa.payment.options.models;

import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.Builder;
import lombok.Getter;
import lombok.extern.jackson.Jacksonized;
import org.eclipse.microprofile.openapi.annotations.media.Schema;

/**
* Model class for the error response
*/
@Getter
@Builder
@Jacksonized
@RegisterForReflection
public class ErrorResponse {

@Schema(example = "500")
private int httpStatusCode;

@Schema(example = "Internal Server Error")
private String httpStatusDescription;

@Schema(example = "An unexpected error has occurred. Please contact support.")
private String errorMessage;

@Schema(example = "ODP-<ERR_ID>")
private String appErrorCode;

@Schema(example = "1724425035")
private Long timestamp;

@Schema(example = "2024-08-23T14:57:15.635528")
private String dateTime;

}
Loading

0 comments on commit 714bb63

Please sign in to comment.