This is an automated email from the ASF dual-hosted git repository. ramyav pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/olingo-odata4.git
The following commit(s) were added to refs/heads/master by this push: new 97a714e [OLINGO-1481]Handle odata v4.01 annotations in edm assisted serailizer 97a714e is described below commit 97a714e34ffc8fc4749ae26b73b72eb3f85f884c Author: ramya vasanth <ramya.vasa...@sap.com> AuthorDate: Thu Sep 17 14:26:24 2020 +0530 [OLINGO-1481]Handle odata v4.01 annotations in edm assisted serailizer --- .../java/org/apache/olingo/server/api/OData.java | 10 + .../olingo/netty/server/core/ODataNettyImpl.java | 6 + .../org/apache/olingo/server/core/ODataImpl.java | 16 + .../serializer/json/EdmAssistedJsonSerializer.java | 39 +- .../apache/olingo/server/core/ODataImplTest.java | 10 + .../json/EdmAssistedJsonSerializerV401Test.java | 701 +++++++++++++++++++++ 6 files changed, 768 insertions(+), 14 deletions(-) diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java index 01a8b0f..2092620 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java @@ -219,6 +219,16 @@ public abstract class OData { /** * Creates a new serializer object capable of working without EDM information + * for rendering content in the specified format. + * @param contentType a content type supported by Olingo + * @param versions Odata Version v4 or v4.01 + */ + public abstract EdmAssistedSerializer createEdmAssistedSerializer(final ContentType contentType, + final List<String> versions) throws SerializerException; + + + /** + * Creates a new serializer object capable of working without EDM information * for rendering delta content in the specified format. * @param contentType a content type supported by Olingo * @param version versions supported by Olingo diff --git a/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyImpl.java b/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyImpl.java index 7250e33..4ad07e8 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyImpl.java @@ -154,6 +154,12 @@ public class ODataNettyImpl extends ODataNetty { public EdmAssistedSerializer createEdmAssistedSerializer(ContentType contentType) throws SerializerException { return odata.createEdmAssistedSerializer(contentType); } + + @Override + public EdmAssistedSerializer createEdmAssistedSerializer(ContentType contentType, + List<String> versions) throws SerializerException { + return odata.createEdmAssistedSerializer(contentType, versions); + } @Override public EdmDeltaSerializer createEdmDeltaSerializer(ContentType contentType, List<String> versions) diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java index 8011dec..507d87c 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java @@ -138,6 +138,22 @@ public class ODataImpl extends OData { ((contentType != null) ? contentType.toContentTypeString() : null)); } + @Override + public EdmAssistedSerializer createEdmAssistedSerializer(final ContentType contentType, + List<String> versions) throws SerializerException { + IConstants constants = new Constantsv00(); + if(versions!=null && !versions.isEmpty() && getMaxVersion(versions) > 4){ + constants = new Constantsv01() ; + } + if (contentType != null && contentType.isCompatible(ContentType.APPLICATION_JSON)) { + return new EdmAssistedJsonSerializer(contentType, constants); + } + throw new SerializerException("Unsupported format: " + + ((contentType != null) ? contentType.toContentTypeString() : null), + SerializerException.MessageKeys.UNSUPPORTED_FORMAT, + ((contentType != null) ? contentType.toContentTypeString() : null)); + } + @Override public EdmDeltaSerializer createEdmDeltaSerializer(final ContentType contentType, final List<String> versions) diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/EdmAssistedJsonSerializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/EdmAssistedJsonSerializer.java index dda5c12..c011fdd 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/EdmAssistedJsonSerializer.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/EdmAssistedJsonSerializer.java @@ -25,6 +25,8 @@ import java.net.URI; import java.util.List; import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.IConstants; +import org.apache.olingo.commons.api.constants.Constantsv00; import org.apache.olingo.commons.api.data.AbstractEntityCollection; import org.apache.olingo.commons.api.data.AbstractODataObject; import org.apache.olingo.commons.api.data.Annotatable; @@ -68,11 +70,20 @@ public class EdmAssistedJsonSerializer implements EdmAssistedSerializer { protected final boolean isIEEE754Compatible; protected final boolean isODataMetadataNone; protected final boolean isODataMetadataFull; + private IConstants constants; public EdmAssistedJsonSerializer(final ContentType contentType) { this.isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType); this.isODataMetadataNone = ContentTypeHelper.isODataMetadataNone(contentType); this.isODataMetadataFull = ContentTypeHelper.isODataMetadataFull(contentType); + this.constants = new Constantsv00(); + } + + public EdmAssistedJsonSerializer(final ContentType contentType, final IConstants constants) { + this.isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType); + this.isODataMetadataNone = ContentTypeHelper.isODataMetadataNone(contentType); + this.isODataMetadataFull = ContentTypeHelper.isODataMetadataFull(contentType); + this.constants = constants; } @Override @@ -135,13 +146,13 @@ public class EdmAssistedJsonSerializer implements EdmAssistedSerializer { if (entityCollection.getCount() != null) { if (isIEEE754Compatible) { - json.writeStringField(Constants.JSON_COUNT, Integer.toString(entityCollection.getCount())); + json.writeStringField(constants.getCount(), Integer.toString(entityCollection.getCount())); } else { - json.writeNumberField(Constants.JSON_COUNT, entityCollection.getCount()); + json.writeNumberField(constants.getCount(), entityCollection.getCount()); } } if (entityCollection.getDeltaLink() != null) { - json.writeStringField(Constants.JSON_DELTA_LINK, entityCollection.getDeltaLink().toASCIIString()); + json.writeStringField(constants.getDeltaLink(), entityCollection.getDeltaLink().toASCIIString()); } for (final Annotation annotation : entityCollection.getAnnotations()) { @@ -155,7 +166,7 @@ public class EdmAssistedJsonSerializer implements EdmAssistedSerializer { json.writeEndArray(); if (entityCollection.getNext() != null) { - json.writeStringField(Constants.JSON_NEXT_LINK, entityCollection.getNext().toASCIIString()); + json.writeStringField(constants.getNextLink(), entityCollection.getNext().toASCIIString()); } json.writeEndObject(); @@ -184,10 +195,10 @@ public class EdmAssistedJsonSerializer implements EdmAssistedSerializer { if (!isODataMetadataNone && entity.getEditLink() != null && entity.getEditLink().getHref() != null) { - json.writeStringField(Constants.JSON_EDIT_LINK, entity.getEditLink().getHref()); + json.writeStringField(constants.getEditLink(), entity.getEditLink().getHref()); if (entity.isMediaEntity()) { - json.writeStringField(Constants.JSON_MEDIA_READ_LINK, entity.getEditLink().getHref() + "/$value"); + json.writeStringField(constants.getMediaReadLink(), entity.getEditLink().getHref() + "/$value"); } } @@ -201,24 +212,24 @@ public class EdmAssistedJsonSerializer implements EdmAssistedSerializer { throws IOException, SerializerException { if (!isODataMetadataNone) { if (contextURLString != null) { - json.writeStringField(Constants.JSON_CONTEXT, contextURLString); + json.writeStringField(constants.getContext(), contextURLString); } if (metadataETag != null) { - json.writeStringField(Constants.JSON_METADATA_ETAG, metadataETag); + json.writeStringField(constants.getMetadataEtag(), metadataETag); } if (eTag != null) { - json.writeStringField(Constants.JSON_ETAG, eTag); + json.writeStringField(constants.getEtag(), eTag); } if(isODataMetadataFull){ if (type != null) { - json.writeStringField(Constants.JSON_TYPE, type); + json.writeStringField(constants.getType(), type); } if (id == null) { if (writeNullId) { - json.writeNullField(Constants.JSON_ID); + json.writeNullField(constants.getId()); } } else { - json.writeStringField(Constants.JSON_ID, id.toASCIIString()); + json.writeStringField(constants.getId(), id.toASCIIString()); } } } @@ -337,7 +348,7 @@ public class EdmAssistedJsonSerializer implements EdmAssistedSerializer { json.writeStartObject(); if (typeName != null && isODataMetadataFull) { - json.writeStringField(Constants.JSON_TYPE, typeName); + json.writeStringField(constants.getType(), typeName); } for (final Property property : value.getValue()) { @@ -393,7 +404,7 @@ public class EdmAssistedJsonSerializer implements EdmAssistedSerializer { } if (typeName != null) { - json.writeStringField(name + Constants.JSON_TYPE, constructTypeExpression(typeName)); + json.writeStringField(name + constants.getType(), constructTypeExpression(typeName)); } } diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/ODataImplTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/ODataImplTest.java index 9bef198..5c56970 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/ODataImplTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/ODataImplTest.java @@ -108,4 +108,14 @@ public class ODataImplTest { public void serializer() throws SerializerException { odata.createSerializer(null); } + + @Test + public void edmAssistedSerializerWithVersion() throws SerializerException { + List<String> versions = new ArrayList<String>(); + versions.add("4.01"); + assertNotNull(odata.createEdmAssistedSerializer(ContentType.APPLICATION_JSON, versions)); + + versions.add("5"); + assertNotNull(odata.createEdmAssistedSerializer(ContentType.APPLICATION_JSON, versions)); + } } diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/EdmAssistedJsonSerializerV401Test.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/EdmAssistedJsonSerializerV401Test.java new file mode 100644 index 0000000..31f76f9 --- /dev/null +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/EdmAssistedJsonSerializerV401Test.java @@ -0,0 +1,701 @@ +/* + * 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.server.core.serializer.json; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.List; +import java.util.TimeZone; +import java.util.UUID; + +import org.apache.commons.io.IOUtils; +import org.apache.olingo.commons.api.data.AbstractEntityCollection; +import org.apache.olingo.commons.api.data.ComplexValue; +import org.apache.olingo.commons.api.data.ContextURL; +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.EntityCollection; +import org.apache.olingo.commons.api.data.Link; +import org.apache.olingo.commons.api.data.Property; +import org.apache.olingo.commons.api.data.ValueType; +import org.apache.olingo.commons.api.edm.EdmEntityContainer; +import org.apache.olingo.commons.api.edm.EdmEntitySet; +import org.apache.olingo.commons.api.edmx.EdmxReference; +import org.apache.olingo.commons.api.format.ContentType; +import org.apache.olingo.server.api.OData; +import org.apache.olingo.server.api.ServiceMetadata; +import org.apache.olingo.server.api.serializer.EdmAssistedSerializer; +import org.apache.olingo.server.api.serializer.EdmAssistedSerializerOptions; +import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.tecsvc.MetadataETagSupport; +import org.apache.olingo.server.tecsvc.provider.EdmTechProvider; +import org.junit.Assert; +import org.junit.Test; + +public class EdmAssistedJsonSerializerV401Test { + private static final OData oData = OData.newInstance(); + private static final ServiceMetadata metadata = oData.createServiceMetadata( + new EdmTechProvider(), Collections.<EdmxReference> emptyList(), null); + private static final EdmEntityContainer entityContainer = metadata.getEdm().getEntityContainer(); + private final EdmAssistedSerializer serializer; + private final EdmAssistedSerializer serializerMin; + private final EdmAssistedSerializer serializerNone; + + public EdmAssistedJsonSerializerV401Test() throws SerializerException { + List<String> versions = new ArrayList<>(); + versions.add("4.01"); + versions.add("4"); + serializer = oData.createEdmAssistedSerializer(ContentType.JSON_FULL_METADATA, versions); + serializerMin = oData.createEdmAssistedSerializer(ContentType.JSON, versions); + serializerNone = oData.createEdmAssistedSerializer(ContentType.JSON_NO_METADATA, versions); + } + + @Test + public void entityCollectionSimple() throws Exception { + Entity entity = new Entity(); + entity.setId(null); + entity.addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, 1.25F)); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + Assert.assertEquals("{\"@context\":\"$metadata#EntitySet(Property1)\"," + + "\"value\":[{\"@id\":null,\"Property1@type\":\"#Single\",\"Property1\":1.25}]}", + serialize(serializer, metadata, null, entityCollection, null)); + } + + @Test + public void entityCollectionWithEdm() throws Exception { + final EdmEntitySet entitySet = entityContainer.getEntitySet("ESTwoPrim"); + Entity entity = new Entity(); + entity.setId(null); + entity.addProperty(new Property(null, "PropertyInt16", ValueType.PRIMITIVE, (short) 1)) + .addProperty(new Property(null, "PropertyString", ValueType.PRIMITIVE, "test")) + .addProperty(new Property(null, "AdditionalProperty", ValueType.PRIMITIVE, (byte) 42)); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + Assert.assertEquals("{\"@context\":\"$metadata#ESTwoPrim\",\"value\":[{\"@id\":null," + + "\"PropertyInt16\":1,\"PropertyString\":\"test\"," + + "\"AdditionalProperty@type\":\"#SByte\",\"AdditionalProperty\":42}]}", + serialize(serializer, metadata, entitySet, entityCollection, null)); + } + + @Test + public void entityCollection() throws Exception { + Entity entity = new Entity(); + entity.setId(null); + entity.addProperty(new Property(null, "Property0", ValueType.PRIMITIVE, null)) + .addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, 1)); + Calendar date = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + date.clear(); + date.set(2000, 1, 29); + entity.addProperty(new Property("Edm.Date", "Property2", ValueType.PRIMITIVE, date)) + .addProperty(new Property("Edm.DateTimeOffset", "Property3", ValueType.PRIMITIVE, date)) + .addProperty(new Property(null, "Property4", ValueType.COLLECTION_PRIMITIVE, + Arrays.asList(true, false, null))); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + entityCollection.setCount(2); + entityCollection.setNext(URI.create("nextLink")); + Assert.assertEquals( + "{\"@context\":\"$metadata#EntitySet(Property0,Property1,Property2,Property3,Property4)\"," + + "\"@count\":2," + + "\"value\":[{\"@id\":null," + + "\"Property0\":null," + + "\"Property1@type\":\"#Int32\",\"Property1\":1," + + "\"Property2@type\":\"#Date\",\"Property2\":\"2000-02-29\"," + + "\"Property3@type\":\"#DateTimeOffset\",\"Property3\":\"2000-02-29T00:00:00Z\"," + + "\"Property4@type\":\"#Collection(Boolean)\",\"Property4\":[true,false,null]}]," + + "\"@nextLink\":\"nextLink\"}", + serialize(serializer, metadata, null, entityCollection, null)); + } + + @Test + public void entityCollectionIEEE754Compatible() throws Exception { + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(new Entity() + .addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, Long.MIN_VALUE)) + .addProperty(new Property(null, "Property2", ValueType.PRIMITIVE, BigDecimal.valueOf(Long.MAX_VALUE, 10))) + .addProperty(new Property("Edm.Byte", "Property3", ValueType.PRIMITIVE, 20))); + entityCollection.setCount(3); + Assert.assertEquals( + "{\"@odata.context\":\"$metadata#EntitySet(Property1,Property2,Property3)\"," + + "\"@odata.count\":\"3\"," + + "\"value\":[{\"@odata.id\":null," + + "\"proper...@odata.type\":\"#Int64\",\"Property1\":\"-9223372036854775808\"," + + "\"proper...@odata.type\":\"#Decimal\",\"Property2\":\"922337203.6854775807\"," + + "\"proper...@odata.type\":\"#Byte\",\"Property3\":20}]}", + serialize( + oData.createEdmAssistedSerializer( + ContentType.create(ContentType.JSON_FULL_METADATA, ContentType.PARAMETER_IEEE754_COMPATIBLE, "true")), + metadata, null, entityCollection, null)); + + List<String> versions = new ArrayList<>(); + versions.add("4.01"); + Assert.assertEquals( + "{\"@context\":\"$metadata#EntitySet(Property1,Property2,Property3)\"," + + "\"@count\":\"3\"," + + "\"value\":[{\"@id\":null," + + "\"Property1@type\":\"#Int64\",\"Property1\":\"-9223372036854775808\"," + + "\"Property2@type\":\"#Decimal\",\"Property2\":\"922337203.6854775807\"," + + "\"Property3@type\":\"#Byte\",\"Property3\":20}]}", + serialize( + oData.createEdmAssistedSerializer( + ContentType.create(ContentType.JSON_FULL_METADATA, + ContentType.PARAMETER_IEEE754_COMPATIBLE, "true"), versions), + metadata, null, entityCollection, null)); + } + + @Test + public void entityCollectionWithComplexProperty() throws Exception { + Entity entity = new Entity(); + entity.setId(null); + entity.addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, 1L)); + ComplexValue complexValue = new ComplexValue(); + complexValue.getValue().add(new Property(null, "Inner1", ValueType.PRIMITIVE, + BigDecimal.TEN.scaleByPowerOfTen(-5))); + Calendar time = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + time.clear(); + time.set(Calendar.HOUR_OF_DAY, 13); + time.set(Calendar.SECOND, 59); + time.set(Calendar.MILLISECOND, 999); + complexValue.getValue().add(new Property("Edm.TimeOfDay", "Inner2", ValueType.PRIMITIVE, time)); + entity.addProperty(new Property("Namespace.ComplexType", "Property2", ValueType.COMPLEX, complexValue)); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + Assert.assertEquals("{\"@context\":\"$metadata#EntitySet(Property1,Property2)\"," + + "\"value\":[{\"@id\":null," + + "\"Property1@type\":\"#Int64\",\"Property1\":1," + + "\"Property2\":{\"@type\":\"#Namespace.ComplexType\"," + + "\"Inner1@type\":\"#Decimal\",\"Inner1\":0.00010," + + "\"Inner2@type\":\"#TimeOfDay\",\"Inner2\":\"13:00:59.999\"}}]}", + serialize(serializer, metadata, null, entityCollection, null)); + } + + @Test + public void entityCollectionWithComplexCollection() throws Exception { + final EdmEntitySet entitySet = entityContainer.getEntitySet("ESMixPrimCollComp"); + ComplexValue complexValue1 = new ComplexValue(); + complexValue1.getValue().add(new Property(null, "PropertyInt16", ValueType.PRIMITIVE, 1)); + complexValue1.getValue().add(new Property(null, "PropertyString", ValueType.PRIMITIVE, "one")); + ComplexValue complexValue2 = new ComplexValue(); + complexValue2.getValue().add(new Property(null, "PropertyInt16", ValueType.PRIMITIVE, 2)); + complexValue2.getValue().add(new Property(null, "PropertyString", ValueType.PRIMITIVE, "two")); + ComplexValue complexValue3 = new ComplexValue(); + complexValue3.getValue().add(new Property(null, "PropertyInt16", ValueType.PRIMITIVE, 3)); + complexValue3.getValue().add(new Property(null, "PropertyString", ValueType.PRIMITIVE, "three")); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(new Entity() + .addProperty(new Property(null, "CollPropertyComp", ValueType.COLLECTION_COMPLEX, + Arrays.asList(complexValue1, complexValue2, complexValue3)))); + Assert.assertEquals("{\"@context\":\"$metadata#ESMixPrimCollComp(CollPropertyComp)\"," + + "\"value\":[{\"@id\":null," + + "\"CollPropertyComp\":[" + + "{\"PropertyInt16\":1,\"PropertyString\":\"one\"}," + + "{\"PropertyInt16\":2,\"PropertyString\":\"two\"}," + + "{\"PropertyInt16\":3,\"PropertyString\":\"three\"}]}]}", + serialize(serializer, metadata, entitySet, entityCollection, "CollPropertyComp")); + } + + @Test + public void entityCollectionWithEmptyCollection() throws Exception { + final EdmEntitySet entitySet = entityContainer.getEntitySet("ESMixPrimCollComp"); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(new Entity() + .addProperty(new Property(null, "CollPropertyString", ValueType.COLLECTION_PRIMITIVE, + Collections.emptyList()))); + Assert.assertEquals( + "{\"@context\":\"$metadata#ESMixPrimCollComp(CollPropertyString)\"," + + "\"value\":[{\"@id\":null,\"CollPropertyString\":[]}]}", + serialize(serializer, metadata, entitySet, entityCollection, "CollPropertyString")); + } + + @Test + public void expand() throws Exception { + final Entity relatedEntity1 = new Entity().addProperty(new Property(null, "Related1", ValueType.PRIMITIVE, 1.5)); + final Entity relatedEntity2 = new Entity().addProperty(new Property(null, "Related1", ValueType.PRIMITIVE, 2.75)); + EntityCollection target = new EntityCollection(); + target.getEntities().add(relatedEntity1); + target.getEntities().add(relatedEntity2); + Link link = new Link(); + link.setTitle("NavigationProperty"); + link.setInlineEntitySet(target); + Entity entity = new Entity(); + entity.setId(null); + entity.addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, (short) 1)); + entity.getNavigationLinks().add(link); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + Assert.assertEquals("{\"@context\":\"$metadata#EntitySet(Property1,NavigationProperty(Related1))\"," + + "\"value\":[{\"@id\":null," + + "\"Property1@type\":\"#Int16\",\"Property1\":1," + + "\"NavigationProperty\":[" + + "{\"@id\":null,\"Related1@type\":\"#Double\",\"Related1\":1.5}," + + "{\"@id\":null,\"Related1@type\":\"#Double\",\"Related1\":2.75}]}]}", + serialize(serializer, metadata, null, entityCollection, "Property1,NavigationProperty(Related1)")); + } + + @Test + public void expandWithEdm() throws Exception { + final EdmEntitySet entitySet = entityContainer.getEntitySet("ESTwoPrim"); + Entity entity = new Entity() + .addProperty(new Property(null, "PropertyInt16", ValueType.PRIMITIVE, (short) 42)) + .addProperty(new Property(null, "PropertyString", ValueType.PRIMITIVE, "test")); + final Entity target = new Entity() + .addProperty(new Property(null, "PropertyInt16", ValueType.PRIMITIVE, (short) 2)) + .addProperty(new Property(null, "PropertyByte", ValueType.PRIMITIVE, 3L)); + Link link = new Link(); + link.setTitle("NavPropertyETAllPrimOne"); + link.setInlineEntity(target); + entity.getNavigationLinks().add(link); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + Assert.assertEquals("{\"@context\":\"$metadata#ESTwoPrim\",\"value\":[{\"@id\":null," + + "\"PropertyInt16\":42,\"PropertyString\":\"test\"," + + "\"NavPropertyETAllPrimOne\":{\"@id\":null,\"PropertyInt16\":2,\"PropertyByte\":3}}]}", + serialize(serializer, metadata, entitySet, entityCollection, null)); + } + + @Test + public void metadata() throws Exception { + final ServiceMetadata metadata = oData.createServiceMetadata(null, Collections.<EdmxReference> emptyList(), + new MetadataETagSupport("W/\"42\"")); + Entity entity = new Entity(); + entity.setType("Namespace.EntityType"); + entity.setId(URI.create("ID")); + entity.setETag("W/\"1000\""); + Link link = new Link(); + link.setHref("editLink"); + entity.setEditLink(link); + entity.setMediaContentSource(URI.create("media")); + entity.addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, + UUID.fromString("12345678-ABCD-1234-CDEF-123456789012"))); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + Assert.assertEquals("{\"@context\":\"$metadata#EntitySet(Property1)\"," + + "\"@metadataEtag\":\"W/\\\"42\\\"\",\"value\":[{" + + "\"@etag\":\"W/\\\"1000\\\"\"," + + "\"@type\":\"#Namespace.EntityType\"," + + "\"@id\":\"ID\"," + + "\"Property1@type\":\"#Guid\",\"Property1\":\"12345678-abcd-1234-cdef-123456789012\"," + + "\"@editLink\":\"editLink\"," + + "\"@mediaReadLink\":\"editLink/$value\"}]}", + serialize(serializer, metadata, null, entityCollection, null)); + + Assert.assertEquals("{\"value\":[{\"Property1\":\"12345678-abcd-1234-cdef-123456789012\"}]}", + serialize(oData.createEdmAssistedSerializer(ContentType.JSON_NO_METADATA), metadata, + null, entityCollection, null)); + } + + @Test(expected = SerializerException.class) + public void enumType() throws Exception { + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add( + new Entity().addProperty(new Property(null, "Property1", ValueType.ENUM, 42))); + serializer.entityCollection(metadata, null, entityCollection, null); + } + + @Test(expected = SerializerException.class) + public void collectionEnumType() throws Exception { + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add( + new Entity().addProperty(new Property(null, "Property1", ValueType.COLLECTION_ENUM, Arrays.asList(42)))); + serializer.entityCollection(metadata, null, entityCollection, null); + } + + @Test(expected = SerializerException.class) + public void geoType() throws Exception { + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add( + new Entity().addProperty(new Property(null, "Property1", ValueType.GEOSPATIAL, 1))); + serializer.entityCollection(metadata, null, entityCollection, null); + } + + @Test(expected = SerializerException.class) + public void unsupportedType() throws Exception { + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add( + new Entity().addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, TimeZone.getDefault()))); + serializer.entityCollection(metadata, null, entityCollection, null); + } + + @Test(expected = SerializerException.class) + public void wrongValueForType() throws Exception { + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add( + new Entity().addProperty(new Property("Edm.SByte", "Property1", ValueType.PRIMITIVE, "-1"))); + serializer.entityCollection(metadata, null, entityCollection, null); + } + + @Test(expected = SerializerException.class) + public void wrongValueForPropertyFacet() throws Exception { + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add( + new Entity().addProperty( + new Property(null, "PropertyDecimal", ValueType.PRIMITIVE, BigDecimal.ONE.scaleByPowerOfTen(-11)))); + serializer.entityCollection(metadata, entityContainer.getEntitySet("ESAllPrim").getEntityType(), entityCollection, + null); + } + + @Test(expected = SerializerException.class) + public void wrongValueForPropertyFacetInComplexProperty() throws Exception { + ComplexValue innerComplexValue = new ComplexValue(); + innerComplexValue.getValue().add(new Property(null, "PropertyDecimal", ValueType.PRIMITIVE, + BigDecimal.ONE.scaleByPowerOfTen(-6))); + ComplexValue complexValue = new ComplexValue(); + complexValue.getValue().add(new Property(null, "PropertyComp", ValueType.COMPLEX, + innerComplexValue)); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add( + new Entity().addProperty( + new Property(null, "CollPropertyComp", ValueType.COLLECTION_COMPLEX, + Collections.singletonList(complexValue)))); + serializer.entityCollection(metadata, entityContainer.getEntitySet("ESKeyNav").getEntityType(), entityCollection, + null); + } + + private String serialize(final EdmAssistedSerializer serializer, final ServiceMetadata metadata, + final EdmEntitySet edmEntitySet, final AbstractEntityCollection entityCollection, final String selectList) + throws SerializerException, IOException { + ContextURL.Builder contextURLBuilder = ContextURL.with(); + contextURLBuilder = edmEntitySet == null ? + contextURLBuilder.entitySetOrSingletonOrType("EntitySet") : + contextURLBuilder.entitySet(edmEntitySet); + if (selectList == null && entityCollection instanceof AbstractEntityCollection) { + if (edmEntitySet == null) { + StringBuilder names = new StringBuilder(); + for (final Property property : + ((AbstractEntityCollection)entityCollection).iterator().next().getProperties()) { + names.append(names.length() > 0 ? ',' : "").append(property.getName()); + } + contextURLBuilder = contextURLBuilder.selectList(names.toString()); + } + } else { + contextURLBuilder = contextURLBuilder.selectList(selectList); + } + return IOUtils.toString( + serializer.entityCollection(metadata, + edmEntitySet == null ? null : edmEntitySet.getEntityType(), + entityCollection, + EdmAssistedSerializerOptions.with().contextURL(contextURLBuilder.build()).build()) + .getContent()); + } + + @Test + public void entityCollectionSimpleMetadataMin() throws Exception { + Entity entity = new Entity(); + entity.setId(null); + entity.addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, 1.25F)); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + Assert.assertEquals("{\"@context\":\"$metadata#EntitySet(Property1)\"," + + "\"value\":[{\"Property1\":1.25}]}", + serialize(serializerMin, metadata, null, entityCollection, null)); + } + + @Test + public void entityCollectionSimpleMetadataNone() throws Exception { + Entity entity = new Entity(); + entity.setId(null); + entity.addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, 1.25F)); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + Assert.assertEquals("{\"value\":[{\"Property1\":1.25}]}", + serialize(serializerNone, metadata, null, entityCollection, null)); + } + + @Test + public void entityCollectionMetadataMin() throws Exception { + Entity entity = new Entity(); + entity.setId(null); + entity.addProperty(new Property(null, "Property0", ValueType.PRIMITIVE, null)) + .addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, 1)); + Calendar date = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + date.clear(); + date.set(2000, 1, 29); + entity.addProperty(new Property("Edm.Date", "Property2", ValueType.PRIMITIVE, date)) + .addProperty(new Property("Edm.DateTimeOffset", "Property3", ValueType.PRIMITIVE, date)) + .addProperty(new Property(null, "Property4", ValueType.COLLECTION_PRIMITIVE, + Arrays.asList(true, false, null))); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + entityCollection.setCount(2); + entityCollection.setNext(URI.create("nextLink")); + Assert.assertEquals( + "{\"@context\":\"$metadata#EntitySet(Property0,Property1,Property2,Property3,Property4)\"," + + "\"@count\":2," + + "\"value\":[{" + + "\"Property0\":null," + + "\"Property1\":1," + + "\"Property2\":\"2000-02-29\"," + + "\"Property3\":\"2000-02-29T00:00:00Z\"," + + "\"Property4\":[true,false,null]}]," + + "\"@nextLink\":\"nextLink\"}", + serialize(serializerMin, metadata, null, entityCollection, null)); + } + + @Test + public void entityCollectionMetadataNone() throws Exception { + Entity entity = new Entity(); + entity.setId(null); + entity.addProperty(new Property(null, "Property0", ValueType.PRIMITIVE, null)) + .addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, 1)); + Calendar date = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + date.clear(); + date.set(2000, 1, 29); + entity.addProperty(new Property("Edm.Date", "Property2", ValueType.PRIMITIVE, date)) + .addProperty(new Property("Edm.DateTimeOffset", "Property3", ValueType.PRIMITIVE, date)) + .addProperty(new Property(null, "Property4", ValueType.COLLECTION_PRIMITIVE, + Arrays.asList(true, false, null))); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + entityCollection.setCount(2); + entityCollection.setNext(URI.create("nextLink")); + Assert.assertEquals( + "{" + + "\"@count\":2," + + "\"value\":[{" + + "\"Property0\":null," + + "\"Property1\":1," + + "\"Property2\":\"2000-02-29\"," + + "\"Property3\":\"2000-02-29T00:00:00Z\"," + + "\"Property4\":[true,false,null]}]," + + "\"@nextLink\":\"nextLink\"}", + serialize(serializerNone, metadata, null, entityCollection, null)); + } + + @Test + public void entityCollectionWithComplexPropertyMetadataMin() throws Exception { + Entity entity = new Entity(); + entity.setId(null); + entity.addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, 1L)); + ComplexValue complexValue = new ComplexValue(); + complexValue.getValue().add(new Property(null, "Inner1", ValueType.PRIMITIVE, + BigDecimal.TEN.scaleByPowerOfTen(-5))); + Calendar time = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + time.clear(); + time.set(Calendar.HOUR_OF_DAY, 13); + time.set(Calendar.SECOND, 59); + time.set(Calendar.MILLISECOND, 999); + complexValue.getValue().add(new Property("Edm.TimeOfDay", "Inner2", ValueType.PRIMITIVE, time)); + entity.addProperty(new Property("Namespace.ComplexType", "Property2", ValueType.COMPLEX, complexValue)); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + Assert.assertEquals("{\"@context\":\"$metadata#EntitySet(Property1,Property2)\"," + + "\"value\":[{" + + "\"Property1\":1," + + "\"Property2\":{" + + "\"Inner1\":0.00010," + + "\"Inner2\":\"13:00:59.999\"}}]}", + serialize(serializerMin, metadata, null, entityCollection, null)); + } + + @Test + public void entityCollectionWithComplexPropertyMetadataNone() throws Exception { + Entity entity = new Entity(); + entity.setId(null); + entity.addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, 1L)); + ComplexValue complexValue = new ComplexValue(); + complexValue.getValue().add(new Property(null, "Inner1", ValueType.PRIMITIVE, + BigDecimal.TEN.scaleByPowerOfTen(-5))); + Calendar time = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + time.clear(); + time.set(Calendar.HOUR_OF_DAY, 13); + time.set(Calendar.SECOND, 59); + time.set(Calendar.MILLISECOND, 999); + complexValue.getValue().add(new Property("Edm.TimeOfDay", "Inner2", ValueType.PRIMITIVE, time)); + entity.addProperty(new Property("Namespace.ComplexType", "Property2", ValueType.COMPLEX, complexValue)); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + Assert.assertEquals("{" + + "\"value\":[{" + + "\"Property1\":1," + + "\"Property2\":{" + + "\"Inner1\":0.00010," + + "\"Inner2\":\"13:00:59.999\"}}]}", + serialize(serializerNone, metadata, null, entityCollection, null)); + } + + @Test + public void entityCollectionWithComplexCollectionMin() throws Exception { + final EdmEntitySet entitySet = entityContainer.getEntitySet("ESMixPrimCollComp"); + ComplexValue complexValue1 = new ComplexValue(); + complexValue1.getValue().add(new Property(null, "PropertyInt16", ValueType.PRIMITIVE, 1)); + complexValue1.getValue().add(new Property(null, "PropertyString", ValueType.PRIMITIVE, "one")); + ComplexValue complexValue2 = new ComplexValue(); + complexValue2.getValue().add(new Property(null, "PropertyInt16", ValueType.PRIMITIVE, 2)); + complexValue2.getValue().add(new Property(null, "PropertyString", ValueType.PRIMITIVE, "two")); + ComplexValue complexValue3 = new ComplexValue(); + complexValue3.getValue().add(new Property(null, "PropertyInt16", ValueType.PRIMITIVE, 3)); + complexValue3.getValue().add(new Property(null, "PropertyString", ValueType.PRIMITIVE, "three")); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(new Entity() + .addProperty(new Property(null, "CollPropertyComp", ValueType.COLLECTION_COMPLEX, + Arrays.asList(complexValue1, complexValue2, complexValue3)))); + Assert.assertEquals("{\"@context\":\"$metadata#ESMixPrimCollComp(CollPropertyComp)\"," + + "\"value\":[{" + + "\"CollPropertyComp\":[" + + "{\"PropertyInt16\":1,\"PropertyString\":\"one\"}," + + "{\"PropertyInt16\":2,\"PropertyString\":\"two\"}," + + "{\"PropertyInt16\":3,\"PropertyString\":\"three\"}]}]}", + serialize(serializerMin, metadata, entitySet, entityCollection, "CollPropertyComp")); + } + + @Test + public void entityCollectionWithComplexCollectionNone() throws Exception { + final EdmEntitySet entitySet = entityContainer.getEntitySet("ESMixPrimCollComp"); + ComplexValue complexValue1 = new ComplexValue(); + complexValue1.getValue().add(new Property(null, "PropertyInt16", ValueType.PRIMITIVE, 1)); + complexValue1.getValue().add(new Property(null, "PropertyString", ValueType.PRIMITIVE, "one")); + ComplexValue complexValue2 = new ComplexValue(); + complexValue2.getValue().add(new Property(null, "PropertyInt16", ValueType.PRIMITIVE, 2)); + complexValue2.getValue().add(new Property(null, "PropertyString", ValueType.PRIMITIVE, "two")); + ComplexValue complexValue3 = new ComplexValue(); + complexValue3.getValue().add(new Property(null, "PropertyInt16", ValueType.PRIMITIVE, 3)); + complexValue3.getValue().add(new Property(null, "PropertyString", ValueType.PRIMITIVE, "three")); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(new Entity() + .addProperty(new Property(null, "CollPropertyComp", ValueType.COLLECTION_COMPLEX, + Arrays.asList(complexValue1, complexValue2, complexValue3)))); + Assert.assertEquals("{" + + "\"value\":[{" + + "\"CollPropertyComp\":[" + + "{\"PropertyInt16\":1,\"PropertyString\":\"one\"}," + + "{\"PropertyInt16\":2,\"PropertyString\":\"two\"}," + + "{\"PropertyInt16\":3,\"PropertyString\":\"three\"}]}]}", + serialize(serializerNone, metadata, entitySet, entityCollection, "CollPropertyComp")); + } + + @Test + public void entityCollectionWithEmptyCollectionMin() throws Exception { + final EdmEntitySet entitySet = entityContainer.getEntitySet("ESMixPrimCollComp"); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(new Entity() + .addProperty(new Property(null, "CollPropertyString", ValueType.COLLECTION_PRIMITIVE, + Collections.emptyList()))); + Assert.assertEquals( + "{\"@context\":\"$metadata#ESMixPrimCollComp(CollPropertyString)\"," + + "\"value\":[{\"CollPropertyString\":[]}]}", + serialize(serializerMin, metadata, entitySet, entityCollection, "CollPropertyString")); + } + + @Test + public void entityCollectionWithEmptyCollectionNone() throws Exception { + final EdmEntitySet entitySet = entityContainer.getEntitySet("ESMixPrimCollComp"); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(new Entity() + .addProperty(new Property(null, "CollPropertyString", ValueType.COLLECTION_PRIMITIVE, + Collections.emptyList()))); + Assert.assertEquals( + "{" + + "\"value\":[{\"CollPropertyString\":[]}]}", + serialize(serializerNone, metadata, entitySet, entityCollection, "CollPropertyString")); + } + + @Test + public void expandMetadataMin() throws Exception { + final Entity relatedEntity1 = new Entity().addProperty(new Property(null, "Related1", ValueType.PRIMITIVE, 1.5)); + final Entity relatedEntity2 = new Entity().addProperty(new Property(null, "Related1", ValueType.PRIMITIVE, 2.75)); + EntityCollection target = new EntityCollection(); + target.getEntities().add(relatedEntity1); + target.getEntities().add(relatedEntity2); + Link link = new Link(); + link.setTitle("NavigationProperty"); + link.setInlineEntitySet(target); + Entity entity = new Entity(); + entity.setId(null); + entity.addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, (short) 1)); + entity.getNavigationLinks().add(link); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + Assert.assertEquals("{\"@context\":\"$metadata#EntitySet(Property1,NavigationProperty(Related1))\"," + + "\"value\":[{" + + "\"Property1\":1," + + "\"NavigationProperty\":[" + + "{\"Related1\":1.5}," + + "{\"Related1\":2.75}]}]}", + serialize(serializerMin, metadata, null, entityCollection, "Property1,NavigationProperty(Related1)")); + } + + @Test + public void expandMetadataNone() throws Exception { + final Entity relatedEntity1 = new Entity().addProperty(new Property(null, "Related1", ValueType.PRIMITIVE, 1.5)); + final Entity relatedEntity2 = new Entity().addProperty(new Property(null, "Related1", ValueType.PRIMITIVE, 2.75)); + EntityCollection target = new EntityCollection(); + target.getEntities().add(relatedEntity1); + target.getEntities().add(relatedEntity2); + Link link = new Link(); + link.setTitle("NavigationProperty"); + link.setInlineEntitySet(target); + Entity entity = new Entity(); + entity.setId(null); + entity.addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, (short) 1)); + entity.getNavigationLinks().add(link); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + Assert.assertEquals("{" + + "\"value\":[{" + + "\"Property1\":1," + + "\"NavigationProperty\":[" + + "{\"Related1\":1.5}," + + "{\"Related1\":2.75}]}]}", + serialize(serializerNone, metadata, null, entityCollection, "Property1,NavigationProperty(Related1)")); + } + + @Test + public void metadataMin() throws Exception { + final ServiceMetadata metadata = oData.createServiceMetadata(null, Collections.<EdmxReference> emptyList(), + new MetadataETagSupport("W/\"42\"")); + Entity entity = new Entity(); + entity.setType("Namespace.EntityType"); + entity.setId(URI.create("ID")); + entity.setETag("W/\"1000\""); + Link link = new Link(); + link.setHref("editLink"); + entity.setEditLink(link); + entity.setMediaContentSource(URI.create("media")); + entity.addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, + UUID.fromString("12345678-ABCD-1234-CDEF-123456789012"))); + EntityCollection entityCollection = new EntityCollection(); + entityCollection.getEntities().add(entity); + Assert.assertEquals("{\"@context\":\"$metadata#EntitySet(Property1)\"," + + "\"@metadataEtag\":\"W/\\\"42\\\"\",\"value\":[{" + + "\"@etag\":\"W/\\\"1000\\\"\"," + + "\"Property1\":\"12345678-abcd-1234-cdef-123456789012\"," + + "\"@editLink\":\"editLink\"," + + "\"@mediaReadLink\":\"editLink/$value\"}]}", + serialize(serializerMin, metadata, null, entityCollection, null)); + } + + @Test + public void entityCollectionWithBigDecimalProperty() throws Exception { + EntityCollection entityCollection = new EntityCollection(); + BigDecimal b = new BigDecimal(1.666666666666666666666666666666667); + b.abs(new MathContext(0, RoundingMode.UNNECESSARY)); + entityCollection.getEntities().add(new Entity() + .addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, b))); + Assert.assertTrue( + serialize(serializerMin, metadata, null, entityCollection, null) + .contains("1.6666666666666667406815349750104360282421112060546875")); + } +}