diff --git a/helm/Chart.yaml b/helm/Chart.yaml index f45146b..494ca23 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: pagopa-afm-calculator description: Microservice that handles calculation for pagoPA Advanced Fees Management type: application -version: 0.35.0 -appVersion: 0.0.1 +version: 0.36.0 +appVersion: 0.0.1-1-NOD-745-nodo-cfg-sync-coprire-l-80-del-codice-con-gli-unit-test dependencies: - name: microservice-chart version: 2.8.0 diff --git a/helm/values-dev.yaml b/helm/values-dev.yaml index 6bde08e..86ed672 100644 --- a/helm/values-dev.yaml +++ b/helm/values-dev.yaml @@ -4,7 +4,7 @@ microservice-chart: fullnameOverride: "" image: repository: ghcr.io/pagopa/pagopa-node-cfg-sync - tag: "0.0.1" + tag: "0.0.1-1-NOD-745-nodo-cfg-sync-coprire-l-80-del-codice-con-gli-unit-test" pullPolicy: Always livenessProbe: httpGet: @@ -64,12 +64,12 @@ microservice-chart: envConfig: WEBSITE_SITE_NAME: 'pagopa-node-cfg-sync' ENV: 'azure-dev' - APP_LOGGING_LEVEL: 'DEBUG' + APP_LOGGING_LEVEL: 'INFO' DEFAULT_LOGGING_LEVEL: 'INFO' CORS_CONFIGURATION: '{"origins": ["*"], "methods": ["*"]}' DB_NEXI_POSTGRES_URL: "jdbc:postgresql://db-postgres-ndp.d.db-nodo-pagamenti.com:5444/ndpspct?prepareThreshold=0¤tSchema=NODO4_CFG" DB_NEXI_POSTGRES_USER: "pp_nodo4_cfg" - DB_NEXI_POSTGRES_ENABLED: "true" + DB_NEXI_POSTGRES_ENABLED: "false" DB_NEXI_POSTGRES_SERVICE_IDENTIFIER: "NDP004DEV" DB_NEXI_ORACLE_URL: "jdbc:oracle:thin:@(DESCRIPTION=(ENABLE=BROKEN)(ADDRESS=(PROTOCOL=tcp)(PORT=1522)(HOST=db-nodo-pagamenti.d.db-nodo-pagamenti.com))(CONNECT_DATA=(SERVICE_NAME=NDPSPCT_PP_NODO4_CFG))(SO_KEEPALIVE=true)(TCP_KEEPIDLE=60)(TCP_KEEPINTVL=30)(TCP_KEEPCNT=15))" DB_NEXI_ORACLE_USER: "NODO4_CFG" @@ -79,7 +79,7 @@ microservice-chart: DB_PAGOPA_POSTGRES_USER: "cfg" DB_PAGOPA_POSTGRES_ENABLED: "true" DB_PAGOPA_POSTGRES_SERVICE_IDENTIFIER: "NDP001DEV" - API_CONFIG_CACHE_URL: "https://api.dev.platform.pagopa.it/api-config-cache/p/v1/stakeholders/node/cache/schemas/v1" + API_CONFIG_CACHE_URL: "https://api.dev.platform.pagopa.it/api-config-cache/o/v1/stakeholders/node/cache/schemas/v1" STAND_IN_MANAGER_URL: "https://api.dev.platform.pagopa.it/stand-in-manager/api/v1" OTEL_SERVICE_NAME: "pagopanodecfgsync" OTEL_RESOURCE_ATTRIBUTES: "deployment.environment=dev" diff --git a/helm/values-prod.yaml b/helm/values-prod.yaml index 28545d5..d3c3bbe 100644 --- a/helm/values-prod.yaml +++ b/helm/values-prod.yaml @@ -4,7 +4,7 @@ microservice-chart: fullnameOverride: "" image: repository: ghcr.io/pagopa/pagopa-node-cfg-sync - tag: "0.0.1" + tag: "0.0.1-1-NOD-745-nodo-cfg-sync-coprire-l-80-del-codice-con-gli-unit-test" pullPolicy: Always livenessProbe: httpGet: @@ -47,8 +47,8 @@ microservice-chart: memory: "512Mi" cpu: "0.25" limits: - memory: "512Mi" - cpu: "0.5" + memory: "1G" + cpu: "1.0" autoscaling: enable: true minReplica: 1 @@ -64,10 +64,23 @@ microservice-chart: envConfig: WEBSITE_SITE_NAME: 'pagopa-node-cfg-sync' # required to show cloud role name in application insights ENV: 'azure-prod' - APP_LOGGING_LEVEL: 'DEBUG' + APP_LOGGING_LEVEL: 'INFO' DEFAULT_LOGGING_LEVEL: 'INFO' CORS_CONFIGURATION: '{"origins": ["*"], "methods": ["*"]}' - CACHET_TX_NAME: "nodo-dei-pagamenti-cache" + DB_NEXI_POSTGRES_URL: "jdbc:postgresql://db-postgres-ndp.p.db-nodo-pagamenti.com:5444/ndpspcp?prepareThreshold=0¤tSchema=NODO4_CFG" + DB_NEXI_POSTGRES_USER: "pp_nodo4_cfg" + DB_NEXI_POSTGRES_ENABLED: "false" + DB_NEXI_POSTGRES_SERVICE_IDENTIFIER: "NDP004DEV" + DB_CONFIG_URL: "jdbc:oracle:thin:@(DESCRIPTION=(ENABLE=BROKEN)(ADDRESS=(PROTOCOL=tcp)(PORT=1521)(HOST=db-nodo-pagamenti.p.db-nodo-pagamenti.com))(CONNECT_DATA=(SERVICE_NAME=NDPSPCP_NODO4_CFG))(SO_KEEPALIVE=true)(TCP_KEEPIDLE=60)(TCP_KEEPINTVL=30)(TCP_KEEPCNT=15))" + DB_NEXI_ORACLE_USER: "NODO4_CFG" + DB_NEXI_ORACLE_ENABLED: "true" + DB_NEXI_ORACLE_SERVICE_IDENTIFIER: "NDP003DEV" + DB_PAGOPA_POSTGRES_URL: "jdbc:postgresql://ndp.p.db-nodo-pagamenti.com:6432/nodo?sslmode=require&prepareThreshold=0¤tSchema=cfg" + DB_PAGOPA_POSTGRES_USER: "cfg" + DB_PAGOPA_POSTGRES_ENABLED: "true" + DB_PAGOPA_POSTGRES_SERVICE_IDENTIFIER: "NDP001DEV" + API_CONFIG_CACHE_URL: "https://api.uat.platform.pagopa.it/api-config-cache/o/v1/stakeholders/node/cache/schemas/v1" + STAND_IN_MANAGER_URL: "https://api.uat.platform.pagopa.it/stand-in-manager/api/v1" OTEL_SERVICE_NAME: "pagopanodecfgsync" OTEL_RESOURCE_ATTRIBUTES: "deployment.environment=prod" OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-collector.elastic-system.svc:4317" @@ -78,12 +91,18 @@ microservice-chart: envSecret: # required APPLICATIONINSIGHTS_CONNECTION_STRING: 'ai-p-connection-string' - CACHE_TX_CONNECTION_STRING: "nodo-dei-pagamenti-cache-tx-connection-string-key" - OTEL_EXPORTER_OTLP_HEADERS: elastic-apm-secret-token + DB_PAGOPA_POSTGRES_PASSWORD: "db-cfg-password" + DB_NEXI_POSTGRES_PASSWORD: "db-nexi-cfg-password" + DB_NEXI_ORACLE_PASSWORD: "oracle-db-cfg-password" + API_CONFIG_CACHE_RX_CONNECTION_STRING: "nodo-dei-pagamenti-cache-sync-rx-connection-string-key" + API_CONFIG_CACHE_SA_CONNECTION_STRING: "nodo-dei-pagamenti-cache-sync-sa-connection-string-key" + STAND_IN_MANAGER_RX_CONNECTION_STRING: "nodo-dei-pagamenti-stand-in-sync-rx-connection-string-key" + STAND_IN_MANAGER_SA_CONNECTION_STRING: "nodo-dei-pagamenti-stand-in-sync-sa-connection-string-key" API_CONFIG_CACHE_SUBSCRIPTION_KEY: "cfg-for-node-subscription-key" STAND_IN_MANAGER_SUBSCRIPTION_KEY: "cfg-for-node-subscription-key" + OTEL_EXPORTER_OTLP_HEADERS: "elastic-apm-secret-token" keyvault: - name: "pagopa-d-nodo-kv" + name: "pagopa-p-nodo-kv" tenantId: "7788edaf-0346-4068-9d79-c868aed15b3d" nodeSelector: {} tolerations: [] @@ -119,7 +138,7 @@ microservice-chart: deployment: create: true image: - repository: ghcr.io/pagopa/yourname # TODO + repository: ghcr.io/pagopa/pagopa-node-cfg-sync tag: "0.0.0" pullPolicy: Always envConfig: {} diff --git a/helm/values-uat.yaml b/helm/values-uat.yaml index 4422eda..9d995e6 100644 --- a/helm/values-uat.yaml +++ b/helm/values-uat.yaml @@ -4,7 +4,7 @@ microservice-chart: fullnameOverride: "" image: repository: ghcr.io/pagopa/pagopa-node-cfg-sync - tag: "0.0.1" + tag: "0.0.1-1-NOD-745-nodo-cfg-sync-coprire-l-80-del-codice-con-gli-unit-test" pullPolicy: Always livenessProbe: httpGet: @@ -47,8 +47,8 @@ microservice-chart: memory: "512Mi" cpu: "0.25" limits: - memory: "512Mi" - cpu: "0.5" + memory: "1G" + cpu: "1.0" autoscaling: enable: true minReplica: 1 @@ -62,13 +62,24 @@ microservice-chart: type: Utilization # Allowed types are 'Utilization' or 'AverageValue' value: "75" envConfig: - WEBSITE_SITE_NAME: 'pagopa-node-cfg-sync' # required to show cloud role name in application insights + WEBSITE_SITE_NAME: 'pagopa-node-cfg-sync' ENV: 'azure-uat' - APP_LOGGING_LEVEL: 'DEBUG' + APP_LOGGING_LEVEL: 'INFO' DEFAULT_LOGGING_LEVEL: 'INFO' CORS_CONFIGURATION: '{"origins": ["*"], "methods": ["*"]}' - CACHET_TX_NAME: "nodo-dei-pagamenti-cache" - API_CONFIG_CACHE_URL: "https://api.uat.platform.pagopa.it/api-config-cache/p/v1/stakeholders/node/cache/schemas/v1" + DB_NEXI_POSTGRES_URL: "jdbc:postgresql://db-postgres-ndp.u.db-nodo-pagamenti.com:5444/ndpspca?prepareThreshold=0¤tSchema=NODO4_CFG" + DB_NEXI_POSTGRES_USER: "pp_nodo4_cfg" + DB_NEXI_POSTGRES_ENABLED: "false" + DB_NEXI_POSTGRES_SERVICE_IDENTIFIER: "NDP004DEV" + DB_CONFIG_URL: "jdbc:oracle:thin:@(DESCRIPTION=(ENABLE=BROKEN)(ADDRESS=(PROTOCOL=tcp)(PORT=1524)(HOST=db-nodo-pagamenti.u.db-nodo-pagamenti.com))(CONNECT_DATA=(SERVICE_NAME=NDPSPCA_NODO4_CFG))(SO_KEEPALIVE=true)(TCP_KEEPIDLE=60)(TCP_KEEPINTVL=30)(TCP_KEEPCNT=15))" + DB_NEXI_ORACLE_USER: "NODO4_CFG" + DB_NEXI_ORACLE_ENABLED: "true" + DB_NEXI_ORACLE_SERVICE_IDENTIFIER: "NDP003DEV" + DB_PAGOPA_POSTGRES_URL: "jdbc:postgresql://ndp.u.db-nodo-pagamenti.com:6432/nodo?sslmode=require&prepareThreshold=0¤tSchema=cfg" + DB_PAGOPA_POSTGRES_USER: "cfg" + DB_PAGOPA_POSTGRES_ENABLED: "true" + DB_PAGOPA_POSTGRES_SERVICE_IDENTIFIER: "NDP001DEV" + API_CONFIG_CACHE_URL: "https://api.uat.platform.pagopa.it/api-config-cache/o/v1/stakeholders/node/cache/schemas/v1" STAND_IN_MANAGER_URL: "https://api.uat.platform.pagopa.it/stand-in-manager/api/v1" OTEL_SERVICE_NAME: "pagopanodecfgsync" OTEL_RESOURCE_ATTRIBUTES: "deployment.environment=uat" @@ -80,12 +91,18 @@ microservice-chart: envSecret: # required APPLICATIONINSIGHTS_CONNECTION_STRING: 'ai-u-connection-string' - CACHE_TX_CONNECTION_STRING: "nodo-dei-pagamenti-cache-tx-connection-string-key" - OTEL_EXPORTER_OTLP_HEADERS: elastic-apm-secret-token + DB_PAGOPA_POSTGRES_PASSWORD: "db-cfg-password" + DB_NEXI_POSTGRES_PASSWORD: "db-nexi-cfg-password" + DB_NEXI_ORACLE_PASSWORD: "oracle-db-cfg-password" + API_CONFIG_CACHE_RX_CONNECTION_STRING: "nodo-dei-pagamenti-cache-sync-rx-connection-string-key" + API_CONFIG_CACHE_SA_CONNECTION_STRING: "nodo-dei-pagamenti-cache-sync-sa-connection-string-key" + STAND_IN_MANAGER_RX_CONNECTION_STRING: "nodo-dei-pagamenti-stand-in-sync-rx-connection-string-key" + STAND_IN_MANAGER_SA_CONNECTION_STRING: "nodo-dei-pagamenti-stand-in-sync-sa-connection-string-key" API_CONFIG_CACHE_SUBSCRIPTION_KEY: "cfg-for-node-subscription-key" STAND_IN_MANAGER_SUBSCRIPTION_KEY: "cfg-for-node-subscription-key" + OTEL_EXPORTER_OTLP_HEADERS: "elastic-apm-secret-token" keyvault: - name: "pagopa-d-nodo-kv" + name: "pagopa-u-nodo-kv" tenantId: "7788edaf-0346-4068-9d79-c868aed15b3d" nodeSelector: {} tolerations: [] diff --git a/openapi/openapi.json b/openapi/openapi.json index 59b57a2..984a107 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -4,7 +4,7 @@ "title": "cfg-sync", "description": "Microservice to update configuration schema of Nodo dei Pagamenti", "termsOfService": "https://www.pagopa.gov.it/", - "version": "0.0.1" + "version": "0.0.1-1-NOD-745-nodo-cfg-sync-coprire-l-80-del-codice-con-gli-unit-test" }, "servers": [ { @@ -265,10 +265,10 @@ "description": "Database sync status result", "example": "done", "enum": [ - "done", - "disabled", - "rollback", - "error" + "DONE", + "DISABLED", + "ROLLBACK", + "ERROR" ] } } diff --git a/pom.xml b/pom.xml index 61a9b8b..1269903 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ it.gov.pagopa.node cfg-sync - 0.0.1 + 0.0.1-1-NOD-745-nodo-cfg-sync-coprire-l-80-del-codice-con-gli-unit-test Microservice to update configuration schema of Nodo dei Pagamenti diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/client/model/StandInManagerResponse.java b/src/main/java/it/gov/pagopa/node/cfgsync/client/model/StandInManagerResponse.java deleted file mode 100644 index bb0892b..0000000 --- a/src/main/java/it/gov/pagopa/node/cfgsync/client/model/StandInManagerResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.gov.pagopa.node.cfgsync.client.model; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -@Data -@NoArgsConstructor -@AllArgsConstructor -public class StandInManagerResponse { - - private List stations; -} diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/config/OpenApiConfig.java b/src/main/java/it/gov/pagopa/node/cfgsync/config/OpenApiConfig.java index 1f76a00..2dece0d 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/config/OpenApiConfig.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/config/OpenApiConfig.java @@ -9,17 +9,17 @@ import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.responses.ApiResponses; import io.swagger.v3.oas.models.security.SecurityScheme; -import java.util.Collections; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; - import it.gov.pagopa.node.cfgsync.util.Constants; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + @Configuration public class OpenApiConfig { diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/config/RequestFilter.java b/src/main/java/it/gov/pagopa/node/cfgsync/config/RequestFilter.java index e3f7563..f48def4 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/config/RequestFilter.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/config/RequestFilter.java @@ -1,15 +1,5 @@ package it.gov.pagopa.node.cfgsync.config; -import java.io.IOException; -import java.util.UUID; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import it.gov.pagopa.node.cfgsync.util.Constants; import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; @@ -17,6 +7,12 @@ import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.UUID; + @Component @Order(Ordered.HIGHEST_PRECEDENCE) @Slf4j diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/config/ResponseValidator.java b/src/main/java/it/gov/pagopa/node/cfgsync/config/ResponseValidator.java index eb5a089..d2db6d3 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/config/ResponseValidator.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/config/ResponseValidator.java @@ -1,9 +1,6 @@ package it.gov.pagopa.node.cfgsync.config; import it.gov.pagopa.node.cfgsync.exception.AppException; -import java.util.Set; -import javax.validation.ConstraintViolation; -import javax.validation.Validator; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; @@ -13,6 +10,10 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; +import javax.validation.ConstraintViolation; +import javax.validation.Validator; +import java.util.Set; + @Aspect @Component public class ResponseValidator { @@ -28,7 +29,7 @@ public class ResponseValidator { * @param result the response to validate */ - @AfterReturning(pointcut = "execution(* it.gov.pagopa.node.cfg_sync.controller.*.*(..))", returning = "result") + @AfterReturning(pointcut = "execution(* it.gov.pagopa.node.cfgsync.controller.*.*(..))", returning = "result") public void validateResponse(JoinPoint joinPoint, Object result) { if (result instanceof ResponseEntity) { validateResponse((ResponseEntity) result); diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/controller/SyncCacheController.java b/src/main/java/it/gov/pagopa/node/cfgsync/controller/SyncCacheController.java index 4095028..a3f19bb 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/controller/SyncCacheController.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/controller/SyncCacheController.java @@ -10,10 +10,11 @@ import it.gov.pagopa.node.cfgsync.model.ProblemJson; import it.gov.pagopa.node.cfgsync.model.SyncStatusEnum; import it.gov.pagopa.node.cfgsync.model.SyncStatusResponse; +import it.gov.pagopa.node.cfgsync.model.TargetRefreshEnum; import it.gov.pagopa.node.cfgsync.service.ApiConfigCacheService; import it.gov.pagopa.node.cfgsync.service.StandInManagerService; +import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; @@ -29,14 +30,12 @@ @RestController @RequestMapping("/ndp") @Validated +@AllArgsConstructor public class SyncCacheController { - @Autowired private ApiConfigCacheService apiConfigCacheService; - @Autowired private StandInManagerService standInManagerService; - @Operation( summary = "Force stand-in configuration to update", security = {@SecurityRequirement(name = "ApiKey")}, @@ -81,8 +80,8 @@ public class SyncCacheController { produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> standin() { - log.debug("Force stand-in configuration to update"); - Map syncStatusEnumMap = standInManagerService.forceStandIn(); + log.debug("[NODE-CFG-SYNC] Force {}, configuration to update", TargetRefreshEnum.standin.label); + Map syncStatusEnumMap = standInManagerService.syncStandIn(); List syncStatusResponseList = syncStatusEnumMap.entrySet() .stream() @@ -136,8 +135,8 @@ public ResponseEntity> standin() { value = "/cache", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> cache() { - log.debug("Force cache configuration to update"); - Map syncStatusEnumMap = apiConfigCacheService.forceCacheUpdate(); + log.debug("[NODE-CFG-SYNC] Force {} configuration to update", TargetRefreshEnum.cache.label); + Map syncStatusEnumMap = apiConfigCacheService.syncCache(); List syncStatusResponseList = syncStatusEnumMap.entrySet() .stream() diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/exception/AppException.java b/src/main/java/it/gov/pagopa/node/cfgsync/exception/AppException.java index f9099ec..69b0fde 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/exception/AppException.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/exception/AppException.java @@ -1,12 +1,13 @@ package it.gov.pagopa.node.cfgsync.exception; -import java.util.Formatter; -import javax.validation.constraints.NotNull; import lombok.EqualsAndHashCode; import lombok.Value; import org.springframework.http.HttpStatus; import org.springframework.validation.annotation.Validated; +import javax.validation.constraints.NotNull; +import java.util.Formatter; + /** * Custom exception. *

See {@link ErrorHandler} @@ -26,19 +27,6 @@ public class AppException extends RuntimeException { */ HttpStatus httpStatus; - /** - * @param httpStatus HTTP status returned to the response - * @param title title returned to the response when this exception occurred - * @param message the detail message returend to the response - * @param cause The cause of this {@link AppException} - */ - public AppException(@NotNull HttpStatus httpStatus, @NotNull String title, - @NotNull String message, Throwable cause) { - super(message, cause); - this.title = title; - this.httpStatus = httpStatus; - } - /** * @param httpStatus HTTP status returned to the response * @param title title returned to the response when this exception occurred @@ -51,7 +39,6 @@ public AppException(@NotNull HttpStatus httpStatus, @NotNull String title, this.httpStatus = httpStatus; } - /** * @param appError Response template returned to the response * @param args {@link Formatter} replaces the placeholders in "details" string of @@ -64,19 +51,6 @@ public AppException(@NotNull AppError appError, Object... args) { this.title = appError.title; } - /** - * @param appError Response template returned to the response - * @param cause The cause of this {@link AppException} - * @param args Arguments for the details of {@link AppError} replaced by the - * {@link Formatter}. If there are more arguments than format specifiers, the - * extra arguments are ignored. - */ - public AppException(@NotNull AppError appError, Throwable cause, Object... args) { - super(formatDetails(appError, args), cause); - this.httpStatus = appError.httpStatus; - this.title = appError.title; - } - private static String formatDetails(AppError appError, Object[] args) { return String.format(appError.details, args); } diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/exception/ErrorHandler.java b/src/main/java/it/gov/pagopa/node/cfgsync/exception/ErrorHandler.java index 6ff4386..7e00900 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/exception/ErrorHandler.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/exception/ErrorHandler.java @@ -1,24 +1,12 @@ package it.gov.pagopa.node.cfgsync.exception; import it.gov.pagopa.node.cfgsync.model.ProblemJson; -import java.util.ArrayList; -import java.util.List; - import lombok.extern.slf4j.Slf4j; -import org.hibernate.exception.ConstraintViolationException; -import org.springframework.beans.TypeMismatchException; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.validation.FieldError; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.WebRequest; -import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; @@ -29,165 +17,7 @@ @Slf4j public class ErrorHandler extends ResponseEntityExceptionHandler { - public static final String INTERNAL_SERVER_ERROR = "INTERNAL SERVER ERROR"; - public static final String BAD_REQUEST = "BAD REQUEST"; - public static final String FOREIGN_KEY_VIOLATION = "23503"; - public static final int CHILD_RECORD_VIOLATION = 2292; - - - /** - * Handle if the input request is not a valid JSON - * - * @param ex {@link HttpMessageNotReadableException} exception raised - * @param headers of the response - * @param status of the response - * @param request from frontend - * @return a {@link ProblemJson} as response with the cause and with a 400 as HTTP status - */ - @Override - public ResponseEntity handleHttpMessageNotReadable(HttpMessageNotReadableException ex, - HttpHeaders headers, HttpStatus status, WebRequest request) { - log.warn("Input not readable: ", ex); - var errorResponse = ProblemJson.builder() - .status(HttpStatus.BAD_REQUEST.value()) - .title(BAD_REQUEST) - .detail("Invalid input format") - .build(); - return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); - } - - /** - * Handle if missing some request parameters in the request - * - * @param ex {@link MissingServletRequestParameterException} exception raised - * @param headers of the response - * @param status of the response - * @param request from frontend - * @return a {@link ProblemJson} as response with the cause and with a 400 as HTTP status - */ - @Override - public ResponseEntity handleMissingServletRequestParameter( - MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, - WebRequest request) { - log.warn("Missing request parameter: ", ex); - var errorResponse = ProblemJson.builder() - .status(HttpStatus.BAD_REQUEST.value()) - .title(BAD_REQUEST) - .detail(ex.getMessage()) - .build(); - return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); - } - - - /** - * Customize the response for TypeMismatchException. - * - * @param ex the exception - * @param headers the headers to be written to the response - * @param status the selected response status - * @param request the current request - * @return a {@code ResponseEntity} instance - */ - @Override - protected ResponseEntity handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers, - HttpStatus status, WebRequest request) { - log.warn("Type mismatch: ", ex); - var errorResponse = ProblemJson.builder() - .status(HttpStatus.BAD_REQUEST.value()) - .title(BAD_REQUEST) - .detail(String.format("Invalid value %s for property %s", ex.getValue(), - ((MethodArgumentTypeMismatchException) ex).getName())) - .build(); - return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); - } - - /** - * Handle if validation constraints are unsatisfied - * - * @param ex {@link MethodArgumentNotValidException} exception raised - * @param headers of the response - * @param status of the response - * @param request from frontend - * @return a {@link ProblemJson} as response with the cause and with a 400 as HTTP status - */ - @Override - protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, - HttpHeaders headers, HttpStatus status, WebRequest request) { - List details = new ArrayList<>(); - for (FieldError error : ex.getBindingResult().getFieldErrors()) { - details.add(error.getField() + ": " + error.getDefaultMessage()); - } - var detailsMessage = String.join(", ", details); - log.warn("Input not valid: " + detailsMessage); - var errorResponse = ProblemJson.builder() - .status(HttpStatus.BAD_REQUEST.value()) - .title(BAD_REQUEST) - .detail(detailsMessage) - .build(); - return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); - } - - @ExceptionHandler({javax.validation.ConstraintViolationException.class}) - public ResponseEntity handleConstraintViolationException( - final javax.validation.ConstraintViolationException ex, final WebRequest request) { - log.warn("Validation Error raised:", ex); - var errorResponse = ProblemJson.builder() - .status(HttpStatus.BAD_REQUEST.value()) - .title(BAD_REQUEST) - .detail(ex.getMessage()) - .build(); - return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); - } - - - /** - * @param ex {@link DataIntegrityViolationException} exception raised when the SQL statement - * cannot be executed - * @param request from frontend - * @return a {@link ProblemJson} as response with the cause and with an appropriated HTTP status - */ - @ExceptionHandler({DataIntegrityViolationException.class}) - public ResponseEntity handleDataIntegrityViolationException( - final DataIntegrityViolationException ex, final WebRequest request) { - ProblemJson errorResponse = null; - - if (ex.getCause() instanceof ConstraintViolationException constraintViolationException) { - String sqlState = constraintViolationException.getSQLState(); - var errorCode = constraintViolationException.getSQLException().getErrorCode(); - // check the reason of ConstraintViolationException: is true if the object is referenced by a foreign key - // more info: https://docs.oracle.com/javadb/10.8.3.0/ref/rrefexcept71493.html - if (sqlState.equals(FOREIGN_KEY_VIOLATION)) { - log.warn("Can't delete from Database", ex); - errorResponse = ProblemJson.builder() - .status(HttpStatus.CONFLICT.value()) - .title("Conflict with the current state of the resource") - .detail("There is a relation with other resource. Delete it first.") - .build(); - } - if (errorCode == CHILD_RECORD_VIOLATION) { - log.warn("Can't update the Database", ex); - errorResponse = ProblemJson.builder() - .status(HttpStatus.CONFLICT.value()) - .title("Conflict with the current state of the resource") - .detail("There is a relation with other resource. Delete it first.") - .build(); - } - } - - // default response - if (errorResponse == null) { - log.warn("Data Integrity Violation", ex); - errorResponse = ProblemJson.builder() - .status(HttpStatus.INTERNAL_SERVER_ERROR.value()) - .title(INTERNAL_SERVER_ERROR) - .detail(ex.getMessage()) - .build(); - } - - return new ResponseEntity<>(errorResponse, HttpStatus.valueOf(errorResponse.getStatus())); - } - /** * Handle if a {@link AppException} is raised diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/model/AppCorsConfiguration.java b/src/main/java/it/gov/pagopa/node/cfgsync/model/AppCorsConfiguration.java index 7805601..48bd506 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/model/AppCorsConfiguration.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/model/AppCorsConfiguration.java @@ -2,12 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.ToString; +import lombok.*; @Data @Builder(toBuilder = true) diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/model/AppInfo.java b/src/main/java/it/gov/pagopa/node/cfgsync/model/AppInfo.java deleted file mode 100644 index 013a08d..0000000 --- a/src/main/java/it/gov/pagopa/node/cfgsync/model/AppInfo.java +++ /dev/null @@ -1,22 +0,0 @@ -package it.gov.pagopa.node.cfgsync.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.ToString; - -@Data -@Builder(toBuilder = true) -@NoArgsConstructor -@AllArgsConstructor(access = AccessLevel.PRIVATE) -@ToString -@JsonIgnoreProperties(ignoreUnknown = true) -public class AppInfo { - - private String name; - private String version; - private String environment; -} diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/model/ProblemJson.java b/src/main/java/it/gov/pagopa/node/cfgsync/model/ProblemJson.java index 92a1f1f..d8e7efd 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/model/ProblemJson.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/model/ProblemJson.java @@ -3,13 +3,10 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + import javax.validation.constraints.Max; import javax.validation.constraints.Min; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.ToString; /** * Object returned as response in case of an error. diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/model/StationsResponse.java b/src/main/java/it/gov/pagopa/node/cfgsync/model/StationsResponse.java index f0cb9e6..b416cc2 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/model/StationsResponse.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/model/StationsResponse.java @@ -9,7 +9,6 @@ @Builder(toBuilder = true) @NoArgsConstructor @AllArgsConstructor(access = AccessLevel.PRIVATE) -@ToString @JsonIgnoreProperties(ignoreUnknown = true) public class StationsResponse { private List stations; diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/model/SyncStatusEnum.java b/src/main/java/it/gov/pagopa/node/cfgsync/model/SyncStatusEnum.java index eb69000..9060ce7 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/model/SyncStatusEnum.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/model/SyncStatusEnum.java @@ -2,8 +2,8 @@ public enum SyncStatusEnum { - done, - disabled, - rollback, - error; + DONE, + DISABLED, + ROLLBACK, + ERROR; } diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/repository/config/NexiOracleConfig.java b/src/main/java/it/gov/pagopa/node/cfgsync/repository/config/NexiOracleConfig.java index 495af15..f7a0e31 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/repository/config/NexiOracleConfig.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/repository/config/NexiOracleConfig.java @@ -2,14 +2,13 @@ import com.zaxxer.hikari.HikariDataSource; import it.gov.pagopa.node.cfgsync.repository.model.ConfigCache; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; -import org.springframework.context.annotation.*; -import org.springframework.core.env.Environment; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @@ -28,9 +27,6 @@ @ConditionalOnProperty(prefix = "spring.datasource.nexi.oracle", name = "enabled") public class NexiOracleConfig { - @Autowired - private Environment env; - @Bean @ConfigurationProperties("spring.datasource.nexi.oracle") public DataSourceProperties nexiOracleDatasourceProperties() { diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/repository/model/StandInStations.java b/src/main/java/it/gov/pagopa/node/cfgsync/repository/model/StandInStations.java index 2f17489..d4be26b 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/repository/model/StandInStations.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/repository/model/StandInStations.java @@ -7,7 +7,6 @@ import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; -import java.io.Serializable; @NoArgsConstructor diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/service/ApiConfigCacheEhConsumer.java b/src/main/java/it/gov/pagopa/node/cfgsync/service/ApiConfigCacheEhConsumer.java index d8ceaf2..51498f2 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/service/ApiConfigCacheEhConsumer.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/service/ApiConfigCacheEhConsumer.java @@ -7,6 +7,7 @@ import com.azure.messaging.eventhubs.models.EventContext; import com.azure.storage.blob.BlobContainerAsyncClient; import com.azure.storage.blob.BlobContainerClientBuilder; +import it.gov.pagopa.node.cfgsync.model.SyncStatusEnum; import it.gov.pagopa.node.cfgsync.model.TargetRefreshEnum; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -15,6 +16,7 @@ import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; +import java.util.Map; @Slf4j @Service @@ -48,15 +50,16 @@ public void post(){ } public void processEvent(EventContext eventContext) { - log.info("Processing event {} from partition {} with sequence number {} with body: {}", + log.info("[NODE-CFG-SYNC] Processing event {} from partition {} with sequence number {} with body: {}", TargetRefreshEnum.cache.label, eventContext.getPartitionContext().getPartitionId(), eventContext.getEventData().getSequenceNumber(), eventContext.getEventData().getBodyAsString()); - apiConfigCacheService.forceCacheUpdate(); + Map syncStatusEnumMap = apiConfigCacheService.syncCache(); + log.info("Processed event {}: {}", TargetRefreshEnum.cache.label, syncStatusEnumMap.toString()); } public void processError(ErrorContext errorContext) { - log.error("Error occurred for {} from partition {}: {}", + log.error("[NODE-CFG-SYNC] Error occurred for {} from partition {}: {}", TargetRefreshEnum.cache.label, errorContext.getPartitionContext().getPartitionId(), errorContext.getThrowable().getMessage(), diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/service/ApiConfigCacheService.java b/src/main/java/it/gov/pagopa/node/cfgsync/service/ApiConfigCacheService.java index 44f07ef..2987ac5 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/service/ApiConfigCacheService.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/service/ApiConfigCacheService.java @@ -13,14 +13,16 @@ import it.gov.pagopa.node.cfgsync.repository.nexioracle.NexiCacheOracleRepository; import it.gov.pagopa.node.cfgsync.repository.nexipostgres.NexiCachePostgresRepository; import it.gov.pagopa.node.cfgsync.repository.pagopa.PagoPACachePostgresRepository; +import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.annotation.PostConstruct; import java.time.Instant; import java.time.ZonedDateTime; import java.util.Collection; @@ -29,147 +31,127 @@ import static it.gov.pagopa.node.cfgsync.util.Constants.*; -@Component +@Service @Setter @Slf4j +@RequiredArgsConstructor public class ApiConfigCacheService extends CommonCacheService { @Value("${api-config-cache.service.enabled}") - private boolean enabled; - @Value("${api-config-cache.service.subscriptionKey}") - private String subscriptionKey; - - private ApiConfigCacheClient apiConfigCacheClient; + private boolean apiConfigCacheServiceEnabled; - @Autowired(required = false) - private PagoPACachePostgresRepository pagopaPostgresRepository; - @Autowired(required = false) - private NexiCachePostgresRepository nexiPostgresRepository; - @Autowired(required = false) - private NexiCacheOracleRepository nexiOracleRepository; - - @Value("${app.identifiers.pagopa-postgres}") - private String pagopaPostgresServiceIdentifier; - - @Value("${app.identifiers.nexi-postgres}") - private String nexiPostgresServiceIdentifier; + @Value("${api-config-cache.service.subscriptionKey}") + private String apiConfigCacheSubscriptionKey; - @Value("${app.identifiers.nexi-oracle}") - private String nexiOracleServiceIdentifier; + @Value("${api-config-cache.service.host}") + private String apiConfigCacheUrl; @Value("${api-config-cache.write.pagopa-postgres}") - private boolean writePagoPa; + private boolean apiConfigCacheWritePagoPa; @Value("${api-config-cache.write.nexi-oracle}") - private boolean writeNexiOracle; + private boolean apiConfigCacheWriteNexiOracle; @Value("${api-config-cache.write.nexi-postgres}") - private boolean writeNexiPostgres; + private boolean apiConfigCacheWriteNexiPostgres; + + private ApiConfigCacheClient apiConfigCacheClient; - public ApiConfigCacheService(@Value("${api-config-cache.service.host}") String apiConfigCacheUrl) { + @Autowired(required = false) + private PagoPACachePostgresRepository pagoPACachePostgresRepository; + @Autowired(required = false) + private NexiCachePostgresRepository nexiCachePostgresRepository; + @Autowired(required = false) + private NexiCacheOracleRepository nexiCacheOracleRepository; + + @PostConstruct + private void setStandInManagerClient() { apiConfigCacheClient = Feign.builder().target(ApiConfigCacheClient.class, apiConfigCacheUrl); } - public Map forceCacheUpdate() { + @Transactional(rollbackFor={SyncDbStatusException.class}) + public Map syncCache() { Map syncStatusMap = new LinkedHashMap<>(); try { - if( !enabled ) { + if( !apiConfigCacheServiceEnabled) { throw new AppException(AppError.SERVICE_DISABLED, TargetRefreshEnum.cache.label); } - log.debug("SyncService api-config-cache get cache"); - Response response = apiConfigCacheClient.getCache(subscriptionKey); + log.debug("[NODE-CFG-SYNC] get cache"); + Response response = apiConfigCacheClient.getCache(apiConfigCacheSubscriptionKey); int httpResponseCode = response.status(); if (httpResponseCode != HttpStatus.OK.value()) { - log.error("SyncService api-config-cache get cache error - result: httpStatusCode[{}]", httpResponseCode); + log.error("[NODE-CFG-SYNC] cache error - result: httpStatusCode[{}]", httpResponseCode); throw new AppException(AppError.INTERNAL_SERVER_ERROR); } - log.info("SyncService api-config-cache get cache successful"); Map> headers = response.headers(); if( headers.isEmpty() ) { - log.error("SyncService api-config-cache get cache error - empty header"); + log.error("[NODE-CFG-SYNC] cache error - empty header"); throw new AppException(AppError.INTERNAL_SERVER_ERROR); } String cacheId = (String) getHeaderParameter(TargetRefreshEnum.cache.label, headers, HEADER_CACHE_ID); String cacheTimestamp = (String) getHeaderParameter(TargetRefreshEnum.cache.label, headers, HEADER_CACHE_TIMESTAMP); String cacheVersion = (String) getHeaderParameter(TargetRefreshEnum.cache.label, headers, HEADER_CACHE_VERSION); - log.info("SyncService cacheId:[{}], cacheTimestamp:[{}], cacheVersion:[{}]", cacheId, Instant.from(ZonedDateTime.parse(cacheTimestamp)), cacheVersion); + log.info("[NODE-CFG-SYNC] cache successful. cacheId:[{}], cacheTimestamp:[{}], cacheVersion:[{}]", cacheId, Instant.from(ZonedDateTime.parse(cacheTimestamp)), cacheVersion); ConfigCache configCache = composeCache(cacheId, ZonedDateTime.parse(cacheTimestamp), cacheVersion, response.body().asInputStream().readAllBytes()); - saveAllDatabases(syncStatusMap, configCache); - } catch (SyncDbStatusException e) { - //viene usata per poter restituire in risposta la mappa degli aggiornamenti - return syncStatusMap; - } catch (FeignException.GatewayTimeout e) { - log.error("SyncService api-config-cache get cache error: Gateway timeout", e); + savePagoPA(syncStatusMap, configCache); + saveNexiPostgres(syncStatusMap, configCache); + saveNexiOracle(syncStatusMap, configCache); + + return composeSyncStatusMapResult(syncStatusMap); + } catch (FeignException fEx) { + log.error("[NODE-CFG-SYNC] cache error: {}", fEx.getMessage(), fEx); throw new AppException(AppError.INTERNAL_SERVER_ERROR); } catch(AppException appException) { throw appException; - } catch (Exception e) { - log.error("SyncService api-config-cache get cache error", e); + } catch (Exception ex) { + log.error("[NODE-CFG-SYNC] cache error: {}", ex.getMessage(), ex); throw new AppException(AppError.INTERNAL_SERVER_ERROR); } - return syncStatusMap; - } - - @Transactional(rollbackFor={SyncDbStatusException.class}) - void saveAllDatabases(Map syncStatusMap, ConfigCache configCache) throws SyncDbStatusException { - savePagoPA(syncStatusMap, configCache); - saveNexiPostgres(syncStatusMap, configCache); - saveNexiOracle(syncStatusMap, configCache); - - if( syncStatusMap.containsValue(SyncStatusEnum.error) ) { - syncStatusMap.forEach((k, v) -> { - if (v == SyncStatusEnum.done) { - syncStatusMap.remove(k); - syncStatusMap.put(k, SyncStatusEnum.rollback); - } - }); - throw new SyncDbStatusException("Rollback sync"); - } } private void savePagoPA(Map syncStatusMap, ConfigCache configCache) { try { - if( writePagoPa ) { - pagopaPostgresRepository.save(configCache); - syncStatusMap.put(pagopaPostgresServiceIdentifier, SyncStatusEnum.done); + if(apiConfigCacheWritePagoPa) { + pagoPACachePostgresRepository.save(configCache); + syncStatusMap.put(getPagopaPostgresServiceIdentifier(), SyncStatusEnum.DONE); } else { - syncStatusMap.put(pagopaPostgresServiceIdentifier, SyncStatusEnum.disabled); + syncStatusMap.put(getPagopaPostgresServiceIdentifier(), SyncStatusEnum.DISABLED); } } catch(Exception ex) { - log.error("SyncService api-config-cache save pagoPA error: {}", ex.getMessage(), ex); - syncStatusMap.put(pagopaPostgresServiceIdentifier, SyncStatusEnum.error); + log.error("[NODE-CFG-SYNC][ALERT] Problem to dump cache on PagoPA PostgreSQL: {}", ex.getMessage(), ex); + syncStatusMap.put(getPagopaPostgresServiceIdentifier(), SyncStatusEnum.ERROR); } } private void saveNexiOracle(Map syncStatusMap, ConfigCache configCache) { try { - if( writeNexiOracle ) { - nexiOracleRepository.save(configCache); - syncStatusMap.put(nexiOracleServiceIdentifier, SyncStatusEnum.done); + if(apiConfigCacheWriteNexiOracle) { + nexiCacheOracleRepository.save(configCache); + syncStatusMap.put(getNexiOracleServiceIdentifier(), SyncStatusEnum.DONE); } else { - syncStatusMap.put(nexiOracleServiceIdentifier, SyncStatusEnum.disabled); + syncStatusMap.put(getNexiOracleServiceIdentifier(), SyncStatusEnum.DISABLED); } } catch(Exception ex) { - log.error("SyncService api-config-cache save Nexi Oracle error: {}", ex.getMessage(), ex); - syncStatusMap.put(nexiOracleServiceIdentifier, SyncStatusEnum.error); + log.error("[NODE-CFG-SYNC][ALERT] Problem to dump cache on Nexi Oracle: {}", ex.getMessage(), ex); + syncStatusMap.put(getNexiOracleServiceIdentifier(), SyncStatusEnum.ERROR); } } private void saveNexiPostgres(Map syncStatusMap, ConfigCache configCache) { try { - if ( writeNexiPostgres ) { - nexiPostgresRepository.save(configCache); - syncStatusMap.put(nexiPostgresServiceIdentifier, SyncStatusEnum.done); + if (apiConfigCacheWriteNexiPostgres) { + nexiCachePostgresRepository.save(configCache); + syncStatusMap.put(getNexiPostgresServiceIdentifier(), SyncStatusEnum.DONE); } else { - syncStatusMap.put(nexiPostgresServiceIdentifier, SyncStatusEnum.disabled); + syncStatusMap.put(getNexiPostgresServiceIdentifier(), SyncStatusEnum.DISABLED); } } catch(Exception ex) { - log.error("SyncService api-config-cache save Nexi Postgres error: {}", ex.getMessage(), ex); - syncStatusMap.put(nexiPostgresServiceIdentifier, SyncStatusEnum.error); + log.error("[NODE-CFG-SYNC][ALERT] Problem to dump cache on Nexi PostgreSQL: {}", ex.getMessage(), ex); + syncStatusMap.put(getNexiPostgresServiceIdentifier(), SyncStatusEnum.ERROR); } } } diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/service/CommonCacheService.java b/src/main/java/it/gov/pagopa/node/cfgsync/service/CommonCacheService.java index ddf0638..6d3d10c 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/service/CommonCacheService.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/service/CommonCacheService.java @@ -2,38 +2,66 @@ import it.gov.pagopa.node.cfgsync.exception.AppError; import it.gov.pagopa.node.cfgsync.exception.AppException; -import it.gov.pagopa.node.cfgsync.model.TargetRefreshEnum; +import it.gov.pagopa.node.cfgsync.exception.SyncDbStatusException; +import it.gov.pagopa.node.cfgsync.model.SyncStatusEnum; import it.gov.pagopa.node.cfgsync.repository.model.ConfigCache; import it.gov.pagopa.node.cfgsync.util.Utils; +import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; import java.io.IOException; import java.time.ZonedDateTime; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @Slf4j +@Getter +@Component +@RequiredArgsConstructor public class CommonCacheService { - @Value("${app.trimCacheColumn}") - private boolean trimCacheColumn; + @Value("${app.identifiers.pagopa-postgres}") + private String pagopaPostgresServiceIdentifier; - protected ConfigCache composeCache(String cacheId, ZonedDateTime timestamp, String cacheVersion, byte[] cache) throws IOException { - String version = trimCacheColumn ? - (String) Utils.trimValueColumn(ConfigCache.class, "version", cacheVersion) : cacheVersion; + @Value("${app.identifiers.nexi-postgres}") + private String nexiPostgresServiceIdentifier; + @Value("${app.identifiers.nexi-oracle}") + private String nexiOracleServiceIdentifier; + + protected ConfigCache composeCache(String cacheId, ZonedDateTime timestamp, String cacheVersion, byte[] cache) throws IOException, SyncDbStatusException { + String version = (String) Utils.trimValueColumn(ConfigCache.class, "version", cacheVersion); return new ConfigCache(cacheId, timestamp, Utils.zipContent(cache), version); } protected Object getHeaderParameter(String target, Map> headers, String key) { List valueList = headers.get(key).stream().toList(); if(valueList.isEmpty()) { - log.error("SyncService {} get cache error - empty parameter '{}'", target, key); + log.error("[NODE-CFG-SYNC] {} get cache error - empty parameter '{}'", target, key); throw new AppException(AppError.INTERNAL_SERVER_ERROR); } return valueList.get(0); } + protected Map composeSyncStatusMapResult(Map syncStatusMap) { + Map syncStatusMapUpdated = new LinkedHashMap<>(); + if( syncStatusMap.containsValue(SyncStatusEnum.ERROR) ) { + syncStatusMap.forEach((k, v) -> { + if (v == SyncStatusEnum.DONE) { + syncStatusMapUpdated.put(k, SyncStatusEnum.ROLLBACK); + } else { + syncStatusMapUpdated.put(k, v); + } + }); + return syncStatusMapUpdated; + } else { + return syncStatusMap; + } + } + } diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/service/StandInManagerEhConsumer.java b/src/main/java/it/gov/pagopa/node/cfgsync/service/StandInManagerEhConsumer.java index 996336c..21aa309 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/service/StandInManagerEhConsumer.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/service/StandInManagerEhConsumer.java @@ -7,6 +7,7 @@ import com.azure.messaging.eventhubs.models.EventContext; import com.azure.storage.blob.BlobContainerAsyncClient; import com.azure.storage.blob.BlobContainerClientBuilder; +import it.gov.pagopa.node.cfgsync.model.SyncStatusEnum; import it.gov.pagopa.node.cfgsync.model.TargetRefreshEnum; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -15,6 +16,7 @@ import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; +import java.util.Map; @Slf4j @Service @@ -48,15 +50,16 @@ public void post(){ } public void processEvent(EventContext eventContext) { - log.info("Processing event {} from partition {} with sequence number {} with body: {}", + log.info("[NODE-CFG-SYNC] Processing event {} from partition {} with sequence number {} with body: {}", TargetRefreshEnum.standin.label, eventContext.getPartitionContext().getPartitionId(), eventContext.getEventData().getSequenceNumber(), eventContext.getEventData().getBodyAsString()); - standInManagerService.forceStandIn(); + Map syncStatusEnumMap = standInManagerService.syncStandIn(); + log.info("Processed event {}: {}", TargetRefreshEnum.standin.label, syncStatusEnumMap.toString()); } public void processError(ErrorContext errorContext) { - log.error("Error occurred for {} from partition {}: {}", + log.error("[NODE-CFG-SYNC] Error occurred for {} from partition {}: {}", TargetRefreshEnum.standin.label, errorContext.getPartitionContext().getPartitionId(), errorContext.getThrowable().getMessage(), diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/service/StandInManagerService.java b/src/main/java/it/gov/pagopa/node/cfgsync/service/StandInManagerService.java index 7bb5585..92a4576 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/service/StandInManagerService.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/service/StandInManagerService.java @@ -15,6 +15,7 @@ import it.gov.pagopa.node.cfgsync.repository.nexioracle.NexiStandInOracleRepository; import it.gov.pagopa.node.cfgsync.repository.nexipostgres.NexiStandInPostgresRepository; import it.gov.pagopa.node.cfgsync.repository.pagopa.PagoPAStandInPostgresRepository; +import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -23,7 +24,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.HashMap; +import javax.annotation.PostConstruct; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -31,137 +32,118 @@ @Service @Setter @Slf4j +@RequiredArgsConstructor public class StandInManagerService extends CommonCacheService { @Value("${stand-in-manager.service.enabled}") - private boolean enabled; - @Value("${stand-in-manager.service.subscriptionKey}") - private String subscriptionKey; - - private StandInManagerClient standInManagerClient; - private ObjectMapper objectMapper; + private boolean standInManagerEnabled; - @Autowired(required = false) - private PagoPAStandInPostgresRepository pagopaPostgresRepository; - @Autowired(required = false) - private NexiStandInPostgresRepository nexiPostgresRepository; - @Autowired(required = false) - private NexiStandInOracleRepository nexiOracleRepository; - - @Value("${app.identifiers.pagopa-postgres}") - private String pagopaPostgresServiceIdentifier; - - @Value("${app.identifiers.nexi-postgres}") - private String nexiPostgresServiceIdentifier; + @Value("${stand-in-manager.service.subscriptionKey}") + private String standInManagerSubscriptionKey; - @Value("${app.identifiers.nexi-oracle}") - private String nexiOracleServiceIdentifier; + @Value("${stand-in-manager.service.host}") + private String standInManagerUrl; @Value("${stand-in-manager.write.pagopa-postgres}") - private boolean writePagoPa; + private boolean standInManagerWritePagoPa; @Value("${stand-in-manager.write.nexi-oracle}") - private boolean writeNexiOracle; + private boolean standInManagerWriteNexiOracle; @Value("${stand-in-manager.write.nexi-postgres}") - private boolean writeNexiPostgres; + private boolean standInManagerWriteNexiPostgres; + + private StandInManagerClient standInManagerClient; - public StandInManagerService(@Value("${stand-in-manager.service.host}") String standInManagerUrl, ObjectMapper objectMapper) { + private final ObjectMapper objectMapper; + + @Autowired(required = false) + private PagoPAStandInPostgresRepository pagoPAStandInPostgresRepository; + @Autowired(required = false) + private NexiStandInPostgresRepository nexiStandInPostgresRepository; + @Autowired(required = false) + private NexiStandInOracleRepository nexiStandInOracleRepository; + + @PostConstruct + private void setStandInManagerClient() { standInManagerClient = Feign.builder().target(StandInManagerClient.class, standInManagerUrl); - this.objectMapper = objectMapper; } - public Map forceStandIn() { + @Transactional(rollbackFor={SyncDbStatusException.class}) + public Map syncStandIn() { Map syncStatusMap = new LinkedHashMap<>(); try { - if( !enabled ) { + if( !standInManagerEnabled) { throw new AppException(AppError.SERVICE_DISABLED, TargetRefreshEnum.standin.label); } - log.debug("SyncService stand-in-manager get stations"); - Response response = standInManagerClient.getCache(subscriptionKey); + log.debug("[NODE-CFG-SYNC] stations"); + Response response = standInManagerClient.getCache(standInManagerSubscriptionKey); int httpResponseCode = response.status(); if (httpResponseCode != HttpStatus.OK.value()) { - log.error("SyncService stand-in-manager get stations error - result: httpStatusCode[{}]", httpResponseCode); + log.error("[NODE-CFG-SYNC] stations error - result: httpStatusCode[{}]", httpResponseCode); throw new AppException(AppError.INTERNAL_SERVER_ERROR); } - log.info("SyncService stand-in-manager get stations successful"); + log.info("[NODE-CFG-SYNC] stations successful"); StationsResponse stations = objectMapper.readValue(response.body().asInputStream().readAllBytes(), StationsResponse.class); - log.info("SyncService {} stations found", stations.getStations().size()); + log.info("[NODE-CFG-SYNC] {} stations found", stations.getStations().size()); List stationsEntities = stations.getStations().stream().map(StandInStations::new).toList(); - return saveAllDatabases(syncStatusMap, stationsEntities); - } catch (FeignException.GatewayTimeout e) { - log.error("SyncService stand-in-manager get stations error: Gateway timeout", e); + savePagoPA(syncStatusMap, stationsEntities); + saveNexiPostgres(syncStatusMap, stationsEntities); + saveNexiOracle(syncStatusMap, stationsEntities); + + return composeSyncStatusMapResult(syncStatusMap); + } catch (FeignException fEx) { + log.error("[NODE-CFG-SYNC] {} get cache error: {}", TargetRefreshEnum.standin.label, fEx.getMessage(), fEx); throw new AppException(AppError.INTERNAL_SERVER_ERROR); } catch(AppException appException) { throw appException; - } catch (Exception e) { - log.error("SyncService stand-in-manager get cache error", e); + } catch (Exception ex) { + log.error("[NODE-CFG-SYNC] {} get cache error: {}", TargetRefreshEnum.standin.label, ex.getMessage(), ex); throw new AppException(AppError.INTERNAL_SERVER_ERROR); } } - @Transactional(rollbackFor={SyncDbStatusException.class}) - Map saveAllDatabases(Map syncStatusMap, List stationsEntities) { - savePagoPA(syncStatusMap, stationsEntities); - saveNexiPostgres(syncStatusMap, stationsEntities); - saveNexiOracle(syncStatusMap, stationsEntities); - - Map syncStatusMapUpdated = new LinkedHashMap<>(); - if( syncStatusMap.containsValue(SyncStatusEnum.error) ) { - syncStatusMap.forEach((k, v) -> { - if (v == SyncStatusEnum.done) { - syncStatusMapUpdated.put(k, SyncStatusEnum.rollback); - } else { - syncStatusMapUpdated.put(k, v); - } - }); - return syncStatusMapUpdated; - } else { - return syncStatusMap; - } - } - private void savePagoPA(Map syncStatusMap, List stationsEntities) { try { - if( writePagoPa ) { - pagopaPostgresRepository.saveAll(stationsEntities); - syncStatusMap.put(pagopaPostgresServiceIdentifier, SyncStatusEnum.done); + if(standInManagerWritePagoPa) { + pagoPAStandInPostgresRepository.saveAll(stationsEntities); + syncStatusMap.put(getPagopaPostgresServiceIdentifier(), SyncStatusEnum.DONE); } else { - syncStatusMap.put(pagopaPostgresServiceIdentifier, SyncStatusEnum.disabled); + syncStatusMap.put(getPagopaPostgresServiceIdentifier(), SyncStatusEnum.DISABLED); } } catch(Exception ex) { - log.error("SyncService stand-in-manager save pagoPA error: {}", ex.getMessage(), ex); - syncStatusMap.put(pagopaPostgresServiceIdentifier, SyncStatusEnum.error); + log.error("[NODE-CFG-SYNC][ALERT] Problem to dump standin stations on PagoPA PostgreSQL: {}", ex.getMessage(), ex); + syncStatusMap.put(getPagopaPostgresServiceIdentifier(), SyncStatusEnum.ERROR); } } private void saveNexiOracle(Map syncStatusMap, List stationsEntities) { try { - if( writeNexiOracle ) { - nexiOracleRepository.saveAll(stationsEntities); - syncStatusMap.put(nexiOracleServiceIdentifier, SyncStatusEnum.done); + if(standInManagerWriteNexiOracle) { + nexiStandInOracleRepository.saveAll(stationsEntities); + syncStatusMap.put(getNexiOracleServiceIdentifier(), SyncStatusEnum.DONE); } else { - syncStatusMap.put(nexiOracleServiceIdentifier, SyncStatusEnum.disabled); + syncStatusMap.put(getNexiOracleServiceIdentifier(), SyncStatusEnum.DISABLED); } } catch(Exception ex) { - log.error("SyncService stand-in-manager save Nexi Oracle error: {}", ex.getMessage(), ex); - syncStatusMap.put(nexiOracleServiceIdentifier, SyncStatusEnum.error); + log.error("[NODE-CFG-SYNC][ALERT] Problem to dump standin stations on Nexi Oracle: {}", ex.getMessage(), ex); + syncStatusMap.put(getNexiOracleServiceIdentifier(), SyncStatusEnum.ERROR); } } private void saveNexiPostgres(Map syncStatusMap, List stationsEntities) { try { - if ( writeNexiPostgres ) { - nexiPostgresRepository.saveAll(stationsEntities); - syncStatusMap.put(nexiPostgresServiceIdentifier, SyncStatusEnum.done); + if (standInManagerWriteNexiPostgres) { + nexiStandInPostgresRepository.saveAll(stationsEntities); + syncStatusMap.put(getNexiPostgresServiceIdentifier(), SyncStatusEnum.DONE); } else { - syncStatusMap.put(nexiPostgresServiceIdentifier, SyncStatusEnum.disabled); + syncStatusMap.put(getNexiPostgresServiceIdentifier(), SyncStatusEnum.DISABLED); } } catch(Exception ex) { - log.error("SyncService stand-in-manager save Nexi Postgres error: {}", ex.getMessage(), ex); - syncStatusMap.put(nexiPostgresServiceIdentifier, SyncStatusEnum.error); + log.error("[NODE-CFG-SYNC][ALERT] Problem to dump standin stations on Nexi PostgreSQL: {}", ex.getMessage(), ex); + syncStatusMap.put(getNexiPostgresServiceIdentifier(), SyncStatusEnum.ERROR); } } } diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/util/Constants.java b/src/main/java/it/gov/pagopa/node/cfgsync/util/Constants.java index 635b731..e0f364a 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/util/Constants.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/util/Constants.java @@ -1,10 +1,10 @@ package it.gov.pagopa.node.cfgsync.util; -import lombok.experimental.UtilityClass; - -@UtilityClass public class Constants { + private Constants() { + throw new IllegalStateException("Constants class"); + } public static final String HEADER_REQUEST_ID = "X-Request-Id"; diff --git a/src/main/java/it/gov/pagopa/node/cfgsync/util/Utils.java b/src/main/java/it/gov/pagopa/node/cfgsync/util/Utils.java index 84ac54e..bef1d43 100644 --- a/src/main/java/it/gov/pagopa/node/cfgsync/util/Utils.java +++ b/src/main/java/it/gov/pagopa/node/cfgsync/util/Utils.java @@ -1,13 +1,20 @@ package it.gov.pagopa.node.cfgsync.util; +import it.gov.pagopa.node.cfgsync.exception.SyncDbStatusException; +import lombok.extern.slf4j.Slf4j; + import javax.persistence.Column; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.util.zip.Deflater; import java.util.zip.GZIPOutputStream; +@Slf4j public class Utils { + private Utils() { + throw new IllegalStateException("Utility class"); + } + public static byte[] zipContent(byte[] input) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(input.length); GZIPOutputStream gzip = new GZIPOutputStream(bos); @@ -18,12 +25,15 @@ public static byte[] zipContent(byte[] input) throws IOException { return compressed; } - public static Object trimValueColumn(Class clazz, String columnName, String value) { + public static Object trimValueColumn(Class clazz, String columnName, String value) throws SyncDbStatusException { try { - int length = clazz.getDeclaredField(columnName).getAnnotation(Column.class).length(); - return value.substring(0, length); + log.debug("[NODE-CFG-SYNC] Verify if trim value column '{}' is necessary", columnName); + int maxColumnLength = clazz.getDeclaredField(columnName).getAnnotation(Column.class).length(); + int valueLength = value.length(); + int lastIndexTrim = Math.min(valueLength, maxColumnLength); + return value.substring(0, lastIndexTrim); } catch (NoSuchFieldException e) { - throw new RuntimeException(e); + throw new SyncDbStatusException(e.getMessage()); } } diff --git a/src/main/resources/application-local.yaml b/src/main/resources/application-local.yaml index 3ba1600..6b0fa3e 100644 --- a/src/main/resources/application-local.yaml +++ b/src/main/resources/application-local.yaml @@ -5,7 +5,6 @@ cors: configuration: '{"origins": ["*"], "methods": ["*"]}' app: - trimCacheColumn: 'true' identifiers: pagopa-postgres: ${DB_PAGOPA_POSTGRES_SERVICE_IDENTIFIER} nexi-postgres: ${DB_NEXI_POSTGRES_SERVICE_IDENTIFIER} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 67e06b9..b2f5c7c 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -33,7 +33,6 @@ management: probes: enabled: 'true' app: - trimCacheColumn: 'true' identifiers: pagopa-postgres: ${DB_PAGOPA_POSTGRES_SERVICE_IDENTIFIER} nexi-postgres: ${DB_NEXI_POSTGRES_SERVICE_IDENTIFIER} @@ -41,6 +40,10 @@ app: spring: datasource: + hikari: + connectionTimeout: 15000 + maxLifetime: 30000 + keepaliveTime: 30000 pagopa: postgres: url: ${DB_PAGOPA_POSTGRES_URL} @@ -81,7 +84,7 @@ stand-in-manager: write: ##permettono di abilitare/disabilitare la scrittura di stand-in sui vari database pagopa-postgres: 'true' - nexi-postgres: 'true' + nexi-postgres: 'false' nexi-oracle: 'true' api-config-cache: @@ -101,7 +104,7 @@ api-config-cache: write: ##permettono di abilitare/disabilitare la scrittura di cache sui vari database pagopa-postgres: 'true' - nexi-postgres: 'true' + nexi-postgres: 'false' nexi-oracle: 'true' logging: diff --git a/src/test/java/it/gov/pagopa/node/cfgsync/ApplicationTest.java b/src/test/java/it/gov/pagopa/node/cfgsync/ApplicationTest.java index 4d5004e..23fb531 100644 --- a/src/test/java/it/gov/pagopa/node/cfgsync/ApplicationTest.java +++ b/src/test/java/it/gov/pagopa/node/cfgsync/ApplicationTest.java @@ -1,11 +1,17 @@ package it.gov.pagopa.node.cfgsync; -import static org.junit.jupiter.api.Assertions.assertTrue; - +import it.gov.pagopa.node.cfgsync.util.Constants; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; -@SpringBootTest +import static org.junit.jupiter.api.Assertions.*; + +@ExtendWith(MockitoExtension.class) class ApplicationTest { @Test @@ -13,4 +19,28 @@ void contextLoads() { // check only if the context is loaded assertTrue(true); } + + @Test + void testConstantsConstructorIsPrivate() throws NoSuchMethodException { + Constructor constructor = Constants.class.getDeclaredConstructor(); + assertTrue(Modifier.isPrivate(constructor.getModifiers())); + constructor.setAccessible(true); + assertThrows(InvocationTargetException.class, constructor::newInstance); + } + + @Test + void constantsTest() { + assertEquals("X-Request-Id", Constants.HEADER_REQUEST_ID); + assertEquals("X-CACHE-ID", Constants.HEADER_CACHE_ID); + assertEquals("X-CACHE-TIMESTAMP", Constants.HEADER_CACHE_TIMESTAMP); + assertEquals("X-CACHE-VERSION", Constants.HEADER_CACHE_VERSION); + } + + @Test + void constantsHelperTest() { + assertEquals("NEXIORACLE", ConstantsHelper.NEXIORACLE_SI); + assertEquals("NEXIPOSTGRES", ConstantsHelper.NEXIPOSTGRES_SI); + assertEquals("PAGOPAPOSTGRES", ConstantsHelper.PAGOPAPOSTGRES_SI); + } + } diff --git a/src/test/java/it/gov/pagopa/node/cfgsync/CacheConsumerTest.java b/src/test/java/it/gov/pagopa/node/cfgsync/CacheConsumerTest.java new file mode 100644 index 0000000..7c74924 --- /dev/null +++ b/src/test/java/it/gov/pagopa/node/cfgsync/CacheConsumerTest.java @@ -0,0 +1,51 @@ +package it.gov.pagopa.node.cfgsync; + +import com.azure.messaging.eventhubs.CheckpointStore; +import com.azure.messaging.eventhubs.EventData; +import com.azure.messaging.eventhubs.checkpointstore.blob.BlobCheckpointStore; +import com.azure.messaging.eventhubs.models.ErrorContext; +import com.azure.messaging.eventhubs.models.EventContext; +import com.azure.messaging.eventhubs.models.LastEnqueuedEventProperties; +import com.azure.messaging.eventhubs.models.PartitionContext; +import it.gov.pagopa.node.cfgsync.service.ApiConfigCacheEhConsumer; +import it.gov.pagopa.node.cfgsync.service.ApiConfigCacheService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.Instant; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class CacheConsumerTest { + + @Mock + ApiConfigCacheService service; + + @Test + void processEvent() { + PartitionContext partitionContext = new PartitionContext("", "", "", "1"); + CheckpointStore checkpointStore = new BlobCheckpointStore(null); + LastEnqueuedEventProperties lastEnqueuedEventProperties = new LastEnqueuedEventProperties(1L, 1L, Instant.now(), Instant.now()); + EventData eventData = new EventData(); + EventContext eventContext = new EventContext(partitionContext, eventData, checkpointStore, lastEnqueuedEventProperties); + + ApiConfigCacheEhConsumer consumer = new ApiConfigCacheEhConsumer(service); + consumer.processEvent(eventContext); + verify(service, times(1)).syncCache(); + } + + @Test + void processError() { + PartitionContext partitionContext = new PartitionContext("", "", "", "1"); + ErrorContext errorContext = new ErrorContext(partitionContext, new Exception("")); + + ApiConfigCacheEhConsumer consumer = new ApiConfigCacheEhConsumer(service); + consumer.processError(errorContext); + verify(service, times(0)).syncCache(); + } + +} diff --git a/src/test/java/it/gov/pagopa/node/cfgsync/CacheSyncTest.java b/src/test/java/it/gov/pagopa/node/cfgsync/CacheSyncTest.java index a2f1aac..dd551bc 100644 --- a/src/test/java/it/gov/pagopa/node/cfgsync/CacheSyncTest.java +++ b/src/test/java/it/gov/pagopa/node/cfgsync/CacheSyncTest.java @@ -1,6 +1,5 @@ package it.gov.pagopa.node.cfgsync; -import com.fasterxml.jackson.databind.ObjectMapper; import feign.Feign; import feign.FeignException; import feign.Request; @@ -8,9 +7,15 @@ import feign.mock.MockClient; import feign.mock.MockTarget; import it.gov.pagopa.node.cfgsync.client.ApiConfigCacheClient; -import it.gov.pagopa.node.cfgsync.client.StandInManagerClient; -import it.gov.pagopa.node.cfgsync.model.*; +import it.gov.pagopa.node.cfgsync.model.ProblemJson; +import it.gov.pagopa.node.cfgsync.model.SyncStatusEnum; +import it.gov.pagopa.node.cfgsync.model.SyncStatusResponse; +import it.gov.pagopa.node.cfgsync.repository.nexioracle.NexiCacheOracleRepository; +import it.gov.pagopa.node.cfgsync.repository.nexipostgres.NexiCachePostgresRepository; +import it.gov.pagopa.node.cfgsync.repository.pagopa.PagoPACachePostgresRepository; import it.gov.pagopa.node.cfgsync.service.ApiConfigCacheService; +import it.gov.pagopa.node.cfgsync.service.CommonCacheService; +import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.runner.RunWith; @@ -21,7 +26,6 @@ import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -29,13 +33,13 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.util.ReflectionTestUtils; -import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.Collection; -import java.util.HashMap; +import java.util.Collections; import java.util.List; import java.util.Map; +import static it.gov.pagopa.node.cfgsync.ConstantsHelper.*; import static it.gov.pagopa.node.cfgsync.util.Constants.*; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.Assert.assertEquals; @@ -54,6 +58,7 @@ class CacheSyncTest { public static final String CLIENT_CACHE_PATH = "/cache"; @Autowired private ApiConfigCacheService cacheManagerService; + @Autowired private CommonCacheService commonCacheService; @Autowired private TestRestTemplate restTemplate; private MockClient mockClient; @@ -62,19 +67,26 @@ class CacheSyncTest { @Mock ApiConfigCacheClient apiConfigCacheClient; + private static final Map> headers; + static { + headers = Map.of(HEADER_CACHE_ID, List.of(String.valueOf(System.currentTimeMillis())), HEADER_CACHE_TIMESTAMP, List.of(Instant.now().toString()), HEADER_CACHE_VERSION, List.of("v1.0.0")); + } + @Test - void syncCache_400() { - ReflectionTestUtils.setField(cacheManagerService, "enabled", false); + void error400() { + ReflectionTestUtils.setField(cacheManagerService, "apiConfigCacheServiceEnabled", false); + ResponseEntity response = restTemplate.exchange(CACHE_URL, HttpMethod.PUT, null, ProblemJson.class); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); assertThat(response.getBody().getTitle()).isEqualTo("Target service disabled"); assertThat(response.getBody().getStatus()).isEqualTo(400); assertThat(response.getBody().getDetail()).isEqualTo("Target service api-config-cache disabled"); - ReflectionTestUtils.setField(cacheManagerService, "enabled", true); + + ReflectionTestUtils.setField(cacheManagerService, "apiConfigCacheServiceEnabled", true); } @Test - void syncCache_500() { + void error500() { mockClient = new MockClient().noContent(feign.mock.HttpMethod.GET, CLIENT_CACHE_PATH); ApiConfigCacheClient apiConfigCacheClient = Feign.builder().client(mockClient).target(new MockTarget<>(ApiConfigCacheClient.class)); @@ -87,12 +99,57 @@ void syncCache_500() { } @Test - void ssyncCache_500_ConnectionRefused() { -// mockClient = new MockClient().noContent(feign.mock.HttpMethod.GET, "/stations"); -// standInManagerClient = -// Feign.builder().client(mockClient).target(new MockTarget<>(StandInManagerClient.class)); -// standInManagerService.setStandInManagerClient(standInManagerClient); + void error500ApiConfigCacheException() { + when(apiConfigCacheClient.getCache(anyString())).thenThrow(FeignException.class); + + cacheManagerService.setApiConfigCacheClient(apiConfigCacheClient); + + ResponseEntity response = restTemplate.exchange(CACHE_URL, HttpMethod.PUT, null, ProblemJson.class); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + assertThat(response.getBody().getStatus()).isEqualTo(500); + assertThat(response.getBody().getTitle()).isEqualTo("Internal Server Error"); + } + + @Test + void error500NullCacheHeader() { + when(apiConfigCacheClient.getCache(anyString())).thenReturn(Response + .builder() + .status(200) + .reason("Mocked") + .headers(null) + .request(mock(Request.class)) + .body(new byte[0]) + .build()); + + cacheManagerService.setApiConfigCacheClient(apiConfigCacheClient); + + ResponseEntity response = restTemplate.exchange(CACHE_URL, HttpMethod.PUT, null, ProblemJson.class); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + assertThat(response.getBody().getStatus()).isEqualTo(500); + assertThat(response.getBody().getTitle()).isEqualTo("Internal Server Error"); + } + + @Test + void error500EmptyKeyCacheHeader() { + when(apiConfigCacheClient.getCache(anyString())).thenReturn(Response + .builder() + .status(200) + .reason("Mocked") + .headers(Map.of(HEADER_CACHE_ID, Collections.emptyList(), HEADER_CACHE_TIMESTAMP, List.of(Instant.now().toString()), HEADER_CACHE_VERSION, List.of("v1.0.0"))) + .request(mock(Request.class)) + .body(new byte[0]) + .build()); + + cacheManagerService.setApiConfigCacheClient(apiConfigCacheClient); + ResponseEntity response = restTemplate.exchange(CACHE_URL, HttpMethod.PUT, null, ProblemJson.class); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + assertThat(response.getBody().getStatus()).isEqualTo(500); + assertThat(response.getBody().getTitle()).isEqualTo("Internal Server Error"); + } + + @Test + void error500ConnectionRefused() { Request request = mock(Request.class); when(apiConfigCacheClient.getCache(anyString())) .thenThrow(new FeignException.NotFound("message", request, null, null)); @@ -103,105 +160,217 @@ void ssyncCache_500_ConnectionRefused() { assertThat(response.getBody().getTitle()).isEqualTo("Internal Server Error"); } -// @Test -// void syncCache_WritePagoPAPostgresDisabled() throws Exception { -// ObjectMapper objectMapper = new ObjectMapper(); -// -//// HttpHeaders headers = new HttpHeaders(); -//// headers.put(HEADER_CACHE_ID, List.of(String.valueOf(System.currentTimeMillis()))); -//// headers.put(HEADER_CACHE_TIMESTAMP, List.of(Instant.now().toString())); -//// headers.put(HEADER_CACHE_VERSION, List.of("v1.0.0")); -// -// Map> headers = new HashMap<>(); -// headers.put(HEADER_CACHE_ID, List.of(String.valueOf(System.currentTimeMillis()))); -// headers.put(HEADER_CACHE_TIMESTAMP, List.of(Instant.now().toString())); -// headers.put(HEADER_CACHE_VERSION, List.of("v1.0.0")); -// -//// mockClient = new MockClient().ok(feign.mock.HttpMethod.GET, CLIENT_CACHE_PATH, ResponseEntity.ok().headers(headers).build().toString()); -// -// Request request = mock(Request.class); -// mockClient = new MockClient() -// .ok( -// feign.mock.HttpMethod.GET, -// CLIENT_CACHE_PATH, -// Response -// .builder() -// .headers(headers) -// .request(request) -// .body(new byte[0]) -// .build().toString()); -//// mockClient = new MockClient() -//// .ok( -//// feign.mock.HttpMethod.GET, -//// CLIENT_CACHE_PATH, -//// Response -//// .builder() -//// .status(200) -//// .reason("Mocked") -//// .headers(headers) -//// .body(new byte[0]) -//// .build() -//// .toString()); -// ApiConfigCacheClient apiConfigCacheClient = -// Feign.builder().client(mockClient).target(new MockTarget<>(ApiConfigCacheClient.class)); -// cacheManagerService.setApiConfigCacheClient(apiConfigCacheClient); -// ReflectionTestUtils.setField(cacheManagerService, "writePagoPa", false); -// -// ResponseEntity> response = restTemplate.exchange(CACHE_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); -// -// assertThat(response.getBody()).isNotNull(); -// assertFalse(response.getHeaders().isEmpty()); -// assertEquals(3, response.getBody()); -// assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo("NDP001"); -// assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.disabled); -// ReflectionTestUtils.setField(cacheManagerService, "writePagoPa", true); -// } - -// @Test -// void syncCache_WriteNexiOracleDisabled() throws Exception { -// ObjectMapper objectMapper = new ObjectMapper(); -// StationsResponse stationsResponse = StationsResponse.builder().stations(List.of("1234567890", "9876543210")).build(); -// -// mockClient = new MockClient().ok(feign.mock.HttpMethod.GET, CLIENT_CACHE_PATH, objectMapper.writeValueAsBytes(stationsResponse)); -// ApiConfigCacheClient apiConfigCacheClient = -// Feign.builder().client(mockClient).target(new MockTarget<>(ApiConfigCacheClient.class)); -// cacheManagerService.setApiConfigCacheClient(apiConfigCacheClient); -// ReflectionTestUtils.setField(cacheManagerService, "writeNexiOracle", false); -// -// ResponseEntity> response = restTemplate.exchange(CACHE_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); -// -// List syncStatusResponseList = response.getBody(); -// -// assertThat(syncStatusResponseList).isNotNull(); -// assertFalse(syncStatusResponseList.isEmpty()); -// assertEquals(3, syncStatusResponseList.size()); -// assertThat(syncStatusResponseList.get(2).getServiceIdentifier()).isEqualTo("NDP003"); -// assertThat(syncStatusResponseList.get(2).getStatus()).isEqualTo(SyncStatusEnum.disabled); -// ReflectionTestUtils.setField(cacheManagerService, "writeNexiOracle", true); -// } - -// @Test -// void syncCache_WriteNexiPostgresDisabled() throws Exception { -// ObjectMapper objectMapper = new ObjectMapper(); -// StationsResponse stationsResponse = StationsResponse.builder().stations(List.of("1234567890", "9876543210")).build(); -// -// mockClient = new MockClient().ok(feign.mock.HttpMethod.GET, CLIENT_CACHE_PATH, objectMapper.writeValueAsBytes(stationsResponse)); -// ApiConfigCacheClient apiConfigCacheClient = -// Feign.builder().client(mockClient).target(new MockTarget<>(ApiConfigCacheClient.class)); -// cacheManagerService.setApiConfigCacheClient(apiConfigCacheClient); -// ReflectionTestUtils.setField(cacheManagerService, "writeNexiPostgres", false); -// -// ResponseEntity> response = restTemplate.exchange(CACHE_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); -// -// List syncStatusResponseList = response.getBody(); -// -// assertThat(syncStatusResponseList).isNotNull(); -// assertFalse(syncStatusResponseList.isEmpty()); -// assertEquals(3, syncStatusResponseList.size()); -// assertThat(syncStatusResponseList.get(1).getServiceIdentifier()).isEqualTo("NDP004DEV"); -// assertThat(syncStatusResponseList.get(1).getStatus()).isEqualTo(SyncStatusEnum.disabled); -// -// ReflectionTestUtils.setField(cacheManagerService, "writeNexiPostgres", true); -// } + @Test + void trimCacheVersionOnDb() { + Map> headersCustom = + Map.of( + HEADER_CACHE_ID, List.of(String.valueOf(System.currentTimeMillis())), + HEADER_CACHE_TIMESTAMP, List.of(Instant.now().toString()), + HEADER_CACHE_VERSION, List.of(StringUtils.repeat("*", 50)) + ); + when(apiConfigCacheClient.getCache(anyString())).thenReturn(Response + .builder() + .status(200) + .reason("Mocked") + .headers(headersCustom) + .request(mock(Request.class)) + .body(new byte[0]) + .build()); + cacheManagerService.setApiConfigCacheClient(apiConfigCacheClient); + + ResponseEntity> response = restTemplate.exchange(CACHE_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); + + assertThat(response.getBody()).isNotNull(); + assertFalse(response.getHeaders().isEmpty()); + assertFalse(response.getBody().isEmpty()); + assertEquals(3, response.getBody().size()); + assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo(PAGOPAPOSTGRES_SI); + assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.DONE); + assertThat(response.getBody().get(1).getServiceIdentifier()).isEqualTo(NEXIPOSTGRES_SI); + assertThat(response.getBody().get(1).getStatus()).isEqualTo(SyncStatusEnum.DONE); + assertThat(response.getBody().get(2).getServiceIdentifier()).isEqualTo(NEXIORACLE_SI); + assertThat(response.getBody().get(2).getStatus()).isEqualTo(SyncStatusEnum.DONE); + } + + @Test + void writePagoPAPostgresDisabled() { + ReflectionTestUtils.setField(cacheManagerService, "apiConfigCacheWritePagoPa", false); + + when(apiConfigCacheClient.getCache(anyString())).thenReturn(Response + .builder() + .status(200) + .reason("Mocked") + .headers(headers) + .request(mock(Request.class)) + .body(new byte[0]) + .build()); + cacheManagerService.setApiConfigCacheClient(apiConfigCacheClient); + + ResponseEntity> response = restTemplate.exchange(CACHE_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); + + assertThat(response.getBody()).isNotNull(); + assertFalse(response.getHeaders().isEmpty()); + assertFalse(response.getBody().isEmpty()); + assertEquals(3, response.getBody().size()); + assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo(PAGOPAPOSTGRES_SI); + assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.DISABLED); + assertThat(response.getBody().get(1).getServiceIdentifier()).isEqualTo(NEXIPOSTGRES_SI); + assertThat(response.getBody().get(1).getStatus()).isEqualTo(SyncStatusEnum.DONE); + assertThat(response.getBody().get(2).getServiceIdentifier()).isEqualTo(NEXIORACLE_SI); + assertThat(response.getBody().get(2).getStatus()).isEqualTo(SyncStatusEnum.DONE); + + ReflectionTestUtils.setField(cacheManagerService, "apiConfigCacheWritePagoPa", true); + } + + @Test + void writeNexiOracleDisabled() { + ReflectionTestUtils.setField(cacheManagerService, "apiConfigCacheWriteNexiOracle", false); + + when(apiConfigCacheClient.getCache(anyString())).thenReturn(Response + .builder() + .status(200) + .reason("Mocked") + .headers(headers) + .request(mock(Request.class)) + .body(new byte[0]) + .build()); + + cacheManagerService.setApiConfigCacheClient(apiConfigCacheClient); + + ResponseEntity> response = restTemplate.exchange(CACHE_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); + + assertThat(response.getBody()).isNotNull(); + assertFalse(response.getHeaders().isEmpty()); + assertFalse(response.getBody().isEmpty()); + assertEquals(3, response.getBody().size()); + assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo(PAGOPAPOSTGRES_SI); + assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.DONE); + assertThat(response.getBody().get(1).getServiceIdentifier()).isEqualTo(NEXIPOSTGRES_SI); + assertThat(response.getBody().get(1).getStatus()).isEqualTo(SyncStatusEnum.DONE); + assertThat(response.getBody().get(2).getServiceIdentifier()).isEqualTo(NEXIORACLE_SI); + assertThat(response.getBody().get(2).getStatus()).isEqualTo(SyncStatusEnum.DISABLED); + + ReflectionTestUtils.setField(cacheManagerService, "apiConfigCacheWriteNexiOracle", true); + } + + @Test + void writeNexiPostgresDisabled() { + ReflectionTestUtils.setField(cacheManagerService, "apiConfigCacheWriteNexiPostgres", false); + + when(apiConfigCacheClient.getCache(anyString())).thenReturn(Response + .builder() + .status(200) + .reason("Mocked") + .headers(headers) + .request(mock(Request.class)) + .body(new byte[0]) + .build()); + + cacheManagerService.setApiConfigCacheClient(apiConfigCacheClient); + + ResponseEntity> response = restTemplate.exchange(CACHE_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); + + assertThat(response.getBody()).isNotNull(); + assertFalse(response.getHeaders().isEmpty()); + assertFalse(response.getBody().isEmpty()); + assertEquals(3, response.getBody().size()); + assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo(PAGOPAPOSTGRES_SI); + assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.DONE); + assertThat(response.getBody().get(1).getServiceIdentifier()).isEqualTo(NEXIPOSTGRES_SI); + assertThat(response.getBody().get(1).getStatus()).isEqualTo(SyncStatusEnum.DISABLED); + assertThat(response.getBody().get(2).getServiceIdentifier()).isEqualTo(NEXIORACLE_SI); + assertThat(response.getBody().get(2).getStatus()).isEqualTo(SyncStatusEnum.DONE); + + ReflectionTestUtils.setField(cacheManagerService, "apiConfigCacheWriteNexiPostgres", true); + } + + @Test + void errorWritePagoPAPostgres() { + PagoPACachePostgresRepository repository = (PagoPACachePostgresRepository)ReflectionTestUtils.getField(cacheManagerService, "pagoPACachePostgresRepository"); + ReflectionTestUtils.setField(cacheManagerService, "pagoPACachePostgresRepository", null); + + when(apiConfigCacheClient.getCache(anyString())).thenReturn(Response + .builder() + .status(200) + .reason("Mocked") + .headers(headers) + .request(mock(Request.class)) + .body(new byte[0]) + .build()); + cacheManagerService.setApiConfigCacheClient(apiConfigCacheClient); + + ResponseEntity> response = restTemplate.exchange(CACHE_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); + + assertThat(response.getBody()).isNotNull(); + assertFalse(response.getBody().isEmpty()); + assertEquals(3, response.getBody().size()); + assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo(PAGOPAPOSTGRES_SI); + assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.ERROR); + assertThat(response.getBody().get(1).getServiceIdentifier()).isEqualTo(NEXIPOSTGRES_SI); + assertThat(response.getBody().get(1).getStatus()).isEqualTo(SyncStatusEnum.ROLLBACK); + assertThat(response.getBody().get(2).getServiceIdentifier()).isEqualTo(NEXIORACLE_SI); + assertThat(response.getBody().get(2).getStatus()).isEqualTo(SyncStatusEnum.ROLLBACK); + + ReflectionTestUtils.setField(cacheManagerService, "pagoPACachePostgresRepository", repository); + } + + @Test + void errorWriteNexiPostgres() { + NexiCachePostgresRepository repository = (NexiCachePostgresRepository)ReflectionTestUtils.getField(cacheManagerService, "nexiCachePostgresRepository"); + ReflectionTestUtils.setField(cacheManagerService, "nexiCachePostgresRepository", null); + + when(apiConfigCacheClient.getCache(anyString())).thenReturn(Response + .builder() + .status(200) + .reason("Mocked") + .headers(headers) + .request(mock(Request.class)) + .body(new byte[0]) + .build()); + cacheManagerService.setApiConfigCacheClient(apiConfigCacheClient); + + ResponseEntity> response = restTemplate.exchange(CACHE_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); + + assertThat(response.getBody()).isNotNull(); + assertFalse(response.getBody().isEmpty()); + assertEquals(3, response.getBody().size()); + assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo(PAGOPAPOSTGRES_SI); + assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.ROLLBACK); + assertThat(response.getBody().get(1).getServiceIdentifier()).isEqualTo(NEXIPOSTGRES_SI); + assertThat(response.getBody().get(1).getStatus()).isEqualTo(SyncStatusEnum.ERROR); + assertThat(response.getBody().get(2).getServiceIdentifier()).isEqualTo(NEXIORACLE_SI); + assertThat(response.getBody().get(2).getStatus()).isEqualTo(SyncStatusEnum.ROLLBACK); + + ReflectionTestUtils.setField(cacheManagerService, "nexiCachePostgresRepository", repository); + } + + @Test + void errorWriteNexiOracle() { + NexiCacheOracleRepository repository = (NexiCacheOracleRepository)ReflectionTestUtils.getField(cacheManagerService, "nexiCacheOracleRepository"); + ReflectionTestUtils.setField(cacheManagerService, "nexiCacheOracleRepository", null); + + when(apiConfigCacheClient.getCache(anyString())).thenReturn(Response + .builder() + .status(200) + .reason("Mocked") + .headers(headers) + .request(mock(Request.class)) + .body(new byte[0]) + .build()); + cacheManagerService.setApiConfigCacheClient(apiConfigCacheClient); + + ResponseEntity> response = restTemplate.exchange(CACHE_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); + + assertThat(response.getBody()).isNotNull(); + assertFalse(response.getBody().isEmpty()); + assertEquals(3, response.getBody().size()); + assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo(PAGOPAPOSTGRES_SI); + assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.ROLLBACK); + assertThat(response.getBody().get(1).getServiceIdentifier()).isEqualTo(NEXIPOSTGRES_SI); + assertThat(response.getBody().get(1).getStatus()).isEqualTo(SyncStatusEnum.ROLLBACK); + assertThat(response.getBody().get(2).getServiceIdentifier()).isEqualTo(NEXIORACLE_SI); + assertThat(response.getBody().get(2).getStatus()).isEqualTo(SyncStatusEnum.ERROR); + ReflectionTestUtils.setField(cacheManagerService, "nexiCacheOracleRepository", repository); + } } diff --git a/src/test/java/it/gov/pagopa/node/cfgsync/ConstantsHelper.java b/src/test/java/it/gov/pagopa/node/cfgsync/ConstantsHelper.java new file mode 100644 index 0000000..15b03ca --- /dev/null +++ b/src/test/java/it/gov/pagopa/node/cfgsync/ConstantsHelper.java @@ -0,0 +1,8 @@ +package it.gov.pagopa.node.cfgsync; + +class ConstantsHelper { + + protected static final String PAGOPAPOSTGRES_SI = "PAGOPAPOSTGRES"; + protected static final String NEXIPOSTGRES_SI = "NEXIPOSTGRES"; + protected static final String NEXIORACLE_SI = "NEXIORACLE"; +} diff --git a/src/test/java/it/gov/pagopa/node/cfgsync/HomeControllerTest.java b/src/test/java/it/gov/pagopa/node/cfgsync/HomeControllerTest.java new file mode 100644 index 0000000..a299f3d --- /dev/null +++ b/src/test/java/it/gov/pagopa/node/cfgsync/HomeControllerTest.java @@ -0,0 +1,52 @@ +package it.gov.pagopa.node.cfgsync; + +import it.gov.pagopa.node.cfgsync.controller.HomeController; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc +@RunWith(SpringRunner.class) +class HomeControllerTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Autowired + private HomeController homeController; + + @Test + void homeSwaggerBasePathSlash() { + ResponseEntity response = restTemplate.exchange("/", HttpMethod.GET, null, String.class); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertFalse(response.getBody().isEmpty()); + assertThat(response.getBody().contains("Swagger UI")); + } + + @Test + void homeSwaggerBasePathEmpty() { + ReflectionTestUtils.setField(homeController, "basePath", ""); + + ResponseEntity response = restTemplate.exchange("/", HttpMethod.GET, null, String.class); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertFalse(response.getBody().isEmpty()); + assertThat(response.getBody().contains("Swagger UI")); + + ReflectionTestUtils.setField(homeController, "basePath", "/"); + } +} diff --git a/src/test/java/it/gov/pagopa/node/cfgsync/OpenApiGenerationTest.java b/src/test/java/it/gov/pagopa/node/cfgsync/OpenApiGenerationTest.java index 8942fd2..e478f34 100644 --- a/src/test/java/it/gov/pagopa/node/cfgsync/OpenApiGenerationTest.java +++ b/src/test/java/it/gov/pagopa/node/cfgsync/OpenApiGenerationTest.java @@ -1,12 +1,6 @@ package it.gov.pagopa.node.cfgsync; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; - import com.fasterxml.jackson.databind.ObjectMapper; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; @@ -16,6 +10,13 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; + @SpringBootTest(classes = Application.class) @AutoConfigureMockMvc class OpenApiGenerationTest { diff --git a/src/test/java/it/gov/pagopa/node/cfgsync/StandInConsumerTest.java b/src/test/java/it/gov/pagopa/node/cfgsync/StandInConsumerTest.java index 4c414e7..7574f88 100644 --- a/src/test/java/it/gov/pagopa/node/cfgsync/StandInConsumerTest.java +++ b/src/test/java/it/gov/pagopa/node/cfgsync/StandInConsumerTest.java @@ -1,81 +1,51 @@ -//package it.gov.pagopa.node.cfgsync; -// -//import com.azure.messaging.eventhubs.models.EventContext; -//import com.fasterxml.jackson.databind.ObjectMapper; -//import feign.Feign; -//import feign.mock.MockClient; -//import feign.mock.MockTarget; -//import it.gov.pagopa.node.cfgsync.client.StandInManagerClient; -//import it.gov.pagopa.node.cfgsync.model.ProblemJson; -//import it.gov.pagopa.node.cfgsync.model.StationsResponse; -//import it.gov.pagopa.node.cfgsync.model.SyncStatusEnum; -//import it.gov.pagopa.node.cfgsync.model.SyncStatusResponse; -//import it.gov.pagopa.node.cfgsync.repository.pagopa.PagoPAStandInPostgresRepository; -//import it.gov.pagopa.node.cfgsync.service.StandInManagerEhConsumer; -//import it.gov.pagopa.node.cfgsync.service.StandInManagerService; -//import org.junit.jupiter.api.Test; -//import org.junit.jupiter.api.extension.ExtendWith; -//import org.junit.runner.RunWith; -//import org.mockito.ArgumentMatchers; -//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.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -//import org.springframework.boot.test.context.SpringBootTest; -//import org.springframework.boot.test.web.client.TestRestTemplate; -//import org.springframework.boot.test.web.server.LocalServerPort; -//import org.springframework.core.ParameterizedTypeReference; -//import org.springframework.http.HttpMethod; -//import org.springframework.http.HttpStatus; -//import org.springframework.http.ResponseEntity; -//import org.springframework.test.context.junit.jupiter.SpringExtension; -//import org.springframework.test.context.junit4.SpringRunner; -//import org.springframework.test.util.ReflectionTestUtils; -// -//import java.util.List; -// -//import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -//import static org.hamcrest.Matchers.any; -//import static org.junit.Assert.assertEquals; -//import static org.junit.Assert.assertFalse; -//import static org.mockito.BDDMockito.given; -//import static org.mockito.Mockito.mock; -//import static org.mockito.Mockito.when; -// -//@ExtendWith(MockitoExtension.class) -//@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -//@AutoConfigureMockMvc -//@RunWith(SpringRunner.class) -//class StandInConsumerTest { -// -// public static final String STATIONS_PATH = "/stations"; -// -// @Autowired private StandInManagerService standInManagerService; -// @Autowired private TestRestTemplate restTemplate; -// private MockClient mockClient; -// -// @Mock StandInManagerEhConsumer standInManagerEhConsumer; -// -// @InjectMocks -// private EventContext eventContext; -// -// @Test -// void syncStandIn_400() { -// -// -// StandInManagerEhConsumer consumerClient = mock(StandInManagerEhConsumer.class); -// -// when(consumerClient.processEvent(any())).thenReturn(eventsPublisher.flux()); -// -// standInManagerEhConsumer.processEvent(eventContext); -// -// Mockito.when(standInManagerEhConsumer.processEvent(ArgumentMatchers.any())) -// .thenAnswer(a -> Mono.just(new Object())); -// -// given(standInManagerEhConsumer.processEvent(eventContext)).then( v -> ""); -// assertThat("pippo").isEqualTo("pippo"); -// } -// -//} +package it.gov.pagopa.node.cfgsync; + +import com.azure.messaging.eventhubs.CheckpointStore; +import com.azure.messaging.eventhubs.EventData; +import com.azure.messaging.eventhubs.checkpointstore.blob.BlobCheckpointStore; +import com.azure.messaging.eventhubs.models.ErrorContext; +import com.azure.messaging.eventhubs.models.EventContext; +import com.azure.messaging.eventhubs.models.LastEnqueuedEventProperties; +import com.azure.messaging.eventhubs.models.PartitionContext; +import it.gov.pagopa.node.cfgsync.service.StandInManagerEhConsumer; +import it.gov.pagopa.node.cfgsync.service.StandInManagerService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.Instant; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class StandInConsumerTest { + + @Mock + StandInManagerService service; + + @Test + void processEvent() { + PartitionContext partitionContext = new PartitionContext("", "", "", "1"); + CheckpointStore checkpointStore = new BlobCheckpointStore(null); + LastEnqueuedEventProperties lastEnqueuedEventProperties = new LastEnqueuedEventProperties(1L, 1L, Instant.now(), Instant.now()); + EventData eventData = new EventData(); + EventContext eventContext = new EventContext(partitionContext, eventData, checkpointStore, lastEnqueuedEventProperties); + + StandInManagerEhConsumer consumer = new StandInManagerEhConsumer(service); + consumer.processEvent(eventContext); + verify(service, times(1)).syncStandIn(); + } + + @Test + void processError() { + PartitionContext partitionContext = new PartitionContext("", "", "", "1"); + ErrorContext errorContext = new ErrorContext(partitionContext, new Exception("")); + + StandInManagerEhConsumer consumer = new StandInManagerEhConsumer(service); + consumer.processError(errorContext); + verify(service, times(0)).syncStandIn(); + } + +} diff --git a/src/test/java/it/gov/pagopa/node/cfgsync/StandInSyncTest.java b/src/test/java/it/gov/pagopa/node/cfgsync/StandInSyncTest.java index 2ca8cd9..357d7ec 100644 --- a/src/test/java/it/gov/pagopa/node/cfgsync/StandInSyncTest.java +++ b/src/test/java/it/gov/pagopa/node/cfgsync/StandInSyncTest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import feign.Feign; +import feign.FeignException; import feign.mock.MockClient; import feign.mock.MockTarget; import it.gov.pagopa.node.cfgsync.client.StandInManagerClient; @@ -9,11 +10,14 @@ import it.gov.pagopa.node.cfgsync.model.StationsResponse; import it.gov.pagopa.node.cfgsync.model.SyncStatusEnum; import it.gov.pagopa.node.cfgsync.model.SyncStatusResponse; +import it.gov.pagopa.node.cfgsync.repository.nexioracle.NexiStandInOracleRepository; +import it.gov.pagopa.node.cfgsync.repository.nexipostgres.NexiStandInPostgresRepository; import it.gov.pagopa.node.cfgsync.repository.pagopa.PagoPAStandInPostgresRepository; import it.gov.pagopa.node.cfgsync.service.StandInManagerService; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; @@ -29,9 +33,12 @@ import java.util.List; +import static it.gov.pagopa.node.cfgsync.ConstantsHelper.*; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -42,26 +49,35 @@ class StandInSyncTest { public static final String STANDIN_URL = "/ndp/stand-in"; public static final String STATIONS_PATH = "/stations"; + static final List stations = List.of("1234567890", "9876543210"); + @Autowired private StandInManagerService standInManagerService; @Autowired private TestRestTemplate restTemplate; private MockClient mockClient; @LocalServerPort private int port; + @Mock + StandInManagerClient standInManagerClient; + + ObjectMapper objectMapper = new ObjectMapper(); + @Test - void syncStandIn_400() { - ReflectionTestUtils.setField(standInManagerService, "enabled", false); + void error400() { + ReflectionTestUtils.setField(standInManagerService, "standInManagerEnabled", false); + ResponseEntity response = restTemplate.exchange(STANDIN_URL, HttpMethod.PUT, null, ProblemJson.class); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); assertThat(response.getBody().getStatus()).isEqualTo(400); assertThat(response.getBody().getTitle()).isEqualTo("Target service disabled"); assertThat(response.getBody().getDetail()).isEqualTo("Target service stand-in-manager disabled"); - ReflectionTestUtils.setField(standInManagerService, "enabled", true); + + ReflectionTestUtils.setField(standInManagerService, "standInManagerEnabled", true); } @Test - void syncStandIn_500() { - mockClient = new MockClient().noContent(feign.mock.HttpMethod.GET, "/stations"); + void error500() { + mockClient = new MockClient().noContent(feign.mock.HttpMethod.GET, STATIONS_PATH); StandInManagerClient standInManagerClient = Feign.builder().client(mockClient).target(new MockTarget<>(StandInManagerClient.class)); standInManagerService.setStandInManagerClient(standInManagerClient); @@ -72,75 +88,93 @@ void syncStandIn_500() { assertThat(response.getBody().getTitle()).isEqualTo("Internal Server Error"); } -// @Test -// void syncStandIn_500_ConnectionRefused() { -// Request request = mock(Request.class); -// when(standInManagerClient.getCache(anyString())) -// .thenThrow(new FeignException.NotFound("message", request, null, null)); -// -// ResponseEntity response = restTemplate.exchange(STANDIN_URL, HttpMethod.PUT, null, ProblemJson.class); -// assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); -// assertThat(response.getBody().getStatus()).isEqualTo(500); -// assertThat(response.getBody().getTitle()).isEqualTo("Internal Server Error"); -// } + @Test + void error500ClientNull() { + mockClient = new MockClient().noContent(feign.mock.HttpMethod.GET, STATIONS_PATH); + + standInManagerService.setStandInManagerClient(null); + + ResponseEntity response = restTemplate.exchange(STANDIN_URL, HttpMethod.PUT, null, ProblemJson.class); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + assertThat(response.getBody().getStatus()).isEqualTo(500); + assertThat(response.getBody().getTitle()).isEqualTo("Internal Server Error"); + } + + @Test + void error500StandInManagerException() { + when(standInManagerClient.getCache(anyString())).thenThrow(FeignException.class); + + standInManagerService.setStandInManagerClient(standInManagerClient); + + ResponseEntity response = restTemplate.exchange(STANDIN_URL, HttpMethod.PUT, null, ProblemJson.class); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + assertThat(response.getBody().getStatus()).isEqualTo(500); + assertThat(response.getBody().getTitle()).isEqualTo("Internal Server Error"); + } @Test - void syncStandIn_ErrorWriteDB() throws Exception { - ObjectMapper objectMapper = new ObjectMapper(); - StationsResponse stationsResponse = StationsResponse.builder().stations(List.of("1234567890", "9876543210")).build(); + void writePagoPAPostgresDisabled() throws Exception { + ReflectionTestUtils.setField(standInManagerService, "standInManagerWritePagoPa", false); + + StationsResponse stationsResponse = StationsResponse.builder().stations(stations).build(); mockClient = new MockClient().ok(feign.mock.HttpMethod.GET, STATIONS_PATH, objectMapper.writeValueAsBytes(stationsResponse)); StandInManagerClient standInManagerClient = Feign.builder().client(mockClient).target(new MockTarget<>(StandInManagerClient.class)); standInManagerService.setStandInManagerClient(standInManagerClient); - PagoPAStandInPostgresRepository paStandInPostgresRepository = (PagoPAStandInPostgresRepository)ReflectionTestUtils.getField(standInManagerService, "pagopaPostgresRepository"); - ReflectionTestUtils.setField(standInManagerService, "pagopaPostgresRepository", null); ResponseEntity> response = restTemplate.exchange(STANDIN_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); assertThat(response.getBody()).isNotNull(); assertFalse(response.getBody().isEmpty()); assertEquals(3, response.getBody().size()); - assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo("NDP001"); - assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.error); - assertThat(response.getBody().get(1).getServiceIdentifier()).isEqualTo("NDP004DEV"); - assertThat(response.getBody().get(1).getStatus()).isEqualTo(SyncStatusEnum.rollback); - assertThat(response.getBody().get(2).getServiceIdentifier()).isEqualTo("NDP003"); - assertThat(response.getBody().get(2).getStatus()).isEqualTo(SyncStatusEnum.rollback); - ReflectionTestUtils.setField(standInManagerService, "pagopaPostgresRepository", paStandInPostgresRepository); + assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo(PAGOPAPOSTGRES_SI); + assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.DISABLED); + assertThat(response.getBody().get(1).getServiceIdentifier()).isEqualTo(NEXIPOSTGRES_SI); + assertThat(response.getBody().get(1).getStatus()).isEqualTo(SyncStatusEnum.DONE); + assertThat(response.getBody().get(2).getServiceIdentifier()).isEqualTo(NEXIORACLE_SI); + assertThat(response.getBody().get(2).getStatus()).isEqualTo(SyncStatusEnum.DONE); + ReflectionTestUtils.setField(standInManagerService, "standInManagerWritePagoPa", true); } @Test - void syncStandIn_WritePagoPAPostgresDisabled() throws Exception { - ObjectMapper objectMapper = new ObjectMapper(); - StationsResponse stationsResponse = StationsResponse.builder().stations(List.of("1234567890", "9876543210")).build(); + void writeNexiOracleDisabled() throws Exception { + ReflectionTestUtils.setField(standInManagerService, "standInManagerWriteNexiOracle", false); + + StationsResponse stationsResponse = StationsResponse.builder().stations(stations).build(); mockClient = new MockClient().ok(feign.mock.HttpMethod.GET, STATIONS_PATH, objectMapper.writeValueAsBytes(stationsResponse)); StandInManagerClient standInManagerClient = Feign.builder().client(mockClient).target(new MockTarget<>(StandInManagerClient.class)); standInManagerService.setStandInManagerClient(standInManagerClient); - ReflectionTestUtils.setField(standInManagerService, "writePagoPa", false); ResponseEntity> response = restTemplate.exchange(STANDIN_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); - assertThat(response.getBody()).isNotNull(); - assertFalse(response.getBody().isEmpty()); - assertEquals(3, response.getBody().size()); - assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo("NDP001"); - assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.disabled); - ReflectionTestUtils.setField(standInManagerService, "writePagoPa", true); + List syncStatusResponseList = response.getBody(); + + assertThat(syncStatusResponseList).isNotNull(); + assertFalse(syncStatusResponseList.isEmpty()); + assertEquals(3, syncStatusResponseList.size()); + assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo(PAGOPAPOSTGRES_SI); + assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.DONE); + assertThat(response.getBody().get(1).getServiceIdentifier()).isEqualTo(NEXIPOSTGRES_SI); + assertThat(response.getBody().get(1).getStatus()).isEqualTo(SyncStatusEnum.DONE); + assertThat(response.getBody().get(2).getServiceIdentifier()).isEqualTo(NEXIORACLE_SI); + assertThat(response.getBody().get(2).getStatus()).isEqualTo(SyncStatusEnum.DISABLED); + + ReflectionTestUtils.setField(standInManagerService, "standInManagerWriteNexiOracle", true); } @Test - void syncStandIn_WriteNexiOracleDisabled() throws Exception { - ObjectMapper objectMapper = new ObjectMapper(); - StationsResponse stationsResponse = StationsResponse.builder().stations(List.of("1234567890", "9876543210")).build(); + void writeNexiPostgresDisabled() throws Exception { + ReflectionTestUtils.setField(standInManagerService, "standInManagerWriteNexiPostgres", false); + + StationsResponse stationsResponse = StationsResponse.builder().stations(stations).build(); mockClient = new MockClient().ok(feign.mock.HttpMethod.GET, STATIONS_PATH, objectMapper.writeValueAsBytes(stationsResponse)); StandInManagerClient standInManagerClient = Feign.builder().client(mockClient).target(new MockTarget<>(StandInManagerClient.class)); standInManagerService.setStandInManagerClient(standInManagerClient); - ReflectionTestUtils.setField(standInManagerService, "writeNexiOracle", false); ResponseEntity> response = restTemplate.exchange(STANDIN_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); @@ -149,33 +183,95 @@ void syncStandIn_WriteNexiOracleDisabled() throws Exception { assertThat(syncStatusResponseList).isNotNull(); assertFalse(syncStatusResponseList.isEmpty()); assertEquals(3, syncStatusResponseList.size()); - assertThat(syncStatusResponseList.get(2).getServiceIdentifier()).isEqualTo("NDP003"); - assertThat(syncStatusResponseList.get(2).getStatus()).isEqualTo(SyncStatusEnum.disabled); - ReflectionTestUtils.setField(standInManagerService, "writeNexiOracle", true); + assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo(PAGOPAPOSTGRES_SI); + assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.DONE); + assertThat(response.getBody().get(1).getServiceIdentifier()).isEqualTo(NEXIPOSTGRES_SI); + assertThat(response.getBody().get(1).getStatus()).isEqualTo(SyncStatusEnum.DISABLED); + assertThat(response.getBody().get(2).getServiceIdentifier()).isEqualTo(NEXIORACLE_SI); + assertThat(response.getBody().get(2).getStatus()).isEqualTo(SyncStatusEnum.DONE); + + ReflectionTestUtils.setField(standInManagerService, "standInManagerWriteNexiPostgres", true); } @Test - void syncStandIn_WriteNexiPostgresDisabled() throws Exception { - ObjectMapper objectMapper = new ObjectMapper(); - StationsResponse stationsResponse = StationsResponse.builder().stations(List.of("1234567890", "9876543210")).build(); + void errorWritePagoPAPostgres() throws Exception { + PagoPAStandInPostgresRepository repository = (PagoPAStandInPostgresRepository)ReflectionTestUtils.getField(standInManagerService, "pagoPAStandInPostgresRepository"); + ReflectionTestUtils.setField(standInManagerService, "pagoPAStandInPostgresRepository", null); + + StationsResponse stationsResponse = StationsResponse.builder().stations(stations).build(); mockClient = new MockClient().ok(feign.mock.HttpMethod.GET, STATIONS_PATH, objectMapper.writeValueAsBytes(stationsResponse)); StandInManagerClient standInManagerClient = Feign.builder().client(mockClient).target(new MockTarget<>(StandInManagerClient.class)); standInManagerService.setStandInManagerClient(standInManagerClient); - ReflectionTestUtils.setField(standInManagerService, "writeNexiPostgres", false); ResponseEntity> response = restTemplate.exchange(STANDIN_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); - List syncStatusResponseList = response.getBody(); + assertThat(response.getBody()).isNotNull(); + assertFalse(response.getBody().isEmpty()); + assertEquals(3, response.getBody().size()); + assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo(PAGOPAPOSTGRES_SI); + assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.ERROR); + assertThat(response.getBody().get(1).getServiceIdentifier()).isEqualTo(NEXIPOSTGRES_SI); + assertThat(response.getBody().get(1).getStatus()).isEqualTo(SyncStatusEnum.ROLLBACK); + assertThat(response.getBody().get(2).getServiceIdentifier()).isEqualTo(NEXIORACLE_SI); + assertThat(response.getBody().get(2).getStatus()).isEqualTo(SyncStatusEnum.ROLLBACK); - assertThat(syncStatusResponseList).isNotNull(); - assertFalse(syncStatusResponseList.isEmpty()); - assertEquals(3, syncStatusResponseList.size()); - assertThat(syncStatusResponseList.get(1).getServiceIdentifier()).isEqualTo("NDP004DEV"); - assertThat(syncStatusResponseList.get(1).getStatus()).isEqualTo(SyncStatusEnum.disabled); + ReflectionTestUtils.setField(standInManagerService, "pagoPAStandInPostgresRepository", repository); + } + + @Test + void errorWriteNexiPostgres() throws Exception { + NexiStandInPostgresRepository repository = (NexiStandInPostgresRepository)ReflectionTestUtils.getField(standInManagerService, "nexiStandInPostgresRepository"); + ReflectionTestUtils.setField(standInManagerService, "nexiStandInPostgresRepository", null); + + StationsResponse stationsResponse = StationsResponse.builder().stations(stations).build(); + + mockClient = new MockClient().ok(feign.mock.HttpMethod.GET, STATIONS_PATH, objectMapper.writeValueAsBytes(stationsResponse)); + StandInManagerClient standInManagerClient = + Feign.builder().client(mockClient).target(new MockTarget<>(StandInManagerClient.class)); + standInManagerService.setStandInManagerClient(standInManagerClient); + + ResponseEntity> response = restTemplate.exchange(STANDIN_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); + + assertThat(response.getBody()).isNotNull(); + assertFalse(response.getBody().isEmpty()); + assertEquals(3, response.getBody().size()); + assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo(PAGOPAPOSTGRES_SI); + assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.ROLLBACK); + assertThat(response.getBody().get(1).getServiceIdentifier()).isEqualTo(NEXIPOSTGRES_SI); + assertThat(response.getBody().get(1).getStatus()).isEqualTo(SyncStatusEnum.ERROR); + assertThat(response.getBody().get(2).getServiceIdentifier()).isEqualTo(NEXIORACLE_SI); + assertThat(response.getBody().get(2).getStatus()).isEqualTo(SyncStatusEnum.ROLLBACK); + + ReflectionTestUtils.setField(standInManagerService, "nexiStandInPostgresRepository", repository); + } + + @Test + void errorWriteNexiOracle() throws Exception { + NexiStandInOracleRepository repository = (NexiStandInOracleRepository)ReflectionTestUtils.getField(standInManagerService, "nexiStandInOracleRepository"); + ReflectionTestUtils.setField(standInManagerService, "nexiStandInOracleRepository", null); + + StationsResponse stationsResponse = StationsResponse.builder().stations(stations).build(); + + mockClient = new MockClient().ok(feign.mock.HttpMethod.GET, STATIONS_PATH, objectMapper.writeValueAsBytes(stationsResponse)); + StandInManagerClient standInManagerClient = + Feign.builder().client(mockClient).target(new MockTarget<>(StandInManagerClient.class)); + standInManagerService.setStandInManagerClient(standInManagerClient); + + ResponseEntity> response = restTemplate.exchange(STANDIN_URL, HttpMethod.PUT, null, new ParameterizedTypeReference<>() {}); + + assertThat(response.getBody()).isNotNull(); + assertFalse(response.getBody().isEmpty()); + assertEquals(3, response.getBody().size()); + assertThat(response.getBody().get(0).getServiceIdentifier()).isEqualTo(PAGOPAPOSTGRES_SI); + assertThat(response.getBody().get(0).getStatus()).isEqualTo(SyncStatusEnum.ROLLBACK); + assertThat(response.getBody().get(1).getServiceIdentifier()).isEqualTo(NEXIPOSTGRES_SI); + assertThat(response.getBody().get(1).getStatus()).isEqualTo(SyncStatusEnum.ROLLBACK); + assertThat(response.getBody().get(2).getServiceIdentifier()).isEqualTo(NEXIORACLE_SI); + assertThat(response.getBody().get(2).getStatus()).isEqualTo(SyncStatusEnum.ERROR); - ReflectionTestUtils.setField(standInManagerService, "writeNexiPostgres", true); + ReflectionTestUtils.setField(standInManagerService, "nexiStandInOracleRepository", repository); } } diff --git a/src/test/java/it/gov/pagopa/node/cfgsync/UtilsTest.java b/src/test/java/it/gov/pagopa/node/cfgsync/UtilsTest.java new file mode 100644 index 0000000..e962849 --- /dev/null +++ b/src/test/java/it/gov/pagopa/node/cfgsync/UtilsTest.java @@ -0,0 +1,31 @@ +package it.gov.pagopa.node.cfgsync; + +import it.gov.pagopa.node.cfgsync.exception.SyncDbStatusException; +import it.gov.pagopa.node.cfgsync.repository.model.ConfigCache; +import it.gov.pagopa.node.cfgsync.util.Utils; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class UtilsTest { + + @Test + void testConstructorIsPrivate() throws NoSuchMethodException { + Constructor constructor = Utils.class.getDeclaredConstructor(); + assertTrue(Modifier.isPrivate(constructor.getModifiers())); + constructor.setAccessible(true); + assertThrows(InvocationTargetException.class, constructor::newInstance); + } + + @Test + void utilsTrimException() { + assertThrows(SyncDbStatusException.class, + () -> Utils.trimValueColumn(ConfigCache.class, "UNKNOWN_FIELD", StringUtils.repeat("*", 50))); + } +} diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml index 66bf745..ccfe5e1 100644 --- a/src/test/resources/application.yaml +++ b/src/test/resources/application.yaml @@ -33,37 +33,33 @@ management: probes: enabled: 'true' app: - trimCacheColumn: 'true' identifiers: - pagopa-postgres: 'NDP001' - nexi-postgres: 'NDP004DEV' - nexi-oracle: 'NDP003' + pagopa-postgres: 'PAGOPAPOSTGRES' + nexi-postgres: 'NEXIPOSTGRES' + nexi-oracle: 'NEXIORACLE' spring: datasource: - url: jdbc:h2:mem:foo;DB_CLOSE_ON_EXIT=FALSE - username: sa - password: driver-class-name: org.h2.Driver pagopa: postgres: url: jdbc:h2:mem:pagopa;DB_CLOSE_ON_EXIT=FALSE username: sa password: - ##permette di abilitare/disabilitare il datasource al db Postgres PagoPA + #permette di abilitare/disabilitare il datasource al db Postgres PagoPA enabled: 'true' nexi: oracle: url: jdbc:h2:mem:nexi_oracle;DB_CLOSE_ON_EXIT=FALSE username: sa password: - ##permette di abilitare/disabilitare il datasource al db Oracle Nexi + #permette di abilitare/disabilitare il datasource al db Oracle Nexi enabled: 'true' postgres: url: jdbc:h2:mem:nexi_postgres;DB_CLOSE_ON_EXIT=FALSE username: sa password: - ##permette di abilitare/disabilitare il datasource al db Postgres Nexi + #permette di abilitare/disabilitare il datasource al db Postgres Nexi enabled: 'true' jpa: database: default @@ -95,10 +91,10 @@ stand-in-manager: sa-name: standinevents consumer-group: $Default consumer: - ##permette di abilitare/disabilitare il consumer + #permette di abilitare/disabilitare il consumer enabled: 'false' write: - ##permettono di abilitare/disabilitare la scrittura di stand-in sui vari database + #permettono di abilitare/disabilitare la scrittura di stand-in sui vari database pagopa-postgres: 'true' nexi-postgres: 'true' nexi-oracle: 'true' @@ -109,16 +105,16 @@ api-config-cache: service: host: http://localhost:1080 subscriptionKey: '' - ##permette di abilitare/disabilitare la chiamata al servizio tramite API + #permette di abilitare/disabilitare la chiamata al servizio tramite API enabled: 'true' sa-connection-string: ${API_CONFIG_CACHE_SA_CONNECTION_STRING} sa-name: cacheevents consumer-group: $Default consumer: - ##permette di abilitare/disabilitare il consumer + #permette di abilitare/disabilitare il consumer enabled: 'false' write: - ##permettono di abilitare/disabilitare la scrittura di cache sui vari database + #permettono di abilitare/disabilitare la scrittura di cache sui vari database pagopa-postgres: 'true' nexi-postgres: 'true' nexi-oracle: 'true'