[ofbiz-plugins] branch trunk updated: Improved: Error handling for the response and added new error 422 (Unprocessable Entity) to handle validation errors(OFBIZ-11995)

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[ofbiz-plugins] branch trunk updated: Improved: Error handling for the response and added new error 422 (Unprocessable Entity) to handle validation errors(OFBIZ-11995)

grv-2
This is an automated email from the ASF dual-hosted git repository.

grv pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-plugins.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 43d4c0b  Improved: Error handling for the response and added new error 422 (Unprocessable Entity) to handle validation errors(OFBIZ-11995)
43d4c0b is described below

commit 43d4c0bd9899b1a22a453cf06a91c3d934cde3c2
Author: Girish Vasmatkar <[hidden email]>
AuthorDate: Mon Sep 21 17:02:24 2020 +0530

    Improved: Error handling for the response and added new error 422 (Unprocessable Entity) to handle validation errors(OFBIZ-11995)
---
 ofbiz-rest-impl/config/ApiUiLabels.xml             | 15 ++++
 .../ofbiz/ws/rs/process/ServiceRequestHandler.java | 93 +++++++++++++++++++--
 .../org/apache/ofbiz/ws/rs/response/Error.java     | 96 ++++++++++++++++++++++
 .../ws/rs/response/{Error.java => Response.java}   | 55 +++++--------
 4 files changed, 221 insertions(+), 38 deletions(-)

diff --git a/ofbiz-rest-impl/config/ApiUiLabels.xml b/ofbiz-rest-impl/config/ApiUiLabels.xml
index 2071e6e..88795e0 100644
--- a/ofbiz-rest-impl/config/ApiUiLabels.xml
+++ b/ofbiz-rest-impl/config/ApiUiLabels.xml
@@ -40,4 +40,19 @@ under the License.
         <value xml:lang="zh">不允许你浏览这个页面。</value>
         <value xml:lang="zh-TW">不允許您檢視這個頁面.</value>
     </property>
+    <property key="GenericServiceValidationErrorMessage">
+        <value xml:lang="en">${service} validation failed. The request contained invalid information and could not be processed.</value>
+    </property>
+    <property key="GenericServiceExecutionGenericEntityOperationErrorMessage">
+        <value xml:lang="en">${service} execution failed. The request contained invalid information and could not be processed.</value>
+    </property>
+    <property key="GenericServiceExecutionGenericExceptionErrorMessage">
+        <value xml:lang="en">${service} execution failed. The service encountered an error; please try again later.</value>
+    </property>
+    <property key="NoSuchEntityDefaultMessage">
+        <value xml:lang="en">${service} execution failed. The requested entity does not exist.</value>
+    </property>
+     <property key="GenericServiceErrorMessage">
+        <value xml:lang="en">${service} returned error. The request contained invalid information and could not be processed.</value>
+    </property>
 </resource>
diff --git a/ofbiz-rest-impl/src/main/java/org/apache/ofbiz/ws/rs/process/ServiceRequestHandler.java b/ofbiz-rest-impl/src/main/java/org/apache/ofbiz/ws/rs/process/ServiceRequestHandler.java
index 51ce63b..b51e3fe 100644
--- a/ofbiz-rest-impl/src/main/java/org/apache/ofbiz/ws/rs/process/ServiceRequestHandler.java
+++ b/ofbiz-rest-impl/src/main/java/org/apache/ofbiz/ws/rs/process/ServiceRequestHandler.java
@@ -19,15 +19,23 @@
 package org.apache.ofbiz.ws.rs.process;
 
 import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
-import javax.ws.rs.InternalServerErrorException;
 import javax.ws.rs.NotFoundException;
 import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.apache.ofbiz.base.util.UtilProperties;
 import org.apache.ofbiz.base.util.UtilValidate;
+import org.apache.ofbiz.entity.GenericEntityException;
+import org.apache.ofbiz.entity.GenericEntityNotFoundException;
+import org.apache.ofbiz.entity.GenericNoSuchEntityException;
 import org.apache.ofbiz.entity.GenericValue;
 import org.apache.ofbiz.service.DispatchContext;
 import org.apache.ofbiz.service.GenericServiceException;
@@ -35,10 +43,15 @@ import org.apache.ofbiz.service.LocalDispatcher;
 import org.apache.ofbiz.service.ModelParam;
 import org.apache.ofbiz.service.ModelService;
 import org.apache.ofbiz.service.ServiceUtil;
+import org.apache.ofbiz.service.ServiceValidationException;
+import org.apache.ofbiz.ws.rs.core.ResponseStatus;
+import org.apache.ofbiz.ws.rs.response.Error;
 import org.apache.ofbiz.ws.rs.util.RestApiUtil;
+import org.codehaus.groovy.runtime.InvokerInvocationException;
 
 public final class ServiceRequestHandler extends RestRequestHandler {
 
+    private static final String DEFAULT_MSG_UI_LABEL_RESOURCE = "ApiUiLabels";
     private String service;
 
     public ServiceRequestHandler(String service) {
@@ -68,7 +81,7 @@ public final class ServiceRequestHandler extends RestRequestHandler {
         try {
             result = dispatcher.runSync(service, serviceContext);
         } catch (GenericServiceException e) {
-            throw new InternalServerErrorException(e.getMessage());
+            return handleException(e);
         }
         Map<String, Object> responseData = new LinkedHashMap<>();
         if (ServiceUtil.isSuccess(result)) {
@@ -84,9 +97,7 @@ public final class ServiceRequestHandler extends RestRequestHandler {
             }
             return RestApiUtil.success((String) result.get(ModelService.SUCCESS_MESSAGE), responseData);
         } else {
-            return RestApiUtil.error(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),
-                    Response.Status.INTERNAL_SERVER_ERROR.getReasonPhrase(),
-                    (String) result.get(ModelService.ERROR_MESSAGE));
+            return errorFromServiceResult(service, result);
         }
     }
 
@@ -99,4 +110,76 @@ public final class ServiceRequestHandler extends RestRequestHandler {
         }
         return svc;
     }
+
+    private Response handleException(GenericServiceException gse) {
+        Response.ResponseBuilder builder = null;
+        Throwable actualCause = gse.getCause();
+        if (actualCause == null) {
+            actualCause = gse;
+        } else if (actualCause instanceof InvokerInvocationException) {
+            actualCause = actualCause.getCause();
+        }
+
+        if (actualCause instanceof ServiceValidationException) {
+            ServiceValidationException validationException = (ServiceValidationException) actualCause;
+            Error error = new Error().type(actualCause.getClass().getSimpleName()).code(Response.Status.BAD_REQUEST.getStatusCode())
+                    .description(Response.Status.BAD_REQUEST.getReasonPhrase())
+                    .message(getErrorMessage(service, "GenericServiceValidationErrorMessage", getHttpRequest().getLocale()))
+                    .errorDesc((validationException.getMessage())).additionalErrors(validationException.getMessageList());
+            builder = Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON).entity(error);
+        } else if (actualCause instanceof GenericNoSuchEntityException
+                || actualCause instanceof GenericEntityNotFoundException) {
+            Error error = new Error().type(actualCause.getClass().getSimpleName()).code(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode())
+                    .description(Response.Status.INTERNAL_SERVER_ERROR.getReasonPhrase())
+                    .message(getErrorMessage(service, "NoSuchEntityDefaultMessage", getHttpRequest().getLocale()))
+                    .errorDesc(ExceptionUtils.getRootCauseMessage(gse));
+            builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR).type(MediaType.APPLICATION_JSON)
+                    .entity(error);
+        } else if (actualCause instanceof GenericEntityException) {
+            Error error = new Error().type(actualCause.getClass().getSimpleName()).code(ResponseStatus.Custom.UNPROCESSABLE_ENTITY.getStatusCode())
+                    .description(ResponseStatus.Custom.UNPROCESSABLE_ENTITY.getReasonPhrase())
+                    .message(getErrorMessage(service, "GenericServiceExecutionGenericEntityOperationErrorMessage", getHttpRequest().getLocale()))
+                    .errorDesc(ExceptionUtils.getRootCauseMessage(gse));
+            builder = Response.status(ResponseStatus.Custom.UNPROCESSABLE_ENTITY).type(MediaType.APPLICATION_JSON)
+                    .entity(error);
+        } else {
+            Error error = new Error().type(actualCause.getClass().getSimpleName()).code(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode())
+                    .description(Response.Status.INTERNAL_SERVER_ERROR.getReasonPhrase())
+                    .message(getErrorMessage(service, "GenericServiceExecutionGenericExceptionErrorMessage", getHttpRequest().getLocale()))
+                    .errorDesc(ExceptionUtils.getRootCauseMessage(gse));
+            builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR).type(MediaType.APPLICATION_JSON)
+                    .entity(error);
+        }
+        return builder.build();
+    }
+
+    private String getErrorMessage(String serviceName, String errorKey, Locale locale) {
+        String error = UtilProperties.getMessage(DEFAULT_MSG_UI_LABEL_RESOURCE, errorKey, locale);
+        error = error.replace("${service}", serviceName);
+        return error;
+    }
+
+    @SuppressWarnings("unchecked")
+    private Response errorFromServiceResult(String service, Map<String, Object> result) {
+        String errorMessage = null;
+        List<String> additionalErrorMessages = new LinkedList<>();
+        if (!UtilValidate.isEmpty(result.get(ModelService.ERROR_MESSAGE))) {
+            errorMessage = result.get(ModelService.ERROR_MESSAGE).toString();
+        }
+        if (!UtilValidate.isEmpty(result.get(ModelService.ERROR_MESSAGE_LIST))) {
+            List<String> errorMessageList = (List<String>) result.get(ModelService.ERROR_MESSAGE_LIST);
+            if (UtilValidate.isEmpty(errorMessage)) {
+                errorMessage = errorMessageList.get(0);
+                errorMessageList.remove(0);
+            }
+            for (int i = 0; i < errorMessageList.size(); i++) {
+                additionalErrorMessages.add(errorMessageList.get(i));
+            }
+        }
+        Error error = new Error().type("ServiceError").code(ResponseStatus.Custom.UNPROCESSABLE_ENTITY.getStatusCode())
+                .description(ResponseStatus.Custom.UNPROCESSABLE_ENTITY.getReasonPhrase())
+                .message(getErrorMessage(service, "GenericServiceErrorMessage", getHttpRequest().getLocale()))
+                .errorDesc(errorMessage);
+        return Response.status(ResponseStatus.Custom.UNPROCESSABLE_ENTITY).type(MediaType.APPLICATION_JSON).entity(error).build();
+    }
 }
diff --git a/ofbiz-rest-impl/src/main/java/org/apache/ofbiz/ws/rs/response/Error.java b/ofbiz-rest-impl/src/main/java/org/apache/ofbiz/ws/rs/response/Error.java
index 24c7732..6f093d6 100644
--- a/ofbiz-rest-impl/src/main/java/org/apache/ofbiz/ws/rs/response/Error.java
+++ b/ofbiz-rest-impl/src/main/java/org/apache/ofbiz/ws/rs/response/Error.java
@@ -20,16 +20,28 @@ package org.apache.ofbiz.ws.rs.response;
 
 import java.util.List;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import com.fasterxml.jackson.annotation.JsonRootName;
 
 @JsonRootName(value = "error")
+@JsonPropertyOrder({ "statusCode", "statusDescription", "errorType", "errorMessage", "errorDescription",
+        "additionalErrors" })
 public class Error {
 
     private int statusCode;
     private String statusDescription;
     private String errorMessage;
+    @JsonProperty("errorType")
+    private String type;
+    @JsonProperty("errorDescription")
+    private String errorDesc;
     private List<String> additionalErrors;
 
+    public Error() {
+
+    }
+
     public Error(int statusCode, String statusDescription, String errorMessage) {
         this.statusCode = statusCode;
         this.statusDescription = statusDescription;
@@ -44,6 +56,69 @@ public class Error {
     }
 
     /**
+     * @param statusCode
+     * @return
+     */
+    public Error code(int statusCode) {
+        this.statusCode = statusCode;
+        return this;
+    }
+
+    /**
+     * @param statusDescription
+     * @return
+     */
+    public Error description(String statusDescription) {
+        this.statusDescription = statusDescription;
+        return this;
+    }
+
+    /**
+     * @param errorMessage
+     * @return
+     */
+    public Error message(String errorMessage) {
+        this.errorMessage = errorMessage;
+        return this;
+    }
+
+    /**
+     * @param type
+     * @return
+     */
+    public Error type(String type) {
+        this.type = type;
+        return this;
+    }
+
+    /**
+     * @param statusCode
+     * @return
+     */
+    public Error statusCode(int statusCode) {
+        this.statusCode = statusCode;
+        return this;
+    }
+
+    /**
+     * @param additionalErrors
+     * @return
+     */
+    public Error additionalErrors(List<String> additionalErrors) {
+        this.additionalErrors = additionalErrors;
+        return this;
+    }
+
+    /**
+     * @param errorDesc
+     * @return
+     */
+    public Error errorDesc(String errorDesc) {
+        this.setErrorDesc(errorDesc);
+        return this;
+    }
+
+    /**
      * @return the statusCode
      */
     public int getStatusCode() {
@@ -51,6 +126,13 @@ public class Error {
     }
 
     /**
+     * @return the type
+     */
+    public String getType() {
+        return type;
+    }
+
+    /**
      * @param statusCode the statusCode to set
      */
     public void setStatusCode(int statusCode) {
@@ -99,4 +181,18 @@ public class Error {
         this.additionalErrors = additionalErrors;
     }
 
+    /**
+     * @return the errorDesc
+     */
+    public String getErrorDesc() {
+        return errorDesc;
+    }
+
+    /**
+     * @param errorDesc the errorDesc to set
+     */
+    public void setErrorDesc(String errorDesc) {
+        this.errorDesc = errorDesc;
+    }
+
 }
diff --git a/ofbiz-rest-impl/src/main/java/org/apache/ofbiz/ws/rs/response/Error.java b/ofbiz-rest-impl/src/main/java/org/apache/ofbiz/ws/rs/response/Response.java
similarity index 58%
copy from ofbiz-rest-impl/src/main/java/org/apache/ofbiz/ws/rs/response/Error.java
copy to ofbiz-rest-impl/src/main/java/org/apache/ofbiz/ws/rs/response/Response.java
index 24c7732..f513eca 100644
--- a/ofbiz-rest-impl/src/main/java/org/apache/ofbiz/ws/rs/response/Error.java
+++ b/ofbiz-rest-impl/src/main/java/org/apache/ofbiz/ws/rs/response/Response.java
@@ -18,30 +18,19 @@
  *******************************************************************************/
 package org.apache.ofbiz.ws.rs.response;
 
-import java.util.List;
-
-import com.fasterxml.jackson.annotation.JsonRootName;
-
-@JsonRootName(value = "error")
-public class Error {
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 
+@JsonPropertyOrder({ "statusCode", "statusDescription", "data", "error" })
+@JsonInclude(Include.NON_NULL)
+public class Response {
     private int statusCode;
     private String statusDescription;
-    private String errorMessage;
-    private List<String> additionalErrors;
-
-    public Error(int statusCode, String statusDescription, String errorMessage) {
-        this.statusCode = statusCode;
-        this.statusDescription = statusDescription;
-        this.errorMessage = errorMessage;
-    }
-
-    public Error(int statusCode, String statusDescription, String errorMessage, List<String> additionalErrors) {
-        this.statusCode = statusCode;
-        this.statusDescription = statusDescription;
-        this.errorMessage = errorMessage;
-        this.additionalErrors = additionalErrors;
-    }
+    @JsonProperty("payload")
+    private Success success;
+    private Error error;
 
     /**
      * @return the statusCode
@@ -72,31 +61,31 @@ public class Error {
     }
 
     /**
-     * @return the errorMessage
+     * @return the success
      */
-    public String getErrorMessage() {
-        return errorMessage;
+    public Success getSuccess() {
+        return success;
     }
 
     /**
-     * @param errorMessage the errorMessage to set
+     * @param success the success to set
      */
-    public void setErrorMessage(String errorMessage) {
-        this.errorMessage = errorMessage;
+    public void setSuccess(Success success) {
+        this.success = success;
     }
 
     /**
-     * @return the additionalErrors
+     * @return the error
      */
-    public List<String> getAdditionalErrors() {
-        return additionalErrors;
+    public Error getError() {
+        return error;
     }
 
     /**
-     * @param additionalErrors the additionalErrors to set
+     * @param error the error to set
      */
-    public void setAdditionalErrors(List<String> additionalErrors) {
-        this.additionalErrors = additionalErrors;
+    public void setError(Error error) {
+        this.error = error;
     }
 
 }