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 9607448234ed3dc6dd671c79b4fc29f1e2579d14..a374b0c636bc8ff945d07bf5bceff30ac8235633 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 cba09df7a39ac0850fc477b4d0dfbf2c17dc5fd7..4e0b50f5c3423a52967f107732eaae4a24e0df30 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 d351ff7fda6a51b6394d9225829cbe546238c408..2fe91262de2dc4119a4b0fa414165cd74aead18b 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())); } } }