Repository: olingo-odata4
Updated Branches:
  refs/heads/olingo274 f02d9a07e -> 6f1f4d05b


[OLINGO-274] fix V4 json error response's deserializing ("details" & 
"innererror")


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/6f1f4d05
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/6f1f4d05
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/6f1f4d05

Branch: refs/heads/olingo274
Commit: 6f1f4d05bda2f3ee75bf2f6fa6a8668f8ab0b18f
Parents: f02d9a0
Author: challenh <[email protected]>
Authored: Wed May 7 12:42:01 2014 +0800
Committer: challenh <[email protected]>
Committed: Wed May 7 12:42:01 2014 +0800

----------------------------------------------------------------------
 fit/src/main/resources/V40/badRequest.json      | 50 ++++++++-----
 fit/src/main/resources/V40/notFound.json        | 44 +++++++++---
 .../olingo/fit/v4/ErrorResponseTestITCase.java  | 76 ++++++++++++++++++++
 .../apache/olingo/commons/api/Constants.java    |  4 ++
 .../olingo/commons/api/domain/ODataError.java   | 17 +++++
 .../commons/api/domain/ODataErrorDetail.java    | 48 +++++++++++++
 .../commons/core/data/AbstractODataError.java   | 32 +++++++--
 .../core/data/JSONODataErrorDeserializer.java   | 34 ++++++++-
 .../data/JSONODataErrorDetailDeserializer.java  | 66 +++++++++++++++++
 .../core/data/JSONODataErrorDetailImpl.java     | 62 ++++++++++++++++
 10 files changed, 402 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6f1f4d05/fit/src/main/resources/V40/badRequest.json
----------------------------------------------------------------------
diff --git a/fit/src/main/resources/V40/badRequest.json 
b/fit/src/main/resources/V40/badRequest.json
index b6bc3ac..fbc0c63 100644
--- a/fit/src/main/resources/V40/badRequest.json
+++ b/fit/src/main/resources/V40/badRequest.json
@@ -1,17 +1,35 @@
 {
-  "odata.error":
-          {
-            "code": "",
-            "message":
-                    {
-                      "lang": "en-US",
-                      "value": "Bad request."
-                    },
-            "innererror":
-                    {
-                      "message": "Bad request.",
-                      "type": "Microsoft.Data.OData.BadRequest",
-                      "stacktrace": " at 
Microsoft.Data.OData.MediaTypeUtils.GetContentTypeFromSettings...."
-                    }
-          }
-}
\ No newline at end of file
+
+  "error": {
+
+    "code": "400",
+
+    "message": "Bad request.",
+
+    "target": "query",
+
+    "details": [
+
+      {
+
+       "code": "400",
+
+       "target": "$search" ,
+
+       "message": "Microsoft.Data.OData.BadRequest"
+
+      }
+
+    ],
+
+    "innererror": {
+
+      "trace": ["at 
Microsoft.Data.OData.MediaTypeUtils.GetContentTypeFromSettings....","callmethod2
 etc"],
+
+      "context": {"key1":"for debug deployment only"}
+
+    }
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6f1f4d05/fit/src/main/resources/V40/notFound.json
----------------------------------------------------------------------
diff --git a/fit/src/main/resources/V40/notFound.json 
b/fit/src/main/resources/V40/notFound.json
index fba2ac4..2af4618 100644
--- a/fit/src/main/resources/V40/notFound.json
+++ b/fit/src/main/resources/V40/notFound.json
@@ -1,11 +1,35 @@
 {
-  "odata.error":
-          {
-            "code": "",
-            "message":
-                    {
-                      "lang": "en-US",
-                      "value": "Resource not found for the segment 'Customer'."
-                    }
-          }
-}
\ No newline at end of file
+
+  "error": {
+
+    "code": "501",
+
+    "message": "Unsupported functionality",
+
+    "target": "query",
+
+    "details": [
+
+      {
+
+       "code": "301",
+
+       "target": "$search",
+
+       "message": "$search query option not supported"
+
+      }
+
+    ],
+
+    "innererror": {
+
+      "trace": ["callmethod1 etc","callmethod2 etc"],
+
+      "context": {"key1":"for debug deployment only"}
+
+    }
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6f1f4d05/fit/src/test/java/org/apache/olingo/fit/v4/ErrorResponseTestITCase.java
----------------------------------------------------------------------
diff --git 
a/fit/src/test/java/org/apache/olingo/fit/v4/ErrorResponseTestITCase.java 
b/fit/src/test/java/org/apache/olingo/fit/v4/ErrorResponseTestITCase.java
new file mode 100644
index 0000000..7e576f0
--- /dev/null
+++ b/fit/src/test/java/org/apache/olingo/fit/v4/ErrorResponseTestITCase.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.fit.v4;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.net.URI;
+import java.util.Dictionary;
+
+import org.apache.olingo.client.api.communication.ODataClientErrorException;
+import 
org.apache.olingo.client.api.communication.request.retrieve.ODataEntityRequest;
+import org.apache.olingo.commons.api.domain.ODataError;
+import org.apache.olingo.commons.api.domain.ODataErrorDetail;
+import org.apache.olingo.commons.api.domain.v4.ODataEntity;
+import org.apache.olingo.commons.api.format.ODataPubFormat;
+import org.junit.Test;
+
+public class ErrorResponseTestITCase extends AbstractTestITCase {
+
+       @Test
+       public void jsonError() {
+
+               ODataPubFormat format = ODataPubFormat.JSON;
+               final URI readURI = 
getClient().getURIBuilder(testStaticServiceRootURL)
+                               
.appendEntitySetSegment("Customers").appendKeySegment(32)
+                               .build();
+
+               final ODataEntityRequest<ODataEntity> req = getClient()
+                               
.getRetrieveRequestFactory().getEntityRequest(readURI);
+               try {
+                       final ODataEntity read = read(format, readURI);
+               } catch (Exception ex) {
+                       ODataError err = ((ODataClientErrorException) 
ex).getODataError();
+
+                       // verify details
+                       ODataErrorDetail detail = (ODataErrorDetail) 
err.getDetails()
+                                       .get(0);
+                       assertEquals("Code should be correct", "301", 
detail.getCode());
+                       assertEquals("Target should be correct", "$search",
+                                       detail.getTarget());
+                       assertEquals("Message should be correct",
+                                       "$search query option not supported", 
detail.getMessage());
+
+                       // verify inner error dictionary
+                       Dictionary<String, Object> innerErr = 
err.getInnerError();
+                       assertEquals("innerError dictionary size should be 
correct", 2,
+                                       innerErr.size());
+                       assertEquals("innerError['context'] should be correct",
+                                       "{\"key1\":\"for debug deployment 
only\"}",
+                                       innerErr.get("context"));
+                       assertEquals("innerError['trace'] should be correct",
+                                       "[\"callmethod1 etc\",\"callmethod2 
etc\"]",
+                                       innerErr.get("trace"));
+                       return;
+               }
+
+               assertNotNull("should have got exception", null);
+       }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6f1f4d05/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java
----------------------------------------------------------------------
diff --git 
a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java 
b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java
index 17a65eb..cc6907c 100644
--- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java
+++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java
@@ -243,6 +243,10 @@ public interface Constants {
 
   public static final String ERROR_TARGET = "target";
 
+  public static final String ERROR_DETAILS = "details";
+  
+  public static final String ERROR_INNERERROR= "innererror";
+
   // canonical functions to be applied via dynamic annotation <tt>Apply</tt>
   public static final String CANONICAL_FUNCTION_CONCAT = "odata.concat";
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6f1f4d05/lib/commons-api/src/main/java/org/apache/olingo/commons/api/domain/ODataError.java
----------------------------------------------------------------------
diff --git 
a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/domain/ODataError.java
 
b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/domain/ODataError.java
index fc309d3..f899d9a 100644
--- 
a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/domain/ODataError.java
+++ 
b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/domain/ODataError.java
@@ -18,6 +18,9 @@
  */
 package org.apache.olingo.commons.api.domain;
 
+import java.util.Dictionary;
+import java.util.List;
+
 /**
  * OData error.
  */
@@ -43,5 +46,19 @@ public interface ODataError {
    * @return error message.
    */
   String getTarget();
+  
+
+  /**
+   * Gets error details.
+   *
+   * @return ODataErrorDetail list.
+   */
+  List<ODataErrorDetail> getDetails();
 
+  /**
+   * Gets server defined key-value pairs for debug environment only.
+   *
+   * @return a Dictionary representing server defined object.
+   */
+  Dictionary<String, Object> getInnerError();
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6f1f4d05/lib/commons-api/src/main/java/org/apache/olingo/commons/api/domain/ODataErrorDetail.java
----------------------------------------------------------------------
diff --git 
a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/domain/ODataErrorDetail.java
 
b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/domain/ODataErrorDetail.java
new file mode 100644
index 0000000..462e91e
--- /dev/null
+++ 
b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/domain/ODataErrorDetail.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.commons.api.domain;
+
+/**
+ * OData details, for example - { "error": {..., "details":[
+ * {"code": "301","target": "$search" ,"message": "$search query option not 
supported"}
+ * ],...}}
+ */
+public interface ODataErrorDetail {
+
+  /**
+   * Gets error code.
+   *
+   * @return error code.
+   */
+  String getCode();
+
+  /**
+   * Gets error message.
+   *
+   * @return error message.
+   */
+  String getMessage();
+
+  /**
+   * Gets error target.
+   *
+   * @return error message.
+   */
+  String getTarget();
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6f1f4d05/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractODataError.java
----------------------------------------------------------------------
diff --git 
a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractODataError.java
 
b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractODataError.java
index 931a38b..62cbebf 100644
--- 
a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractODataError.java
+++ 
b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractODataError.java
@@ -18,9 +18,12 @@
  */
 package org.apache.olingo.commons.core.data;
 
+import java.util.Dictionary;
+import java.util.List;
+
 import org.apache.olingo.commons.api.domain.ODataError;
+import org.apache.olingo.commons.api.domain.ODataErrorDetail;
 
-// TODO p2 supports V4:
 //  {
 //       "error": {
 //         "code": "501",
@@ -29,10 +32,10 @@ import org.apache.olingo.commons.api.domain.ODataError;
 //         "details": [
 //           {
 //            "code": "301",
-//            "target": "$search"  
-//            "message": "$search query option not supported",
+//            "target": "$search",
+//            "message": "$search query option not supported"
 //           }
-//         ]
+//         ],
 //         "innererror": {
 //           "trace": [...],
 //           "context": {...}
@@ -47,6 +50,10 @@ public abstract class AbstractODataError implements 
ODataError {
 
   private String target;
 
+  private List<ODataErrorDetail> details;
+  
+  private Dictionary<String,Object> innerError;
+  
   @Override
   public String getCode() {
     return code;
@@ -74,4 +81,21 @@ public abstract class AbstractODataError implements 
ODataError {
     this.target = target;
   }
 
+  @Override
+  public List<ODataErrorDetail> getDetails() {
+    return details;
+  }
+
+  public void setDetails(final List<ODataErrorDetail> detail) {
+    this.details = detail;
+  }
+
+  @Override
+  public Dictionary<String,Object> getInnerError() {
+    return innerError;
+  }
+
+  public void setInnerError(final Dictionary<String,Object> innerError) {
+    this.innerError = innerError;
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6f1f4d05/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONODataErrorDeserializer.java
----------------------------------------------------------------------
diff --git 
a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONODataErrorDeserializer.java
 
b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONODataErrorDeserializer.java
index 43ee06c..50fa1f9 100644
--- 
a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONODataErrorDeserializer.java
+++ 
b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONODataErrorDeserializer.java
@@ -20,13 +20,23 @@ package org.apache.olingo.commons.core.data;
 
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+
 import java.io.IOException;
 import java.net.URI;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+
 import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.Entity;
 import org.apache.olingo.commons.api.data.ResWrap;
+import org.apache.olingo.commons.api.domain.ODataErrorDetail;
 
 public class JSONODataErrorDeserializer extends 
AbstractJsonDeserializer<JSONODataErrorImpl> {
 
@@ -54,8 +64,30 @@ public class JSONODataErrorDeserializer extends 
AbstractJsonDeserializer<JSONODa
       if (errorNode.has(Constants.ERROR_TARGET)) {
         error.setTarget(errorNode.get(Constants.ERROR_TARGET).textValue());
       }
+      if (errorNode.hasNonNull(Constants.ERROR_DETAILS)) {
+         List<ODataErrorDetail> details = new ArrayList<ODataErrorDetail>(); 
+          for (final Iterator<JsonNode> itor = 
errorNode.get(Constants.ERROR_DETAILS).iterator(); itor.hasNext();) {
+                 details.add(
+                    
itor.next().traverse(parser.getCodec()).<ResWrap<JSONODataErrorDetailImpl>>readValueAs(
+                            new TypeReference<JSONODataErrorDetailImpl>() {
+                            }).getPayload());
+          }
+          
+          error.setDetails(details);
+      }
+      if (errorNode.hasNonNull(Constants.ERROR_INNERERROR)) {
+         JsonNode innerError = errorNode.get(Constants.ERROR_INNERERROR);
+         Dictionary<String, Object> innerErr = new Hashtable<String, 
Object>(); 
+          for (final Iterator<String> itor = innerError.fieldNames(); 
itor.hasNext();) {
+               String keyTmp = itor.next();
+               String val = innerError.get(keyTmp).toString();
+               innerErr.put(keyTmp,val);
+          }
+          
+          error.setInnerError(innerErr);
+      }
     }
-
+    
     return new ResWrap<JSONODataErrorImpl>((URI) null, null, error);
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6f1f4d05/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONODataErrorDetailDeserializer.java
----------------------------------------------------------------------
diff --git 
a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONODataErrorDetailDeserializer.java
 
b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONODataErrorDetailDeserializer.java
new file mode 100644
index 0000000..9c2ce7b
--- /dev/null
+++ 
b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONODataErrorDetailDeserializer.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.commons.core.data;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.ResWrap;
+import org.apache.olingo.commons.api.domain.ODataErrorDetail;
+
+public class JSONODataErrorDetailDeserializer extends
+               AbstractJsonDeserializer<JSONODataErrorDetailImpl> {
+
+       @Override
+       protected ResWrap<JSONODataErrorDetailImpl> doDeserialize(
+                       final JsonParser parser, final DeserializationContext 
ctxt)
+                       throws IOException, JsonProcessingException {
+
+               final JSONODataErrorDetailImpl error = new 
JSONODataErrorDetailImpl();
+               final JsonNode errorNode = parser.getCodec().readTree(parser);
+               if (errorNode.has(Constants.ERROR_CODE)) {
+                       
error.setCode(errorNode.get(Constants.ERROR_CODE).textValue());
+               }
+               if (errorNode.has(Constants.ERROR_MESSAGE)) {
+                       final JsonNode message = 
errorNode.get(Constants.ERROR_MESSAGE);
+                       if (message.isValueNode()) {
+                               error.setMessage(message.textValue());
+                       } else if (message.isObject()) {
+                               
error.setMessage(message.get(Constants.VALUE).asText());
+                       }
+               }
+               if (errorNode.has(Constants.ERROR_TARGET)) {
+                       
error.setTarget(errorNode.get(Constants.ERROR_TARGET).textValue());
+               }
+
+               return new ResWrap<JSONODataErrorDetailImpl>((URI) null, null, 
error);
+       }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6f1f4d05/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONODataErrorDetailImpl.java
----------------------------------------------------------------------
diff --git 
a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONODataErrorDetailImpl.java
 
b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONODataErrorDetailImpl.java
new file mode 100644
index 0000000..2f4585a
--- /dev/null
+++ 
b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONODataErrorDetailImpl.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.commons.core.data;
+
+import org.apache.olingo.commons.api.domain.ODataErrorDetail;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+/*
+ * JSONODataErrorDetailImpl, using the JSONODataErrorDetailDeserializer 
similar to JSONODataErrorImpl's.
+ */
+@JsonDeserialize(using = JSONODataErrorDetailDeserializer.class)
+public class JSONODataErrorDetailImpl implements ODataErrorDetail {
+
+       private String code;
+
+       private String message;
+
+       private String target;
+
+       @Override
+       public String getCode() {
+               return code;
+       }
+
+       public void setCode(final String code) {
+               this.code = code;
+       }
+
+       @Override
+       public String getMessage() {
+               return message;
+       }
+
+       public void setMessage(final String message) {
+               this.message = message;
+       }
+
+       @Override
+       public String getTarget() {
+               return target;
+       }
+
+       public void setTarget(final String target) {
+               this.target = target;
+       }
+}
\ No newline at end of file

Reply via email to