From 5e71f3ba409a439d6be23ebd08abe849182496be Mon Sep 17 00:00:00 2001 From: Andrea De Rinaldis <117269497+andrea-deri@users.noreply.github.com> Date: Thu, 28 Mar 2024 11:09:13 +0100 Subject: [PATCH] feat: Unified error handling (#4) * [NOD-781] feat: add error json * [NOD-781] feat: refactored code and updated missing classes --------- Co-authored-by: maxsca <130107847+maxsca@users.noreply.github.com> --- .gitattributes | 4 + .../wispconverter/config/WebMvcConfig.java | 40 +++ .../controller/HomeController.java | 5 +- .../controller/RedirectController.java | 27 +- .../controller/advice/ErrorHandler.java | 247 ++++++++---------- .../advice/model/ApiErrorResponse.java | 65 +++++ .../advice/model/ProblemJsonResponse.java | 37 --- .../exception/AppClientException.java | 16 ++ .../wispconverter/exception/AppError.java | 30 --- .../exception/AppErrorCodeMessageEnum.java | 31 +++ .../wispconverter/exception/AppException.java | 90 +------ .../wispconverter/service/CacheService.java | 7 +- .../service/ConverterService.java | 8 +- .../service/DebtPositionService.java | 6 +- .../service/NAVGeneratorService.java | 7 +- .../wispconverter/util/AppErrorUtil.java | 38 +++ .../pagopa/wispconverter/util/Constants.java | 2 +- .../wispconverter/util/JaxbElementUtil.java | 16 +- .../util/aspect/LoggingAspect.java | 24 +- .../util/aspect/ResponseValidator.java | 4 +- .../resources/messages/messages_en.properties | 5 + .../resources/messages/messages_it.properties | 5 + 22 files changed, 369 insertions(+), 345 deletions(-) create mode 100644 .gitattributes create mode 100644 src/main/java/it/gov/pagopa/wispconverter/controller/advice/model/ApiErrorResponse.java delete mode 100644 src/main/java/it/gov/pagopa/wispconverter/controller/advice/model/ProblemJsonResponse.java create mode 100644 src/main/java/it/gov/pagopa/wispconverter/exception/AppClientException.java delete mode 100644 src/main/java/it/gov/pagopa/wispconverter/exception/AppError.java create mode 100644 src/main/java/it/gov/pagopa/wispconverter/exception/AppErrorCodeMessageEnum.java create mode 100644 src/main/java/it/gov/pagopa/wispconverter/util/AppErrorUtil.java create mode 100644 src/main/resources/messages/messages_en.properties create mode 100644 src/main/resources/messages/messages_it.properties diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..0af93609 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto working-tree-encoding=UTF-8 + +*.sh text eol=lf working-tree-encoding=UTF-8 diff --git a/src/main/java/it/gov/pagopa/wispconverter/config/WebMvcConfig.java b/src/main/java/it/gov/pagopa/wispconverter/config/WebMvcConfig.java index 8d4f79b7..f875884e 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/config/WebMvcConfig.java +++ b/src/main/java/it/gov/pagopa/wispconverter/config/WebMvcConfig.java @@ -4,14 +4,27 @@ import it.gov.pagopa.wispconverter.config.model.AppCors; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; +import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver; + +import java.util.Arrays; +import java.util.List; +import java.util.Locale; @Configuration public class WebMvcConfig implements WebMvcConfigurer { + List locales = Arrays.asList(Locale.ENGLISH, Locale.ITALIAN); + @Value("${cors.configuration}") private String corsConfiguration; @@ -25,6 +38,33 @@ public void addCorsMappings(CorsRegistry registry) { .allowedOrigins(appCors.getOrigins()) .allowedMethods(appCors.getMethods()); } + + @Bean + public LocaleResolver localeResolver() { + AcceptHeaderLocaleResolver acceptHeaderLocaleResolver = new AcceptHeaderLocaleResolver(); + acceptHeaderLocaleResolver.setDefaultLocale(Locale.ENGLISH); + acceptHeaderLocaleResolver.setSupportedLocales(locales); + return acceptHeaderLocaleResolver; + } + + @Bean + public MessageSource messageSource() { + ReloadableResourceBundleMessageSource messageSource = + new ReloadableResourceBundleMessageSource(); + messageSource.setBasename("classpath:messages"); + messageSource.setDefaultEncoding("UTF-8"); + messageSource.setUseCodeAsDefaultMessage(true); + return messageSource; + } + + @Primary + @Bean + @Override + public LocalValidatorFactoryBean getValidator() { + LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean(); + bean.setValidationMessageSource(messageSource()); + return bean; + } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/controller/HomeController.java b/src/main/java/it/gov/pagopa/wispconverter/controller/HomeController.java index a44faaf1..9bc1f512 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/controller/HomeController.java +++ b/src/main/java/it/gov/pagopa/wispconverter/controller/HomeController.java @@ -7,6 +7,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; import it.gov.pagopa.wispconverter.controller.model.AppInfoResponse; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; @@ -17,6 +18,7 @@ @RestController @Validated +@Tag(name = "Home", description = "Application info APIs") public class HomeController { @Value("${server.servlet.context-path}") @@ -52,9 +54,6 @@ public RedirectView home() { @Operation(summary = "Return OK if application is started", security = {@SecurityRequirement(name = "ApiKey")}, tags = {"Home"}) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "OK.", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = AppInfoResponse.class))), - //@ApiResponse(responseCode = "401", description = "Wrong or missing function key.", content = @Content(schema = @Schema())), - //@ApiResponse(responseCode = "403", description = "Forbidden.", content = @Content(schema = @Schema())), - //@ApiResponse(responseCode = "500", description = "Service unavailable.", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = ProblemJson.class)))} }) @GetMapping("/info") public AppInfoResponse healthCheck() { diff --git a/src/main/java/it/gov/pagopa/wispconverter/controller/RedirectController.java b/src/main/java/it/gov/pagopa/wispconverter/controller/RedirectController.java index a0201f8c..42bf3f3f 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/controller/RedirectController.java +++ b/src/main/java/it/gov/pagopa/wispconverter/controller/RedirectController.java @@ -7,9 +7,11 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; import it.gov.pagopa.wispconverter.service.ConverterService; import it.gov.pagopa.wispconverter.service.model.ConversionResultDTO; import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotBlank; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; @@ -23,33 +25,18 @@ @RequestMapping("/redirect") @Validated @RequiredArgsConstructor +@Tag(name = "Redirect", description = "Conversion and redirection APIs") public class RedirectController { private final ConverterService converterService; - /* - public static ResponseEntity generateConversionResponse(ConversionResult conversionResult) { - ResponseEntity result; - if (conversionResult.isSuccess()) { - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.setLocation(conversionResult.getUri()); - result = new ResponseEntity<>(httpHeaders, HttpStatus.FOUND); - } else { - result = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(conversionResult.getErrorPage()); - } - return result; - } - */ - - @Operation(summary = "", description = "", security = {@SecurityRequirement(name = "ApiKey")}, tags = {"Home"}) + @Operation(summary = "", description = "", security = {@SecurityRequirement(name = "ApiKey")}, tags = {"Redirect"}) @ApiResponses(value = { - @ApiResponse(responseCode = "302", description = "Redirect to Checkout service.", content = @Content(schema = @Schema())), - //@ApiResponse(responseCode = "401", description = "Wrong or missing function key.", content = @Content(schema = @Schema())), - //@ApiResponse(responseCode = "403", description = "Forbidden.", content = @Content(schema = @Schema())), - //@ApiResponse(responseCode = "500", description = "Internal server error.", content = @Content(mediaType = MediaType.TEXT_HTML_VALUE)) + @ApiResponse(responseCode = "302", description = "Redirect to Checkout service.", content = @Content(schema = @Schema())) }) @GetMapping - public void redirect(@Parameter(description = "", example = "identificativoIntermediarioPA_sessionId") @RequestParam("sessionId") String sessionId, + public void redirect(@Parameter(description = "", example = "identificativoIntermediarioPA_sessionId") + @NotBlank(message = "{redirect.session-id.not-blank}") @RequestParam("sessionId") String sessionId, HttpServletResponse response) throws IOException { ConversionResultDTO conversionResultDTO = converterService.convert(sessionId); response.sendRedirect(conversionResultDTO.getUri()); diff --git a/src/main/java/it/gov/pagopa/wispconverter/controller/advice/ErrorHandler.java b/src/main/java/it/gov/pagopa/wispconverter/controller/advice/ErrorHandler.java index 007df958..1f24ad4e 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/controller/advice/ErrorHandler.java +++ b/src/main/java/it/gov/pagopa/wispconverter/controller/advice/ErrorHandler.java @@ -1,176 +1,139 @@ package it.gov.pagopa.wispconverter.controller.advice; -import it.gov.pagopa.wispconverter.controller.advice.model.ProblemJsonResponse; -import it.gov.pagopa.wispconverter.exception.AppError; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import it.gov.pagopa.wispconverter.controller.advice.model.ApiErrorResponse; +import it.gov.pagopa.wispconverter.exception.AppClientException; +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; import it.gov.pagopa.wispconverter.exception.AppException; +import it.gov.pagopa.wispconverter.util.AppErrorUtil; import jakarta.validation.ConstraintViolationException; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.TypeMismatchException; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.HttpStatusCode; -import org.springframework.http.ResponseEntity; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.http.*; 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; -import java.util.ArrayList; import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * All Exceptions are handled by this class */ @ControllerAdvice @Slf4j +@RequiredArgsConstructor public class ErrorHandler extends ResponseEntityExceptionHandler { - /** - * 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 ProblemJsonResponse} as response with the cause and with a 400 as HTTP status - */ - @Override - protected ResponseEntity handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) { - log.warn("Input not readable: ", ex); - var errorResponse = ProblemJsonResponse.builder() - .status(HttpStatus.BAD_REQUEST.value()) - .title(AppError.BAD_REQUEST.getTitle()) - .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 ProblemJsonResponse} as response with the cause and with a 400 as HTTP status - */ - @Override - protected ResponseEntity handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) { - log.warn("Missing request parameter: ", ex); - var errorResponse = ProblemJsonResponse.builder() - .status(HttpStatus.BAD_REQUEST.value()) - .title(AppError.BAD_REQUEST.getTitle()) - .detail(ex.getMessage()) - .build(); - return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); - } - + private final AppErrorUtil appErrorUtil; - /** - * 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, HttpStatusCode status, WebRequest request) { - log.warn("Type mismatch: ", ex); - var errorResponse = ProblemJsonResponse.builder() - .status(HttpStatus.BAD_REQUEST.value()) - .title(AppError.BAD_REQUEST.getTitle()) - .detail(String.format("Invalid value %s for property %s", ex.getValue(), - ((MethodArgumentTypeMismatchException) ex).getName())) - .build(); - return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); + @ApiResponses(value = { + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = { + @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = ApiErrorResponse.class), examples = {@ExampleObject( + """ + { + "errorId": "68ce8c6a-6d53-486c-97fe-79430d24fb7d", + "timestamp": "2023-10-09T08:01:39.421224Z", + "httpStatusCode": 500, + "httpStatusDescription": "Internal Server Error", + "appErrorCode": "WIC-0500", + "message": "An unexpected error has occurred. Please contact support" + } + """ + )}) + }), + @ApiResponse(responseCode = "400", description = "Bad Request", content = { + @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = ApiErrorResponse.class), examples = {@ExampleObject( + """ + { + "timestamp": "2023-10-09T07:53:14.077792Z", + "httpStatusCode": 400, + "httpStatusDescription": "Bad Request", + "appErrorCode": "WIC-0400", + "message": "Bad request", + "errors": [ + { + "message": "Field error in ..." + } + ] + } + """ + )}) + }), + @ApiResponse(responseCode = "404", description = "Not found", content = { + @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = ApiErrorResponse.class), examples = {@ExampleObject( + """ + { + "timestamp": "2023-10-09T07:53:43.367312Z", + "httpStatusCode": 404, + "httpStatusDescription": "Not Found", + "appErrorCode": "WIC-0404", + "message": "Request POST /api/v1/..... not found" + } + """ + )}) + }) + }) + @ExceptionHandler({AppException.class, AppClientException.class}) + public ResponseEntity handleAppException(AppException appEx) { + Pair httpStatusApiErrorResponsePair = appErrorUtil.buildApiErrorResponse(appEx, null, null); + return ResponseEntity.status(httpStatusApiErrorResponsePair.getLeft()) + .body(httpStatusApiErrorResponsePair.getRight()); } - /** - * 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 ProblemJsonResponse} as response with the cause and with a 400 as HTTP status - */ @Override protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode 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 = ProblemJsonResponse.builder() - .status(HttpStatus.BAD_REQUEST.value()) - .title(AppError.BAD_REQUEST.getTitle()) - .detail(detailsMessage) - .build(); - return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); - } - - @ExceptionHandler({ConstraintViolationException.class}) - public ResponseEntity handleConstraintViolationException( - final ConstraintViolationException ex, final WebRequest request) { - log.warn("Validation Error raised:", ex); - var errorResponse = ProblemJsonResponse.builder() - .status(HttpStatus.BAD_REQUEST.value()) - .title(AppError.BAD_REQUEST.getTitle()) - .detail(ex.getMessage()) - .build(); - return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); + List errorMessages = ex.getBindingResult().getAllErrors().stream() + .map(oe -> ApiErrorResponse.ErrorMessage.builder().message(oe.toString()).build()) + .collect(Collectors.toList()); + AppException appEx = new AppException(ex, AppErrorCodeMessageEnum.BAD_REQUEST); + Pair httpStatusApiErrorResponsePair = appErrorUtil.buildApiErrorResponse(appEx, null, errorMessages); + return ResponseEntity.status(httpStatusApiErrorResponsePair.getLeft()) + .body(httpStatusApiErrorResponsePair.getRight()); } + @ExceptionHandler(Exception.class) + public ResponseEntity handleGenericException(Exception ex, WebRequest request) { + String errorId = UUID.randomUUID().toString(); + log.error(String.format("ExceptionHandler: ErrorId=[%s] %s", errorId, ex.getMessage()), ex); - /** - * Handle if a {@link AppException} is raised - * - * @param ex {@link AppException} exception raised - * @param request from frontend - * @return a {@link ProblemJsonResponse} as response with the cause and with an appropriated HTTP status - */ - @ExceptionHandler({AppException.class}) - public ResponseEntity handleAppException(final AppException ex, - final WebRequest request) { - if (ex.getCause() != null) { - log.warn("App Exception raised: " + ex.getMessage() + "\nCause of the App Exception: ", - ex.getCause()); - } else { - log.warn("App Exception raised: ", ex); - } - var errorResponse = ProblemJsonResponse.builder() - .status(ex.getHttpStatus().value()) - .title(ex.getTitle()) - .detail(ex.getMessage()) - .build(); - return new ResponseEntity<>(errorResponse, ex.getHttpStatus()); + AppException appEx = new AppException(ex, AppErrorCodeMessageEnum.ERROR); + // errorId viene usato solo per i casi di eccezioni non gestite + Pair httpStatusApiErrorResponsePair = appErrorUtil.buildApiErrorResponse(appEx, errorId, null); + return ResponseEntity.status(httpStatusApiErrorResponsePair.getLeft()) + .body(httpStatusApiErrorResponsePair.getRight()); } + @ExceptionHandler(ConstraintViolationException.class) + public final ResponseEntity handleConstraintViolation(ConstraintViolationException ex, WebRequest request) { + List errorMessages = ex.getConstraintViolations().stream() + .map(oe -> ApiErrorResponse.ErrorMessage.builder().message(oe.getMessage()).build()) + .collect(Collectors.toList()); + AppException appEx = new AppException(ex, AppErrorCodeMessageEnum.BAD_REQUEST); + Pair httpStatusApiErrorResponsePair = appErrorUtil.buildApiErrorResponse(appEx, null, errorMessages); + return ResponseEntity.status(httpStatusApiErrorResponsePair.getLeft()) + .body(httpStatusApiErrorResponsePair.getRight()); + } - /** - * Handle if a {@link Exception} is raised - * - * @param ex {@link Exception} exception raised - * @param request from frontend - * @return a {@link ProblemJsonResponse} as response with the cause and with 500 as HTTP status - */ - @ExceptionHandler({Exception.class}) - public ResponseEntity handleGenericException(final Exception ex, - final WebRequest request) { - log.error("Generic Exception raised:", ex); - var errorResponse = ProblemJsonResponse.builder() - .status(HttpStatus.INTERNAL_SERVER_ERROR.value()) - .title(AppError.INTERNAL_SERVER_ERROR.getTitle()) - .detail(ex.getMessage()) - .build(); - return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); + @Override + protected ResponseEntity handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) { + List errorMessages = Stream.of(ex.getCause().getMessage()) + .map(oe -> ApiErrorResponse.ErrorMessage.builder().message(oe).build()) + .collect(Collectors.toList()); + AppException appEx = new AppException(ex, AppErrorCodeMessageEnum.BAD_REQUEST); + Pair httpStatusApiErrorResponsePair = appErrorUtil.buildApiErrorResponse(appEx, null, errorMessages); + return ResponseEntity.status(httpStatusApiErrorResponsePair.getLeft()) + .body(httpStatusApiErrorResponsePair.getRight()); } + } diff --git a/src/main/java/it/gov/pagopa/wispconverter/controller/advice/model/ApiErrorResponse.java b/src/main/java/it/gov/pagopa/wispconverter/controller/advice/model/ApiErrorResponse.java new file mode 100644 index 00000000..9205a4c0 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/controller/advice/model/ApiErrorResponse.java @@ -0,0 +1,65 @@ +package it.gov.pagopa.wispconverter.controller.advice.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.time.Instant; +import java.util.List; + +@Getter +@Builder +@JsonPropertyOrder({ + "errorId", + "timestamp", + "httpStatusCode", + "httpStatusDescription", + "appErrorCode", + "message", + "errors" +}) +@NoArgsConstructor +@AllArgsConstructor +public class ApiErrorResponse { + + @JsonInclude(JsonInclude.Include.NON_NULL) + @Schema(example = "50905466-1881-457b-b42f-fb7b2bfb1610", description = "This field exist only if status=500") + private String errorId; + + @Schema(example = "2023-10-05T11:22:57.494928Z", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'", timezone = "UTC") + private Instant timestamp; + + @Schema(example = "500", requiredMode = Schema.RequiredMode.REQUIRED) + private int httpStatusCode; + + @Schema(example = "Internal Server Error", requiredMode = Schema.RequiredMode.REQUIRED) + private String httpStatusDescription; + + @Schema(example = "WIC-0500", requiredMode = Schema.RequiredMode.REQUIRED) + private String appErrorCode; + + @Schema(example = "An unexpected error has occurred. Please contact support.", requiredMode = Schema.RequiredMode.REQUIRED) + private String message; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private List errors; + + @Builder + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + @JsonPropertyOrder({"path", "message"}) + public static class ErrorMessage { + + @Schema(example = "object.field") + @JsonInclude(JsonInclude.Include.NON_NULL) + private String path; + + @Schema(example = "An unexpected error has occurred. Please contact support.", requiredMode = Schema.RequiredMode.REQUIRED) + private String message; + } +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/controller/advice/model/ProblemJsonResponse.java b/src/main/java/it/gov/pagopa/wispconverter/controller/advice/model/ProblemJsonResponse.java deleted file mode 100644 index 6e7586a4..00000000 --- a/src/main/java/it/gov/pagopa/wispconverter/controller/advice/model/ProblemJsonResponse.java +++ /dev/null @@ -1,37 +0,0 @@ -package it.gov.pagopa.wispconverter.controller.advice.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import it.gov.pagopa.wispconverter.controller.advice.ErrorHandler; -import jakarta.validation.constraints.Max; -import jakarta.validation.constraints.Min; -import lombok.*; - -/** - * Object returned as response in case of an error. - *

See {@link ErrorHandler} - */ -@Data -@Builder(toBuilder = true) -@NoArgsConstructor -@AllArgsConstructor -@ToString -@JsonIgnoreProperties(ignoreUnknown = true) -public class ProblemJsonResponse { - - @JsonProperty("title") - @Schema(description = "A short, summary of the problem type. Written in english and readable for engineers (usually not suited for non technical stakeholders and not localized); example: Service Unavailable") - private String title; - - @JsonProperty("status") - @Schema(example = "200", description = "The HTTP status code generated by the origin server for this occurrence of the problem.") - @Min(100) - @Max(600) - private Integer status; - - @JsonProperty("detail") - @Schema(example = "There was an error processing the request", description = "A human readable explanation specific to this occurrence of the problem.") - private String detail; - -} diff --git a/src/main/java/it/gov/pagopa/wispconverter/exception/AppClientException.java b/src/main/java/it/gov/pagopa/wispconverter/exception/AppClientException.java new file mode 100644 index 00000000..174b09a1 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/exception/AppClientException.java @@ -0,0 +1,16 @@ +package it.gov.pagopa.wispconverter.exception; + +import lombok.Getter; + +import java.io.Serializable; + +@Getter +public class AppClientException extends AppException { + + private final int httpStatusCode; + + public AppClientException(int httpStatusCode, AppErrorCodeMessageEnum codeMessage, Serializable... args) { + super(codeMessage, args); + this.httpStatusCode = httpStatusCode; + } +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/exception/AppError.java b/src/main/java/it/gov/pagopa/wispconverter/exception/AppError.java deleted file mode 100644 index d4805dff..00000000 --- a/src/main/java/it/gov/pagopa/wispconverter/exception/AppError.java +++ /dev/null @@ -1,30 +0,0 @@ -package it.gov.pagopa.wispconverter.exception; - -import lombok.Getter; -import org.springframework.http.HttpStatus; - - -@Getter -public enum AppError { - INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "Internal Server Error", "Something was wrong"), - BAD_REQUEST(HttpStatus.INTERNAL_SERVER_ERROR, "Bad Request", "%s"), - UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "Unauthorized", "Error during authentication"), - FORBIDDEN(HttpStatus.FORBIDDEN, "Forbidden", "This method is forbidden"), - RESPONSE_NOT_READABLE(HttpStatus.BAD_GATEWAY, "Response Not Readable", "The response body is not readable"), - PAYLOAD_CONVERSION_ERROR(HttpStatus.UNPROCESSABLE_ENTITY, "Malformed payload", "The payload is malformed - %s"), - UNKNOWN(null, null, null); - - - public final HttpStatus httpStatus; - public final String title; - public final String details; - - - AppError(HttpStatus httpStatus, String title, String details) { - this.httpStatus = httpStatus; - this.title = title; - this.details = details; - } -} - - diff --git a/src/main/java/it/gov/pagopa/wispconverter/exception/AppErrorCodeMessageEnum.java b/src/main/java/it/gov/pagopa/wispconverter/exception/AppErrorCodeMessageEnum.java new file mode 100644 index 00000000..c54cc0a0 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/exception/AppErrorCodeMessageEnum.java @@ -0,0 +1,31 @@ +package it.gov.pagopa.wispconverter.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public enum AppErrorCodeMessageEnum { + UNKNOWN("0000", "UNKNOWN.error", HttpStatus.INTERNAL_SERVER_ERROR), + ERROR("0500", "system.error", HttpStatus.INTERNAL_SERVER_ERROR), + BAD_REQUEST("0400", "bad.request", HttpStatus.BAD_REQUEST), + + PARSING_("1000", "", HttpStatus.BAD_REQUEST), + PARSING_JAXB_EMPTY_NODE_ELEMENT("1001", "jaxb.node-element.empty", HttpStatus.BAD_REQUEST), + PARSING_JAXB_PARSE_ERROR("1002", "jaxb.parse", HttpStatus.BAD_REQUEST), + + PERSISTENCE_("2000", "", HttpStatus.BAD_REQUEST), + + CLIENT_("3000", "", HttpStatus.BAD_REQUEST), + + ; + + private final String errorCode; + private final String errorMessageKey; + private final HttpStatus httpStatus; + + AppErrorCodeMessageEnum(String errorCode, String errorMessageKey, HttpStatus httpStatus) { + this.errorCode = errorCode; + this.errorMessageKey = errorMessageKey; + this.httpStatus = httpStatus; + } +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/exception/AppException.java b/src/main/java/it/gov/pagopa/wispconverter/exception/AppException.java index e71461b5..66533eea 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/exception/AppException.java +++ b/src/main/java/it/gov/pagopa/wispconverter/exception/AppException.java @@ -1,89 +1,25 @@ package it.gov.pagopa.wispconverter.exception; -import it.gov.pagopa.wispconverter.controller.advice.ErrorHandler; -import jakarta.validation.constraints.NotNull; -import lombok.EqualsAndHashCode; -import lombok.Value; -import org.springframework.http.HttpStatus; -import org.springframework.validation.annotation.Validated; +import lombok.Getter; -import java.util.Formatter; +import java.io.Serializable; -/** - * Custom exception. - *

See {@link ErrorHandler} - */ -@EqualsAndHashCode(callSuper = true) -@Value -@Validated +@Getter public class AppException extends RuntimeException { - /** - * title returned to the response when this exception occurred - */ - String title; + private final transient AppErrorCodeMessageEnum codeMessage; - /** - * http status returned to the response when this exception occurred - */ - HttpStatus httpStatus; + private final transient Object[] args; - /** - * @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; + public AppException(Throwable cause, AppErrorCodeMessageEnum codeMessage, Serializable... args) { + super(cause); + this.codeMessage = codeMessage; + this.args = args.length > 0 ? args.clone() : null; } - /** - * @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 - */ - public AppException(@NotNull HttpStatus httpStatus, @NotNull String title, @NotNull String message) { - super(message); - this.title = title; - this.httpStatus = httpStatus; - } - - - /** - * @param appError Response template returned to the response - * @param args {@link Formatter} replaces the placeholders in "details" string of - * {@link AppError} with the arguments. If there are more arguments than format - * specifiers, the extra arguments are ignored. - */ - public AppException(@NotNull AppError appError, Object... args) { - super(formatDetails(appError, args)); - this.httpStatus = appError.httpStatus; - 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); - } - - @Override - public String toString() { - return "AppException(" + httpStatus + ", " + title + ")" + super.toString(); + public AppException(AppErrorCodeMessageEnum codeMessage, Serializable... args) { + super(); + this.codeMessage = codeMessage; + this.args = args.length > 0 ? args.clone() : null; } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/CacheService.java b/src/main/java/it/gov/pagopa/wispconverter/service/CacheService.java index 453dd8e8..c86b31b4 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/CacheService.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/CacheService.java @@ -3,7 +3,8 @@ import feign.FeignException; import io.lettuce.core.RedisException; import it.gov.pagopa.wispconverter.client.decoupler.DecouplerCachingClient; -import it.gov.pagopa.wispconverter.exception.AppError; +import it.gov.pagopa.wispconverter.exception.AppClientException; +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; import it.gov.pagopa.wispconverter.exception.AppException; import it.gov.pagopa.wispconverter.repository.CacheRepository; import it.gov.pagopa.wispconverter.service.model.RPTContentDTO; @@ -48,9 +49,9 @@ public void storeRequestMappingInCache(List rptContentDTOs, Strin } catch (FeignException e) { - throw new AppException(AppError.UNKNOWN); + throw new AppClientException(e.status(), AppErrorCodeMessageEnum.CLIENT_); } catch (RedisException e) { - throw new AppException(AppError.UNKNOWN); + throw new AppException(AppErrorCodeMessageEnum.PERSISTENCE_); } } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/ConverterService.java b/src/main/java/it/gov/pagopa/wispconverter/service/ConverterService.java index f5b722c3..b3206759 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/ConverterService.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/ConverterService.java @@ -8,7 +8,7 @@ import gov.telematici.pagamenti.ws.ppthead.IntestazionePPT; import it.gov.digitpa.schemas._2011.pagamenti.CtRichiestaPagamentoTelematico; import it.gov.pagopa.wispconverter.client.gpd.model.PaymentPosition; -import it.gov.pagopa.wispconverter.exception.AppError; +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; import it.gov.pagopa.wispconverter.exception.AppException; import it.gov.pagopa.wispconverter.repository.RPTRequestRepository; import it.gov.pagopa.wispconverter.repository.model.RPTRequestEntity; @@ -92,9 +92,9 @@ private PaymentPosition mapRPTToDebtPosition(RPTRequestDTO rptRequestDTO, RPTCon private RPTRequestEntity getRPTRequestEntity(String sessionId) { try { Optional optRPTReqEntity = this.rptRequestRepository.findById(sessionId); - return optRPTReqEntity.orElseThrow(() -> new AppException(AppError.UNKNOWN)); + return optRPTReqEntity.orElseThrow(() -> new AppException(AppErrorCodeMessageEnum.PERSISTENCE_)); } catch (CosmosException | CosmosAccessException e) { - throw new AppException(AppError.UNKNOWN); + throw new AppException(AppErrorCodeMessageEnum.PERSISTENCE_); } // TODO RE } @@ -135,7 +135,7 @@ private List getRPTContentDTO(String primitive, String payload) t .build(); }).toList(); } - default -> throw new AppException(AppError.UNKNOWN); + default -> throw new AppException(AppErrorCodeMessageEnum.PARSING_); } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/DebtPositionService.java b/src/main/java/it/gov/pagopa/wispconverter/service/DebtPositionService.java index a2d14dd9..d5e6d17c 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/DebtPositionService.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/DebtPositionService.java @@ -4,8 +4,8 @@ import it.gov.pagopa.wispconverter.client.gpd.GPDClient; import it.gov.pagopa.wispconverter.client.gpd.model.MultiplePaymentPosition; import it.gov.pagopa.wispconverter.client.gpd.model.PaymentPosition; -import it.gov.pagopa.wispconverter.exception.AppError; -import it.gov.pagopa.wispconverter.exception.AppException; +import it.gov.pagopa.wispconverter.exception.AppClientException; +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; import it.gov.pagopa.wispconverter.service.mapper.DebtPositionMapper; import it.gov.pagopa.wispconverter.service.model.RPTContentDTO; import lombok.RequiredArgsConstructor; @@ -43,7 +43,7 @@ public void executeBulkCreation(List rptContentDTOs) { }); } catch (FeignException e) { - throw new AppException(AppError.UNKNOWN); + throw new AppClientException(e.status(), AppErrorCodeMessageEnum.CLIENT_); } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/NAVGeneratorService.java b/src/main/java/it/gov/pagopa/wispconverter/service/NAVGeneratorService.java index 53f5f103..9e04c80e 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/NAVGeneratorService.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/NAVGeneratorService.java @@ -4,7 +4,8 @@ import it.gov.pagopa.wispconverter.client.iuvgenerator.IUVGeneratorClient; import it.gov.pagopa.wispconverter.client.iuvgenerator.model.IUVGeneratorRequest; import it.gov.pagopa.wispconverter.client.iuvgenerator.model.IUVGeneratorResponse; -import it.gov.pagopa.wispconverter.exception.AppError; +import it.gov.pagopa.wispconverter.exception.AppClientException; +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; import it.gov.pagopa.wispconverter.exception.AppException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -35,11 +36,11 @@ public String getNAVCodeFromIUVGenerator(String creditorInstitutionCode) { try { IUVGeneratorResponse response = this.iuvGeneratorClient.generate(creditorInstitutionCode, request); if (response == null) { - throw new AppException(AppError.UNKNOWN); + throw new AppException(AppErrorCodeMessageEnum.CLIENT_); } navCode = response.getIuv(); } catch (FeignException e) { - throw new AppException(AppError.UNKNOWN); + throw new AppClientException(e.status(), AppErrorCodeMessageEnum.CLIENT_); } return navCode; } diff --git a/src/main/java/it/gov/pagopa/wispconverter/util/AppErrorUtil.java b/src/main/java/it/gov/pagopa/wispconverter/util/AppErrorUtil.java new file mode 100644 index 00000000..ef197eb6 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/util/AppErrorUtil.java @@ -0,0 +1,38 @@ +package it.gov.pagopa.wispconverter.util; + +import it.gov.pagopa.wispconverter.controller.advice.model.ApiErrorResponse; +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; +import it.gov.pagopa.wispconverter.exception.AppException; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.List; +import java.util.Locale; + +@Component +@RequiredArgsConstructor +public class AppErrorUtil { + private final MessageSource messageSource; + + public Pair buildApiErrorResponse(AppException appEx, String errorId, List errorMessageList) { + Locale locale = LocaleContextHolder.getLocale(); + AppErrorCodeMessageEnum codeMessage = appEx.getCodeMessage(); + HttpStatus status = codeMessage.getHttpStatus(); + String errorMessageKey = codeMessage.getErrorMessageKey(); + + return Pair.of(status, ApiErrorResponse.builder() + .errorId(errorId) + .timestamp(Instant.now()) + .httpStatusCode(status.value()) + .httpStatusDescription(status.getReasonPhrase()) + .appErrorCode(String.format("%s-%s", Constants.SERVICE_CODE_APP, codeMessage.getErrorCode())) + .message(messageSource.getMessage(errorMessageKey, appEx.getArgs(), locale)) + .errors(errorMessageList) + .build()); + } +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/util/Constants.java b/src/main/java/it/gov/pagopa/wispconverter/util/Constants.java index 77809473..f1df407c 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/util/Constants.java +++ b/src/main/java/it/gov/pagopa/wispconverter/util/Constants.java @@ -5,7 +5,7 @@ @UtilityClass public class Constants { - + public static final String SERVICE_CODE_APP = "WIC"; public static final String HEADER_REQUEST_ID = "X-Request-Id"; } diff --git a/src/main/java/it/gov/pagopa/wispconverter/util/JaxbElementUtil.java b/src/main/java/it/gov/pagopa/wispconverter/util/JaxbElementUtil.java index 28e7e305..b078e6f8 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/util/JaxbElementUtil.java +++ b/src/main/java/it/gov/pagopa/wispconverter/util/JaxbElementUtil.java @@ -1,6 +1,6 @@ package it.gov.pagopa.wispconverter.util; -import it.gov.pagopa.wispconverter.exception.AppError; +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; import it.gov.pagopa.wispconverter.exception.AppException; import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.JAXBElement; @@ -45,12 +45,12 @@ private Element convertToElement(InputSource is, String nameSpaceUri, String loc Document doc = db.parse(is); NodeList nodeList = doc.getElementsByTagNameNS(nameSpaceUri, localName); if (nodeList.getLength() == 0) { - throw new AppException(AppError.PAYLOAD_CONVERSION_ERROR, "NodeList is empty"); + throw new AppException(AppErrorCodeMessageEnum.PARSING_JAXB_EMPTY_NODE_ELEMENT, 0); } return (Element) nodeList.item(0); } catch (ParserConfigurationException | IOException | SAXException e) { log.error("Errore durante il parsing", e); - throw new AppException(AppError.PAYLOAD_CONVERSION_ERROR, e, e.getMessage()); + throw new AppException(e, AppErrorCodeMessageEnum.PARSING_JAXB_PARSE_ERROR, e.getMessage()); } } @@ -62,7 +62,7 @@ public T convertToBean(Element element, Class targetType) { return jaxbElement.getValue(); } catch (JAXBException e) { log.error("Errore durante unmarshal", e); - throw new AppException(AppError.PAYLOAD_CONVERSION_ERROR, e, e.getMessage()); + throw new AppException(AppErrorCodeMessageEnum.PARSING_, e, e.getMessage()); } } @@ -79,12 +79,12 @@ public Element convertToRPTElement(byte[] source) { public T getSoapHeader(Envelope envelope, Class targetType) { Header header = envelope.getHeader(); if (header == null) { - throw new AppException(AppError.PAYLOAD_CONVERSION_ERROR, "header is null"); + throw new AppException(AppErrorCodeMessageEnum.PARSING_, "header is null"); } List list = header.getAny(); if (list == null || list.isEmpty()) { - throw new AppException(AppError.PAYLOAD_CONVERSION_ERROR, "headerValue is null or is empty"); + throw new AppException(AppErrorCodeMessageEnum.PARSING_, "headerValue is null or is empty"); } Element element = (Element) list.get(0); return convertToBean(element, targetType); @@ -93,12 +93,12 @@ public T getSoapHeader(Envelope envelope, Class targetType) { public T getSoapBody(Envelope envelope, Class targetType) { Body body = envelope.getBody(); if (body == null) { - throw new AppException(AppError.PAYLOAD_CONVERSION_ERROR, "body is null"); + throw new AppException(AppErrorCodeMessageEnum.PARSING_, "body is null"); } List list = body.getAny(); if (list == null || list.isEmpty()) { - throw new AppException(AppError.PAYLOAD_CONVERSION_ERROR, "bodyValue is null or is empty"); + throw new AppException(AppErrorCodeMessageEnum.PARSING_, "bodyValue is null or is empty"); } Element element = (Element) list.get(0); return convertToBean(element, targetType); diff --git a/src/main/java/it/gov/pagopa/wispconverter/util/aspect/LoggingAspect.java b/src/main/java/it/gov/pagopa/wispconverter/util/aspect/LoggingAspect.java index 0b0014f7..c3fe6265 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/util/aspect/LoggingAspect.java +++ b/src/main/java/it/gov/pagopa/wispconverter/util/aspect/LoggingAspect.java @@ -1,7 +1,7 @@ package it.gov.pagopa.wispconverter.util.aspect; -import it.gov.pagopa.wispconverter.controller.advice.model.ProblemJsonResponse; -import it.gov.pagopa.wispconverter.exception.AppError; +import it.gov.pagopa.wispconverter.controller.advice.model.ApiErrorResponse; +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; import jakarta.annotation.PostConstruct; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -43,7 +43,7 @@ public class LoggingAspect { public static final String ARGS = "args"; @Autowired - HttpServletRequest httRequest; + HttpServletRequest httpRequest; @Autowired HttpServletResponse httpResponse; @@ -57,16 +57,16 @@ public class LoggingAspect { @Value("${info.properties.environment}") private String environment; - private static String getDetail(ResponseEntity result) { - if (result != null && result.getBody() != null && result.getBody().getDetail() != null) { - return result.getBody().getDetail(); - } else return AppError.UNKNOWN.getDetails(); + private static String getDetail(ResponseEntity result) { + if (result != null && result.getBody() != null && result.getBody().getMessage() != null) { + return result.getBody().getMessage(); + } else return AppErrorCodeMessageEnum.UNKNOWN.getErrorCode(); } - private static String getTitle(ResponseEntity result) { - if (result != null && result.getBody() != null && result.getBody().getTitle() != null) { - return result.getBody().getTitle(); - } else return AppError.UNKNOWN.getTitle(); + private static String getTitle(ResponseEntity result) { + if (result != null && result.getBody() != null && result.getBody().getAppErrorCode() != null) { + return result.getBody().getAppErrorCode(); + } else return AppErrorCodeMessageEnum.UNKNOWN.getErrorCode(); } public static String getExecutionTime() { @@ -140,7 +140,7 @@ public Object logApiInvocation(ProceedingJoinPoint joinPoint) throws Throwable { } @AfterReturning(value = "execution(* *..exception.ErrorHandler.*(..))", returning = "result") - public void trowingApiInvocation(JoinPoint joinPoint, ResponseEntity result) { + public void trowingApiInvocation(JoinPoint joinPoint, ResponseEntity result) { MDC.put(STATUS, "KO"); MDC.put(CODE, String.valueOf(result.getStatusCode().value())); MDC.put(RESPONSE_TIME, getExecutionTime()); diff --git a/src/main/java/it/gov/pagopa/wispconverter/util/aspect/ResponseValidator.java b/src/main/java/it/gov/pagopa/wispconverter/util/aspect/ResponseValidator.java index 73d80332..3373102d 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/util/aspect/ResponseValidator.java +++ b/src/main/java/it/gov/pagopa/wispconverter/util/aspect/ResponseValidator.java @@ -1,5 +1,6 @@ package it.gov.pagopa.wispconverter.util.aspect; +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; import it.gov.pagopa.wispconverter.exception.AppException; import jakarta.validation.ConstraintViolation; import jakarta.validation.Validator; @@ -8,7 +9,6 @@ import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @@ -45,7 +45,7 @@ private void validateResponse(ResponseEntity response) { sb.append(error.getPropertyPath()).append(" ").append(error.getMessage()).append(". "); } var msg = StringUtils.chop(sb.toString()); - throw new AppException(HttpStatus.INTERNAL_SERVER_ERROR, "Invalid response", msg); + throw new AppException(AppErrorCodeMessageEnum.ERROR, "Invalid response", msg); } } } diff --git a/src/main/resources/messages/messages_en.properties b/src/main/resources/messages/messages_en.properties new file mode 100644 index 00000000..01039953 --- /dev/null +++ b/src/main/resources/messages/messages_en.properties @@ -0,0 +1,5 @@ +system.error=An unexpected error has occurred. Please contact support +bad.request=Bad request +jaxb.node-element.empty=JAXB conversion error. Found {0} node +jaxb.parse=JAXB parsing error. {0} +redirect.session-id.not-blank=SessionID passed in request must be not-blank. \ No newline at end of file diff --git a/src/main/resources/messages/messages_it.properties b/src/main/resources/messages/messages_it.properties new file mode 100644 index 00000000..f4e4dbf2 --- /dev/null +++ b/src/main/resources/messages/messages_it.properties @@ -0,0 +1,5 @@ +system.error=Si รจ verificato un errore imprevisto. Si prega di contattare l'assistenza +bad.request=Richiesta errata +jaxb.node-element.empty=Errore di conversione JAXB. Trovati {0} nodi +jaxb.parse=Errore di parsing JAXB. {0} +redirect.session-id.not-blank=Il SessionID passato nella richiesta deve essere una stringa non vuota. \ No newline at end of file