From adb065ec4ddde16c332af96a3ab4210ddb3df528 Mon Sep 17 00:00:00 2001 From: "Zickermann, Jan" <jan.zickermann@dataport.de> Date: Mon, 22 Jan 2024 15:27:05 +0100 Subject: [PATCH] OZG-4799 Handle other Exceptions --- .../{ErrorConstants.java => ErrorType.java} | 17 ++++++--- ...nHandler.java => ExceptionTranslator.java} | 33 ++++++----------- ...st.java => ExceptionTranslatorITCase.java} | 35 ++++++++++++++----- 3 files changed, 49 insertions(+), 36 deletions(-) rename src/main/java/de/ozgcloud/admin/web/error/{ErrorConstants.java => ErrorType.java} (72%) rename src/main/java/de/ozgcloud/admin/web/error/{RestResponseEntityExceptionHandler.java => ExceptionTranslator.java} (54%) rename src/test/java/de/ozgcloud/admin/web/error/{RestResponseEntityExceptionHandlerTest.java => ExceptionTranslatorITCase.java} (73%) diff --git a/src/main/java/de/ozgcloud/admin/web/error/ErrorConstants.java b/src/main/java/de/ozgcloud/admin/web/error/ErrorType.java similarity index 72% rename from src/main/java/de/ozgcloud/admin/web/error/ErrorConstants.java rename to src/main/java/de/ozgcloud/admin/web/error/ErrorType.java index 96074482..a374b0c6 100644 --- a/src/main/java/de/ozgcloud/admin/web/error/ErrorConstants.java +++ b/src/main/java/de/ozgcloud/admin/web/error/ErrorType.java @@ -21,12 +21,19 @@ */ package de.ozgcloud.admin.web.error; -import java.net.URI; +import org.springframework.http.HttpStatus; -public class ErrorConstants { +import lombok.AllArgsConstructor; +import lombok.Getter; - private ErrorConstants() {} +@AllArgsConstructor +@Getter +public enum ErrorType { + UNKNOWN(HttpStatus.INTERNAL_SERVER_ERROR), + AUTHORIZATION(HttpStatus.UNAUTHORIZED), + FUNCTIONAL(HttpStatus.BAD_REQUEST), + TECHNICAL(HttpStatus.INTERNAL_SERVER_ERROR), + ; - private static final String PROBLEM_BASE_URL = "https://administration.ozg-sh.de/problem"; - public static final URI NOT_AUTHORIZED_PROBLEM = URI.create(PROBLEM_BASE_URL + "/not-authorized"); + private final HttpStatus status; } diff --git a/src/main/java/de/ozgcloud/admin/web/error/RestResponseEntityExceptionHandler.java b/src/main/java/de/ozgcloud/admin/web/error/ExceptionTranslator.java similarity index 54% rename from src/main/java/de/ozgcloud/admin/web/error/RestResponseEntityExceptionHandler.java rename to src/main/java/de/ozgcloud/admin/web/error/ExceptionTranslator.java index cba09df7..4e0b50f5 100644 --- a/src/main/java/de/ozgcloud/admin/web/error/RestResponseEntityExceptionHandler.java +++ b/src/main/java/de/ozgcloud/admin/web/error/ExceptionTranslator.java @@ -21,39 +21,26 @@ */ package de.ozgcloud.admin.web.error; -import static de.ozgcloud.admin.web.error.ErrorConstants.*; - import java.nio.file.AccessDeniedException; +import java.util.Map; -import org.springframework.context.i18n.LocaleContextHolder; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ProblemDetail; -import org.springframework.http.ResponseEntity; import org.springframework.web.ErrorResponse; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; -import lombok.extern.slf4j.Slf4j; +import de.ozgcloud.common.errorhandling.TechnicalException; @RestControllerAdvice -@Slf4j -public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { +public class ExceptionTranslator extends ResponseEntityExceptionHandler { - @ExceptionHandler({ AccessDeniedException.class }) - public ResponseEntity<ProblemDetail> handleAccessDeniedException(AccessDeniedException ex) { - log.error(ex.getMessage(), ex); - return handleErrorResponse(ErrorResponse - .builder(ex, HttpStatus.FORBIDDEN, ex.getLocalizedMessage()) - .type(NOT_AUTHORIZED_PROBLEM) - .build()); - } + @ExceptionHandler({ RuntimeException.class, AccessDeniedException.class, TechnicalException.class }) + public ErrorResponse handleAccessDeniedException(Exception ex) { + var errorType = Map.of( + AccessDeniedException.class, ErrorType.AUTHORIZATION, + TechnicalException.class, ErrorType.TECHNICAL + ).getOrDefault(ex.getClass(), ErrorType.UNKNOWN); - private ResponseEntity<ProblemDetail> handleErrorResponse(ErrorResponse errorResponse) { - var body = errorResponse.updateAndGetBody(this.getMessageSource(), LocaleContextHolder.getLocale()); - var headers = new HttpHeaders(); - return new ResponseEntity<>(body, headers, errorResponse.getStatusCode()); + return ErrorResponse.builder(ex, errorType.getStatus(), ex.getLocalizedMessage()).build(); } - } diff --git a/src/test/java/de/ozgcloud/admin/web/error/RestResponseEntityExceptionHandlerTest.java b/src/test/java/de/ozgcloud/admin/web/error/ExceptionTranslatorITCase.java similarity index 73% rename from src/test/java/de/ozgcloud/admin/web/error/RestResponseEntityExceptionHandlerTest.java rename to src/test/java/de/ozgcloud/admin/web/error/ExceptionTranslatorITCase.java index d351ff7f..2fe91262 100644 --- a/src/test/java/de/ozgcloud/admin/web/error/RestResponseEntityExceptionHandlerTest.java +++ b/src/test/java/de/ozgcloud/admin/web/error/ExceptionTranslatorITCase.java @@ -21,7 +21,6 @@ */ package de.ozgcloud.admin.web.error; -import static org.hamcrest.Matchers.*; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -41,9 +40,10 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import de.ozgcloud.admin.service.BasicAppInfoService; import de.ozgcloud.admin.web.controller.BasicAppInfoController; +import de.ozgcloud.common.errorhandling.TechnicalException; @SpringBootTest -class RestResponseEntityExceptionHandlerTest { +class ExceptionTranslatorITCase { @Spy @InjectMocks @@ -58,7 +58,7 @@ class RestResponseEntityExceptionHandlerTest { void mock() { mockMvc = MockMvcBuilders .standaloneSetup(basicAppInfoController) - .setControllerAdvice(new RestResponseEntityExceptionHandler()) + .setControllerAdvice(new ExceptionTranslator()) .build(); } @@ -66,16 +66,35 @@ class RestResponseEntityExceptionHandlerTest { @Nested class TestErrorHandler { - @Test - void shouldHandleAccessDenied() throws Exception { + private void throwWhenApiCalled(Exception ex) { when(basicAppInfoService.getJavaVersion()) .thenAnswer(invocation -> { - throw new AccessDeniedException("test no access"); + throw ex; }); + } + + @Test + void shouldHandleAccessDenied() throws Exception { + throwWhenApiCalled(new AccessDeniedException("test no access")); + + mockMvc.perform(get("/api")) + .andExpect(status().is(ErrorType.AUTHORIZATION.getStatus().value())); + } + + @Test + void shouldHandleTechnicalError() throws Exception { + throwWhenApiCalled(new TechnicalException("test technical error")); + + mockMvc.perform(get("/api")) + .andExpect(status().is(ErrorType.TECHNICAL.getStatus().value())); + } + + @Test + void shouldRuntimeError() throws Exception { + throwWhenApiCalled(new RuntimeException("test unknown error")); mockMvc.perform(get("/api")) - .andExpect(status().is4xxClientError()) - .andExpect(jsonPath("$.type", equalTo(ErrorConstants.NOT_AUTHORIZED_PROBLEM.toString()))); + .andExpect(status().is(ErrorType.UNKNOWN.getStatus().value())); } } } -- GitLab