You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2014/05/07 06:43:18 UTC

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

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 <ch...@apache.org>
Authored: Wed May 7 12:42:01 2014 +0800
Committer: challenh <ch...@apache.org>
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