Repository: olingo-odata4 Updated Branches: refs/heads/OLINGO-422-SelectExpandSupport 1976c3407 -> b41129cf9
[OLINGO-422] Support for expand in server serializer Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/b41129cf Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/b41129cf Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/b41129cf Branch: refs/heads/OLINGO-422-SelectExpandSupport Commit: b41129cf91401822451e74fe0bff355d9043dc58 Parents: 1976c34 Author: Michael Bolz <[email protected]> Authored: Thu Sep 11 14:05:43 2014 +0200 Committer: Michael Bolz <[email protected]> Committed: Thu Sep 11 14:05:43 2014 +0200 ---------------------------------------------------------------------- .../serializer/json/ODataJsonSerializer.java | 121 ++++++------ .../serializer/utils/ExpandSelectHelper.java | 127 ++++++++++++ .../json/ODataJsonSerializerTest.java | 191 ++++++++++++++++++- 3 files changed, 370 insertions(+), 69 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b41129cf/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java index 665c258..2e4402b 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java @@ -20,7 +20,6 @@ package org.apache.olingo.server.core.serializer.json; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -29,12 +28,14 @@ import org.apache.olingo.commons.api.Constants; import org.apache.olingo.commons.api.data.ContextURL; import org.apache.olingo.commons.api.data.Entity; import org.apache.olingo.commons.api.data.EntitySet; +import org.apache.olingo.commons.api.data.Link; import org.apache.olingo.commons.api.data.LinkedComplexValue; import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.edm.EdmComplexType; import org.apache.olingo.commons.api.edm.EdmEntitySet; import org.apache.olingo.commons.api.edm.EdmEntityType; +import org.apache.olingo.commons.api.edm.EdmNavigationProperty; import org.apache.olingo.commons.api.edm.EdmPrimitiveType; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; @@ -44,12 +45,10 @@ import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory; import org.apache.olingo.server.api.ODataServerError; import org.apache.olingo.server.api.serializer.ODataSerializer; import org.apache.olingo.server.api.serializer.ODataSerializerException; -import org.apache.olingo.server.api.uri.UriResource; -import org.apache.olingo.server.api.uri.UriResourceProperty; import org.apache.olingo.server.api.uri.queryoption.ExpandItem; -import org.apache.olingo.server.api.uri.queryoption.SelectItem; import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer; import org.apache.olingo.server.core.serializer.utils.ContextURLBuilder; +import org.apache.olingo.server.core.serializer.utils.ExpandSelectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -143,11 +142,7 @@ public class ODataJsonSerializer implements ODataSerializer { json.writeNumberField(Constants.JSON_COUNT, entitySet.getCount()); } json.writeFieldName(Constants.VALUE); - json.writeStartArray(); - for (Entity entity : entitySet.getEntities()) { - writeEntity(edmEntitySet, entity, null, options, json); - } - json.writeEndArray(); + writeEntitySet(edmEntitySet.getEntityType(), entitySet, options, json); if (entitySet.getNext() != null) { json.writeStringField(Constants.JSON_NEXT_LINK, entitySet.getNext().toASCIIString()); } @@ -169,7 +164,7 @@ public class ODataJsonSerializer implements ODataSerializer { CircleStreamBuffer buffer = new CircleStreamBuffer(); try { JsonGenerator json = new JsonFactory().createGenerator(buffer.getOutputStream()); - writeEntity(edmEntitySet, entity, contextURL, options, json); + writeEntity(edmEntitySet.getEntityType(), entity, contextURL, options, json); json.close(); } catch (final IOException e) { throw new ODataSerializerException("An I/O exception occurred.", e, @@ -178,9 +173,17 @@ public class ODataJsonSerializer implements ODataSerializer { return buffer.getInputStream(); } - protected void writeEntity(final EdmEntitySet entitySet, final Entity entity, final ContextURL contextURL, + protected void writeEntitySet(final EdmEntityType entityType, final EntitySet entitySet, + final ExpandItem options, final JsonGenerator json) throws IOException, ODataSerializerException { + json.writeStartArray(); + for (final Entity entity : entitySet.getEntities()) { + writeEntity(entityType, entity, null, options, json); + } + json.writeEndArray(); + } + + protected void writeEntity(final EdmEntityType entityType, final Entity entity, final ContextURL contextURL, final ExpandItem options, final JsonGenerator json) throws IOException, ODataSerializerException { - final EdmEntityType entityType = entitySet.getEntityType(); json.writeStartObject(); if (format != ODataFormat.JSON_NO_METADATA) { if (contextURL != null) { @@ -198,67 +201,66 @@ public class ODataJsonSerializer implements ODataSerializer { } } } - final boolean all = isAll(options); - final Set<String> selected = all ? null : getSelectedPropertyNames(options.getSelectOption().getSelectItems()); + writeProperties(entityType, entity, options, json); + writeNavigationProperties(entityType, entity, options, json); + json.writeEndObject(); + } + + protected void writeProperties(final EdmEntityType entityType, final Entity entity, final ExpandItem options, + final JsonGenerator json) throws IOException, ODataSerializerException { + final boolean all = ExpandSelectHelper.isAll(options); + final Set<String> selected = all ? null : + ExpandSelectHelper.getSelectedPropertyNames(options.getSelectOption().getSelectItems()); for (final String propertyName : entityType.getPropertyNames()) { if (all || selected.contains(propertyName)) { final EdmProperty edmProperty = (EdmProperty) entityType.getProperty(propertyName); final Property property = entity.getProperty(propertyName); final Set<List<String>> selectedPaths = all || edmProperty.isPrimitive() ? null : - getSelectedPaths(options.getSelectOption().getSelectItems(), propertyName); + ExpandSelectHelper.getSelectedPaths(options.getSelectOption().getSelectItems(), propertyName); writeProperty(edmProperty, property, selectedPaths, json); } } - json.writeEndObject(); } - private boolean isAll(final ExpandItem options) { - if (options == null || options.getSelectOption() == null - || options.getSelectOption().getSelectItems() == null - || options.getSelectOption().getSelectItems().isEmpty()) { - return true; - } else { - for (final SelectItem item : options.getSelectOption().getSelectItems()) { - if (item.isStar()) { - return true; + protected void writeNavigationProperties(final EdmEntityType entityType, final Entity entity, + final ExpandItem options, final JsonGenerator json) throws ODataSerializerException, IOException { + if (options != null && (options.isRef() || options.getLevelsOption() != null)) { + throw new ODataSerializerException("Expand options $ref and $levels are not supported.", + ODataSerializerException.MessageKeys.NOT_IMPLEMENTED); + } + if (ExpandSelectHelper.hasExpand(options)) { + final boolean expandAll = ExpandSelectHelper.isExpandAll(options); + final Set<String> expanded = expandAll ? null : + ExpandSelectHelper.getExpandedPropertyNames(options.getExpandOption().getExpandItems()); + for (final String propertyName : entityType.getNavigationPropertyNames()) { + if (expandAll || expanded.contains(propertyName)) { + final EdmNavigationProperty property = entityType.getNavigationProperty(propertyName); + final Link navigationLink = entity.getNavigationLink(property.getName()); + final ExpandItem innerOptions = expandAll ? null : + ExpandSelectHelper.getExpandItem(options.getExpandOption().getExpandItems(), propertyName); + writeExpandedNavigationProperty(property, navigationLink, innerOptions, json); } } - return false; } } - private Set<String> getSelectedPropertyNames(final List<SelectItem> selectItems) { - Set<String> selected = new HashSet<String>(); - for (final SelectItem item : selectItems) { - final UriResource resource = item.getResourcePath().getUriResourceParts().get(0); - if (resource instanceof UriResourceProperty) { - selected.add(((UriResourceProperty) resource).getProperty().getName()); + protected void writeExpandedNavigationProperty(final EdmNavigationProperty property, final Link navigationLink, + final ExpandItem innerOptions, JsonGenerator json) throws IOException, ODataSerializerException { + json.writeFieldName(property.getName()); + if (property.isCollection()) { + if (navigationLink == null || navigationLink.getInlineEntitySet() == null) { + json.writeStartArray(); + json.writeEndArray(); + } else { + writeEntitySet(property.getType(), navigationLink.getInlineEntitySet(), innerOptions, json); } - } - return selected; - } - - private Set<List<String>> getSelectedPaths(final List<SelectItem> selectItems, final String propertyName) { - Set<List<String>> selectedPaths = new HashSet<List<String>>(); - for (final SelectItem item : selectItems) { - final List<UriResource> parts = item.getResourcePath().getUriResourceParts(); - final UriResource resource = parts.get(0); - if (resource instanceof UriResourceProperty - && propertyName.equals(((UriResourceProperty) resource).getProperty().getName())) { - if (parts.size() > 1) { - List<String> path = new ArrayList<String>(); - for (final UriResource part : parts.subList(1, parts.size())) { - if (part instanceof UriResourceProperty) { - path.add(((UriResourceProperty) part).getProperty().getName()); - } - } - selectedPaths.add(path); - } else { - return null; - } + } else { + if (navigationLink == null || navigationLink.getInlineEntity() == null) { + json.writeNull(); + } else { + writeEntity(property.getType(), navigationLink.getInlineEntity(), null, innerOptions, json); } } - return selectedPaths.isEmpty() ? null : selectedPaths; } protected void writeProperty(final EdmProperty edmProperty, final Property property, @@ -338,8 +340,7 @@ public class ODataJsonSerializer implements ODataSerializer { } protected void writePrimitiveValue(final EdmProperty edmProperty, final Object primitiveValue, - final JsonGenerator json) - throws EdmPrimitiveTypeException, IOException { + final JsonGenerator json) throws EdmPrimitiveTypeException, IOException { final EdmPrimitiveType type = (EdmPrimitiveType) edmProperty.getType(); final String value = type.valueToString(primitiveValue, edmProperty.isNullable(), edmProperty.getMaxLength(), @@ -360,7 +361,7 @@ public class ODataJsonSerializer implements ODataSerializer { } } - private void writeComplexValue(final EdmProperty edmProperty, final List<Property> properties, + protected void writeComplexValue(final EdmProperty edmProperty, final List<Property> properties, final Set<List<String>> selectedPaths, JsonGenerator json) throws IOException, EdmPrimitiveTypeException, ODataSerializerException { final EdmComplexType type = (EdmComplexType) edmProperty.getType(); @@ -385,7 +386,7 @@ public class ODataJsonSerializer implements ODataSerializer { return null; } - private boolean isSelected(final Set<List<String>> selectedPaths, final String propertyName) { + private static boolean isSelected(final Set<List<String>> selectedPaths, final String propertyName) { for (final List<String> path : selectedPaths) { if (propertyName.equals(path.get(0))) { return true; @@ -394,7 +395,7 @@ public class ODataJsonSerializer implements ODataSerializer { return false; } - private Set<List<String>> getReducedSelectedPaths(final Set<List<String>> selectedPaths, + private static Set<List<String>> getReducedSelectedPaths(final Set<List<String>> selectedPaths, final String propertyName) { Set<List<String>> reducedPaths = new HashSet<List<String>>(); for (final List<String> path : selectedPaths) { http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b41129cf/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ExpandSelectHelper.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ExpandSelectHelper.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ExpandSelectHelper.java new file mode 100644 index 0000000..09bb50d --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ExpandSelectHelper.java @@ -0,0 +1,127 @@ +/* + * 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.utils; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.olingo.server.api.serializer.ODataSerializerException; +import org.apache.olingo.server.api.uri.UriResource; +import org.apache.olingo.server.api.uri.UriResourceNavigation; +import org.apache.olingo.server.api.uri.UriResourceProperty; +import org.apache.olingo.server.api.uri.queryoption.ExpandItem; +import org.apache.olingo.server.api.uri.queryoption.SelectItem; + +public abstract class ExpandSelectHelper { + + public static boolean isAll(final ExpandItem options) { + if (options == null || options.getSelectOption() == null + || options.getSelectOption().getSelectItems() == null + || options.getSelectOption().getSelectItems().isEmpty()) { + return true; + } else { + for (final SelectItem item : options.getSelectOption().getSelectItems()) { + if (item.isStar()) { + return true; + } + } + return false; + } + } + + public static Set<String> getSelectedPropertyNames(final List<SelectItem> selectItems) { + Set<String> selected = new HashSet<String>(); + for (final SelectItem item : selectItems) { + final UriResource resource = item.getResourcePath().getUriResourceParts().get(0); + if (resource instanceof UriResourceProperty) { + selected.add(((UriResourceProperty) resource).getProperty().getName()); + } + } + return selected; + } + + public static Set<List<String>> getSelectedPaths(final List<SelectItem> selectItems, final String propertyName) { + Set<List<String>> selectedPaths = new HashSet<List<String>>(); + for (final SelectItem item : selectItems) { + final List<UriResource> parts = item.getResourcePath().getUriResourceParts(); + final UriResource resource = parts.get(0); + if (resource instanceof UriResourceProperty + && propertyName.equals(((UriResourceProperty) resource).getProperty().getName())) { + if (parts.size() > 1) { + List<String> path = new ArrayList<String>(); + for (final UriResource part : parts.subList(1, parts.size())) { + if (part instanceof UriResourceProperty) { + path.add(((UriResourceProperty) part).getProperty().getName()); + } + } + selectedPaths.add(path); + } else { + return null; + } + } + } + return selectedPaths.isEmpty() ? null : selectedPaths; + } + + public static boolean hasExpand(final ExpandItem options) { + return options != null && options.getExpandOption() != null && options.getExpandOption().getExpandItems() != null + && !options.getExpandOption().getExpandItems().isEmpty(); + } + + public static boolean isExpandAll(final ExpandItem options) { + for (final ExpandItem item : options.getExpandOption().getExpandItems()) { + if (item.isStar()) { + return true; + } + } + return false; + } + + public static Set<String> getExpandedPropertyNames(final List<ExpandItem> expandItems) + throws ODataSerializerException { + Set<String> expanded = new HashSet<String>(); + for (final ExpandItem item : expandItems) { + final List<UriResource> resourceParts = item.getResourcePath().getUriResourceParts(); + if (resourceParts.size() == 1) { + final UriResource resource = resourceParts.get(0); + if (resource instanceof UriResourceNavigation) { + expanded.add(((UriResourceNavigation) resource).getProperty().getName()); + } + } else { + throw new ODataSerializerException("Expand is not supported within complex properties.", + ODataSerializerException.MessageKeys.NOT_IMPLEMENTED); + } + } + return expanded; + } + + public static ExpandItem getExpandItem(final List<ExpandItem> expandItems, final String propertyName) { + for (final ExpandItem item : expandItems) { + final UriResource resource = item.getResourcePath().getUriResourceParts().get(0); + if (resource instanceof UriResourceNavigation + && propertyName.equals(((UriResourceNavigation) resource).getProperty().getName())) { + return item; + } + } + return null; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b41129cf/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java index e33e1dd..66363ae 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java @@ -31,19 +31,24 @@ import org.apache.olingo.commons.api.data.Entity; import org.apache.olingo.commons.api.data.EntitySet; import org.apache.olingo.commons.api.data.ValueType; import org.apache.olingo.commons.api.edm.Edm; +import org.apache.olingo.commons.api.edm.EdmElement; import org.apache.olingo.commons.api.edm.EdmEntityContainer; import org.apache.olingo.commons.api.edm.EdmEntitySet; +import org.apache.olingo.commons.api.edm.EdmNavigationProperty; import org.apache.olingo.commons.api.edm.EdmProperty; import org.apache.olingo.commons.api.edm.EdmStructuredType; import org.apache.olingo.commons.api.edm.FullQualifiedName; +import org.apache.olingo.commons.api.edm.constants.EdmTypeKind; import org.apache.olingo.commons.api.format.ODataFormat; import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.serializer.ODataSerializer; import org.apache.olingo.server.api.serializer.ODataSerializerException; import org.apache.olingo.server.api.uri.UriInfoResource; import org.apache.olingo.server.api.uri.UriResource; +import org.apache.olingo.server.api.uri.UriResourceNavigation; import org.apache.olingo.server.api.uri.UriResourceProperty; import org.apache.olingo.server.api.uri.queryoption.ExpandItem; +import org.apache.olingo.server.api.uri.queryoption.ExpandOption; import org.apache.olingo.server.api.uri.queryoption.SelectItem; import org.apache.olingo.server.api.uri.queryoption.SelectOption; import org.apache.olingo.server.tecsvc.data.DataProvider; @@ -55,11 +60,11 @@ import org.mockito.Mockito; public class ODataJsonSerializerTest { - private final Edm edm = OData.newInstance().createEdm(new EdmTechProvider()); - private final EdmEntityContainer entityContainer = edm.getEntityContainer( + private static final Edm edm = OData.newInstance().createEdm(new EdmTechProvider()); + private static final EdmEntityContainer entityContainer = edm.getEntityContainer( new FullQualifiedName("olingo.odata.test1", "Container")); private final DataProvider data = new DataProvider(); - private ODataSerializer serializer = new ODataJsonSerializer(ODataFormat.JSON); + private final ODataSerializer serializer = new ODataJsonSerializer(ODataFormat.JSON); @Test public void entitySimple() throws Exception { @@ -399,18 +404,173 @@ public class ODataJsonSerializerTest { resultString); } - private SelectItem mockSelectItem(final EdmEntitySet edmEntitySet, final String... names) { + @Test + public void expand() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESTwoPrim"); + final Entity entity = data.readAll(edmEntitySet).getEntities().get(3); + final ExpandOption expand = mockExpandOption(Arrays.asList( + mockExpandItem(edmEntitySet, "NavPropertyETAllPrimOne"))); + ExpandItem options = Mockito.mock(ExpandItem.class); + Mockito.when(options.getExpandOption()).thenReturn(expand); + InputStream result = serializer.entity(edmEntitySet, entity, + ContextURL.Builder.create().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build(), + options); + final String resultString = IOUtils.toString(result); + Assert.assertEquals("{\"@odata.context\":\"$metadata#ESTwoPrim/$entity\"," + + "\"PropertyInt16\":32767,\"PropertyString\":\"Test String4\"," + + "\"NavPropertyETAllPrimOne\":{" + + "\"PropertyInt16\":32767," + + "\"PropertyString\":\"First Resource - positive values\"," + + "\"PropertyBoolean\":true," + + "\"PropertyByte\":255," + + "\"PropertySByte\":127," + + "\"PropertyInt32\":2147483647," + + "\"PropertyInt64\":9223372036854775807," + + "\"PropertySingle\":1.79E20," + + "\"PropertyDouble\":-1.79E19," + + "\"PropertyDecimal\":34," + + "\"PropertyBinary\":\"ASNFZ4mrze8=\"," + + "\"PropertyDate\":\"2012-12-03\"," + + "\"PropertyDateTimeOffset\":\"2012-12-03T07:16:23Z\"," + + "\"PropertyDuration\":\"PT6S\"," + + "\"PropertyGuid\":\"01234567-89ab-cdef-0123-456789abcdef\"," + + "\"PropertyTimeOfDay\":\"03:26:05\"}}", + resultString); + } + + @Test + public void expandSelect() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESTwoPrim"); + final Entity entity = data.readAll(edmEntitySet).getEntities().get(3); + final SelectOption select = mockSelectOption(Arrays.asList( + mockSelectItem(entityContainer.getEntitySet("ESAllPrim"), "PropertyDate"))); + ExpandItem expandItem = mockExpandItem(edmEntitySet, "NavPropertyETAllPrimOne"); + Mockito.when(expandItem.getSelectOption()).thenReturn(select); + final ExpandOption expand = mockExpandOption(Arrays.asList(expandItem)); + ExpandItem options = Mockito.mock(ExpandItem.class); + Mockito.when(options.getExpandOption()).thenReturn(expand); + InputStream result = + new ODataJsonSerializer(ODataFormat.JSON_NO_METADATA) // serializer + .entity(edmEntitySet, entity, + null, // ContextURL.Builder.create().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build(), + options); + final String resultString = IOUtils.toString(result); + Assert.assertEquals("{" + // + "\"@odata.context\":\"$metadata#ESTwoPrim(NavPropertyETAllPrimOne(PropertyDate))/$entity\"," + + "\"PropertyInt16\":32767,\"PropertyString\":\"Test String4\"," + + "\"NavPropertyETAllPrimOne\":{\"PropertyDate\":\"2012-12-03\"}}", + resultString); + } + + @Test + public void expandAll() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim"); + final Entity entity = data.readAll(edmEntitySet).getEntities().get(0); + final ExpandItem expandItem = mockExpandItem(edmEntitySet, "NavPropertyETTwoPrimOne"); + ExpandItem expandItemAll = Mockito.mock(ExpandItem.class); + Mockito.when(expandItemAll.isStar()).thenReturn(true); + final ExpandOption expand = mockExpandOption(Arrays.asList(expandItem, expandItem, expandItemAll)); + final SelectOption select = mockSelectOption(Arrays.asList(mockSelectItem(edmEntitySet, "PropertySByte"))); + ExpandItem options = Mockito.mock(ExpandItem.class); + Mockito.when(options.getExpandOption()).thenReturn(expand); + Mockito.when(options.getSelectOption()).thenReturn(select); + InputStream result = + new ODataJsonSerializer(ODataFormat.JSON_NO_METADATA) // serializer + .entity(edmEntitySet, entity, + null, // ContextURL.Builder.create().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build(), + options); + final String resultString = IOUtils.toString(result); + Assert.assertEquals("{" + // + "\"@odata.context\":\"$metadata#ESAllPrim(PropertySByte)/$entity\"," + + "\"PropertySByte\":127," + + "\"NavPropertyETTwoPrimOne\":{\"PropertyInt16\":32767,\"PropertyString\":\"Test String4\"}," + + "\"NavPropertyETTwoPrimMany\":[{\"PropertyInt16\":-365,\"PropertyString\":\"Test String2\"}]}", + resultString); + } + + @Test + public void expandNoData() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim"); + final Entity entity = data.readAll(edmEntitySet).getEntities().get(1); + ExpandItem expandItemAll = Mockito.mock(ExpandItem.class); + Mockito.when(expandItemAll.isStar()).thenReturn(true); + final ExpandOption expand = mockExpandOption(Arrays.asList(expandItemAll)); + final SelectOption select = mockSelectOption(Arrays.asList(mockSelectItem(edmEntitySet, "PropertyTimeOfDay"))); + ExpandItem options = Mockito.mock(ExpandItem.class); + Mockito.when(options.getExpandOption()).thenReturn(expand); + Mockito.when(options.getSelectOption()).thenReturn(select); + InputStream result = + new ODataJsonSerializer(ODataFormat.JSON_NO_METADATA) // serializer + .entity(edmEntitySet, entity, + null, // ContextURL.Builder.create().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build(), + options); + final String resultString = IOUtils.toString(result); + Assert.assertEquals("{" + // + "\"@odata.context\":\"$metadata#ESAllPrim(PropertyTimeOfDay)/$entity\"," + + "\"PropertyTimeOfDay\":\"23:49:14\"," + + "\"NavPropertyETTwoPrimOne\":null,\"NavPropertyETTwoPrimMany\":[]}", + resultString); + } + + @Test + public void expandTwoLevels() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESTwoPrim"); + final EdmEntitySet innerEntitySet = entityContainer.getEntitySet("ESAllPrim"); + final Entity entity = data.readAll(edmEntitySet).getEntities().get(1); + ExpandItem expandItemSecond = Mockito.mock(ExpandItem.class); + Mockito.when(expandItemSecond.isStar()).thenReturn(true); + final ExpandOption expandInner = mockExpandOption(Arrays.asList(expandItemSecond)); + ExpandItem expandItemFirst = mockExpandItem(edmEntitySet, "NavPropertyETAllPrimMany"); + Mockito.when(expandItemFirst.getExpandOption()).thenReturn(expandInner); + final SelectOption select = mockSelectOption(Arrays.asList( + mockSelectItem(innerEntitySet, "PropertyInt32"))); + Mockito.when(expandItemFirst.getSelectOption()).thenReturn(select); + final ExpandOption expand = mockExpandOption(Arrays.asList(expandItemFirst)); + ExpandItem options = Mockito.mock(ExpandItem.class); + Mockito.when(options.getExpandOption()).thenReturn(expand); + InputStream result = + new ODataJsonSerializer(ODataFormat.JSON_NO_METADATA) // serializer + .entity(edmEntitySet, entity, + null, // ContextURL.Builder.create().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build(), + options); + final String resultString = IOUtils.toString(result); + Assert.assertEquals("{" + // + "\"@odata.context\":\"$metadata#ESTwoPrim(NavPropertyETAllPrimMany(PropertyInt32))/$entity\"," + + "\"PropertyInt16\":-365,\"PropertyString\":\"Test String2\"," + + "\"NavPropertyETAllPrimMany\":[" + + "{\"PropertyInt32\":-2147483648,\"NavPropertyETTwoPrimOne\":null,\"NavPropertyETTwoPrimMany\":[]}," + + "{\"PropertyInt32\":0,\"NavPropertyETTwoPrimOne\":null," + + "\"NavPropertyETTwoPrimMany\":[" + + "{\"PropertyInt16\":32766,\"PropertyString\":\"Test String1\"}," + + "{\"PropertyInt16\":-32766,\"PropertyString\":\"Test String3\"}," + + "{\"PropertyInt16\":32767,\"PropertyString\":\"Test String4\"}]}]}", + resultString); + } + + private UriInfoResource mockResource(final EdmEntitySet edmEntitySet, final String... names) { EdmStructuredType type = edmEntitySet.getEntityType(); List<UriResource> elements = new ArrayList<UriResource>(); for (final String name : Arrays.asList(names)) { - UriResourceProperty element = Mockito.mock(UriResourceProperty.class); - final EdmProperty property = (EdmProperty) type.getProperty(name); - Mockito.when(element.getProperty()).thenReturn(property); - elements.add(element); - type = property.isPrimitive() ? null : (EdmStructuredType) property.getType(); + final EdmElement edmElement = type.getProperty(name); + if (edmElement.getType().getKind() == EdmTypeKind.ENTITY) { + UriResourceNavigation element = Mockito.mock(UriResourceNavigation.class); + Mockito.when(element.getProperty()).thenReturn((EdmNavigationProperty) edmElement); + elements.add(element); + } else { + final EdmProperty property = (EdmProperty) edmElement; + UriResourceProperty element = Mockito.mock(UriResourceProperty.class); + Mockito.when(element.getProperty()).thenReturn(property); + elements.add(element); + type = property.isPrimitive() ? null : (EdmStructuredType) property.getType(); + } } UriInfoResource resource = Mockito.mock(UriInfoResource.class); Mockito.when(resource.getUriResourceParts()).thenReturn(elements); + return resource; + } + + private SelectItem mockSelectItem(final EdmEntitySet edmEntitySet, final String... names) { + final UriInfoResource resource = mockResource(edmEntitySet, names); SelectItem selectItem = Mockito.mock(SelectItem.class); Mockito.when(selectItem.getResourcePath()).thenReturn(resource); return selectItem; @@ -421,4 +581,17 @@ public class ODataJsonSerializerTest { Mockito.when(select.getSelectItems()).thenReturn(selectItems); return select; } + + private ExpandItem mockExpandItem(final EdmEntitySet edmEntitySet, final String... names) { + final UriInfoResource resource = mockResource(edmEntitySet, names); + ExpandItem expandItem = Mockito.mock(ExpandItem.class); + Mockito.when(expandItem.getResourcePath()).thenReturn(resource); + return expandItem; + } + + private ExpandOption mockExpandOption(final List<ExpandItem> expandItems) { + ExpandOption expand = Mockito.mock(ExpandOption.class); + Mockito.when(expand.getExpandItems()).thenReturn(expandItems); + return expand; + } }
