Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/olingo-odata4
This commit is contained in:
commit
8bd9e0ca97
|
@ -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...."
|
||||
}
|
||||
}
|
||||
|
||||
"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"}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,35 @@
|
|||
{
|
||||
"odata.error":
|
||||
{
|
||||
"code": "",
|
||||
"message":
|
||||
{
|
||||
"lang": "en-US",
|
||||
"value": "Resource not found for the segment 'Customer'."
|
||||
}
|
||||
}
|
||||
|
||||
"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"}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -245,6 +245,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";
|
||||
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
*/
|
||||
package org.apache.olingo.commons.api.domain;
|
||||
|
||||
import java.util.Dictionary;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* OData error.
|
||||
*/
|
||||
|
@ -44,4 +47,18 @@ public interface ODataError {
|
|||
*/
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -18,9 +18,12 @@
|
|||
*/
|
||||
package org.apache.olingo.commons.core.data;
|
||||
|
||||
import org.apache.olingo.commons.api.domain.ODataError;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,6 +64,28 @@ 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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue