From 0af7b0e78199d0fe70f7774d84d6be65cf2ba5cd Mon Sep 17 00:00:00 2001 From: evilimkova <evilimkova@onpointserv.com> Date: Thu, 27 Apr 2023 08:41:09 +0200 Subject: [PATCH] Adding error handling --- .../modulecertificate/service/ApiError.java | 64 +++++++++++++++++++ .../service/CertificateController.java | 12 ++-- .../CustomRestGlobalExceptionHandling.java | 60 +++++++++++++++++ .../service/ResourceNotFoundException.java | 22 ------- .../CertificateControllerTests.java | 14 ++-- 5 files changed, 135 insertions(+), 37 deletions(-) create mode 100644 application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/ApiError.java create mode 100644 application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/CustomRestGlobalExceptionHandling.java delete mode 100644 application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/ResourceNotFoundException.java diff --git a/application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/ApiError.java b/application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/ApiError.java new file mode 100644 index 00000000..f7a6bd00 --- /dev/null +++ b/application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/ApiError.java @@ -0,0 +1,64 @@ +package org.fuseri.modulecertificate.service; + +import org.springframework.http.HttpStatus; + +import java.time.LocalDateTime; + +public class ApiError { + + private LocalDateTime timestamp; + private HttpStatus status; + private String message; + private String path; + + public ApiError(LocalDateTime timestamp, HttpStatus status, String message, String path) { + this.timestamp = timestamp; + this.status = status; + this.message = message; + this.path = path; + } + + public LocalDateTime getTimestamp() { + return timestamp; + } + + public void setTimestamp(LocalDateTime timestamp) { + this.timestamp = timestamp; + } + + public HttpStatus getStatus() { + return status; + } + + public void setStatus(HttpStatus status) { + this.status = status; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + @Override + public String toString() { + return "ApiError{" + + "timestamp=" + timestamp + + ", status=" + status + + ", message='" + message + '\'' + + ", path='" + path + '\'' + + '}'; + } +} + + diff --git a/application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/CertificateController.java b/application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/CertificateController.java index e63254df..91ee0e5d 100644 --- a/application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/CertificateController.java +++ b/application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/CertificateController.java @@ -44,7 +44,7 @@ public class CertificateController { description = "Generates certificate, saves it into database and returns certificate information and certificate file.") @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "Certificate generated successfully."), - @ApiResponse(responseCode = "400", description = "Invalid input.") + @ApiResponse(responseCode = "500", description = "Invalid input.") }) @PostMapping public ResponseEntity<CertificateSimpleDto> generate(@Valid @RequestBody CertificateCreateDto certificateCreateDto) { @@ -65,11 +65,7 @@ public class CertificateController { }) @GetMapping("/{id}") public ResponseEntity<CertificateSimpleDto> find(@NotNull @PathVariable Long id) { - try { return ResponseEntity.ok(certificateFacade.findById(id)); - } catch (EntityNotFoundException e) { - return ResponseEntity.notFound().build(); - } } /** @@ -82,7 +78,7 @@ public class CertificateController { @Operation(summary = "Get certificates for user", description = "Returns certificates for given user in list.") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successfully retrieved certificates"), - @ApiResponse(responseCode = "400", description = "Invalid input."), + @ApiResponse(responseCode = "500", description = "Invalid input."), }) @GetMapping("/findForUser") public ResponseEntity<List<CertificateSimpleDto>> findForUser(@RequestParam Long userId) { @@ -101,7 +97,7 @@ public class CertificateController { description = "Returns certificates for given user and course in list.") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successfully retrieved certificates"), - @ApiResponse(responseCode = "400", description = "Invalid input."), + @ApiResponse(responseCode = "500", description = "Invalid input."), }) @GetMapping("/findForUserAndCourse") public ResponseEntity<List<CertificateSimpleDto>> findForUserAndCourse(@RequestParam Long userId, @RequestParam Long courseId) { @@ -131,7 +127,7 @@ public class CertificateController { @Operation(summary = "Get certificates in paginated format", description = "Returns certificates in paginated format.") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successfully retrieved paginated certificates"), - @ApiResponse(responseCode = "400", description = "Invalid page number supplied"), + @ApiResponse(responseCode = "500", description = "Invalid page number supplied"), }) @GetMapping public ResponseEntity<Page<CertificateSimpleDto>> findAllCertificates(Pageable pageable) { diff --git a/application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/CustomRestGlobalExceptionHandling.java b/application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/CustomRestGlobalExceptionHandling.java new file mode 100644 index 00000000..a92bf270 --- /dev/null +++ b/application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/CustomRestGlobalExceptionHandling.java @@ -0,0 +1,60 @@ +package org.fuseri.modulecertificate.service; + +/** + * @author Michal Badin - Formula 1 team + * modified by Ester VilĂmková + */ + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.util.UrlPathHelper; + +import java.time.Clock; +import java.time.LocalDateTime; + +@RestControllerAdvice +public class CustomRestGlobalExceptionHandling { + + private static final UrlPathHelper URL_PATH_HELPER = new UrlPathHelper(); + + @ExceptionHandler({ResponseStatusException.class}) + public ResponseEntity<ApiError> handleResponseStatus( + final ResponseStatusException ex, final HttpServletRequest request) { + final ApiError apiError = new ApiError( + LocalDateTime.now(Clock.systemUTC()), + HttpStatus.NOT_FOUND, + ex.getLocalizedMessage(), + URL_PATH_HELPER.getRequestUri(request)); + return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus()); + } + + /** + * Handle all the exceptions not matched by above-mentioned definitions. + * + * @param ex the ex + * @param request the request + * @return the response entity + */ + @ExceptionHandler({Exception.class}) + public ResponseEntity<ApiError> handleAll(final Exception ex, HttpServletRequest request) { + final ApiError apiError = new ApiError( + LocalDateTime.now(Clock.systemUTC()), + HttpStatus.INTERNAL_SERVER_ERROR, + getInitialException(ex).getLocalizedMessage(), + URL_PATH_HELPER.getRequestUri(request)); + return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus()); + } + + private Exception getInitialException(Exception ex) { + while (ex.getCause() != null) { + ex = (Exception) ex.getCause(); + } + return ex; + } +} + diff --git a/application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/ResourceNotFoundException.java b/application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/ResourceNotFoundException.java deleted file mode 100644 index d24c445d..00000000 --- a/application/module-certificate/src/main/java/org/fuseri/modulecertificate/service/ResourceNotFoundException.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.fuseri.modulecertificate.service; - -public class ResourceNotFoundException extends RuntimeException { - public ResourceNotFoundException() { - } - - public ResourceNotFoundException(String message) { - super(message); - } - - public ResourceNotFoundException(String message, Throwable cause) { - super(message, cause); - } - - public ResourceNotFoundException(Throwable cause) { - super(cause); - } - - public ResourceNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } -} diff --git a/application/module-certificate/src/test/java/org/fuseri/modulecertificate/CertificateControllerTests.java b/application/module-certificate/src/test/java/org/fuseri/modulecertificate/CertificateControllerTests.java index a74e9f10..d4d4e27c 100644 --- a/application/module-certificate/src/test/java/org/fuseri/modulecertificate/CertificateControllerTests.java +++ b/application/module-certificate/src/test/java/org/fuseri/modulecertificate/CertificateControllerTests.java @@ -61,7 +61,7 @@ class CertificateControllerTests { mockMvc.perform(post("/certificates/generate") .content(asJsonString(new CertificateCreateDto(null, COURSE))) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().is4xxClientError()); + .andExpect(status().is5xxServerError()); } @Test @@ -69,14 +69,14 @@ class CertificateControllerTests { mockMvc.perform(post("/certificates/generate") .content(asJsonString(new CertificateCreateDto(USER, null))) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().is4xxClientError()); + .andExpect(status().is5xxServerError()); } @Test void generateCertificateWithoutParams() throws Exception { mockMvc.perform(post("/certificates/generate") .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().is4xxClientError()); + .andExpect(status().is5xxServerError()); } @Test @@ -107,7 +107,7 @@ class CertificateControllerTests { @Test void findCertificatesWithoutUserId() throws Exception { mockMvc.perform(get("/certificates/findForUser")) - .andExpect(status().is4xxClientError()); + .andExpect(status().is5xxServerError()); } @Test @@ -128,20 +128,20 @@ class CertificateControllerTests { void findCertificateIdWithoutUserId() throws Exception { mockMvc.perform(get("/certificates/findForUserAndCourse") .param("courseId", "0")) - .andExpect(status().is4xxClientError()); + .andExpect(status().is5xxServerError()); } @Test void findCertificateIdWithoutCourseId() throws Exception { mockMvc.perform(get("/certificates/findForUserAndCourse") .param("userId", "0")) - .andExpect(status().is4xxClientError()); + .andExpect(status().is5xxServerError()); } @Test void findCertificateIdWithoutParams() throws Exception { mockMvc.perform(get("/certificates/findForUserAndCourse")) - .andExpect(status().is4xxClientError()); + .andExpect(status().is5xxServerError()); } @Test -- GitLab