Repository: olingo-odata4 Updated Branches: refs/heads/master 19f3792f9 -> 3ba5cb36d
[OLINGO-267] JSON conformance test added Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/3ba5cb36 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/3ba5cb36 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/3ba5cb36 Branch: refs/heads/master Commit: 3ba5cb36d155876fd53ba3a5822cf190ce268788 Parents: 19f3792 Author: Francesco Chicchiriccò <--global> Authored: Thu May 22 17:05:11 2014 +0200 Committer: Francesco Chicchiriccò <--global> Committed: Thu May 22 17:05:11 2014 +0200 ---------------------------------------------------------------------- .../fit/v4/JSONFormatConformanceTestITCase.java | 332 +++++++++++++++++++ .../olingo/commons/api/format/ContentType.java | 1 - .../core/data/AbstractJsonDeserializer.java | 2 +- 3 files changed, 333 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/3ba5cb36/fit/src/test/java/org/apache/olingo/fit/v4/JSONFormatConformanceTestITCase.java ---------------------------------------------------------------------- diff --git a/fit/src/test/java/org/apache/olingo/fit/v4/JSONFormatConformanceTestITCase.java b/fit/src/test/java/org/apache/olingo/fit/v4/JSONFormatConformanceTestITCase.java new file mode 100644 index 0000000..9dcff7f --- /dev/null +++ b/fit/src/test/java/org/apache/olingo/fit/v4/JSONFormatConformanceTestITCase.java @@ -0,0 +1,332 @@ +/* + * 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.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.math.BigDecimal; +import java.net.URI; +import org.apache.commons.io.IOUtils; +import org.apache.olingo.client.api.communication.request.retrieve.ODataEntityRequest; +import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse; +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.EntitySet; +import org.apache.olingo.commons.api.data.ResWrap; +import org.apache.olingo.commons.api.domain.ODataLinkType; +import org.apache.olingo.commons.api.domain.v4.ODataAnnotation; +import org.apache.olingo.commons.api.domain.v4.ODataEntity; +import org.apache.olingo.commons.api.domain.v4.ODataEntitySet; +import org.apache.olingo.commons.api.domain.v4.ODataLink; +import org.apache.olingo.commons.api.domain.v4.ODataProperty; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; +import org.apache.olingo.commons.api.format.ODataPubFormat; +import org.junit.Test; + +/** + * The test cases in this class are inspired by client conformance criteria defined in the <a + * href="http://docs.oasis-open.org/odata/odata-json-format/v4.0/os/odata-json-format-v4.0-os.html#_Toc372793094">specs + * </a>. + */ +public class JSONFormatConformanceTestITCase extends AbstractTestITCase { + + /** + * MUST either: + * <ol> + * <li>understand <tt>odata.metadata=minimal</tt> (section 3.1.1) or</li> + * <li>explicitly specify <tt>odata.metadata=none</tt>(section 3.1.3) or <tt>odata.metadata=full</tt> (section 3.1.2) + * in the request (client)</li> + * </ol> + * . + */ + @Test + public void item1() throws EdmPrimitiveTypeException { + final URI uri = edmClient.newURIBuilder(). + appendEntitySetSegment("Accounts").appendKeySegment(102). + appendNavigationSegment("MyPaymentInstruments").appendKeySegment(102902).build(); + final ODataEntityRequest<ODataEntity> req = edmClient.getRetrieveRequestFactory().getEntityRequest(uri); + + // request format (via Accept header) is set to minimal by default + assertEquals("application/json;odata.metadata=minimal", req.getAccept()); + + final ODataRetrieveResponse<ODataEntity> res = req.execute(); + + // response is odata.metadata=minimal + assertFalse(res.getContentType().contains("odata.metadata=none")); + assertFalse(res.getContentType().contains("odata.metadata=full")); + + // response payload is understood + final ODataEntity entity = res.getBody(); + assertNotNull(entity); + assertEquals("Microsoft.Test.OData.Services.ODataWCFService.PaymentInstrument", entity.getTypeName().toString()); + assertEquals(102902, entity.getProperty("PaymentInstrumentID").getPrimitiveValue().toCastValue(Integer.class), 0); + assertEquals("Edm.DateTimeOffset", entity.getProperty("CreatedDate").getPrimitiveValue().getTypeName()); + } + + /** + * MUST be prepared to consume a response with full metadata. + */ + @Test + public void item2() { + final URI uri = edmClient.newURIBuilder(testStaticServiceRootURL). + appendEntitySetSegment("Accounts").appendKeySegment(102).build(); + final ODataEntityRequest<ODataEntity> req = edmClient.getRetrieveRequestFactory().getEntityRequest(uri); + req.setFormat(ODataPubFormat.JSON_FULL_METADATA); + + // request format (via Accept header) is set to full metadata + assertEquals("application/json;odata.metadata=full", req.getAccept()); + + final ODataRetrieveResponse<ODataEntity> res = req.execute(); + + // response is odata.metadata=full + assertTrue(res.getContentType().contains("odata.metadata=full")); + + // response payload is understood (including links, only returned with full metadata) + final ODataEntity entity = res.getBody(); + assertNotNull(entity); + assertEquals(ODataLinkType.ENTITY_SET_NAVIGATION, entity.getNavigationLink("MyPaymentInstruments").getType()); + assertEquals(ODataLinkType.ENTITY_SET_NAVIGATION, entity.getNavigationLink("ActiveSubscriptions").getType()); + } + + /** + * MUST be prepared to receive all data types (section 7.1) + * <ol> + * <li>defined in this specification (client)</li> + * <li>exposed by the service (service)</li> + * </ol> + * . + */ + @Test + public void item3() throws EdmPrimitiveTypeException { + final String fromSection71 = "{" + + "\"NullValue\": null," + + "\"TrueValue\": true," + + "\"FalseValue\": false," + + "\"[email protected]\": \"Binary\"," + + "\"BinaryValue\": \"T0RhdGE\"," + + "\"IntegerValue\": -128," + + "\"DoubleValue\": 3.1415926535897931," + + "\"[email protected]\": \"Single\"," + + "\"SingleValue\": \"INF\"," + + "\"[email protected]\": \"Decimal\"," + + "\"DecimalValue\": 34.95," + + "\"StringValue\": \"Say \\\"Hello\\\",\\nthen go\"," + + "\"[email protected]\": \"Date\"," + + "\"DateValue\": \"2012-12-03\"," + + "\"[email protected]\": \"DateTimeOffset\"," + + "\"DateTimeOffsetValue\": \"2012-12-03T07:16:23Z\"," + + "\"[email protected]\": \"Duration\"," + + "\"DurationValue\": \"P12DT23H59M59.999999999999S\"," + + "\"[email protected]\": \"TimeOfDay\"," + + "\"TimeOfDayValue\": \"07:59:59.999\"," + + "\"[email protected]\": \"Guid\"," + + "\"GuidValue\": \"01234567-89ab-cdef-0123-456789abcdef\"," + + "\"[email protected]\": \"Int64\"," + + "\"Int64Value\": 0," + + "\"[email protected]\": \"Test.Color\"," + + "\"ColorEnumValue\": \"Yellow\"," + + "\"GeographyPoint\": {\"type\": \"Point\",\"coordinates\":[142.1,64.1]}" + + "}"; + + final ODataEntity entity = client.getReader().readEntity(IOUtils.toInputStream(fromSection71), ODataPubFormat.JSON); + + assertTrue(entity.getProperty("NullValue").hasNullValue()); + + assertEquals(EdmPrimitiveTypeKind.Boolean, entity.getProperty("TrueValue").getPrimitiveValue().getTypeKind()); + assertEquals(Boolean.TRUE, entity.getProperty("TrueValue").getPrimitiveValue().toCastValue(Boolean.class)); + + assertEquals(EdmPrimitiveTypeKind.Boolean, entity.getProperty("FalseValue").getPrimitiveValue().getTypeKind()); + assertEquals(Boolean.FALSE, entity.getProperty("FalseValue").getPrimitiveValue().toCastValue(Boolean.class)); + + assertEquals(EdmPrimitiveTypeKind.Binary, entity.getProperty("BinaryValue").getPrimitiveValue().getTypeKind()); + + assertEquals(EdmPrimitiveTypeKind.Int32, entity.getProperty("IntegerValue").getPrimitiveValue().getTypeKind()); + assertEquals(-128, entity.getProperty("IntegerValue").getPrimitiveValue().toCastValue(Integer.class), 0); + + assertEquals(EdmPrimitiveTypeKind.Double, entity.getProperty("DoubleValue").getPrimitiveValue().getTypeKind()); + assertEquals(3.1415926535897931, + entity.getProperty("DoubleValue").getPrimitiveValue().toCastValue(Double.class), 0); + + assertEquals(EdmPrimitiveTypeKind.Single, entity.getProperty("SingleValue").getPrimitiveValue().getTypeKind()); + assertEquals(Float.POSITIVE_INFINITY, + entity.getProperty("SingleValue").getPrimitiveValue().toCastValue(Float.class), 0); + + assertEquals(EdmPrimitiveTypeKind.Decimal, entity.getProperty("DecimalValue").getPrimitiveValue().getTypeKind()); + assertEquals(BigDecimal.valueOf(34.95), + entity.getProperty("DecimalValue").getPrimitiveValue().toCastValue(BigDecimal.class)); + + assertEquals(EdmPrimitiveTypeKind.String, entity.getProperty("StringValue").getPrimitiveValue().getTypeKind()); + assertEquals("Say \"Hello\",\nthen go", + entity.getProperty("StringValue").getPrimitiveValue().toCastValue(String.class)); + + assertEquals(EdmPrimitiveTypeKind.Date, entity.getProperty("DateValue").getPrimitiveValue().getTypeKind()); + + assertEquals(EdmPrimitiveTypeKind.DateTimeOffset, + entity.getProperty("DateTimeOffsetValue").getPrimitiveValue().getTypeKind()); + + assertEquals(EdmPrimitiveTypeKind.Duration, entity.getProperty("DurationValue").getPrimitiveValue().getTypeKind()); + + assertEquals(EdmPrimitiveTypeKind.TimeOfDay, + entity.getProperty("TimeOfDayValue").getPrimitiveValue().getTypeKind()); + + assertEquals(EdmPrimitiveTypeKind.Guid, entity.getProperty("GuidValue").getPrimitiveValue().getTypeKind()); + + assertEquals(EdmPrimitiveTypeKind.Int64, entity.getProperty("Int64Value").getPrimitiveValue().getTypeKind()); + + assertTrue(entity.getProperty("ColorEnumValue").hasEnumValue()); + + assertEquals(EdmPrimitiveTypeKind.GeographyPoint, + entity.getProperty("GeographyPoint").getPrimitiveValue().getTypeKind()); + } + + /** + * MUST interpret all odata annotations defined according to the OData-Version header of the payload (section 4.5). + */ + @Test + public void item4() { + final String fromSection45_1 = "{" + + "\"@odata.context\": \"http://host/service/$metadata#Customers/$entity\"," + + "\"@odata.metadataEtag\": \"W/\\\"A1FF3E230954908F\\\"\"," + + "\"@odata.etag\": \"W/\\\"A1FF3E230954908G\\\"\"," + + "\"@odata.type\": \"#Model.VipCustomer\"," + + "\"@odata.id\": \"http://host/service/Employees(PersonID=3)\"," + + "\"@odata.editLink\": \"People(976)\"," + + "\"@odata.mediaEditLink\": \"Employees(1)/$value\"," + + "\"@odata.mediaContentType\": \"image/jpeg\"," + + "\"@odata.mediaEtag\": \"W/\\\"A1FF3E230954908H\\\"\"," + + "\"[email protected]\": \"People(976)/Parent\"," + + "\"[email protected]\": \"People(976)/Parent\"" + + "}"; + + final ResWrap<Entity> entity = + client.getDeserializer().toEntity(IOUtils.toInputStream(fromSection45_1), ODataPubFormat.JSON); + + assertEquals("http://host/service/$metadata#Customers/$entity", entity.getContextURL().getURI().toASCIIString()); + assertEquals("W/\"A1FF3E230954908F\"", entity.getMetadataETag()); + assertEquals("W/\"A1FF3E230954908G\"", entity.getPayload().getETag()); + assertEquals("Model.VipCustomer", entity.getPayload().getType()); + assertEquals("http://host/service/Employees(PersonID=3)", entity.getPayload().getId()); + assertEquals("People(976)", entity.getPayload().getEditLink().getHref()); + assertEquals("Employees(1)/$value", entity.getPayload().getMediaContentSource().toASCIIString()); + assertEquals("image/jpeg", entity.getPayload().getMediaContentType()); + assertEquals("W/\"A1FF3E230954908H\"", entity.getPayload().getMediaETag()); + assertEquals("People(976)/Parent", entity.getPayload().getNavigationLink("Parent").getHref()); + assertEquals("People(976)/Parent", entity.getPayload().getAssociationLink("Parent").getHref()); + + final String fromSection45_2 = "{" + + " \"@odata.count\": 5," + + " \"value\": []," + + " \"@odata.nextLink\": \"Customers?$expand=Orders&$skipToken=5\"," + + " \"@odata.deltaLink\": \"Customers?$expand=Orders&$deltatoken=8015\"" + + "}"; + + final ResWrap<EntitySet> entitySet = + client.getDeserializer().toEntitySet(IOUtils.toInputStream(fromSection45_2), ODataPubFormat.JSON); + + assertEquals(5, entitySet.getPayload().getCount(), 0); + assertEquals("Customers?$expand=Orders&$skipToken=5", entitySet.getPayload().getNext().toASCIIString()); + assertEquals("Customers?$expand=Orders&$deltatoken=8015", entitySet.getPayload().getDeltaLink().toASCIIString()); + } + + /** + * MUST be prepared to receive any annotations, including custom annotations and <tt>odata</tt> annotations not + * defined in the <tt>OData-Version</tt> header of the payload (section 20). + */ + @Test + public void item5() throws EdmPrimitiveTypeException { + final String sample = "{" + + " \"@odata.context\": \"http://host/service/$metadata#Customers\"," + + " \"@odata.notdefined\": 11," + + " \"@com.contoso.customer.setkind\": \"VIPs\"," + + " \"value\": [" + + " {" + + " \"@com.contoso.display.highlight\": true," + + " \"ID\": \"ALFKI\"," + + " \"[email protected]\": { \"title\": true, \"order\": 1 }," + + " \"CompanyName\": \"Alfreds Futterkiste\"," + + " \"[email protected]\": { \"order\": 2 }," + + " \"[email protected]\": \"People(976)/Orders\"" + + " }" + + " ]" + + "}"; + + final ODataEntitySet entitySet = client.getReader(). + readEntitySet(IOUtils.toInputStream(sample), ODataPubFormat.JSON); + + assertEquals(2, entitySet.getAnnotations().size()); + + final ODataAnnotation notdefined = entitySet.getAnnotations().get(0); + assertEquals("odata.notdefined", notdefined.getTerm()); + assertEquals(11, notdefined.getPrimitiveValue().toCastValue(Integer.class), 0); + + final ODataAnnotation setkind = entitySet.getAnnotations().get(1); + assertEquals("com.contoso.customer.setkind", setkind.getTerm()); + assertEquals("VIPs", setkind.getPrimitiveValue().toCastValue(String.class)); + + final ODataEntity entity = entitySet.getEntities().get(0); + assertEquals(1, entity.getAnnotations().size()); + + final ODataAnnotation highlight = entity.getAnnotations().get(0); + assertEquals("com.contoso.display.highlight", highlight.getTerm()); + assertEquals(Boolean.TRUE, highlight.getPrimitiveValue().toCastValue(Boolean.class)); + + final ODataProperty property = entity.getProperty("CompanyName"); + assertEquals(1, property.getAnnotations().size()); + + final ODataAnnotation style = property.getAnnotations().get(0); + assertEquals("com.contoso.display.style", style.getTerm()); + assertTrue(style.hasComplexValue()); + assertEquals(Boolean.TRUE, style.getComplexValue().get("title").getPrimitiveValue().toCastValue(Boolean.class)); + assertEquals(1, style.getComplexValue().get("order").getPrimitiveValue().toCastValue(Integer.class), 0); + + final ODataLink orders = (ODataLink) entity.getNavigationLink("Orders"); + assertEquals(1, orders.getAnnotations().size()); + + final ODataAnnotation style2 = orders.getAnnotations().get(0); + assertEquals("com.contoso.display.style", style2.getTerm()); + assertTrue(style2.hasComplexValue()); + assertEquals(2, style2.getComplexValue().get("order").getPrimitiveValue().toCastValue(Integer.class), 0); + } + + /** + * MUST NOT require <tt>odata.streaming=true</tt> in the <tt>Content-Type</tt> header (section 4.4). + */ + @Test + public void item6() throws EdmPrimitiveTypeException { + final URI uri = edmClient.newURIBuilder(). + appendEntitySetSegment("Accounts").appendKeySegment(102). + appendNavigationSegment("MyPaymentInstruments").appendKeySegment(102902).build(); + final ODataEntityRequest<ODataEntity> req = edmClient.getRetrieveRequestFactory().getEntityRequest(uri); + + // request format (via Accept header) does not contain odata.streaming=true + assertEquals("application/json;odata.metadata=minimal", req.getAccept()); + + final ODataRetrieveResponse<ODataEntity> res = req.execute(); + + // response payload is understood + final ODataEntity entity = res.getBody(); + assertNotNull(entity); + assertEquals("Microsoft.Test.OData.Services.ODataWCFService.PaymentInstrument", entity.getTypeName().toString()); + assertEquals(102902, entity.getProperty("PaymentInstrumentID").getPrimitiveValue().toCastValue(Integer.class), 0); + assertEquals("Edm.DateTimeOffset", entity.getProperty("CreatedDate").getPrimitiveValue().getTypeName()); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/3ba5cb36/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/ContentType.java ---------------------------------------------------------------------- diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/ContentType.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/ContentType.java index 9e6cfed..8e5f27d 100644 --- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/ContentType.java +++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/ContentType.java @@ -55,7 +55,6 @@ public abstract class ContentType { new EnumMap<ODataServiceVersion, Map<String, String>>(ODataServiceVersion.class); static { - final Map<String, String> v3 = new HashMap<String, String>(); v3.put(ODataPubFormat.JSON_NO_METADATA.name(), ContentType.APPLICATION_JSON + ";odata=nometadata"); v3.put(ODataPubFormat.JSON.name(), ContentType.APPLICATION_JSON + ";odata=minimalmetadata"); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/3ba5cb36/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractJsonDeserializer.java ---------------------------------------------------------------------- diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractJsonDeserializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractJsonDeserializer.java index 33d8243..133c916 100644 --- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractJsonDeserializer.java +++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractJsonDeserializer.java @@ -197,7 +197,7 @@ abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<ResW } else if (node.isArray()) { type = ODataPropertyType.COLLECTION; } else if (node.isObject()) { - if (node.has(Constants.ATTR_TYPE) && node.has(Constants.JSON_CRS)) { + if (node.has(Constants.ATTR_TYPE)) { type = ODataPropertyType.PRIMITIVE; typeInfo = new EdmTypeInfo.Builder(). setTypeExpression("Edm.Geography" + node.get(Constants.ATTR_TYPE).asText()).build();
