[OLINGO-545] Single valued navigation properties can be null
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/4768048f Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/4768048f Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/4768048f Branch: refs/heads/master Commit: 4768048fba0546fdd26632e5ea4d9640f4632275 Parents: f6fa1ee Author: Christian Holzer <[email protected]> Authored: Wed Mar 11 17:21:21 2015 +0100 Committer: Christian Holzer <[email protected]> Committed: Thu Mar 12 16:52:40 2015 +0100 ---------------------------------------------------------------------- .../olingo/fit/tecsvc/client/BindingITCase.java | 48 ++++++++++++++++++++ .../olingo/server/tecsvc/data/DataProvider.java | 36 +++++++++++++-- 2 files changed, 81 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4768048f/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 09dcaa0..82280bc 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 @@ -36,6 +36,7 @@ import org.apache.olingo.client.core.ODataClientFactory; import org.apache.olingo.commons.api.domain.ODataEntity; 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; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; import org.apache.olingo.commons.api.edm.FullQualifiedName; @@ -261,6 +262,53 @@ public class BindingITCase extends AbstractBaseTestITCase { } } + @Test + public void testUpdateSingleNavigationPropertyWithNull() { + final ODataClient client = getClient(); + final URI entityURI = + client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_KEY_NAV).appendKeySegment(1).build(); + final ODataObjectFactory of = client.getObjectFactory(); + + // Request to single (non collection) navigation property + ODataEntity entity = of.newEntity(ET_KEY_NAV); + final ODataProperty navPropery = of.newComplexProperty(NAV_PROPERTY_ET_KEY_NAV_ONE, null); + entity.getProperties().add(navPropery); + + ODataEntityUpdateResponse<ODataEntity> updateResponse = + client.getCUDRequestFactory().getEntityUpdateRequest(entityURI, UpdateType.PATCH, entity).execute(); + assertEquals(HttpStatusCode.NO_CONTENT.getStatusCode(), updateResponse.getStatusCode()); + + final ODataEntityRequest<ODataEntity> getRequest = + client.getRetrieveRequestFactory().getEntityRequest( + client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_KEY_NAV).appendKeySegment(1).expand( + NAV_PROPERTY_ET_KEY_NAV_ONE).build()); + getRequest.addCustomHeader(HttpHeader.COOKIE, updateResponse.getHeader(HttpHeader.SET_COOKIE).iterator().next()); + final ODataRetrieveResponse<ODataEntity> getResponse = getRequest.execute(); + + ODataProperty property = getResponse.getBody().getProperty(NAV_PROPERTY_ET_KEY_NAV_ONE); + assertEquals(null, property.getPrimitiveValue()); + } + + @Test + public void testUpdateCollectionNavigationPropertyWithNull() { + final ODataClient client = getClient(); + final URI entityURI = + client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_KEY_NAV).appendKeySegment(1).build(); + final ODataObjectFactory of = client.getObjectFactory(); + + // Request to single (non collection) navigation property + ODataEntity entity = of.newEntity(ET_KEY_NAV); + final ODataProperty navPropery = of.newComplexProperty(NAV_PROPERTY_ET_KEY_NAV_MANY, null); + entity.getProperties().add(navPropery); + + try { + client.getCUDRequestFactory().getEntityUpdateRequest(entityURI, UpdateType.PATCH, entity).execute(); + fail(); + } 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/4768048f/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 f18a0de..61b763c 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 @@ -236,6 +236,8 @@ public class DataProvider { // Deep insert (only if not an update) if (isInsert) { handleDeepInsert(rawBaseUri, edmEntitySet, entity, changedEntity); + } else { + handleDeleteSingleNavigationProperties(edmEntitySet, entity, changedEntity); } final boolean navigationBindingsAvailable = !changedEntity.getNavigationBindings().isEmpty(); @@ -244,6 +246,28 @@ public class DataProvider { } } + private void handleDeleteSingleNavigationProperties(EdmEntitySet edmEntitySet, Entity entity, Entity changedEntity) + throws DataProviderException { + final EdmEntityType entityType = edmEntitySet.getEntityType(); + final List<String> navigationPropertyNames = entityType.getNavigationPropertyNames(); + + for (final String navPropertyName : navigationPropertyNames) { + final Link navigationLink = changedEntity.getNavigationLink(navPropertyName); + final EdmNavigationProperty navigationProperty = entityType.getNavigationProperty(navPropertyName); + if (!navigationProperty.isCollection() && navigationLink != null && navigationLink.getInlineEntity() == null) { + + // Check if partner is available + if (navigationProperty.getPartner() != null && entity.getNavigationLink(navPropertyName) != null) { + Entity partnerEntity = entity.getNavigationLink(navPropertyName).getInlineEntity(); + removeLink(navigationProperty.getPartner(), partnerEntity); + } + + // Remove link + removeLink(navigationProperty, entity); + } + } + } + private void applyNavigationBinding(final String rawBaseUri, final EdmEntitySet edmEntitySet, final Entity entity, final List<Link> navigationBindings) throws DataProviderException { @@ -303,15 +327,21 @@ public class DataProvider { for (final Entity inlineEntity : entities) { createLink(navigationProperty, entity, inlineEntity); } - } else { - final Entity inlineEntity = - createInlineEntity(rawBaseUri, target, navigationLink.getInlineEntity()); + } else if (!navigationProperty.isCollection() && navigationLink.getInlineEntity() != null) { + final Entity inlineEntity = createInlineEntity(rawBaseUri, target, navigationLink.getInlineEntity()); createLink(navigationProperty, entity, inlineEntity); } } } } + private void removeLink(EdmNavigationProperty navigationProperty, Entity entity) { + final Link link = entity.getNavigationLink(navigationProperty.getName()); + if(link != null) { + entity.getNavigationLinks().remove(link); + } + } + private List<Entity> createInlineEntities(final String rawBaseUri, final EdmEntitySet targetEntitySet, final EntitySet changedEntitySet) throws DataProviderException { List<Entity> entities = new ArrayList<Entity>();
