diff --git a/src/main/java/it/gov/pagopa/afm/calculator/config/LoggingAspect.java b/src/main/java/it/gov/pagopa/afm/calculator/config/LoggingAspect.java index d0be5ee0..a3758a61 100644 --- a/src/main/java/it/gov/pagopa/afm/calculator/config/LoggingAspect.java +++ b/src/main/java/it/gov/pagopa/afm/calculator/config/LoggingAspect.java @@ -30,124 +30,128 @@ @Slf4j public class LoggingAspect { - public static final String START_TIME = "startTime"; - public static final String METHOD = "method"; - public static final String STATUS = "status"; - public static final String CODE = "httpCode"; - public static final String RESPONSE_TIME = "responseTime"; - - @Value("${info.application.artifactId}") - private String artifactId; - - @Value("${info.application.version}") - private String version; - - @Value("${info.properties.environment}") - private String environment; - - @Autowired HttpServletRequest httRequest; - - @Autowired HttpServletResponse httpResponse; - - @Pointcut("@within(org.springframework.web.bind.annotation.RestController)") - public void restController() { - // all rest controllers - } - - @Pointcut("@within(org.springframework.stereotype.Repository)") - public void repository() { - // all repository methods - } - - @Pointcut("@within(org.springframework.stereotype.Service)") - public void service() { - // all service methods - } - - /** Log essential info of application during the startup. */ - @PostConstruct - public void logStartup() { - log.info("-> Starting {} version {} - environment {}", artifactId, version, environment); - } - - /** - * If DEBUG log-level is enabled prints the env variables and the application properties. - * - * @param event Context of application - */ - @EventListener - public void handleContextRefresh(ContextRefreshedEvent event) { - final Environment env = event.getApplicationContext().getEnvironment(); - log.debug("Active profiles: {}", Arrays.toString(env.getActiveProfiles())); - final MutablePropertySources sources = ((AbstractEnvironment) env).getPropertySources(); - StreamSupport.stream(sources.spliterator(), false) - .filter(EnumerablePropertySource.class::isInstance) - .map(ps -> ((EnumerablePropertySource) ps).getPropertyNames()) - .flatMap(Arrays::stream) - .distinct() - .filter( - prop -> - !(prop.toLowerCase().contains("credentials") - || prop.toLowerCase().contains("password") - || prop.toLowerCase().contains("pass") - || prop.toLowerCase().contains("pwd") - || prop.toLowerCase().contains("key") - || prop.toLowerCase().contains("secret"))) - .forEach(prop -> log.debug("{}: {}", prop, env.getProperty(prop))); - } - - @Around(value = "restController()") - public Object logApiInvocation(ProceedingJoinPoint joinPoint) throws Throwable { - MDC.put(METHOD, joinPoint.getSignature().getName()); - MDC.put(START_TIME, String.valueOf(System.currentTimeMillis())); - log.info("{} {}", httRequest.getMethod(), httRequest.getRequestURI()); - log.info( - "Invoking API operation {} - args: {}", - joinPoint.getSignature().getName(), - joinPoint.getArgs()); - - Object result = joinPoint.proceed(); - - MDC.put(STATUS, "OK"); - MDC.put(CODE, String.valueOf(httpResponse.getStatus())); - MDC.put(RESPONSE_TIME, getExecutionTime()); - log.info( - "Successful API operation {} - result: {}", joinPoint.getSignature().getName(), result); - MDC.remove(STATUS); - MDC.remove(CODE); - MDC.remove(RESPONSE_TIME); - MDC.remove(START_TIME); - return result; - } - - @AfterReturning(value = "execution(* *..exception.ErrorHandler.*(..))", returning = "result") - public void trowingApiInvocation(JoinPoint joinPoint, ResponseEntity result) { - MDC.put(STATUS, "KO"); - MDC.put(CODE, String.valueOf(result.getStatusCodeValue())); - MDC.put(RESPONSE_TIME, getExecutionTime()); - log.info("Failed API operation {} - error: {}", MDC.get(METHOD), result); - MDC.remove(STATUS); - MDC.remove(CODE); - MDC.remove(RESPONSE_TIME); - MDC.remove(START_TIME); - } - - @Around(value = "repository() || service()") - public Object logTrace(ProceedingJoinPoint joinPoint) throws Throwable { - log.debug( - "Call method {} - args: {}", joinPoint.getSignature().toShortString(), joinPoint.getArgs()); - Object result = joinPoint.proceed(); - log.debug("Return method {} - result: {}", joinPoint.getSignature().toShortString(), result); - return result; - } - - private static String getExecutionTime() { - String startTime = MDC.get(START_TIME); - if (startTime != null) { - long endTime = System.currentTimeMillis(); - long executionTime = endTime - Long.parseLong(startTime); - return String.valueOf(executionTime); + public static final String START_TIME = "startTime"; + public static final String METHOD = "method"; + public static final String STATUS = "status"; + public static final String CODE = "httpCode"; + public static final String RESPONSE_TIME = "responseTime"; + + @Value("${info.application.artifactId}") + private String artifactId; + + @Value("${info.application.version}") + private String version; + + @Value("${info.properties.environment}") + private String environment; + + @Autowired + HttpServletRequest httRequest; + + @Autowired + HttpServletResponse httpResponse; + + @Pointcut("@within(org.springframework.web.bind.annotation.RestController)") + public void restController() { + // all rest controllers + } + + @Pointcut("@within(org.springframework.stereotype.Repository)") + public void repository() { + // all repository methods + } + + @Pointcut("@within(org.springframework.stereotype.Service)") + public void service() { + // all service methods + } + + /** + * Log essential info of application during the startup. + */ + @PostConstruct + public void logStartup() { + log.info("-> Starting {} version {} - environment {}", artifactId, version, environment); + } + + /** + * If DEBUG log-level is enabled prints the env variables and the application properties. + * + * @param event Context of application + */ + @EventListener + public void handleContextRefresh(ContextRefreshedEvent event) { + final Environment env = event.getApplicationContext().getEnvironment(); + log.debug("Active profiles: {}", Arrays.toString(env.getActiveProfiles())); + final MutablePropertySources sources = ((AbstractEnvironment) env).getPropertySources(); + StreamSupport.stream(sources.spliterator(), false) + .filter(EnumerablePropertySource.class::isInstance) + .map(ps -> ((EnumerablePropertySource) ps).getPropertyNames()) + .flatMap(Arrays::stream) + .distinct() + .filter( + prop -> + !(prop.toLowerCase().contains("credentials") + || prop.toLowerCase().contains("password") + || prop.toLowerCase().contains("pass") + || prop.toLowerCase().contains("pwd") + || prop.toLowerCase().contains("key") + || prop.toLowerCase().contains("secret"))) + .forEach(prop -> log.debug("{}: {}", prop, env.getProperty(prop))); + } + + @Around(value = "restController()") + public Object logApiInvocation(ProceedingJoinPoint joinPoint) throws Throwable { + MDC.put(METHOD, joinPoint.getSignature().getName()); + MDC.put(START_TIME, String.valueOf(System.currentTimeMillis())); + log.info("{} {}", httRequest.getMethod(), httRequest.getRequestURI()); + log.info( + "Invoking API operation {} - args: {}", + joinPoint.getSignature().getName(), + joinPoint.getArgs()); + + Object result = joinPoint.proceed(); + + MDC.put(STATUS, "OK"); + MDC.put(CODE, String.valueOf(httpResponse.getStatus())); + MDC.put(RESPONSE_TIME, getExecutionTime()); + log.info( + "Successful API operation {} - result: {}", joinPoint.getSignature().getName(), result); + MDC.remove(STATUS); + MDC.remove(CODE); + MDC.remove(RESPONSE_TIME); + MDC.remove(START_TIME); + return result; + } + + @AfterReturning(value = "execution(* *..exception.ErrorHandler.*(..))", returning = "result") + public void trowingApiInvocation(JoinPoint joinPoint, ResponseEntity result) { + MDC.put(STATUS, "KO"); + MDC.put(CODE, String.valueOf(result.getStatusCodeValue())); + MDC.put(RESPONSE_TIME, getExecutionTime()); + log.info("Failed API operation {} - error: {}", MDC.get(METHOD), result); + MDC.remove(STATUS); + MDC.remove(CODE); + MDC.remove(RESPONSE_TIME); + MDC.remove(START_TIME); + } + + @Around(value = "repository() || service()") + public Object logTrace(ProceedingJoinPoint joinPoint) throws Throwable { + log.debug( + "Call method {} - args: {}", joinPoint.getSignature().toShortString(), joinPoint.getArgs()); + Object result = joinPoint.proceed(); + log.debug("Return method {} - result: {}", joinPoint.getSignature().toShortString(), result); + return result; + } + + private static String getExecutionTime() { + String startTime = MDC.get(START_TIME); + if(startTime != null) { + long endTime = System.currentTimeMillis(); + long executionTime = endTime - Long.parseLong(startTime); + return String.valueOf(executionTime); + } + return "1"; } - return "1"; - } } diff --git a/src/main/java/it/gov/pagopa/afm/calculator/service/CalculatorService.java b/src/main/java/it/gov/pagopa/afm/calculator/service/CalculatorService.java index aeac536b..ef5db1dc 100644 --- a/src/main/java/it/gov/pagopa/afm/calculator/service/CalculatorService.java +++ b/src/main/java/it/gov/pagopa/afm/calculator/service/CalculatorService.java @@ -1,8 +1,5 @@ package it.gov.pagopa.afm.calculator.service; -import static it.gov.pagopa.afm.calculator.service.UtilityComponent.inTransferList; -import static it.gov.pagopa.afm.calculator.service.UtilityComponent.isGlobal; - import it.gov.pagopa.afm.calculator.entity.CiBundle; import it.gov.pagopa.afm.calculator.entity.IssuerRangeEntity; import it.gov.pagopa.afm.calculator.entity.ValidBundle; @@ -13,12 +10,6 @@ import it.gov.pagopa.afm.calculator.model.calculator.BundleOption; import it.gov.pagopa.afm.calculator.model.calculator.Transfer; import it.gov.pagopa.afm.calculator.repository.CosmosRepository; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import javax.validation.Valid; import lombok.Setter; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -27,6 +18,16 @@ import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; +import javax.validation.Valid; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static it.gov.pagopa.afm.calculator.service.UtilityComponent.inTransferList; +import static it.gov.pagopa.afm.calculator.service.UtilityComponent.isGlobal; + @Service @Setter public class CalculatorService { @@ -113,6 +114,8 @@ private List calculateTaxPayerFee( // sort according onus and taxpayer fee Collections.sort(transfers); + sortByFeePerPsp(transfers); + return transfers.stream().limit(limit).collect(Collectors.toList()); } @@ -167,7 +170,9 @@ private void analyzeTransferList( for (CiBundle cibundle : ciBundles) { if (cibundle.getAttributes() != null && !cibundle.getAttributes().isEmpty()) { transfers.addAll( - cibundle.getAttributes().parallelStream() + cibundle + .getAttributes() + .parallelStream() .filter( attribute -> (attribute.getTransferCategory() != null @@ -180,7 +185,9 @@ private void analyzeTransferList( createTransfer(bundle.getPaymentAmount(), 0, bundle, null, paymentOption)) .collect(Collectors.toList())); transfers.addAll( - cibundle.getAttributes().parallelStream() + cibundle + .getAttributes() + .parallelStream() .filter( attribute -> (attribute.getTransferCategory() == null @@ -273,4 +280,21 @@ && isOnusBundle(bundle)) { private boolean isBelowThreshold(long paymentAmount) { return paymentAmount < Long.parseLong(StringUtils.trim(amountThreshold)); } + + /** + * sort by bundles' fee grouped by PSP + * + * @param transfers list of transfers to sort + */ + private static void sortByFeePerPsp(List transfers) { + transfers.sort( + (t1, t2) -> { + int primarySort = t1.getIdPsp().compareTo(t2.getIdPsp()); + if (primarySort == 0) { + // if two bundles are of the same PSP we'll sort by fees + return t1.getTaxPayerFee().compareTo(t2.getTaxPayerFee()); + } + return 0; // fixed to 0 because we don't want to sort by PSP name. + }); + } }