From 8425ec32b4ebe83f30c2a07459854970a3f1227f Mon Sep 17 00:00:00 2001 From: Martin Rojas Miguel Angel Date: Wed, 20 Jun 2018 15:24:47 +0200 Subject: [PATCH] TSK-575 Created General handling error log system for REST API --- .../impl/ClassificationServiceImpl.java | 4 +- .../rest/GenenalExceptionHandlingTest.java | 135 ++++++++++++++++++ .../rest/TaskanaRestExceptionHandler.java | 16 +++ 3 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 rest/taskana-rest-spring-example/src/test/java/pro/taskana/rest/GenenalExceptionHandlingTest.java diff --git a/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationServiceImpl.java b/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationServiceImpl.java index 90938fadf..6cdd7772f 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationServiceImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationServiceImpl.java @@ -364,7 +364,7 @@ public class ClassificationServiceImpl implements ClassificationService { Classification classification = this.classificationMapper.findByKeyAndDomain(classificationKey, domain); if (classification == null) { throw new ClassificationNotFoundException(classificationKey, domain, - "The classification " + classificationKey + "wasn't found in the domain " + domain); + "The classification \"" + classificationKey + "\" wasn't found in the domain " + domain); } deleteClassification(classification.getId()); } finally { @@ -383,7 +383,7 @@ public class ClassificationServiceImpl implements ClassificationService { Classification classification = this.classificationMapper.findById(classificationId); if (classification == null) { throw new ClassificationNotFoundException(classificationId, - "The classification " + classificationId + "wasn't found"); + "The classification \"" + classificationId + "\" wasn't found"); } if (classification.getDomain().equals("")) { diff --git a/rest/taskana-rest-spring-example/src/test/java/pro/taskana/rest/GenenalExceptionHandlingTest.java b/rest/taskana-rest-spring-example/src/test/java/pro/taskana/rest/GenenalExceptionHandlingTest.java new file mode 100644 index 000000000..71b8e95f0 --- /dev/null +++ b/rest/taskana-rest-spring-example/src/test/java/pro/taskana/rest/GenenalExceptionHandlingTest.java @@ -0,0 +1,135 @@ +package pro.taskana.rest; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.verify; + +import java.util.Collections; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; + +import ch.qos.logback.classic.Logger; + +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.hateoas.PagedResources; +import org.springframework.hateoas.hal.Jackson2HalModule; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.RestTemplate; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.Appender; +import pro.taskana.ldap.LdapCacheTestImpl; +import pro.taskana.rest.resource.AccessIdResource; +import pro.taskana.rest.resource.ClassificationSummaryResource; +import pro.taskana.rest.resource.assembler.ClassificationResourceAssembler; +import pro.taskana.rest.resource.assembler.TaskResourceAssembler; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = RestConfiguration.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { + "devMode=true"}) +public class GenenalExceptionHandlingTest { + + @Autowired + private ClassificationResourceAssembler classificationResourceAssembler; + + @Autowired + private TaskResourceAssembler taskResourceAssembler; + + String server = "http://127.0.0.1:"; + RestTemplate template; + HttpEntity request; + HttpHeaders headers = new HttpHeaders(); + @LocalServerPort + int port; + @Mock + private Appender mockAppender; + + @Captor + private ArgumentCaptor captorLoggingEvent; + + @Before + public void before() { + template = getRestTemplate(); + headers.add("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x"); + request = new HttpEntity(headers); + final Logger logger = (Logger) LoggerFactory.getLogger(TaskanaRestExceptionHandler.class); + logger.addAppender(mockAppender); + } + + //Always have this teardown otherwise we can stuff up our expectations. Besides, it's + //good coding practise + @After + public void teardown() { + final Logger logger = (Logger) LoggerFactory.getLogger(TaskanaRestExceptionHandler.class); + logger.detachAppender(mockAppender); + } + + @Test + public void testAccessIdValidationMinimunValueExceptionIsLogged() { + try { + + AccessIdController.setLdapCache(new LdapCacheTestImpl()); + ResponseEntity> response = template.exchange( + server + port + "/v1/access-ids?searchFor=al", HttpMethod.GET, request, + new ParameterizedTypeReference>() { + + }); + } catch (Exception ex) { + verify(mockAppender).doAppend(captorLoggingEvent.capture()); + assertTrue(captorLoggingEvent.getValue().getMessage().contains("is too short. Minimum Length =")); + } + } + + @Test + public void testDeleteNonExisitingClassificationExceptionIsLogged() { + try { + ResponseEntity> response = template.exchange( + server + port + "/v1/classifications/non-existing-id", HttpMethod.DELETE, request, + new ParameterizedTypeReference>() { + + }); + } catch (Exception ex){ + verify(mockAppender).doAppend(captorLoggingEvent.capture()); + assertTrue(captorLoggingEvent.getValue().getMessage().contains("The classification \"non-existing-id\" wasn't found")); + } + + } + + /** + * Return a REST template which is capable of dealing with responses in HAL format + * + * @return RestTemplate + */ + private RestTemplate getRestTemplate() { + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.registerModule(new Jackson2HalModule()); + + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + converter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/hal+json")); + converter.setObjectMapper(mapper); + + RestTemplate template = new RestTemplate(Collections.>singletonList(converter)); + return template; + } +} diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskanaRestExceptionHandler.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskanaRestExceptionHandler.java index 0dcd3a39f..c055bd39a 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskanaRestExceptionHandler.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskanaRestExceptionHandler.java @@ -1,5 +1,7 @@ package pro.taskana.rest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.HttpStatus; @@ -34,6 +36,8 @@ import pro.taskana.exceptions.WorkbasketNotFoundException; @ControllerAdvice public class TaskanaRestExceptionHandler extends ResponseEntityExceptionHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(TaskanaRestExceptionHandler.class); + @ExceptionHandler(InvalidArgumentException.class) protected ResponseEntity handleInvalidArgument(InvalidArgumentException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.BAD_REQUEST); @@ -112,9 +116,21 @@ public class TaskanaRestExceptionHandler extends ResponseEntityExceptionHandler return buildResponse(ex, req, HttpStatus.BAD_REQUEST); } + @ExceptionHandler(Exception.class) + protected ResponseEntity handleGeneralException(Exception ex, WebRequest req) { + return buildResponse(ex, req, HttpStatus.BAD_REQUEST); + } + private ResponseEntity buildResponse(Exception ex, WebRequest req, HttpStatus status) { TaskanaErrorData errorData = new TaskanaErrorData(status, ex, req); + logError(ex, errorData); return new ResponseEntity<>(errorData, status); } + private void logError(Exception ex, TaskanaErrorData errorData) { + LOGGER.error( + "Error occured during processing of rest request:\n" + errorData.toString(), + ex); + } + }