From e131bf1bb08a3ebe81522ae90a3ce60e72cc865b Mon Sep 17 00:00:00 2001 From: talig Date: Wed, 15 Aug 2018 13:32:41 +0300 Subject: [PATCH] Map unexpected error to response Change-Id: I84293aa040b736670885f01d625862cf41e359d2 Issue-ID: SDC-1647 Signed-off-by: talig --- workflow-bdd/features/VersionState.feature | 2 +- .../onap/sdc/workflow/api/ExceptionsHandler.java | 76 ++++++++++++++-------- .../api/types/UnexpectedErrorResponse.java | 18 +++++ .../sdc/workflow/api/ExceptionsHandlerTest.java | 54 +++++++++++++++ 4 files changed, 123 insertions(+), 27 deletions(-) create mode 100644 workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/types/UnexpectedErrorResponse.java create mode 100644 workflow-designer-be/src/test/java/org/onap/sdc/workflow/api/ExceptionsHandlerTest.java diff --git a/workflow-bdd/features/VersionState.feature b/workflow-bdd/features/VersionState.feature index 1bb72ab8..09e93a75 100644 --- a/workflow-bdd/features/VersionState.feature +++ b/workflow-bdd/features/VersionState.feature @@ -51,5 +51,5 @@ Feature: Workflow Version State When I want to update for path "/workflows/{item.id}/versions/{item.versionId}" with the input data from the context Scenario: Create second version based on non CERTIFIED one - invalid - Then I want the following to fail with response status code 403 + Then I want the following to fail with response status code 422 When I want to create for path "/workflows/{item.id}/versions?baseVersionId={item.versionId}" with the input data from the context \ No newline at end of file diff --git a/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/ExceptionsHandler.java b/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/ExceptionsHandler.java index 5e6fc07d..f46d19be 100644 --- a/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/ExceptionsHandler.java +++ b/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/ExceptionsHandler.java @@ -17,12 +17,15 @@ package org.onap.sdc.workflow.api; import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.FORBIDDEN; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; +import java.util.function.Function; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.commons.text.RandomStringGenerator; import org.onap.sdc.workflow.api.types.ErrorResponse; +import org.onap.sdc.workflow.api.types.UnexpectedErrorResponse; import org.onap.sdc.workflow.services.exceptions.EntityNotFoundException; import org.onap.sdc.workflow.services.exceptions.InvalidArtifactException; import org.onap.sdc.workflow.services.exceptions.UniqueValueViolationException; @@ -30,6 +33,8 @@ import org.onap.sdc.workflow.services.exceptions.VersionCreationException; import org.onap.sdc.workflow.services.exceptions.VersionModificationException; import org.onap.sdc.workflow.services.exceptions.VersionStateModificationException; import org.onap.sdc.workflow.services.exceptions.VersionStatusModificationException; +import org.openecomp.sdc.logging.api.Logger; +import org.openecomp.sdc.logging.api.LoggerFactory; import org.springframework.context.support.DefaultMessageSourceResolvable; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -46,44 +51,63 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExcep @RestController public class ExceptionsHandler extends ResponseEntityExceptionHandler { - @ExceptionHandler(EntityNotFoundException.class) - public final ResponseEntity handleNotFoundException(Exception exception) { - return new ResponseEntity<>(new ErrorResponse(exception.getMessage()), NOT_FOUND); - } + private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionsHandler.class); + private static final String LOG_MSG = "Exception was mapped to {} response"; + private static final String UNEXPECTED_ERROR_MSG = "Something bad happened. Please contact support with code %s"; + private static final RandomStringGenerator CODE_GENERATOR = + new RandomStringGenerator.Builder().withinRange('A', 'Z').build(); + private static final int CODE_LENGTH = 8; - @ExceptionHandler({InvalidArtifactException.class, VersionModificationException.class, - VersionStateModificationException.class, VersionStatusModificationException.class, - UniqueValueViolationException.class}) - public final ResponseEntity handleUnprocessableEntityException(Exception exception) { - return new ResponseEntity<>(new ErrorResponse(exception.getMessage()), UNPROCESSABLE_ENTITY); - } + private static final Function UNEXPECTED_EXCEPTION_MAPPER = + isDevInfoDisabled() + ? e -> new UnexpectedErrorResponse(getUnexpectedErrorMessage()) + : e -> new UnexpectedErrorResponse(getUnexpectedErrorMessage(), ExceptionUtils.getStackTrace(e)); - @ExceptionHandler(VersionCreationException.class) - public final ResponseEntity handleVersioningErrorException(VersionCreationException exception) { - return new ResponseEntity<>(new ErrorResponse(exception.getMessage()), FORBIDDEN); - } - - @ExceptionHandler(Exception.class) - public final ResponseEntity handleUnexpectedException(Exception exception) { - return new ResponseEntity<>(new ErrorResponse(exception.getMessage()), INTERNAL_SERVER_ERROR); - } - - //For missing header exceptions @Override public ResponseEntity handleServletRequestBindingException(ServletRequestBindingException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + //For missing header exceptions + LOGGER.debug(LOG_MSG, BAD_REQUEST, ex); return new ResponseEntity<>(new ErrorResponse(ex.getMessage()), BAD_REQUEST); } @Override protected final ResponseEntity handleMethodArgumentNotValid(final MethodArgumentNotValidException exception, - final HttpHeaders headers, - final HttpStatus status, - final WebRequest request) { - + final HttpHeaders headers, final HttpStatus status, final WebRequest request) { + LOGGER.debug(LOG_MSG, BAD_REQUEST, exception); String errorMsg = exception.getBindingResult().getFieldErrors().stream() .map(DefaultMessageSourceResolvable::getDefaultMessage).findFirst() .orElse(exception.getMessage()); return new ResponseEntity<>(new ErrorResponse(errorMsg), BAD_REQUEST); } + + @ExceptionHandler(EntityNotFoundException.class) + public final ResponseEntity handleNotFoundException(Exception exception) { + LOGGER.debug(LOG_MSG, NOT_FOUND, exception); + return new ResponseEntity<>(new ErrorResponse(exception.getMessage()), NOT_FOUND); + } + + @ExceptionHandler( + {InvalidArtifactException.class, VersionCreationException.class, VersionModificationException.class, + VersionStateModificationException.class, VersionStatusModificationException.class, + UniqueValueViolationException.class}) + public final ResponseEntity handleUnprocessableEntityException(Exception exception) { + LOGGER.debug(LOG_MSG, UNPROCESSABLE_ENTITY, exception); + return new ResponseEntity<>(new ErrorResponse(exception.getMessage()), UNPROCESSABLE_ENTITY); + } + + @ExceptionHandler(Exception.class) + public final ResponseEntity handleUnexpectedException(Exception exception) { + UnexpectedErrorResponse response = UNEXPECTED_EXCEPTION_MAPPER.apply(exception); + LOGGER.error(response.getMessage(), exception); + return new ResponseEntity<>(response, INTERNAL_SERVER_ERROR); + } + + private static boolean isDevInfoDisabled() { + return Boolean.FALSE.toString().equalsIgnoreCase(System.getProperty("errors.includeDevInfo")); + } + + private static String getUnexpectedErrorMessage() { + return String.format(UNEXPECTED_ERROR_MSG, CODE_GENERATOR.generate(CODE_LENGTH)); + } } diff --git a/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/types/UnexpectedErrorResponse.java b/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/types/UnexpectedErrorResponse.java new file mode 100644 index 00000000..fbc5ab39 --- /dev/null +++ b/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/types/UnexpectedErrorResponse.java @@ -0,0 +1,18 @@ +package org.onap.sdc.workflow.api.types; + +import lombok.Getter; + +@Getter +public class UnexpectedErrorResponse extends ErrorResponse { + + private String devInfo; + + public UnexpectedErrorResponse(String message) { + super(message); + } + + public UnexpectedErrorResponse(String message, String devInfo) { + super(message); + this.devInfo = devInfo; + } +} diff --git a/workflow-designer-be/src/test/java/org/onap/sdc/workflow/api/ExceptionsHandlerTest.java b/workflow-designer-be/src/test/java/org/onap/sdc/workflow/api/ExceptionsHandlerTest.java new file mode 100644 index 00000000..e4008bb0 --- /dev/null +++ b/workflow-designer-be/src/test/java/org/onap/sdc/workflow/api/ExceptionsHandlerTest.java @@ -0,0 +1,54 @@ +package org.onap.sdc.workflow.api; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; +import static org.springframework.http.HttpStatus.NOT_FOUND; +import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.onap.sdc.workflow.api.types.ErrorResponse; +import org.onap.sdc.workflow.api.types.UnexpectedErrorResponse; +import org.onap.sdc.workflow.services.exceptions.EntityNotFoundException; +import org.onap.sdc.workflow.services.exceptions.VersionModificationException; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +public class ExceptionsHandlerTest { + + @InjectMocks + private ExceptionsHandler exceptionsHandler; + + @Test + public void handleNotFoundException() { + EntityNotFoundException exception = new EntityNotFoundException("message"); + ResponseEntity response = exceptionsHandler.handleNotFoundException(exception); + + assertEquals(NOT_FOUND, response.getStatusCode()); + assertEquals(exception.getMessage(), response.getBody().getMessage()); + } + + @Test + public void handleUnprocessableEntityException() { + VersionModificationException exception = new VersionModificationException("1", "2"); + ResponseEntity response = exceptionsHandler.handleUnprocessableEntityException(exception); + + assertEquals(UNPROCESSABLE_ENTITY, response.getStatusCode()); + assertEquals(exception.getMessage(), response.getBody().getMessage()); + } + + @Test + public void handleUnexpectedException() { + Exception exception = new Exception("message"); + ResponseEntity response = exceptionsHandler.handleUnexpectedException(exception); + + assertEquals(INTERNAL_SERVER_ERROR, response.getStatusCode()); + assertNotNull(response.getBody().getMessage()); + assertFalse(response.getBody().getMessage().contains(exception.getMessage())); + assertNotNull(response.getBody().getDevInfo()); + } +} \ No newline at end of file -- 2.16.6