Skip to content

Commit

Permalink
Merge pull request #138 from earlspilner/fix/issue130
Browse files Browse the repository at this point in the history
fix: issue130 Return a 404 error while adding a pet to an unexisting owner
  • Loading branch information
arey committed Aug 17, 2024
2 parents 08778e6 + 5c87a4a commit 311c3ce
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -33,53 +34,76 @@
import static org.springframework.http.HttpStatus.BAD_REQUEST;

/**
* Global Exception handler for REST controllers.
* <p>
* This class handles exceptions thrown by REST controllers and returns
* appropriate HTTP responses to the client.
*
* @author Vitaliy Fedoriv
* @author Alexander Dudkin
*/

@ControllerAdvice
public class ExceptionControllerAdvice {

@ExceptionHandler(Exception.class)
public ResponseEntity<String> exception(Exception e) {
ObjectMapper mapper = new ObjectMapper();
ErrorInfo errorInfo = new ErrorInfo(e);
String respJSONstring = "{}";
try {
respJSONstring = mapper.writeValueAsString(errorInfo);
} catch (JsonProcessingException e1) {
e1.printStackTrace();
/**
* Record for storing error information.
* <p>
* This record encapsulates the class name and message of the exception.
*
* @param className The name of the exception class
* @param exMessage The message of the exception
*/
private record ErrorInfo(String className, String exMessage) {
public ErrorInfo(Exception ex) {
this(ex.getClass().getName(), ex.getLocalizedMessage());
}
return ResponseEntity.badRequest().body(respJSONstring);
}

/**
* Handles all general exceptions by returning a 500 Internal Server Error status with error details.
*
* @param e The exception to be handled
* @return A {@link ResponseEntity} containing the error information and a 500 Internal Server Error status
*/
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorInfo> handleGeneralException(Exception e) {
ErrorInfo info = new ErrorInfo(e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(info);
}

/**
* Handles {@link DataIntegrityViolationException} which typically indicates database constraint violations.
* This method returns a 404 Not Found status if an entity does not exist.
*
* @param ex The {@link DataIntegrityViolationException} to be handled
* @return A {@link ResponseEntity} containing the error information and a 404 Not Found status
*/
@ExceptionHandler(DataIntegrityViolationException.class)
@ResponseStatus(code = HttpStatus.NOT_FOUND)
@ResponseBody
public ResponseEntity<ErrorInfo> handleDataIntegrityViolationException(DataIntegrityViolationException ex) {
ErrorInfo errorInfo = new ErrorInfo(ex);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorInfo);
}

/**
* Handles exception thrown by Bean Validation on controller methods parameters
*
* @param ex The thrown exception
* @param request the current web request
*
* @return an empty response entity
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(code = BAD_REQUEST)
@ResponseStatus(BAD_REQUEST)
@ResponseBody
public ResponseEntity<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, WebRequest request) {
public ResponseEntity<ErrorInfo> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
BindingErrorsResponse errors = new BindingErrorsResponse();
BindingResult bindingResult = ex.getBindingResult();
HttpHeaders headers = new HttpHeaders();
if (bindingResult.hasErrors()) {
errors.addAllErrors(bindingResult);
headers.add("errors", errors.toJSON());
return ResponseEntity.badRequest().body(new ErrorInfo("MethodArgumentNotValidException", "Validation failed"));
}
return new ResponseEntity<>(headers, HttpStatus.BAD_REQUEST);
return ResponseEntity.badRequest().build();
}

private class ErrorInfo {
public final String className;
public final String exMessage;

public ErrorInfo(Exception ex) {
this.className = ex.getClass().getName();
this.exMessage = ex.getLocalizedMessage();
}
}
}
12 changes: 6 additions & 6 deletions src/main/resources/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ paths:
required: true
responses:
201:
description: The pet owner was sucessfully added.
description: The pet owner was successfully added.
content:
application/json:
schema:
Expand Down Expand Up @@ -314,7 +314,7 @@ paths:
required: true
responses:
201:
description: The pet was sucessfully added.
description: The pet was successfully added.
content:
application/json:
schema:
Expand All @@ -326,7 +326,7 @@ paths:
schema:
$ref: '#/components/schemas/RestError'
404:
description: Pet not found.
description: Pet or Owner not found.
content:
application/json:
schema:
Expand Down Expand Up @@ -489,7 +489,7 @@ paths:
required: true
responses:
201:
description: The vet visit was sucessfully added.
description: The vet visit was successfully added.
content:
application/json:
schema:
Expand Down Expand Up @@ -1849,7 +1849,7 @@ components:
readOnly: true
timestamp:
title: Timestamp
description: The time the error occured.
description: The time the error occurred.
type: string
format: date-time
example: '2019-08-21T21:41:46.158+0000'
Expand Down Expand Up @@ -1886,7 +1886,7 @@ components:
properties:
message:
title: Message
description: The valiation message.
description: The validation message.
type: string
example: "[Path '/lastName'] Instance type (null) does not match any allowed primitive type (allowed: [\"string\"])"
readOnly: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,8 @@ void testAddPetError() throws Exception {
String newPetAsJSON = mapper.writeValueAsString(newPet);
given(this.clinicService.findPetById(999)).willReturn(null);
this.mockMvc.perform(post("/api/pets")
.content(new String()).accept(MediaType.APPLICATION_JSON_VALUE).contentType(MediaType.APPLICATION_JSON_VALUE))
// set empty JSON to force 400 error
.content("{}").accept(MediaType.APPLICATION_JSON_VALUE).contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(status().isBadRequest());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ void testCreateUserSuccess() throws Exception {
@WithMockUser(roles = "ADMIN")
void testCreateUserError() throws Exception {
User user = new User();
user.setUsername("username");
user.setUsername(""); // set empty username to force 400 error
user.setPassword("password");
user.setEnabled(true);
ObjectMapper mapper = new ObjectMapper();
Expand Down

0 comments on commit 311c3ce

Please sign in to comment.