initial commit

This commit is contained in:
2025-06-12 13:52:55 +02:00
parent 762bb977ac
commit b435c2e5f5
6 changed files with 265 additions and 5 deletions

View File

@@ -83,11 +83,6 @@
<artifactId>commons-collections4</artifactId> <artifactId>commons-collections4</artifactId>
<version>4.4</version> <version>4.4</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.1</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -0,0 +1,15 @@
package zw.qantra.tm.exceptions;
public class ApiException extends RuntimeException {
private String message;
public ApiException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
}

View File

@@ -0,0 +1,99 @@
package zw.qantra.tm.exceptions;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import zw.qantra.tm.rest.ApiResponse;
import java.time.Instant;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* * Handle all exceptions and java bean validation errors
* for all endpoints' income data that use the @Valid annotation
*
* @author Ehab Qadah
*/
@ControllerAdvice
public class GeneralExceptionHandler extends ResponseEntityExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(getClass().getName());
public static final String ACCESS_DENIED = "Access denied!";
public static final String INVALID_REQUEST = "Invalid request";
public static final String ERROR_MESSAGE_TEMPLATE = "message: %s %n requested uri: %s";
public static final String LIST_JOIN_DELIMITER = ",";
public static final String FIELD_ERROR_SEPARATOR = ": ";
private static final String ERRORS_FOR_PATH = "errors {} for path {}";
private static final String PATH = "path";
private static final String ERRORS = "error";
private static final String STATUS = "status";
private static final String MESSAGE = "message";
private static final String TIMESTAMP = "timestamp";
private static final String TYPE = "type";
private static final String BODY = "body";
/**
* A general handler for all uncaught exceptions
*/
@ExceptionHandler({Exception.class})
public ResponseEntity<Object> handleAllExceptions(Exception exception, WebRequest request) {
exception.printStackTrace();
ResponseStatus responseStatus =
exception.getClass().getAnnotation(ResponseStatus.class);
final HttpStatus status =
responseStatus!=null ? responseStatus.value():HttpStatus.INTERNAL_SERVER_ERROR;
final String localizedMessage = exception.getLocalizedMessage();
final String path = request.getDescription(false);
String message = (StringUtils.isNotEmpty(localizedMessage) ? localizedMessage:status.getReasonPhrase());
return getExceptionResponseEntity(exception, status, request, Collections.singletonList(message));
}
/**
* Build detailed information about the exception in the response
*/
private ResponseEntity<Object> getExceptionResponseEntity(final Exception exception,
final HttpStatus status,
final WebRequest request,
final List<String> errors) {
final Map<String, Object> body = new LinkedHashMap<>();
final String path = request.getDescription(false);
body.put(TIMESTAMP, Instant.now());
body.put(STATUS, status.value());
body.put(ERRORS, errors);
body.put(TYPE, exception.getClass().getSimpleName());
body.put(PATH, path);
body.put(BODY, request.getAttribute("mobileNumber", RequestAttributes.SCOPE_REQUEST));
body.put(MESSAGE, getMessageForStatus(status));
final String errorsMessage = CollectionUtils.isNotEmpty(errors) ?
errors.stream().filter(StringUtils::isNotEmpty).collect(Collectors.joining(LIST_JOIN_DELIMITER))
:status.getReasonPhrase();
logger.info(ERRORS_FOR_PATH, errorsMessage, path);
return ApiResponse.build(status, getMessageForStatus(status), null, errors, false);
}
private String getMessageForStatus(HttpStatus status) {
switch (status) {
case UNAUTHORIZED:
return ACCESS_DENIED;
case BAD_REQUEST:
return INVALID_REQUEST;
default:
return status.getReasonPhrase();
}
}
}

View File

@@ -0,0 +1,25 @@
package zw.qantra.tm.exceptions;
public class RestException extends RuntimeException {
private String message;
private Object responsePayload;
public RestException(Object responsePayload, String message) {
this.responsePayload = responsePayload;
this.message = message;
}
public Object getResponsePayload() {
return responsePayload;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@@ -0,0 +1,46 @@
package zw.qantra.tm.exceptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.client.ResponseErrorHandler;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.stream.Collectors;
@Component
public class RestTemplateResponseErrorHandler
implements ResponseErrorHandler {
private Logger logger = LoggerFactory.getLogger(this.getClass().toString());
@Override
public boolean hasError(ClientHttpResponse httpResponse)
throws IOException {
return (
httpResponse.getStatusCode().is5xxServerError()
|| httpResponse.getStatusCode().is4xxClientError());
}
@Override
public void handleError(ClientHttpResponse httpResponse)
throws IOException {
logger.info("Rest error code: " + httpResponse.getStatusCode());
if (httpResponse.getStatusCode()
.is5xxServerError()) {
// handle SERVER_ERROR
logger.info("Server Error");
} else if (httpResponse.getStatusCode().is4xxClientError()) {
// handle CLIENT_ERROR
logger.info("Client Error");
BufferedReader reader = new BufferedReader(new InputStreamReader(httpResponse.getBody()));
String httpBodyResponse = reader.lines().collect(Collectors.joining(""));
throw new RestException(httpBodyResponse, "Client Error");
}
}
}

View File

@@ -0,0 +1,80 @@
package zw.qantra.tm.rest;
import lombok.Getter;
import lombok.Setter;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import java.util.HashMap;
import java.util.Map;
@Setter
@Getter
public class ApiResponse {
private String message;
private HashMap<Object, Object> response;
private Boolean success;
private HashMap<Object, Object> body;
private Map<String, ?> errors = new HashMap<>();
// private Object body;
private HttpStatus status = HttpStatus.OK;
public ApiResponse(String message) {
super();
this.message = message;
}
public ResponseEntity ApiResponse(Object obj, HttpStatus status) {
return new ResponseEntity<Object>(obj, status);
}
public static ResponseEntity ok(Object obj){
return ApiResponse.build(HttpStatus.OK, "SUCCESSFUL", obj, null, true);
}
public static ResponseEntity created(Object obj){
return ApiResponse.build(HttpStatus.CREATED, "CREATED", obj, null, true);
}
public static ResponseEntity badRequest(Object obj){
return ApiResponse.build(HttpStatus.BAD_REQUEST, "BAD REQUEST", null, obj, false);
}
public static ResponseEntity notFound(){
return ApiResponse.build(HttpStatus.NOT_FOUND, "NOT FOUND", null, null, false);
}
public static ResponseEntity notContent(){
return ApiResponse.build(HttpStatus.NO_CONTENT, "DELETED", null, null, false);
}
public static ResponseEntity badRequest(Map<String, Object> errors){
return ApiResponse.build(HttpStatus.BAD_REQUEST, "Problem with request", null, errors, false);
}
public static ResponseEntity build(HttpStatus status, String message, Object body, Object errors, Boolean success){
Map<String, Object> map = new HashMap<>();
map.put("message", message);
map.put("body", body);
map.put("errors", errors);
map.put("success", success);
map.put("status", status.value());
return new ResponseEntity<Object>(map, status);
}
public ResponseEntity build(){
Map<String, Object> map = new HashMap<>();
map.put("message", this.message);
map.put("body", this.body);
map.put("errors", this.errors);
// map.put("status", this.errors);
return new ResponseEntity<Object>(map, this.status);
}
}