From 66de6acbc088e07f68ed4492e4cae92461038d61 Mon Sep 17 00:00:00 2001 From: Harshita Yadav Date: Wed, 6 May 2026 00:21:05 +0530 Subject: [PATCH] fix: align API status codes and swagger --- .../BeneficiaryRegistrationController.java | 26 +++-- .../java/com/iemr/ecd/dto/ErrorResponse.java | 26 +++++ .../CustomExceptionResponse.java | 95 +++++++------------ .../EcdGlobalExceptionHandler.java | 77 +++++++++++---- 4 files changed, 138 insertions(+), 86 deletions(-) create mode 100644 src/main/java/com/iemr/ecd/dto/ErrorResponse.java diff --git a/src/main/java/com/iemr/ecd/controller/associate/BeneficiaryRegistrationController.java b/src/main/java/com/iemr/ecd/controller/associate/BeneficiaryRegistrationController.java index 2635cd2..30d5b0d 100644 --- a/src/main/java/com/iemr/ecd/controller/associate/BeneficiaryRegistrationController.java +++ b/src/main/java/com/iemr/ecd/controller/associate/BeneficiaryRegistrationController.java @@ -32,12 +32,14 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.iemr.ecd.dto.ErrorResponse; import com.iemr.ecd.dto.RequestBeneficiaryRegistrationDTO; import com.iemr.ecd.service.associate.BeneficiaryRegistrationServiceImpl; import com.iemr.ecd.utils.advice.exception_handler.CustomExceptionResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; @@ -54,10 +56,14 @@ public class BeneficiaryRegistrationController { @ApiResponses(value = { @ApiResponse(responseCode = CustomExceptionResponse.SUCCESS_SC_V, description = CustomExceptionResponse.SUCCESS_SC, content = { @Content(mediaType = "application/json") }), - @ApiResponse(responseCode = CustomExceptionResponse.NOT_FOUND_SC_V, description = CustomExceptionResponse.NOT_FOUND_SC), - @ApiResponse(responseCode = CustomExceptionResponse.INTERNAL_SERVER_ERROR_SC_V, description = CustomExceptionResponse.INTERNAL_SERVER_ERROR_SC), - @ApiResponse(responseCode = CustomExceptionResponse.DB_EXCEPTION_SC_V, description = CustomExceptionResponse.DB_EXCEPTION_SC), - @ApiResponse(responseCode = CustomExceptionResponse.BAD_REQUEST_SC_V, description = CustomExceptionResponse.BAD_REQUEST_SC) }) + @ApiResponse(responseCode = CustomExceptionResponse.NOT_FOUND_SC_V, description = CustomExceptionResponse.NOT_FOUND_SC, content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)) }), + @ApiResponse(responseCode = CustomExceptionResponse.INTERNAL_SERVER_ERROR_SC_V, description = CustomExceptionResponse.INTERNAL_SERVER_ERROR_SC, content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)) }), + @ApiResponse(responseCode = CustomExceptionResponse.DB_EXCEPTION_SC_V, description = CustomExceptionResponse.DB_EXCEPTION_SC, content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)) }), + @ApiResponse(responseCode = CustomExceptionResponse.BAD_REQUEST_SC_V, description = CustomExceptionResponse.BAD_REQUEST_SC, content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)) }) }) public ResponseEntity beneficiaryRegistration(@RequestBody RequestBeneficiaryRegistrationDTO request, @RequestHeader(value = "Authorization") String Authorization) { @@ -70,10 +76,14 @@ public ResponseEntity beneficiaryRegistration(@RequestBody RequestBenefi @ApiResponses(value = { @ApiResponse(responseCode = CustomExceptionResponse.SUCCESS_SC_V, description = CustomExceptionResponse.SUCCESS_SC, content = { @Content(mediaType = "application/json") }), - @ApiResponse(responseCode = CustomExceptionResponse.NOT_FOUND_SC_V, description = CustomExceptionResponse.NOT_FOUND_SC), - @ApiResponse(responseCode = CustomExceptionResponse.INTERNAL_SERVER_ERROR_SC_V, description = CustomExceptionResponse.INTERNAL_SERVER_ERROR_SC), - @ApiResponse(responseCode = CustomExceptionResponse.DB_EXCEPTION_SC_V, description = CustomExceptionResponse.DB_EXCEPTION_SC), - @ApiResponse(responseCode = CustomExceptionResponse.BAD_REQUEST_SC_V, description = CustomExceptionResponse.BAD_REQUEST_SC) }) + @ApiResponse(responseCode = CustomExceptionResponse.NOT_FOUND_SC_V, description = CustomExceptionResponse.NOT_FOUND_SC, content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)) }), + @ApiResponse(responseCode = CustomExceptionResponse.INTERNAL_SERVER_ERROR_SC_V, description = CustomExceptionResponse.INTERNAL_SERVER_ERROR_SC, content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)) }), + @ApiResponse(responseCode = CustomExceptionResponse.DB_EXCEPTION_SC_V, description = CustomExceptionResponse.DB_EXCEPTION_SC, content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)) }), + @ApiResponse(responseCode = CustomExceptionResponse.BAD_REQUEST_SC_V, description = CustomExceptionResponse.BAD_REQUEST_SC, content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)) }) }) public ResponseEntity updateBeneficiaryDetails(@RequestBody RequestBeneficiaryRegistrationDTO request, @RequestHeader(value = "Authorization") String Authorization) { diff --git a/src/main/java/com/iemr/ecd/dto/ErrorResponse.java b/src/main/java/com/iemr/ecd/dto/ErrorResponse.java new file mode 100644 index 0000000..a90a10a --- /dev/null +++ b/src/main/java/com/iemr/ecd/dto/ErrorResponse.java @@ -0,0 +1,26 @@ +package com.iemr.ecd.dto; + +import java.time.LocalDateTime; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class ErrorResponse { + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private LocalDateTime timestamp; + private int status; + private String error; + private String message; + private String path; + + public ErrorResponse(LocalDateTime timestamp, int status, String error, String message, String path) { + this.timestamp = timestamp; + this.status = status; + this.error = error; + this.message = message; + this.path = path; + } +} diff --git a/src/main/java/com/iemr/ecd/utils/advice/exception_handler/CustomExceptionResponse.java b/src/main/java/com/iemr/ecd/utils/advice/exception_handler/CustomExceptionResponse.java index 8b3cbc7..6d2784e 100644 --- a/src/main/java/com/iemr/ecd/utils/advice/exception_handler/CustomExceptionResponse.java +++ b/src/main/java/com/iemr/ecd/utils/advice/exception_handler/CustomExceptionResponse.java @@ -42,34 +42,36 @@ public class CustomExceptionResponse { private Object data; private final Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName()); public static final int SUCCESS = 200; - public static final int GENERIC_FAILURE = 5000; - public static final int OBJECT_FAILURE = 5001; - public static final int USERID_FAILURE = 5002; - public static final int PASSWORD_FAILURE = 5003; - public static final int PREVILAGE_FAILURE = 5004; - public static final int CODE_EXCEPTION = 5005; - public static final int ENVIRONMENT_EXCEPTION = 5006; - public static final int PARSE_EXCEPTION = 5007; - public static final int DB_EXCEPTION = 5008; public static final int BAD_REQUEST = 400; + public static final int UNAUTHORIZED = 401; + public static final int FORBIDDEN = 403; public static final int NOT_FOUND = 404; + public static final int CONFLICT = 409; + public static final int INTERNAL_SERVER_ERROR = 500; + public static final int DB_EXCEPTION = 500; public static final String SUCCESS_SC = "SUCCESS"; - public static final String NOT_FOUND_SC = "NOT_FOUND"; - public static final String DB_EXCEPTION_SC = "DB_EXCEPTION"; public static final String BAD_REQUEST_SC = "BAD_REQUEST"; + public static final String UNAUTHORIZED_SC = "UNAUTHORIZED"; + public static final String FORBIDDEN_SC = "FORBIDDEN"; + public static final String NOT_FOUND_SC = "NOT_FOUND"; + public static final String CONFLICT_SC = "CONFLICT"; public static final String INTERNAL_SERVER_ERROR_SC = "INTERNAL_SERVER_ERROR"; + public static final String DB_EXCEPTION_SC = "DATABASE_ERROR"; public static final String SUCCESS_SC_V = "200"; - public static final String NOT_FOUND_SC_V = "404"; - public static final String DB_EXCEPTION_SC_V = "5008"; public static final String BAD_REQUEST_SC_V = "400"; + public static final String UNAUTHORIZED_SC_V = "401"; + public static final String FORBIDDEN_SC_V = "403"; + public static final String NOT_FOUND_SC_V = "404"; + public static final String CONFLICT_SC_V = "409"; public static final String INTERNAL_SERVER_ERROR_SC_V = "500"; + public static final String DB_EXCEPTION_SC_V = "500"; @Expose - private int statusCode = GENERIC_FAILURE; + private int statusCode = INTERNAL_SERVER_ERROR; @Expose - private String errorMessage = "Failed with generic error"; + private String errorMessage = "Internal Server Error"; @Expose private String status = "FAILURE"; private static final String RESPONSE = "{\"response\":\"$$STRING\"}"; @@ -102,85 +104,54 @@ public void setError(Throwable thrown) { Date currDate = Calendar.getInstance().getTime(); logger.info("error happened due to " + thrown.getClass().getSimpleName() + " at " + currDate.toString()); - switch (thrown.getCause().getClass().getSimpleName()) { + String causeClass = thrown.getClass().getSimpleName(); + if (thrown.getCause() != null) { + causeClass = thrown.getCause().getClass().getSimpleName(); + } + + switch (causeClass) { case "IEMRException": - this.statusCode = USERID_FAILURE; + this.statusCode = UNAUTHORIZED; status = "User login failed"; errorMessage = thrown.getMessage(); break; case "JSONException": - this.statusCode = OBJECT_FAILURE; + this.statusCode = BAD_REQUEST; status = "Invalid object conversion"; errorMessage = "Invalid object conversion"; break; case "SQLException": - this.statusCode = DB_EXCEPTION; - status = DB_EXCEPTION_SC; - errorMessage = thrown.getMessage(); - break; case "SQLGrammarException": - this.statusCode = DB_EXCEPTION; - status = DB_EXCEPTION_SC; - errorMessage = thrown.getMessage(); - break; case "DataException": - this.statusCode = DB_EXCEPTION; - status = DB_EXCEPTION_SC; - errorMessage = thrown.getMessage(); - break; case "ConstraintViolationException": - this.statusCode = DB_EXCEPTION; - status = DB_EXCEPTION_SC; - errorMessage = thrown.getMessage(); - break; case "GenericJDBCException": - this.statusCode = DB_EXCEPTION; - status = DB_EXCEPTION_SC; - errorMessage = thrown.getMessage(); - break; case "JDBCConnectionException": - this.statusCode = DB_EXCEPTION; - status = DB_EXCEPTION_SC; - errorMessage = thrown.getMessage(); - break; case "LockAcquisitionException": - this.statusCode = DB_EXCEPTION; - status = DB_EXCEPTION_SC; - errorMessage = thrown.getMessage(); - break; case "InvalidDataAccessResourceUsageException": - this.statusCode = DB_EXCEPTION; + case "JDBCException": + this.statusCode = INTERNAL_SERVER_ERROR; status = DB_EXCEPTION_SC; - errorMessage = thrown.getMessage(); + errorMessage = "Database error occurred"; break; case "ParseException": case "NullPointerException": - case "ArrayIndexOutOfBoundsException": - case "IOException": case "ConnectException": case "ConnectIOException": - this.statusCode = ENVIRONMENT_EXCEPTION; - status = "Failed with connection issues at " + currDate.toString() + "Please try after some time. " - + "If error is still seen, contact your administrator."; - errorMessage = thrown.getMessage(); - break; - case "JDBCException": - this.statusCode = DB_EXCEPTION; - status = DB_EXCEPTION_SC; + this.statusCode = INTERNAL_SERVER_ERROR; + status = "Environment or connection error"; errorMessage = thrown.getMessage(); break; default: - this.statusCode = GENERIC_FAILURE; - status = "Failed with " + thrown.getMessage() + " at " + currDate.toString() - + ".Please try after some time. If error is still seen, contact your administrator."; + this.statusCode = INTERNAL_SERVER_ERROR; + status = "An unexpected error occurred"; errorMessage = thrown.getMessage(); break; } - logger.error("Failure happend with " + thrown.getMessage() + "at " + currDate.toString(), thrown); + logger.error("Failure happened with " + thrown.getMessage() + "at " + currDate.toString(), thrown); } public void setError(int errorCode, String message, String status) { diff --git a/src/main/java/com/iemr/ecd/utils/advice/exception_handler/EcdGlobalExceptionHandler.java b/src/main/java/com/iemr/ecd/utils/advice/exception_handler/EcdGlobalExceptionHandler.java index 60044ed..092a049 100644 --- a/src/main/java/com/iemr/ecd/utils/advice/exception_handler/EcdGlobalExceptionHandler.java +++ b/src/main/java/com/iemr/ecd/utils/advice/exception_handler/EcdGlobalExceptionHandler.java @@ -21,34 +21,79 @@ */ package com.iemr.ecd.utils.advice.exception_handler; +import java.time.LocalDateTime; +import java.util.stream.Collectors; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; -@RestControllerAdvice -public class EcdGlobalExceptionHandler extends ResponseEntityExceptionHandler { +import com.iemr.ecd.dto.ErrorResponse; - Logger logger = LoggerFactory.getLogger(this.getClass().getName()); +import jakarta.servlet.http.HttpServletRequest; - @ExceptionHandler - public CustomExceptionResponse handleInvalidRequestParameterException(InvalidRequestException e) { - logger.error("invalid request exception : " + e); - CustomExceptionResponse customExceptionResponse = new CustomExceptionResponse(); - customExceptionResponse.setError(e); +@RestControllerAdvice +public class EcdGlobalExceptionHandler { - return customExceptionResponse; + private static final Logger logger = LoggerFactory.getLogger(EcdGlobalExceptionHandler.class); + @ExceptionHandler(InvalidRequestException.class) + public ResponseEntity handleInvalidRequestException(InvalidRequestException ex, HttpServletRequest request) { + logger.error("Invalid request: {}", ex.getMessage()); + ErrorResponse error = ErrorResponse.builder() + .timestamp(LocalDateTime.now()) + .status(HttpStatus.BAD_REQUEST.value()) + .error(HttpStatus.BAD_REQUEST.getReasonPhrase()) + .message(ex.getMessage()) + .path(request.getRequestURI()) + .build(); + return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST); } - @ExceptionHandler - public CustomExceptionResponse handleGeneralException(ECDException e) { - logger.error("ECD exception : " + e); - CustomExceptionResponse customExceptionResponse = new CustomExceptionResponse(); - customExceptionResponse.setError(e); + @ExceptionHandler(ECDException.class) + public ResponseEntity handleECDException(ECDException ex, HttpServletRequest request) { + logger.error("ECD error: {}", ex.getMessage()); + ErrorResponse error = ErrorResponse.builder() + .timestamp(LocalDateTime.now()) + .status(HttpStatus.INTERNAL_SERVER_ERROR.value()) + .error(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()) + .message(ex.getMessage()) + .path(request.getRequestURI()) + .build(); + return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR); + } - return customExceptionResponse; + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleValidationException(MethodArgumentNotValidException ex, HttpServletRequest request) { + String errorMessage = ex.getBindingResult().getFieldErrors().stream() + .map(error -> error.getField() + ": " + error.getDefaultMessage()) + .collect(Collectors.joining(", ")); + + logger.error("Validation error: {}", errorMessage); + ErrorResponse error = ErrorResponse.builder() + .timestamp(LocalDateTime.now()) + .status(HttpStatus.BAD_REQUEST.value()) + .error(HttpStatus.BAD_REQUEST.getReasonPhrase()) + .message(errorMessage) + .path(request.getRequestURI()) + .build(); + return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST); } + @ExceptionHandler(Exception.class) + public ResponseEntity handleGeneralException(Exception ex, HttpServletRequest request) { + logger.error("Unexpected error: ", ex); + ErrorResponse error = ErrorResponse.builder() + .timestamp(LocalDateTime.now()) + .status(HttpStatus.INTERNAL_SERVER_ERROR.value()) + .error(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()) + .message("An unexpected error occurred. Please contact administrator.") + .path(request.getRequestURI()) + .build(); + return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR); + } }