You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2015/04/07 08:40:59 UTC
[1/3] olingo-odata4 git commit: [OLINGO-545] TecSvc: Request
validation added
Repository: olingo-odata4
Updated Branches:
refs/heads/master 97a017843 -> d4c2b89e4
[OLINGO-545] TecSvc: Request validation 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/583c4bd0
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/583c4bd0
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/583c4bd0
Branch: refs/heads/master
Commit: 583c4bd07886ce38dae7fa41bcfbd8404d1b6abf
Parents: 97a0178
Author: Christian Holzer <c....@sap.com>
Authored: Mon Apr 6 08:25:00 2015 +0200
Committer: Christian Holzer <c....@sap.com>
Committed: Tue Apr 7 08:25:43 2015 +0200
----------------------------------------------------------------------
.../olingo/fit/tecsvc/client/BasicITCase.java | 38 +++
.../olingo/fit/tecsvc/client/BindingITCase.java | 35 ++-
.../fit/tecsvc/client/DeepInsertITCase.java | 176 ++++++++++++-
.../tecsvc/client/FilterSystemQueryITCase.java | 25 +-
.../olingo/server/tecsvc/data/DataProvider.java | 26 +-
.../server/tecsvc/data/RequestValidator.java | 255 +++++++++++++++++++
.../processor/TechnicalEntityProcessor.java | 27 +-
7 files changed, 548 insertions(+), 34 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/583c4bd0/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java
index b7bac00..5b8949a 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java
@@ -30,8 +30,10 @@ import static org.junit.Assert.fail;
import java.net.URI;
import java.util.Collections;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
+import org.apache.olingo.client.api.EdmEnabledODataClient;
import org.apache.olingo.client.api.ODataClient;
import org.apache.olingo.client.api.communication.ODataClientErrorException;
import org.apache.olingo.client.api.communication.request.cud.ODataDeleteRequest;
@@ -266,6 +268,7 @@ public class BasicITCase extends AbstractBaseTestITCase {
ODataEntity newEntity = factory.newEntity(new FullQualifiedName("olingo.odata.test1", "ETAllPrim"));
newEntity.getProperties().add(factory.newPrimitiveProperty("PropertyInt64",
factory.newPrimitiveValueBuilder().buildInt32(42)));
+
final URI uri = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim").appendKeySegment(32767)
.build();
final ODataEntityUpdateRequest<ODataEntity> request = client.getCUDRequestFactory().getEntityUpdateRequest(
@@ -373,6 +376,12 @@ public class BasicITCase extends AbstractBaseTestITCase {
ODataEntity newEntity = factory.newEntity(new FullQualifiedName("olingo.odata.test1", "ETAllPrim"));
newEntity.getProperties().add(factory.newPrimitiveProperty("PropertyInt64",
factory.newPrimitiveValueBuilder().buildInt32(42)));
+ newEntity.addLink(factory.newEntityNavigationLink("NavPropertyETTwoPrimOne",
+ client.newURIBuilder(SERVICE_URI)
+ .appendEntitySetSegment("ESTwoPrim")
+ .appendKeySegment(32766)
+ .build()));
+
final ODataEntityCreateRequest<ODataEntity> createRequest = client.getCUDRequestFactory().getEntityCreateRequest(
client.newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim").build(),
newEntity);
@@ -518,6 +527,18 @@ public class BasicITCase extends AbstractBaseTestITCase {
.add(of.newPrimitiveProperty("PropertyInt16", of.newPrimitiveValueBuilder().buildInt16((short) 2)))
.add(of.newPrimitiveProperty("PropertySingle", of.newPrimitiveValueBuilder().buildSingle(2.0f))))))));
+ entity.addLink(of.newEntityNavigationLink("NavPropertyETTwoKeyNavOne",
+ getClient().newURIBuilder(SERVICE_URI)
+ .appendEntitySetSegment("ESTwoKeyNav")
+ .appendKeySegment(new LinkedHashMap<String, Object>() {
+ private static final long serialVersionUID = 1L;
+
+ {
+ put("PropertyInt16", 1);
+ put("PropertyString", "1");
+ }
+ }).build()));
+
final ODataEntityCreateResponse<ODataEntity> response = getClient().getCUDRequestFactory().getEntityCreateRequest(
getClient().newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESKeyNav").build(),
entity).execute();
@@ -576,6 +597,23 @@ public class BasicITCase extends AbstractBaseTestITCase {
assertNull(innerComplexProperty2.get("NotAvailableProperty"));
}
+ @Test
+ public void testComplexPropertyWithNotNullablePrimitiveValue() {
+ final EdmEnabledODataClient client = ODataClientFactory.getEdmEnabledClient(SERVICE_URI);
+ final ODataObjectFactory of = client.getObjectFactory();
+
+ // PropertyComp is null, but the primitive values in PropertyComp must not be null
+ final ODataEntity entity = of.newEntity(new FullQualifiedName("olingo.odata.test1", "ETMixPrimCollComp"));
+ final URI targetURI = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESMixPrimCollComp").build();
+
+ try {
+ client.getCUDRequestFactory().getEntityCreateRequest(targetURI, entity).execute();
+ fail("Expecting bad request");
+ } catch (ODataClientErrorException e) {
+ assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), e.getStatusLine().getStatusCode());
+ }
+ }
+
@Override
protected ODataClient getClient() {
ODataClient odata = ODataClientFactory.getClient();
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/583c4bd0/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BindingITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BindingITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BindingITCase.java
index 75cadd0..74b7718 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BindingITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BindingITCase.java
@@ -24,6 +24,7 @@ import static org.junit.Assert.fail;
import java.net.URI;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import org.apache.olingo.client.api.EdmEnabledODataClient;
import org.apache.olingo.client.api.ODataClient;
@@ -69,9 +70,9 @@ public class BindingITCase extends AbstractBaseTestITCase {
private static final String PROPERTY_COMP_ALL_PRIM = "PropertyCompAllPrim";
private static final String NAV_PROPERTY_ET_KEY_NAV_ONE = "NavPropertyETKeyNavOne";
private static final String NAV_PROPERTY_ET_KEY_NAV_MANY = "NavPropertyETKeyNavMany";
+ private static final String NAV_PROPERTY_ET_TWO_KEY_NAV_ONE = "NavPropertyETTwoKeyNavOne";
private static final String NAV_PROPERTY_ET_TWO_KEY_NAV_MANY = "NavPropertyETTwoKeyNavMany";
-
@Test
public void testCreateBindingSimple() throws EdmPrimitiveTypeException {
final ODataClient client = getClient();
@@ -101,6 +102,17 @@ public class BindingITCase extends AbstractBaseTestITCase {
.add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 42)))))));
// Bind existing entities via binding synatx
+ entity.addLink(of.newEntityNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE,
+ client.newURIBuilder(SERVICE_URI)
+ .appendEntitySetSegment(ES_TWO_KEY_NAV)
+ .appendKeySegment(new LinkedHashMap<String, Object>() {
+ private static final long serialVersionUID = 3109256773218160485L;
+ {
+ put(PROPERTY_INT16, 3);
+ put(PROPERTY_STRING, "1");
+ }
+ }).build()));
+
final ODataLink navLinkOne =
of.newEntityNavigationLink(NAV_PROPERTY_ET_KEY_NAV_ONE, client.newURIBuilder(SERVICE_URI)
.appendEntitySetSegment(
@@ -331,10 +343,31 @@ public class BindingITCase extends AbstractBaseTestITCase {
innerEntity.getProperties().add(of.newComplexProperty(PROPERTY_COMP_TWO_PRIM, of.newComplexValue(CT_TWO_PRIM)
.add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 1)))
.add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString("2")))));
+ innerEntity.addLink(of.newEntityNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE,
+ client.newURIBuilder(SERVICE_URI)
+ .appendEntitySetSegment(ES_TWO_KEY_NAV)
+ .appendKeySegment(new LinkedHashMap<String, Object>() {
+ private static final long serialVersionUID = 3109256773218160485L;
+ {
+ put(PROPERTY_INT16, 3);
+ put(PROPERTY_STRING, "1");
+ }
+ }).build()));
final ODataInlineEntity inlineLink = of.newDeepInsertEntity(NAV_PROPERTY_ET_KEY_NAV_ONE, innerEntity);
entity.addLink(inlineLink);
+ entity.addLink(of.newEntityNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE,
+ client.newURIBuilder(SERVICE_URI)
+ .appendEntitySetSegment(ES_TWO_KEY_NAV)
+ .appendKeySegment(new LinkedHashMap<String, Object>() {
+ private static final long serialVersionUID = 3109256773218160485L;
+ {
+ put(PROPERTY_INT16, 3);
+ put(PROPERTY_STRING, "1");
+ }
+ }).build()));
+
final URI bindingURI = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_KEY_NAV)
.appendKeySegment(3)
.build();
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/583c4bd0/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
index ba33e3f..5255712 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
@@ -20,6 +20,7 @@ package org.apache.olingo.fit.tecsvc.client;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
import java.net.URI;
import java.util.HashMap;
@@ -27,8 +28,12 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
+import org.apache.olingo.client.api.EdmEnabledODataClient;
import org.apache.olingo.client.api.ODataClient;
+import org.apache.olingo.client.api.communication.ODataClientErrorException;
+import org.apache.olingo.client.api.communication.request.cud.ODataEntityCreateRequest;
import org.apache.olingo.client.api.communication.request.retrieve.ODataEntityRequest;
+import org.apache.olingo.client.api.communication.request.retrieve.ODataEntitySetRequest;
import org.apache.olingo.client.api.communication.response.ODataEntityCreateResponse;
import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
import org.apache.olingo.client.core.ODataClientFactory;
@@ -62,8 +67,10 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
private static final String CT_TWO_PRIM = "CTTwoPrim";
private static final String CT_ALL_PRIM = "CTAllPrim";
private static final String CT_NAV_FIVE_PROP = "CTNavFiveProp";
+ private static final String CT_BASE_PRIM_COMP_NAV = "CTBasePrimCompNav";
private static final String PROPERTY_INT16 = "PropertyInt16";
private static final String PROPERTY_STRING = "PropertyString";
+ private static final String PROPERTY_COMP = "PropertyComp";
private static final String PROPERTY_COMP_NAV = "PropertyCompNav";
private static final String PROPERTY_COMP_COMP_NAV = "PropertyCompCompNav";
private static final String PROPERTY_COMP_TWO_PRIM = "PropertyCompTwoPrim";
@@ -71,6 +78,8 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
private static final String NAV_PROPERTY_ET_KEY_NAV_ONE = "NavPropertyETKeyNavOne";
private static final String NAV_PROPERTY_ET_TWO_KEY_NAV_ONE = "NavPropertyETTwoKeyNavOne";
private static final String NAV_PROPERTY_ET_TWO_KEY_NAV_MANY = "NavPropertyETTwoKeyNavMany";
+ private static final String COL_PROPERTY_STRING = "CollPropertyString";
+ private static final String EDM_STRING = "Edm.String";
@Test
public void testDeepInsertExpandedResponse() {
@@ -96,6 +105,10 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
.add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 42)))
.add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString(
"String Property level 1, complex level 1")))));
+ firstLevelTwoKeyNav.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
+ firstLevelTwoKeyNav.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_BASE_PRIM_COMP_NAV)));
final ODataInlineEntity firstLevelTwoKeyOneInline =
of.newDeepInsertEntity(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE, firstLevelTwoKeyNav);
entity.addLink(firstLevelTwoKeyOneInline);
@@ -107,7 +120,11 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
.add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 421)))
.add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString(
"String Property level 2, complex level 1")))));
-
+ secondLevelTwoKeyNav.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
+ secondLevelTwoKeyNav.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_BASE_PRIM_COMP_NAV)));
+
// Binding links
secondLevelTwoKeyNav.addLink(of.newEntityNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE, client.newURIBuilder(
SERVICE_URI).appendEntitySetSegment(ES_TWO_KEY_NAV).appendKeySegment(new LinkedHashMap<String, Object>() {
@@ -129,14 +146,22 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
.add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 431)))
.add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString(
"String Property level 3, complex level 1")))));
-
+ thirdLevelTwoKeyNavMany1.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
+ thirdLevelTwoKeyNavMany1.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_BASE_PRIM_COMP_NAV)));
+
final ODataEntity thirdLevelTwoKeyNavMany2 = of.newEntity(ET_TWO_KEY_NAV);
thirdLevelTwoKeyNavMany2.getProperties().add(
of.newComplexProperty(PROPERTY_COMP_TWO_PRIM, of.newComplexValue(CT_TWO_PRIM)
.add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 432)))
.add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString(
"String Property level 3, complex level 1")))));
-
+ thirdLevelTwoKeyNavMany2.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
+ thirdLevelTwoKeyNavMany2.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_BASE_PRIM_COMP_NAV)));
+
final ODataEntitySet entitySetThirdLevelTwoKeyNavMany = of.newEntitySet();
entitySetThirdLevelTwoKeyNavMany.getEntities().add(thirdLevelTwoKeyNavMany1);
entitySetThirdLevelTwoKeyNavMany.getEntities().add(thirdLevelTwoKeyNavMany2);
@@ -150,7 +175,11 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
.add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 422)))
.add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString(
"String Property level 1, complex level 1")))));
-
+ firstLevelTwoKeyNavMany1.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
+ firstLevelTwoKeyNavMany1.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_BASE_PRIM_COMP_NAV)));
+
final ODataEntitySet entitySetfirstLevelTwoKeyNavMany = of.newEntitySet();
entitySetfirstLevelTwoKeyNavMany.getEntities().add(firstLevelTwoKeyNavMany1);
entity.addLink(of.newDeepInsertEntitySet(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY,
@@ -172,7 +201,8 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
resultEntityFirstLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE).asInlineEntity().getEntity();
assertEquals(421, resultEntitySecondLevel.getProperty(PROPERTY_COMP_TWO_PRIM).getComplexValue().get(PROPERTY_INT16)
.getPrimitiveValue().toValue());
- assertEquals("String Property level 2, complex level 1", resultEntitySecondLevel.getProperty(PROPERTY_COMP_TWO_PRIM)
+ assertEquals("String Property level 2, complex level 1", resultEntitySecondLevel
+ .getProperty(PROPERTY_COMP_TWO_PRIM)
.getComplexValue().get(PROPERTY_STRING)
.getPrimitiveValue().toValue());
@@ -198,7 +228,7 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
assertEquals("String Property level 1, complex level 1", firstLevelEntitySetNavMany.getEntities().get(0)
.getProperty(PROPERTY_COMP_TWO_PRIM).getComplexValue().get(PROPERTY_STRING).getPrimitiveValue().toValue());
}
-
+
@Test
public void testSimpleDeepInsert() throws EdmPrimitiveTypeException {
final ODataClient client = getClient();
@@ -234,6 +264,8 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
.add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 43)));
inlineEntitySingle.getProperties()
.add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString("43")));
+ inlineEntitySingle.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
inlineEntitySingle.getProperties()
.add(of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_PRIM_COMP)
.add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 431)))));
@@ -253,6 +285,8 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
inlineEntityCol1.getProperties()
.add(of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_PRIM_COMP)
.add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 441)))));
+ inlineEntityCol1.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
inlineEntityCol1.getProperties()
.add(of.newComplexProperty(PROPERTY_COMP_TWO_PRIM, of.newComplexValue(CT_TWO_PRIM)
.add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 442)))
@@ -266,6 +300,8 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
inlineEntityCol2.getProperties()
.add(of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_PRIM_COMP)
.add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 451)))));
+ inlineEntityCol2.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
inlineEntityCol2.getProperties()
.add(of.newComplexProperty(PROPERTY_COMP_TWO_PRIM, of.newComplexValue(CT_TWO_PRIM)
.add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 452)))
@@ -412,6 +448,18 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
.add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString("42")))
.add(of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_NAV_FIVE_PROP)
.add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 42)))))));
+ entity.addLink(of.newEntityNavigationLink("NavPropertyETTwoKeyNavOne",
+ client.newURIBuilder(SERVICE_URI)
+ .appendEntitySetSegment(ES_TWO_KEY_NAV)
+ .appendKeySegment(new LinkedHashMap<String, Object>() {
+ private static final long serialVersionUID = 1L;
+
+ {
+ put(PROPERTY_INT16, 1);
+ put(PROPERTY_STRING, "1");
+ }
+ })
+ .build()));
// Prepare inline entity(EntitySet: ESKeyNav, Type: ETKeyNav)
final ODataEntity innerEntity = of.newEntity(ET_KEY_NAV);
@@ -436,7 +484,19 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
.add(of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_NAV_FIVE_PROP)
.add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder()
.buildInt16((short) 431)))))));
-
+ innerEntity.addLink(of.newEntityNavigationLink("NavPropertyETTwoKeyNavOne",
+ client.newURIBuilder(SERVICE_URI)
+ .appendEntitySetSegment(ES_TWO_KEY_NAV)
+ .appendKeySegment(new LinkedHashMap<String, Object>() {
+ private static final long serialVersionUID = 1L;
+
+ {
+ put(PROPERTY_INT16, 1);
+ put(PROPERTY_STRING, "1");
+ }
+ })
+ .build()));
+
ODataInlineEntity inlineEntity = of.newDeepInsertEntity(NAV_PROPERTY_ET_KEY_NAV_ONE, innerEntity);
entity.addLink(inlineEntity);
@@ -475,6 +535,108 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
.getPrimitiveValue().toValue());
}
+ @Test
+ public void testConsistency() throws EdmPrimitiveTypeException {
+ final EdmEnabledODataClient client = ODataClientFactory.getEdmEnabledClient(SERVICE_URI);
+ final ODataObjectFactory of = client.getObjectFactory();
+ final String cookie = getCookie();
+
+ // Do not set PropertyString(Nullable=false)
+ final ODataEntity entity = of.newEntity(ET_KEY_NAV);
+ entity.getProperties().add(
+ of.newCollectionProperty(COL_PROPERTY_STRING,
+ of.newCollectionValue(EDM_STRING).add(
+ of.newPrimitiveValueBuilder().buildString("Test"))));
+
+ final URI targetURI = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_KEY_NAV).build();
+
+ try {
+ ODataEntityCreateRequest<ODataEntity> request = client.getCUDRequestFactory()
+ .getEntityCreateRequest(targetURI, entity);
+ request.addCustomHeader(HttpHeader.COOKIE, cookie);
+ request.execute();
+ fail("Expecting bad request");
+ } catch (ODataClientErrorException e) {
+ assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), e.getStatusLine().getStatusCode());
+ }
+
+ // Entity must not be created
+ validateSet(targetURI, cookie, (short) 1, (short) 2, (short) 3);
+ }
+
+ @Test
+ public void testInvalidType() throws EdmPrimitiveTypeException {
+ final EdmEnabledODataClient client = ODataClientFactory.getEdmEnabledClient(SERVICE_URI);
+ final ODataObjectFactory of = client.getObjectFactory();
+ final String cookie = getCookie();
+
+ final ODataEntity entity = of.newEntity(ET_KEY_NAV);
+ entity.getProperties().add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildInt32(1)));
+ final URI targetURI = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_KEY_NAV).build();
+
+ try {
+ ODataEntityCreateRequest<ODataEntity> request = client.getCUDRequestFactory()
+ .getEntityCreateRequest(targetURI, entity);
+ request.addCustomHeader(HttpHeader.COOKIE, cookie);
+ request.execute();
+ } catch (ODataClientErrorException e) {
+ assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), e.getStatusLine().getStatusCode());
+ }
+
+ validateSet(targetURI, cookie, (short) 1, (short) 2, (short) 3);
+
+ entity.getProperties().add(
+ of.newCollectionProperty(PROPERTY_STRING,
+ of.newCollectionValue(EDM_STRING).add(
+ of.newPrimitiveValueBuilder().buildString("Test"))));
+
+ try {
+ ODataEntityCreateRequest<ODataEntity> request = client.getCUDRequestFactory()
+ .getEntityCreateRequest(targetURI, entity);
+ request.addCustomHeader(HttpHeader.COOKIE, cookie);
+ request.execute();
+ } catch (ODataClientErrorException e) {
+ assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), e.getStatusLine().getStatusCode());
+ }
+
+ validateSet(targetURI, cookie, (short) 1, (short) 2, (short) 3);
+ }
+
+ private String getCookie() {
+ final EdmEnabledODataClient client = ODataClientFactory.getEdmEnabledClient(SERVICE_URI);
+ final ODataRetrieveResponse<ODataEntitySet> response = client.getRetrieveRequestFactory()
+ .getEntitySetRequest(client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_KEY_NAV).build())
+ .execute();
+
+ return response.getHeader(HttpHeader.SET_COOKIE).iterator().next();
+ }
+
+ private void validateSet(final URI uri, final String cookie, final short... keys) throws EdmPrimitiveTypeException {
+ final EdmEnabledODataClient client = ODataClientFactory.getEdmEnabledClient(SERVICE_URI);
+ final ODataEntitySetRequest<ODataEntitySet> request = client.getRetrieveRequestFactory()
+ .getEntitySetRequest(uri);
+ request.addCustomHeader(HttpHeader.COOKIE, cookie);
+ final ODataRetrieveResponse<ODataEntitySet> response = request.execute();
+
+ assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
+ assertEquals(3, response.getBody().getEntities().size());
+
+ for (final ODataEntity responseEntity : response.getBody().getEntities()) {
+ short propertyInt16 = responseEntity.getProperty(PROPERTY_INT16).getPrimitiveValue().toCastValue(Short.class);
+
+ boolean found = false;
+ for (int i = 0; i < keys.length && !found; i++) {
+ if (propertyInt16 == keys[i]) {
+ found = true;
+ }
+ }
+
+ if (!found) {
+ fail("Invalid key " + propertyInt16);
+ }
+ }
+ }
+
@Override
protected ODataClient getClient() {
ODataClient odata = ODataClientFactory.getClient();
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/583c4bd0/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
index 8cb249d..16529dd 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
@@ -21,6 +21,7 @@ package org.apache.olingo.fit.tecsvc.client;
import static org.junit.Assert.assertEquals;
import java.net.URI;
+import java.util.LinkedHashMap;
import org.apache.olingo.client.api.ODataClient;
import org.apache.olingo.client.api.communication.ODataClientErrorException;
@@ -289,7 +290,12 @@ public class FilterSystemQueryITCase extends AbstractBaseTestITCase {
entity.getProperties().add(
objectFactory.newPrimitiveProperty("PropertyInt16", objectFactory.newPrimitiveValueBuilder()
.buildInt16((short) 1)));
-
+ entity.addLink(objectFactory.newEntityNavigationLink("NavPropertyETTwoPrimOne",
+ client.newURIBuilder(SERVICE_URI)
+ .appendEntitySetSegment("ESTwoPrim")
+ .appendKeySegment(32766)
+ .build()));
+
final URI uri = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim").build();
ODataEntityCreateResponse<ODataEntity> createResponse =
client.getCUDRequestFactory().getEntityCreateRequest(uri, entity).execute();
@@ -921,7 +927,7 @@ public class FilterSystemQueryITCase extends AbstractBaseTestITCase {
@Test
public void testNullComplexProperty() {
// Create a new entry.The complex property PropertyCompComp is set to null. So the structure of the property
- // is still there, but filled is null value (primitive types)
+ // is still there, but filled is null values (primitive types)
// We define a filter, which returns all entry where PropertyCompComp/PropertyComp/PropertyInt16 is equals to 1
final ODataClient client = getClient();
@@ -948,7 +954,20 @@ public class FilterSystemQueryITCase extends AbstractBaseTestITCase {
.add(factory.newPrimitiveProperty(
"PropertyString",
factory.newPrimitiveValueBuilder().buildString("Test2")))));
-
+
+ newEntity.addLink(factory.newEntityNavigationLink("NavPropertyETTwoKeyNavOne",
+ client.newURIBuilder(SERVICE_URI)
+ .appendEntitySetSegment(ES_TWO_KEY_NAV)
+ .appendKeySegment(new LinkedHashMap<String, Object>() {
+ private static final long serialVersionUID = 1L;
+
+ {
+ put("PropertyInt16", 1);
+ put("PropertyString", "1");
+ }
+ })
+ .build()));
+
final URI uri = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESKeyNav").build();
ODataEntityCreateRequest<ODataEntity> request =
client.getCUDRequestFactory().getEntityCreateRequest(uri, newEntity);
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/583c4bd0/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
index 96f0cd7..d47aa46 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
@@ -6,9 +6,9 @@
* 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
@@ -152,7 +152,6 @@ public class DataProvider {
private Map<String, Object> findFreeComposedKey(final List<Entity> entities, final EdmEntityType entityType)
throws DataProviderException {
// Weak key construction
- // 3e € entity: (V k € keys: k !€ e.ki) => e.(k1, k2, k3) !€ entitySet
final HashMap<String, Object> keys = new HashMap<String, Object>();
for (final String keyName : entityType.getKeyPredicateNames()) {
final EdmType type = entityType.getProperty(keyName).getType();
@@ -243,15 +242,15 @@ public class DataProvider {
patch);
}
}
-
- // For insert operations collection navigation property bind operations and deep insert operations can be combined.
+
+ // For insert operations collection navigation property bind operations and deep insert operations can be combined.
// In this case, the bind operations MUST appear before the deep insert operations in the payload.
// => Apply bindings first
final boolean navigationBindingsAvailable = !changedEntity.getNavigationBindings().isEmpty();
if (navigationBindingsAvailable) {
applyNavigationBinding(rawBaseUri, edmEntitySet, entity, changedEntity.getNavigationBindings());
}
-
+
// Deep insert (only if not an update)
if (isInsert) {
handleDeepInsert(rawBaseUri, edmEntitySet, entity, changedEntity);
@@ -311,10 +310,6 @@ public class DataProvider {
.getKeyPredicatesFromEntityLink(edm, bindingLink, rawBaseUri);
final Entity entity = read(edmEntitySetTarget, keys);
- if (entity == null) {
- throw new DataProviderException("Entity " + bindingLink + " not found", HttpStatusCode.NOT_FOUND);
- }
-
return entity;
} catch (DeserializerException e) {
throw new DataProviderException("Invalid entity binding link", HttpStatusCode.BAD_REQUEST);
@@ -400,9 +395,6 @@ public class DataProvider {
if (edmProperty.isPrimitive()) {
if (newProperty != null || !patch) {
final Object value = newProperty == null ? null : newProperty.getValue();
- if (value == null && !edmProperty.isNullable()) {
- throw new DataProviderException("Cannot null non-nullable property!", HttpStatusCode.BAD_REQUEST);
- }
property.setValue(property.getValueType(), value);
}
} else if (edmProperty.isCollection()) {
@@ -440,7 +432,7 @@ public class DataProvider {
private ComplexValue createComplexValue(final EdmProperty edmProperty, final ComplexValue complexValue,
final boolean patch) throws DataProviderException {
final ComplexValueImpl result = new ComplexValueImpl();
- final EdmComplexType edmType = (EdmComplexType) edmProperty.getType();
+ final EdmComplexType edmType = (EdmComplexType) edmProperty.getType();
final List<Property> givenProperties = complexValue.getValue();
// Create ALL properties, even if no value is given. Check if null is allowed
@@ -454,12 +446,10 @@ public class DataProvider {
updateProperty(innerEdmProperty, newProperty, currentProperty, patch);
} else {
if (innerEdmProperty.isNullable()) {
- // Check complex properties ... may be null is not allowed
- if(edmProperty.getType().getKind() == EdmTypeKind.COMPLEX) {
+ // Check complex properties ... maybe null is not allowed
+ if (edmProperty.getType().getKind() == EdmTypeKind.COMPLEX) {
updateProperty(innerEdmProperty, newProperty, null, patch);
}
- } else {
- throw new DataProviderException("Null is not allowed for property " + edmProperty.getName());
}
}
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/583c4bd0/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/RequestValidator.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/RequestValidator.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/RequestValidator.java
new file mode 100644
index 0000000..113b252
--- /dev/null
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/RequestValidator.java
@@ -0,0 +1,255 @@
+/*
+ * 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.tecsvc.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.olingo.commons.api.data.ComplexValue;
+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.Linked;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.edm.Edm;
+import org.apache.olingo.commons.api.edm.EdmBindingTarget;
+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.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmStructuredType;
+import org.apache.olingo.commons.api.http.HttpStatusCode;
+import org.apache.olingo.server.api.deserializer.DeserializerException;
+import org.apache.olingo.server.api.uri.UriHelper;
+import org.apache.olingo.server.api.uri.UriParameter;
+import org.apache.olingo.server.tecsvc.data.DataProvider.DataProviderException;
+
+public class RequestValidator {
+ private DataProvider provider;
+ private boolean isInsert;
+ private UriHelper uriHelper;
+ private Edm edm;
+ private String rawServiceRoot;
+
+ public RequestValidator(final DataProvider provider, final boolean isInsert, final UriHelper uriHelper,
+ final Edm edm, final String rawServiceRoot) {
+ this.provider = provider;
+ this.isInsert = isInsert;
+ this.uriHelper = uriHelper;
+ this.edm = edm;
+ this.rawServiceRoot = rawServiceRoot;
+ }
+
+ public void validate(final EdmBindingTarget edmBindingTarget, final Entity entity)
+ throws DataProviderException {
+ final List<String> path = new ArrayList<String>();
+
+ validateEntitySetProperties(entity.getProperties(), edmBindingTarget, edmBindingTarget.getEntityType(), path);
+ validateNavigationProperties(entity, edmBindingTarget, edmBindingTarget.getEntityType(), path);
+ }
+
+ private void validateNavigationProperties(final Linked entity, final EdmBindingTarget edmBindingTarget,
+ final EdmStructuredType edmType, final List<String> path) throws DataProviderException {
+ for (final String navPropertyName : edmType.getNavigationPropertyNames()) {
+ final EdmNavigationProperty edmProperty = edmType.getNavigationProperty(navPropertyName);
+ if(entity == null && !edmProperty.isNullable()) {
+ throw new DataProviderException("Navigation property " + navPropertyName + " must not be null",
+ HttpStatusCode.BAD_REQUEST);
+ } else if(entity != null) {
+ final Link navigationBinding = entity.getNavigationBinding(navPropertyName);
+ final Link navigationLink = entity.getNavigationLink(navPropertyName);
+ final List<String> newPath = new ArrayList<String>(path);
+ newPath.add(edmProperty.getName());
+ final EdmBindingTarget target = edmBindingTarget.getRelatedBindingTarget(buildPath(newPath));
+
+ final ValidatioResult bindingResult = validateBinding(navigationBinding, edmProperty, target);
+ final ValidatioResult linkResult = validateNavigationLink(navigationLink,
+ edmProperty,
+ target);
+
+ if (( isInsert && !edmProperty.isNullable() && (bindingResult != ValidatioResult.FOUND
+ && linkResult != ValidatioResult.FOUND))
+ || (!isInsert && !edmProperty.isNullable() && linkResult == ValidatioResult.EMPTY)) {
+ throw new DataProviderException("Navigation property " + navPropertyName + " must not be null",
+ HttpStatusCode.BAD_REQUEST);
+ }
+ }
+ }
+ }
+
+ private String buildPath(final List<String> path) {
+ final StringBuilder builder = new StringBuilder();
+
+ for(final String segment : path) {
+ if(builder.length() > 0) {
+ builder.append("/");
+ }
+
+ builder.append(segment);
+ }
+
+ return builder.toString();
+ }
+
+ private ValidatioResult validateBinding(final Link navigationBindung, final EdmNavigationProperty edmProperty,
+ final EdmBindingTarget edmBindingTarget) throws DataProviderException {
+ if(navigationBindung == null) {
+ return ValidatioResult.NOT_FOUND;
+ }
+
+ if (edmProperty.isCollection()) {
+ if(navigationBindung.getBindingLinks().size() == 0) {
+ return ValidatioResult.EMPTY;
+ }
+
+ for (final String bindingLink : navigationBindung.getBindingLinks()) {
+ validateLink(bindingLink, edmBindingTarget);
+ }
+ } else {
+ if(navigationBindung.getBindingLink() == null) {
+ return ValidatioResult.EMPTY;
+ }
+
+ validateLink(navigationBindung.getBindingLink(), edmBindingTarget);
+ }
+
+ return ValidatioResult.FOUND;
+ }
+
+ private ValidatioResult validateNavigationLink(final Link navigationLink, final EdmNavigationProperty edmProperty,
+ final EdmBindingTarget edmBindingTarget) throws DataProviderException {
+ if(navigationLink == null) {
+ return ValidatioResult.NOT_FOUND;
+ }
+
+ if(edmProperty.isCollection()) {
+ final EntitySet inlineEntitySet = navigationLink.getInlineEntitySet();
+ if(!isInsert && inlineEntitySet.getEntities().size() > 0) {
+ throw new DataProvider.DataProviderException("Deep update is not allowed", HttpStatusCode.BAD_REQUEST);
+ } else {
+ for(final Entity entity : navigationLink.getInlineEntitySet().getEntities()) {
+ validate(edmBindingTarget, entity);
+ }
+ }
+ } else {
+ final Entity inlineEntity = navigationLink.getInlineEntity();
+ if(!isInsert && inlineEntity != null) {
+ throw new DataProvider.DataProviderException("Deep update is not allowed", HttpStatusCode.BAD_REQUEST);
+ } else if(inlineEntity != null) {
+ validate(edmBindingTarget, navigationLink.getInlineEntity());
+ }
+ }
+
+ return ValidatioResult.FOUND;
+ }
+
+ private void validateLink(final String bindingLink, final EdmBindingTarget edmBindungTarget)
+ throws DataProviderException {
+ try {
+ final List<UriParameter> keys = uriHelper.getKeyPredicatesFromEntityLink(edm, bindingLink, rawServiceRoot);
+ final Entity entity = provider.read((EdmEntitySet)edmBindungTarget, keys);
+
+ if (entity == null) {
+ throw new DataProviderException("Entity not found", HttpStatusCode.NOT_FOUND);
+ }
+ } catch (DeserializerException e) {
+ throw new DataProviderException("Invalid binding link", HttpStatusCode.BAD_REQUEST);
+ }
+ }
+
+ private void validateEntitySetProperties(final List<Property> properties, final EdmBindingTarget edmBindingTarget,
+ final EdmEntityType edmType, final List<String> path) throws DataProviderException {
+ validateProperties(properties, edmBindingTarget, edmType, edmType.getKeyPredicateNames(), path);
+ }
+
+ private void validateProperties(final List<Property> properties, final EdmBindingTarget edmBingingTarget,
+ final EdmStructuredType edmType, final List<String> keyPredicateNames, final List<String> path)
+ throws DataProviderException {
+
+ for(final String propertyName : edmType.getPropertyNames()) {
+ final EdmProperty edmProperty = (EdmProperty) edmType.getProperty(propertyName);
+
+ // Ignore key properties, they are set automatically
+ if(!keyPredicateNames.contains(propertyName)) {
+ final Property property = getProperty(properties, propertyName);
+
+ // Check if all "not nullable" properties are set
+ if(!edmProperty.isNullable()) {
+ if((property != null && property.isNull()) // Update,insert; Property is explicit set to null
+ || (isInsert && property == null) ) { // Insert; Property not provided
+ throw new DataProviderException("Property " + propertyName + " must not be null",
+ HttpStatusCode.BAD_REQUEST);
+ }
+ }
+
+ // Validate property value
+ validatePropertyValue(property, edmProperty, edmBingingTarget, path);
+ }
+ }
+ }
+
+ private void validatePropertyValue(final Property property, final EdmProperty edmProperty,
+ final EdmBindingTarget edmBindingTarget, final List<String> path) throws DataProviderException {
+
+ final ArrayList<String> newPath = new ArrayList<String>(path);
+ newPath.add(edmProperty.getName());
+
+ if (edmProperty.isCollection()) {
+ if (edmProperty.getType() instanceof EdmComplexType && property != null) {
+ for (final Object value : property.asCollection()) {
+ validateComplexValue((ComplexValue) value,
+ edmBindingTarget,
+ (EdmComplexType) edmProperty.getType(),
+ newPath);
+ }
+ }
+ } else if (edmProperty.getType() instanceof EdmComplexType) {
+ validateComplexValue((property == null) ? null : property.asComplex(),
+ edmBindingTarget,
+ (EdmComplexType) edmProperty.getType(),
+ newPath);
+ }
+ }
+
+ private void validateComplexValue(final ComplexValue value, final EdmBindingTarget edmBindingTarget,
+ final EdmComplexType edmType, final List<String> path) throws DataProviderException {
+ // The whole complex property can be nullable but nested primitive, navigation properties can be not nullable
+ final List<Property> properties = (value == null) ? new ArrayList<Property>() : value.getValue();
+
+ validateProperties(properties, edmBindingTarget, edmType, new ArrayList<String>(0), path);
+ validateNavigationProperties(value, edmBindingTarget, edmType, path);
+ }
+
+ private Property getProperty(final List<Property> properties, final String name) {
+ for(final Property property : properties) {
+ if(property.getName().equals(name)) {
+ return property;
+ }
+ }
+
+ return null;
+ }
+
+ private static enum ValidatioResult {
+ FOUND,
+ NOT_FOUND,
+ EMPTY
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/583c4bd0/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
index e105a2f..ed120b9 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
@@ -60,6 +60,7 @@ import org.apache.olingo.server.api.uri.UriResourceFunction;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.api.uri.queryoption.SelectOption;
import org.apache.olingo.server.tecsvc.data.DataProvider;
+import org.apache.olingo.server.tecsvc.data.RequestValidator;
import org.apache.olingo.server.tecsvc.processor.queryoptions.ExpandSystemQueryOptionHandler;
import org.apache.olingo.server.tecsvc.processor.queryoptions.options.CountHandler;
import org.apache.olingo.server.tecsvc.processor.queryoptions.options.FilterHandler;
@@ -230,17 +231,25 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
final UriResourceEntitySet resourceEntitySet = (UriResourceEntitySet) uriInfo.getUriResourceParts().get(0);
final EdmEntitySet edmEntitySet = resourceEntitySet.getEntitySet();
final EdmEntityType edmEntityType = edmEntitySet.getEntityType();
-
- Entity entity = dataProvider.create(edmEntitySet);
+
+ final Entity entity;
ExpandOption expand = null;
if (edmEntityType.hasStream()) { // called from createMediaEntity(...), not directly
+ entity = dataProvider.create(edmEntitySet);
dataProvider.setMedia(entity, odata.createFixedFormatDeserializer().binary(request.getBody()),
requestFormat.toContentTypeString());
} else {
final DeserializerResult deserializerResult = odata.createDeserializer(ODataFormat.fromContentType(requestFormat))
.entity(request.getBody(), edmEntityType);
- dataProvider.update(request.getRawBaseUri(), edmEntitySet, entity,
- deserializerResult.getEntity(), false, true);
+ new RequestValidator(dataProvider,
+ true, // Insert
+ odata.createUriHelper(),
+ serviceMetadata.getEdm(),
+ request.getRawBaseUri()
+ ).validate(edmEntitySet, deserializerResult.getEntity());
+
+ entity = dataProvider.create(edmEntitySet);
+ dataProvider.update(request.getRawBaseUri(), edmEntitySet, entity, deserializerResult.getEntity(), false, true);
expand = deserializerResult.getExpandTree();
}
@@ -265,8 +274,16 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo);
Entity entity = readEntity(uriInfo);
checkRequestFormat(requestFormat);
- ODataDeserializer deserializer = odata.createDeserializer(ODataFormat.fromContentType(requestFormat));
+ final ODataDeserializer deserializer = odata.createDeserializer(ODataFormat.fromContentType(requestFormat));
final Entity changedEntity = deserializer.entity(request.getBody(), edmEntitySet.getEntityType()).getEntity();
+
+ new RequestValidator(dataProvider,
+ false, // Update
+ odata.createUriHelper(),
+ serviceMetadata.getEdm(),
+ request.getRawBaseUri()
+ ).validate(edmEntitySet, changedEntity);
+
dataProvider.update(request.getRawBaseUri(), edmEntitySet, entity, changedEntity,
request.getMethod() == HttpMethod.PATCH, false);
response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
[3/3] olingo-odata4 git commit: [OLINGO-545] TecSvc: Test added for
deep insets to navigation properties in complex values
Posted by ch...@apache.org.
[OLINGO-545] TecSvc: Test added for deep insets to navigation properties in complex values
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/d4c2b89e
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/d4c2b89e
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/d4c2b89e
Branch: refs/heads/master
Commit: d4c2b89e46dcb067272ea370ae4d965c48f101b1
Parents: 518a3a4
Author: Christian Holzer <c....@sap.com>
Authored: Mon Apr 6 08:25:00 2015 +0200
Committer: Christian Holzer <c....@sap.com>
Committed: Tue Apr 7 08:26:17 2015 +0200
----------------------------------------------------------------------
.../fit/tecsvc/client/DeepInsertITCase.java | 62 +++++++++++++++++++-
1 file changed, 61 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/d4c2b89e/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
index 5255712..22f56f5 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
@@ -20,6 +20,7 @@ package org.apache.olingo.fit.tecsvc.client;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.net.URI;
@@ -37,10 +38,12 @@ import org.apache.olingo.client.api.communication.request.retrieve.ODataEntitySe
import org.apache.olingo.client.api.communication.response.ODataEntityCreateResponse;
import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
import org.apache.olingo.client.core.ODataClientFactory;
+import org.apache.olingo.commons.api.domain.ODataComplexValue;
import org.apache.olingo.commons.api.domain.ODataEntity;
import org.apache.olingo.commons.api.domain.ODataEntitySet;
import org.apache.olingo.commons.api.domain.ODataInlineEntity;
import org.apache.olingo.commons.api.domain.ODataInlineEntitySet;
+import org.apache.olingo.commons.api.domain.ODataLink;
import org.apache.olingo.commons.api.domain.ODataObjectFactory;
import org.apache.olingo.commons.api.domain.ODataProperty;
import org.apache.olingo.commons.api.domain.ODataValue;
@@ -79,6 +82,7 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
private static final String NAV_PROPERTY_ET_TWO_KEY_NAV_ONE = "NavPropertyETTwoKeyNavOne";
private static final String NAV_PROPERTY_ET_TWO_KEY_NAV_MANY = "NavPropertyETTwoKeyNavMany";
private static final String COL_PROPERTY_STRING = "CollPropertyString";
+ private static final String COL_PROPERTY_COMP_NAV = "CollPropertyCompNav";
private static final String EDM_STRING = "Edm.String";
@Test
@@ -601,7 +605,63 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
validateSet(targetURI, cookie, (short) 1, (short) 2, (short) 3);
}
-
+
+ @Test
+ @org.junit.Ignore
+ public void testDeepInsertOnNavigationPropertyInComplexProperty() {
+ final EdmEnabledODataClient client = ODataClientFactory.getEdmEnabledClient(SERVICE_URI);
+ final ODataObjectFactory of = client.getObjectFactory();
+
+ final ODataEntity inlineEntity = of.newEntity(ET_TWO_KEY_NAV);
+ inlineEntity.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
+ inlineEntity.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_BASE_PRIM_COMP_NAV)));
+ inlineEntity.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP_TWO_PRIM, of.newComplexValue(CT_TWO_PRIM)
+ .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 1)))
+ .add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString("1")))));
+
+ final ODataEntity entity = of.newEntity(ET_TWO_KEY_NAV);
+ entity.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
+ entity.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_BASE_PRIM_COMP_NAV)));
+ entity.getProperties().add(
+ of.newComplexProperty(PROPERTY_COMP_TWO_PRIM, of.newComplexValue(CT_TWO_PRIM)
+ .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 2)))
+ .add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString("2")))));
+
+ final ODataLink link = of.newDeepInsertEntity(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE, inlineEntity);
+ final ODataComplexValue complexValueCreate = of.newComplexValue(CT_NAV_FIVE_PROP);
+ complexValueCreate.getNavigationLinks().add(link);
+
+ entity.getProperties().add(
+ of.newCollectionProperty(COL_PROPERTY_COMP_NAV, of.newCollectionValue(CT_NAV_FIVE_PROP)
+ .add(complexValueCreate)));
+
+ final URI targetURI = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_TWO_KEY_NAV).build();
+ final ODataEntityCreateResponse<ODataEntity> response = client.getCUDRequestFactory()
+ .getEntityCreateRequest(targetURI, entity)
+ .execute();
+
+ assertEquals(HttpStatusCode.CREATED.getStatusCode(), response.getStatusCode());
+ final Iterator<ODataValue> iter = response.getBody()
+ .getProperty(COL_PROPERTY_COMP_NAV)
+ .getCollectionValue()
+ .iterator();
+
+ assertTrue(iter.hasNext());
+ final ODataComplexValue complexValue = iter.next().asComplex();
+ final ODataLink linkedEntity = complexValue.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE);
+ assertNotNull(linkedEntity);
+ assertEquals(1, linkedEntity.asInlineEntity()
+ .getEntity()
+ .getProperty(PROPERTY_INT16)
+ .getPrimitiveValue()
+ .toValue());
+ }
+
private String getCookie() {
final EdmEnabledODataClient client = ODataClientFactory.getEdmEnabledClient(SERVICE_URI);
final ODataRetrieveResponse<ODataEntitySet> response = client.getRetrieveRequestFactory()
[2/3] olingo-odata4 git commit: [OLINGO-545] TecSvc: Support of
cyclic expands and filtering on arbitrary level
Posted by ch...@apache.org.
[OLINGO-545] TecSvc: Support of cyclic expands and filtering on arbitrary
level
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/518a3a41
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/518a3a41
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/518a3a41
Branch: refs/heads/master
Commit: 518a3a418e9e79e2a3ce5feb61e175083812c2d8
Parents: 583c4bd
Author: Christian Holzer <c....@sap.com>
Authored: Mon Apr 6 08:25:00 2015 +0200
Committer: Christian Holzer <c....@sap.com>
Committed: Tue Apr 7 08:26:05 2015 +0200
----------------------------------------------------------------------
.../ExpandWithSystemQueryOptionsITCase.java | 160 +++++++++++++-
.../processor/TechnicalEntityProcessor.java | 6 +-
.../ExpandSystemQueryOptionHandler.java | 208 +++++++++++++------
.../expression/ExpressionVisitorImpl.java | 10 +-
.../queryoptions/options/FilterHandler.java | 6 +-
.../queryoptions/options/OrderByHandler.java | 12 +-
6 files changed, 320 insertions(+), 82 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/518a3a41/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java
index 2db9535..1a6b411 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java
@@ -19,6 +19,7 @@
package org.apache.olingo.fit.tecsvc.client;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import java.net.URI;
@@ -119,7 +120,7 @@ public class ExpandWithSystemQueryOptionsITCase extends AbstractBaseTestITCase {
}
}
}
-
+
@Test
public void testSkip() {
final Map<QueryOption, Object> options = new HashMap<QueryOption, Object>();
@@ -290,7 +291,162 @@ public class ExpandWithSystemQueryOptionsITCase extends AbstractBaseTestITCase {
assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
assertEquals(4, entities.size());
}
-
+
+ @Test
+ public void testCyclicExpand() {
+ // Expand entity in the following order
+ // 1 => 2 => 1
+ // Entity with Key (PropertyInt16=1, PrroperyString='1') holds references to (PropertyInt16=1, PropertyString='1')
+ // and (PropertyInt16=1, PropertyString='2')
+ // Entity with Key (PropertyInt16=1, PropertyString='2') holds references to (PropertyInt16=1, PropertyString='1')
+ // Define filters to select explicit the entities at any level => Circle
+
+ final ODataClient client = getClient();
+ final Map<QueryOption, Object> options = new HashMap<QueryOption, Object>();
+ options.put(QueryOption.EXPAND, NAV_PROPERTY_ET_TWO_KEY_NAV_MANY
+ + "($expand=" + NAV_PROPERTY_ET_TWO_KEY_NAV_MANY
+ + "($expand=" + NAV_PROPERTY_ET_TWO_KEY_NAV_MANY + "))");
+ options.put(QueryOption.FILTER, "PropertyString eq '2'");
+
+ final Map<String, Object> keys = new HashMap<String, Object>();
+ keys.put(PROPERTY_INT16, 1);
+ keys.put(PROPERTY_STRING, "1");
+
+ final URI uri = client.newURIBuilder(SERVICE_URI)
+ .appendEntitySetSegment(ES_TWO_KEY_NAV)
+ .appendKeySegment(keys)
+ .expandWithOptions(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY, options)
+ .build();
+
+ final ODataRetrieveResponse<ODataEntity> response = client.getRetrieveRequestFactory()
+ .getEntityRequest(uri)
+ .execute();
+
+ assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
+ assertNotNull(response.getBody().getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY));
+ assertEquals(1, response.getBody().getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+ .asInlineEntitySet()
+ .getEntitySet()
+ .getEntities()
+ .size());
+
+ final ODataEntity entitySecondLevel = response.getBody().getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+ .asInlineEntitySet()
+ .getEntitySet()
+ .getEntities()
+ .get(0);
+
+ assertEquals(1, entitySecondLevel.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
+ assertEquals("2", entitySecondLevel.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+
+ assertNotNull(entitySecondLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY));
+ assertEquals(1, entitySecondLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+ .asInlineEntitySet()
+ .getEntitySet()
+ .getEntities()
+ .size());
+
+ final ODataEntity entityThirdLevel = entitySecondLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+ .asInlineEntitySet()
+ .getEntitySet()
+ .getEntities()
+ .get(0);
+
+ assertEquals(1, entityThirdLevel.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
+ assertEquals("1", entityThirdLevel.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+
+ assertNotNull(entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY));
+ assertEquals(2, entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+ .asInlineEntitySet()
+ .getEntitySet()
+ .getEntities()
+ .size());
+
+ final List<ODataEntity> fourthLevelEntites = entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+ .asInlineEntitySet()
+ .getEntitySet()
+ .getEntities();
+
+ assertEquals(1, fourthLevelEntites.get(0).getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
+ assertEquals("1", fourthLevelEntites.get(0).getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+
+ assertEquals(1, fourthLevelEntites.get(1).getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
+ assertEquals("2", fourthLevelEntites.get(1).getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+ }
+
+ @Test
+ public void testSystemQueryOptionOnThirdLevel() {
+ final ODataClient client = getClient();
+ final Map<QueryOption, Object> options = new HashMap<QueryOption, Object>();
+ options.put(QueryOption.EXPAND, NAV_PROPERTY_ET_TWO_KEY_NAV_MANY
+ + "($expand=" + NAV_PROPERTY_ET_TWO_KEY_NAV_MANY
+ + "($expand=" + NAV_PROPERTY_ET_TWO_KEY_NAV_MANY
+ + ";$filter=PropertyString eq '1'))");
+ options.put(QueryOption.FILTER, "PropertyString eq '2'");
+
+ final Map<String, Object> keys = new HashMap<String, Object>();
+ keys.put(PROPERTY_INT16, 1);
+ keys.put(PROPERTY_STRING, "1");
+
+ final URI uri = client.newURIBuilder(SERVICE_URI)
+ .appendEntitySetSegment(ES_TWO_KEY_NAV)
+ .appendKeySegment(keys)
+ .expandWithOptions(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY, options)
+ .build();
+
+ final ODataRetrieveResponse<ODataEntity> response = client.getRetrieveRequestFactory()
+ .getEntityRequest(uri)
+ .execute();
+
+ assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
+ assertNotNull(response.getBody().getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY));
+ assertEquals(1, response.getBody().getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+ .asInlineEntitySet()
+ .getEntitySet()
+ .getEntities()
+ .size());
+
+ final ODataEntity entitySecondLevel = response.getBody().getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+ .asInlineEntitySet()
+ .getEntitySet()
+ .getEntities()
+ .get(0);
+
+ assertEquals(1, entitySecondLevel.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
+ assertEquals("2", entitySecondLevel.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+
+ assertNotNull(entitySecondLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY));
+ assertEquals(1, entitySecondLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+ .asInlineEntitySet()
+ .getEntitySet()
+ .getEntities()
+ .size());
+
+ final ODataEntity entityThirdLevel = entitySecondLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+ .asInlineEntitySet()
+ .getEntitySet()
+ .getEntities()
+ .get(0);
+
+ assertEquals(1, entityThirdLevel.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
+ assertEquals("1", entityThirdLevel.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+
+ assertNotNull(entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY));
+ assertEquals(1, entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+ .asInlineEntitySet()
+ .getEntitySet()
+ .getEntities()
+ .size());
+
+ final List<ODataEntity> fourthLevelEntites = entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+ .asInlineEntitySet()
+ .getEntitySet()
+ .getEntities();
+
+ assertEquals(1, fourthLevelEntites.get(0).getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
+ assertEquals("1", fourthLevelEntites.get(0).getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+ }
+
private ODataRetrieveResponse<ODataEntitySet> buildRequest(final String entitySet, final String navigationProperty,
final Map<QueryOption, Object> expandOptions) {
return buildRequest(entitySet, navigationProperty, expandOptions, null);
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/518a3a41/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
index ed120b9..a2774e3 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
@@ -125,7 +125,9 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
// Create a shallow copy of each entity. So the expanded navigation properties can be modified for serialization,
// without affecting the data stored in the database.
final ExpandSystemQueryOptionHandler expandHandler = new ExpandSystemQueryOptionHandler();
- final EntitySet entitySetSerialization = expandHandler.copyEntitySetShallowRekursive(entitySet);
+ final EntitySet entitySetSerialization = expandHandler.transformEntitySetGraphToTree(entitySet,
+ edmEntitySet,
+ expand);
expandHandler.applyExpandQueryOptions(entitySetSerialization, edmEntitySet, expand);
// Serialize
@@ -186,7 +188,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
final SelectOption select = uriInfo.getSelectOption();
final ExpandSystemQueryOptionHandler expandHandler = new ExpandSystemQueryOptionHandler();
- final Entity entitySerialization = expandHandler.copyEntityShallowRekursive(entity);
+ final Entity entitySerialization = expandHandler.transformEntityGraphToTree(entity, edmEntitySet, expand);
expandHandler.applyExpandQueryOptions(entitySerialization, edmEntitySet, expand);
response.setContent(serializer.entity(
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/518a3a41/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java
index 3ae3b3a..ce1b774 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java
@@ -18,15 +18,18 @@
*/
package org.apache.olingo.server.tecsvc.processor.queryoptions;
-import java.util.IdentityHashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
+import java.util.Set;
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.edm.EdmBindingTarget;
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.http.HttpStatusCode;
import org.apache.olingo.commons.core.data.EntityImpl;
import org.apache.olingo.commons.core.data.EntitySetImpl;
@@ -47,8 +50,6 @@ import org.apache.olingo.server.tecsvc.processor.queryoptions.options.SkipHandle
import org.apache.olingo.server.tecsvc.processor.queryoptions.options.TopHandler;
public class ExpandSystemQueryOptionHandler {
- private IdentityHashMap<Entity, Entity> copiedEntities = new IdentityHashMap<Entity, Entity>();
- private IdentityHashMap<EntitySet, EntitySet> copiedEntitySets = new IdentityHashMap<EntitySet, EntitySet>();
public void applyExpandQueryOptions(final EntitySet entitySet, final EdmEntitySet edmEntitySet,
final ExpandOption expandOption) throws ODataApplicationException {
@@ -70,20 +71,26 @@ public class ExpandSystemQueryOptionHandler {
applyExpandOptionToEntity(entity, edmEntitySet, expandOption);
}
- private void applyExpandOptionToEntity(final Entity entity, final EdmEntitySet edmEntitySet,
+ private void applyExpandOptionToEntity(final Entity entity, final EdmBindingTarget edmBindingTarget,
final ExpandOption expandOption) throws ODataApplicationException {
- final EdmEntityType entityType = edmEntitySet.getEntityType();
+ final EdmEntityType entityType = edmBindingTarget.getEntityType();
for (ExpandItem item : expandOption.getExpandItems()) {
final List<UriResource> uriResourceParts = item.getResourcePath().getUriResourceParts();
if (uriResourceParts.size() == 1 && uriResourceParts.get(0) instanceof UriResourceNavigation) {
final String navPropertyName = ((UriResourceNavigation) uriResourceParts.get(0)).getProperty().getName();
- final EdmEntitySet targetEdmEntitySet = (EdmEntitySet) edmEntitySet.getRelatedBindingTarget(navPropertyName);
+ final EdmBindingTarget targetEdmEntitySet = edmBindingTarget.getRelatedBindingTarget(navPropertyName);
final Link link = entity.getNavigationLink(navPropertyName);
if (link != null && entityType.getNavigationProperty(navPropertyName).isCollection()) {
- applyOptionsToEntityCollection(link.getInlineEntitySet(), targetEdmEntitySet, item.getFilterOption(),
- item.getOrderByOption(), item.getCountOption(), item.getSkipOption(), item.getTopOption());
+ applyOptionsToEntityCollection(link.getInlineEntitySet(),
+ targetEdmEntitySet,
+ item.getFilterOption(),
+ item.getOrderByOption(),
+ item.getCountOption(),
+ item.getSkipOption(),
+ item.getTopOption(),
+ item.getExpandOption());
}
} else {
throw new ODataApplicationException("Not supported resource part in expand system query option",
@@ -92,77 +99,150 @@ public class ExpandSystemQueryOptionHandler {
}
}
- private void applyOptionsToEntityCollection(final EntitySet entitySet, final EdmEntitySet edmEntitySet,
+ private void applyOptionsToEntityCollection(final EntitySet entitySet, final EdmBindingTarget edmBindingTarget,
final FilterOption filterOption, final OrderByOption orderByOption, final CountOption countOption,
- final SkipOption skipOption, final TopOption topOption) throws ODataApplicationException {
+ final SkipOption skipOption, final TopOption topOption, final ExpandOption expandOption)
+ throws ODataApplicationException {
- FilterHandler.applyFilterSystemQuery(filterOption, entitySet, edmEntitySet);
- OrderByHandler.applyOrderByOption(orderByOption, entitySet, edmEntitySet);
+ FilterHandler.applyFilterSystemQuery(filterOption, entitySet, edmBindingTarget);
+ OrderByHandler.applyOrderByOption(orderByOption, entitySet, edmBindingTarget);
// TODO Add CountHandler
SkipHandler.applySkipSystemQueryHandler(skipOption, entitySet);
TopHandler.applyTopSystemQueryOption(topOption, entitySet);
+
+ // Apply nested expand system query options to remaining entities
+ if(expandOption != null) {
+ for(final Entity entity : entitySet.getEntities()) {
+ applyExpandOptionToEntity(entity, edmBindingTarget, expandOption);
+ }
+ }
}
- public EntitySet copyEntitySetShallowRekursive(final EntitySet entitySet) {
- if (!copiedEntitySets.containsKey(entitySet)) {
- final EntitySet copiedEntitySet = new EntitySetImpl();
- copiedEntitySet.setCount(entitySet.getCount());
- copiedEntitySet.setDeltaLink(entitySet.getDeltaLink());
- copiedEntitySet.setNext(entitySet.getNext());
-
- copiedEntitySets.put(entitySet, copiedEntitySet);
- copiedEntitySets.put(copiedEntitySet, copiedEntitySet);
-
- for (Entity entity : entitySet.getEntities()) {
- copiedEntitySet.getEntities().add(copyEntityShallowRekursive(entity));
- }
- return copiedEntitySet;
+ public EntitySet transformEntitySetGraphToTree(final EntitySet entitySet, final EdmBindingTarget edmBindingTarget,
+ final ExpandOption expand) throws ODataApplicationException {
+
+ final EntitySet newEntitySet = newEntitySet(entitySet);
+
+ for(final Entity entity : entitySet.getEntities()) {
+ newEntitySet.getEntities().add(transformEntityGraphToTree(entity, edmBindingTarget, expand));
}
- return copiedEntitySets.get(entitySet);
+
+ return newEntitySet;
}
+
+ public Entity transformEntityGraphToTree(final Entity entity, EdmBindingTarget edmEntitySet,
+ final ExpandOption expand) throws ODataApplicationException {
- public Entity copyEntityShallowRekursive(final Entity entity) {
- if (!copiedEntities.containsKey(entity)) {
- final Entity copiedEntity = new EntityImpl();
- copiedEntity.getProperties().addAll(entity.getProperties());
- copiedEntity.getAnnotations().addAll(entity.getAnnotations());
- copiedEntity.getAssociationLinks().addAll(entity.getAssociationLinks());
- copiedEntity.setEditLink(entity.getEditLink());
- copiedEntity.setId(entity.getId());
- copiedEntity.setMediaContentSource(entity.getMediaContentSource());
- copiedEntity.setMediaContentType(entity.getMediaContentType());
- copiedEntity.setMediaETag(entity.getMediaETag());
- copiedEntity.getOperations().addAll(entity.getOperations());
- copiedEntity.setSelfLink(entity.getSelfLink());
- copiedEntity.setType(entity.getType());
- copiedEntity.getNavigationBindings().addAll(entity.getNavigationBindings());
-
- copiedEntities.put(entity, copiedEntity);
- copiedEntities.put(copiedEntity, copiedEntity);
-
- // The system query options change the amount and sequence of inline entities (feeds)
- // So we have to make a shallow copy of all navigation link lists
- // Make sure, that each entity is only copied once.
- // Otherwise an infinite loop can occur caused by cyclic navigation relationships.
+ final Entity newEntity = newEntity(entity);
+ if (hasExpandItems(expand)) {
+ final boolean expandAll = expandAll(expand);
+ final Set<String> expanded = expandAll ? null : getExpandedPropertyNames(expand.getExpandItems());
+ final EdmEntityType edmType = edmEntitySet.getEntityType();
+
for (final Link link : entity.getNavigationLinks()) {
- final Link newLink = new LinkImpl();
- newLink.setMediaETag(link.getMediaETag());
- newLink.setTitle(link.getTitle());
- newLink.setType(link.getType());
- newLink.setRel(link.getRel());
-
- final EntitySet inlineEntitySet = link.getInlineEntitySet();
- if (inlineEntitySet != null) {
- newLink.setInlineEntitySet(copyEntitySetShallowRekursive(inlineEntitySet));
- } else if (link.getInlineEntity() != null) {
- newLink.setInlineEntity(copyEntityShallowRekursive(link.getInlineEntity()));
+ final String propertyName = link.getTitle();
+
+ if (expandAll || expanded.contains(propertyName)) {
+ final EdmNavigationProperty edmNavigationProperty = edmType.getNavigationProperty(propertyName);
+ final EdmBindingTarget edmBindingTarget = edmEntitySet.getRelatedBindingTarget(propertyName);
+ final Link newLink = newLink(link);
+ newEntity.getNavigationLinks().add(newLink);
+ final ExpandOption innerExpandOption = getInnerExpandOption(expand, propertyName);
+
+ if(edmNavigationProperty.isCollection()) {
+ newLink.setInlineEntitySet(transformEntitySetGraphToTree(link.getInlineEntitySet(),
+ edmBindingTarget,
+ innerExpandOption));
+ } else {
+ newLink.setInlineEntity(transformEntityGraphToTree(link.getInlineEntity(),
+ edmBindingTarget,
+ innerExpandOption));
+ }
}
- copiedEntity.getNavigationLinks().add(newLink);
}
+
+ }
+ return newEntity;
+ }
+
+ public EntitySet newEntitySet(final EntitySet entitySet) {
+ final EntitySet newEntitySet = new EntitySetImpl();
+ newEntitySet.setCount(entitySet.getCount());
+ newEntitySet.setDeltaLink(entitySet.getDeltaLink());
+ newEntitySet.setNext(entitySet.getNext());
+
+ return newEntitySet;
+ }
+
+ private Entity newEntity(final Entity entity) {
+ final Entity newEntity = new EntityImpl();
+
+ newEntity.getProperties().addAll(entity.getProperties());
+ newEntity.getAnnotations().addAll(entity.getAnnotations());
+ newEntity.getAssociationLinks().addAll(entity.getAssociationLinks());
+ newEntity.setEditLink(entity.getEditLink());
+ newEntity.setId(entity.getId());
+ newEntity.setMediaContentSource(entity.getMediaContentSource());
+ newEntity.setMediaContentType(entity.getMediaContentType());
+ newEntity.setMediaETag(entity.getMediaETag());
+ newEntity.getOperations().addAll(entity.getOperations());
+ newEntity.setSelfLink(entity.getSelfLink());
+ newEntity.setType(entity.getType());
+ newEntity.getNavigationBindings().addAll(entity.getNavigationBindings());
+
+ return newEntity;
+ }
+
+ private Link newLink(Link link) {
+ final Link newLink = new LinkImpl();
+ newLink.setMediaETag(link.getMediaETag());
+ newLink.setTitle(link.getTitle());
+ newLink.setType(link.getType());
+ newLink.setRel(link.getRel());
+
+ return newLink;
+ }
+
+ private boolean hasExpandItems(ExpandOption expand) {
+ return expand != null && expand.getExpandItems() != null && !expand.getExpandItems().isEmpty();
+ }
+
+ private boolean expandAll(ExpandOption expand) {
+ for (final ExpandItem item : expand.getExpandItems()) {
+ if (item.isStar()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private Set<String> getExpandedPropertyNames(List<ExpandItem> expandItems) throws ODataApplicationException {
+ 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 ODataApplicationException("Expand is not supported within complex properties.",
+ HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
+ }
+ }
+ return expanded;
+ }
- return copiedEntity;
+ private ExpandOption getInnerExpandOption(final ExpandOption expand, final String propertyName) {
+ for(final ExpandItem item : expand.getExpandItems()) {
+ final UriResource resource = item.getResourcePath().getUriResourceParts().get(0);
+ if(resource instanceof UriResourceNavigation
+ && propertyName.equals(((UriResourceNavigation) resource).getProperty().getName())) {
+ return item.getExpandOption();
+ }
}
- return copiedEntities.get(entity);
+
+ return null;
}
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/518a3a41/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java
index 3a91f49..ab0eca3 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java
@@ -23,8 +23,8 @@ import java.util.Locale;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.edm.EdmBindingTarget;
import org.apache.olingo.commons.api.edm.EdmComplexType;
-import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEnumType;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.edm.EdmType;
@@ -49,11 +49,11 @@ import org.apache.olingo.server.tecsvc.processor.queryoptions.expression.operati
public class ExpressionVisitorImpl implements ExpressionVisitor<VisitorOperand> {
final private Entity entity;
- final private EdmEntitySet edmEntitySet;
+ final private EdmBindingTarget bindingTarget;
- public ExpressionVisitorImpl(Entity entity, EdmEntitySet edmEntitySet) {
+ public ExpressionVisitorImpl(Entity entity, EdmBindingTarget bindingTarget) {
this.entity = entity;
- this.edmEntitySet = edmEntitySet;
+ this.bindingTarget = bindingTarget;
}
@Override
@@ -183,7 +183,7 @@ public class ExpressionVisitorImpl implements ExpressionVisitor<VisitorOperand>
Property currentProperty = entity.getProperty(uriResourceParts.get(0).toString());
EdmType currentType = ((UriResourcePartTyped) uriResourceParts.get(0)).getType();
- EdmProperty currentEdmProperty = edmEntitySet.getEntityType()
+ EdmProperty currentEdmProperty = bindingTarget.getEntityType()
.getStructuralProperty(uriResourceParts.get(0).toString());
for (int i = 1; i < uriResourceParts.size(); i++) {
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/518a3a41/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/FilterHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/FilterHandler.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/FilterHandler.java
index f1ba4ee..d4068a1 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/FilterHandler.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/FilterHandler.java
@@ -23,7 +23,7 @@ import java.util.Locale;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntitySet;
-import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmBindingTarget;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.commons.core.edm.primitivetype.EdmBoolean;
import org.apache.olingo.server.api.ODataApplicationException;
@@ -53,8 +53,8 @@ import org.apache.olingo.server.tecsvc.processor.queryoptions.expression.operand
public class FilterHandler {
- public static void applyFilterSystemQuery(FilterOption filterOption, EntitySet entitySet, EdmEntitySet edmEntitySet)
- throws ODataApplicationException {
+ public static void applyFilterSystemQuery(FilterOption filterOption, EntitySet entitySet,
+ EdmBindingTarget edmEntitySet) throws ODataApplicationException {
if (filterOption == null) {
return;
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/518a3a41/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/OrderByHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/OrderByHandler.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/OrderByHandler.java
index 71b9a81..d166c4e 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/OrderByHandler.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/OrderByHandler.java
@@ -24,7 +24,7 @@ import java.util.Locale;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntitySet;
-import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmBindingTarget;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.uri.queryoption.OrderByItem;
@@ -35,14 +35,14 @@ import org.apache.olingo.server.tecsvc.processor.queryoptions.expression.operand
public class OrderByHandler {
public static void applyOrderByOption(final OrderByOption orderByOption, final EntitySet entitySet,
- final EdmEntitySet edmEntitySet) throws ODataApplicationException {
+ final EdmBindingTarget edmBindingTarget) throws ODataApplicationException {
if (orderByOption == null) {
return;
}
try {
- applyOrderByOptionInternal(orderByOption, entitySet, edmEntitySet);
+ applyOrderByOptionInternal(orderByOption, entitySet, edmBindingTarget);
} catch (SystemQueryOptionsRuntimeException e) {
if (e.getCause() instanceof ODataApplicationException) {
// Throw the nested exception, to send the correct HTTP status code in the HTTP response
@@ -55,7 +55,7 @@ public class OrderByHandler {
}
private static void applyOrderByOptionInternal(final OrderByOption orderByOption, final EntitySet entitySet,
- final EdmEntitySet edmEntitySet) throws ODataApplicationException {
+ final EdmBindingTarget edmBindingTarget) throws ODataApplicationException {
Collections.sort(entitySet.getEntities(), new Comparator<Entity>() {
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -69,9 +69,9 @@ public class OrderByHandler {
try {
final OrderByItem item = orderByOption.getOrders().get(i);
final TypedOperand op1 =
- item.getExpression().accept(new ExpressionVisitorImpl(e1, edmEntitySet)).asTypedOperand();
+ item.getExpression().accept(new ExpressionVisitorImpl(e1, edmBindingTarget)).asTypedOperand();
final TypedOperand op2 =
- item.getExpression().accept(new ExpressionVisitorImpl(e2, edmEntitySet)).asTypedOperand();
+ item.getExpression().accept(new ExpressionVisitorImpl(e2, edmBindingTarget)).asTypedOperand();
if (op1.isNull() || op2.isNull()) {
if (op1.isNull() && op2.isNull()) {