Repository: olingo-odata4 Updated Branches: refs/heads/master ecf9b9f9b -> 6a736db10
[OLINGO-888] Support Singletons in OData dispatcher Contributed by Archana Rai via https://issues.apache.org/jira/browse/OLINGO-888 Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/84a052dc Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/84a052dc Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/84a052dc Branch: refs/heads/master Commit: 84a052dcfa3825c37377c4cfd93c01f2c7e6af53 Parents: ecf9b9f Author: Christian Amend <[email protected]> Authored: Tue Nov 22 14:13:14 2016 +0100 Committer: Christian Amend <[email protected]> Committed: Tue Nov 22 14:13:14 2016 +0100 ---------------------------------------------------------------------- .../olingo/server/core/ODataDispatcher.java | 215 ++++++++++++------- .../olingo/server/tecsvc/data/DataCreator.java | 38 +++- .../olingo/server/tecsvc/data/DataProvider.java | 9 + .../processor/TechnicalEntityProcessor.java | 39 +++- .../tecsvc/processor/TechnicalProcessor.java | 53 +++-- .../server/core/ODataHandlerImplTest.java | 163 +++++++++++++- 6 files changed, 420 insertions(+), 97 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/84a052dc/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java index 138f45a..5d48e34 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java @@ -25,6 +25,7 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.edm.EdmReturnType; import org.apache.olingo.commons.api.edm.EdmType; import org.apache.olingo.commons.api.edm.constants.EdmTypeKind; +import org.apache.olingo.commons.api.edm.EdmSingleton; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.http.HttpHeader; import org.apache.olingo.commons.api.http.HttpMethod; @@ -70,6 +71,7 @@ import org.apache.olingo.server.api.uri.UriResourcePrimitiveProperty; import org.apache.olingo.server.api.uri.UriResourceProperty; import org.apache.olingo.server.core.batchhandler.BatchHandler; import org.apache.olingo.server.core.etag.PreconditionsValidator; +import org.apache.olingo.server.api.uri.UriResourceSingleton; public class ODataDispatcher { @@ -144,7 +146,11 @@ public class ODataDispatcher { handleEntityDispatching(request, response, ((UriResourcePartTyped) lastPathSegment).isCollection(), isEntityOrNavigationMedia(lastPathSegment)); break; - + + case singleton: + handleSingleEntityDispatching(request, response, isSingletonMedia(lastPathSegment), true); + break; + case count: checkMethod(request.getMethod(), HttpMethod.GET); handleCountDispatching(request, response, lastPathSegmentIndex); @@ -304,62 +310,80 @@ public class ODataDispatcher { private void handleValueDispatching(final ODataRequest request, final ODataResponse response, final int lastPathSegmentIndex) throws ODataApplicationException, ODataLibraryException { // The URI Parser already checked if $value is allowed here so we only have to dispatch to the correct processor - final HttpMethod method = request.getMethod(); final UriResource resource = uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1); if (resource instanceof UriResourceProperty || resource instanceof UriResourceFunction && ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.PRIMITIVE) { - final EdmType type = resource instanceof UriResourceProperty ? ((UriResourceProperty) resource).getType() - : ((UriResourceFunction) resource).getType(); - final RepresentationType valueRepresentationType = - type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary) ? RepresentationType.BINARY - : RepresentationType.VALUE; - if (method == HttpMethod.GET) { - final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, handler.getCustomContentTypeSupport(), valueRepresentationType); + handlePrimitiveValueDispatching(request, response, resource); + } else { + handleMediaValueDispatching(request, response, resource); + } + } - handler.selectProcessor(PrimitiveValueProcessor.class) - .readPrimitiveValue(request, response, uriInfo, requestedContentType); - } else if (method == HttpMethod.PUT && resource instanceof UriResourceProperty) { - validatePreconditions(request, false); - final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE), - valueRepresentationType, true); - final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, handler.getCustomContentTypeSupport(), valueRepresentationType); - handler.selectProcessor(PrimitiveValueProcessor.class) - .updatePrimitiveValue(request, response, uriInfo, requestFormat, responseFormat); - } else if (method == HttpMethod.DELETE && resource instanceof UriResourceProperty) { - validatePreconditions(request, false); - handler.selectProcessor(PrimitiveValueProcessor.class) - .deletePrimitiveValue(request, response, uriInfo); - } else { - throwMethodNotAllowed(method); - } + private void handleMediaValueDispatching(final ODataRequest request, final ODataResponse response, + final UriResource resource) throws ContentNegotiatorException, + ODataApplicationException, ODataLibraryException, + ODataHandlerException, PreconditionException { + final HttpMethod method = request.getMethod(); + if (method == HttpMethod.GET) { + // This can be a GET on an EntitySet, Navigation or Function + final ContentType requestedContentType = ContentNegotiator. + doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), RepresentationType.MEDIA); + handler.selectProcessor(MediaEntityProcessor.class) + .readMediaEntity(request, response, uriInfo, requestedContentType); + // PUT and DELETE can only be called on EntitySets or Navigation properties which are media resources + } else if (method == HttpMethod.PUT && (isEntityOrNavigationMedia(resource) + || isSingletonMedia(resource))) { + validatePreconditions(request, true); + final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); + final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY); + handler.selectProcessor(MediaEntityProcessor.class) + .updateMediaEntity(request, response, uriInfo, requestFormat, responseFormat); + } else if (method == HttpMethod.DELETE && isEntityOrNavigationMedia(resource)) { + validatePreconditions(request, true); + handler.selectProcessor(MediaEntityProcessor.class) + .deleteMediaEntity(request, response, uriInfo); } else { - if (method == HttpMethod.GET) { - // This can be a GET on an EntitySet, Navigation or Function - final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, handler.getCustomContentTypeSupport(), RepresentationType.MEDIA); - handler.selectProcessor(MediaEntityProcessor.class) - .readMediaEntity(request, response, uriInfo, requestedContentType); - // PUT and DELETE can only be called on EntitySets or Navigation properties which are media resources - } else if (method == HttpMethod.PUT && isEntityOrNavigationMedia(resource)) { - validatePreconditions(request, true); - final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); - final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY); - handler.selectProcessor(MediaEntityProcessor.class) - .updateMediaEntity(request, response, uriInfo, requestFormat, responseFormat); - } else if (method == HttpMethod.DELETE && isEntityOrNavigationMedia(resource)) { - validatePreconditions(request, true); - handler.selectProcessor(MediaEntityProcessor.class) - .deleteMediaEntity(request, response, uriInfo); - } else { - throwMethodNotAllowed(method); - } + throwMethodNotAllowed(method); } } + + private void handlePrimitiveValueDispatching(final ODataRequest request, final ODataResponse response, + final UriResource resource) throws ContentNegotiatorException, + ODataApplicationException, ODataLibraryException, + ODataHandlerException, PreconditionException { + final HttpMethod method = request.getMethod(); + final EdmType type = resource instanceof UriResourceProperty ? + ((UriResourceProperty) resource).getType() : ((UriResourceFunction) resource).getType(); + final RepresentationType valueRepresentationType = + type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary) ? + RepresentationType.BINARY : RepresentationType.VALUE; + if (method == HttpMethod.GET) { + final ContentType requestedContentType = ContentNegotiator. + doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), valueRepresentationType); + handler.selectProcessor(PrimitiveValueProcessor.class) + .readPrimitiveValue(request, response, uriInfo, requestedContentType); + } else if (method == HttpMethod.PUT && resource instanceof UriResourceProperty) { + validatePreconditions(request, false); + final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE), + valueRepresentationType, true); + final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), valueRepresentationType); + handler.selectProcessor(PrimitiveValueProcessor.class) + .updatePrimitiveValue(request, response, uriInfo, requestFormat, responseFormat); + } else if (method == HttpMethod.DELETE && resource instanceof UriResourceProperty) { + validatePreconditions(request, false); + handler.selectProcessor(PrimitiveValueProcessor.class) + .deletePrimitiveValue(request, response, uriInfo); + } else { + throwMethodNotAllowed(method); + } + } + private void handleComplexDispatching(final ODataRequest request, final ODataResponse response, final boolean isCollection) throws ODataApplicationException, ODataLibraryException { final HttpMethod method = request.getMethod(); @@ -466,44 +490,76 @@ public class ODataDispatcher { private void handleEntityDispatching(final ODataRequest request, final ODataResponse response, final boolean isCollection, final boolean isMedia) throws ODataApplicationException, ODataLibraryException { - final HttpMethod method = request.getMethod(); if (isCollection) { - if (method == HttpMethod.GET) { - final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, handler.getCustomContentTypeSupport(), RepresentationType.COLLECTION_ENTITY); - handler.selectProcessor(EntityCollectionProcessor.class) - .readEntityCollection(request, response, uriInfo, requestedContentType); - } else if (method == HttpMethod.POST) { - final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY); - if (isMedia) { - final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); - handler.selectProcessor(MediaEntityProcessor.class) - .createMediaEntity(request, response, uriInfo, requestFormat, responseFormat); - } else { - final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE), - RepresentationType.ENTITY, true); - handler.selectProcessor(EntityProcessor.class) - .createEntity(request, response, uriInfo, requestFormat, responseFormat); - } + handleEntityCollectionDispatching(request, response, isMedia); } else { - throwMethodNotAllowed(method); + handleSingleEntityDispatching(request, response, isMedia, false); + } + } + + + private void handleEntityCollectionDispatching(final ODataRequest request, final ODataResponse response, + final boolean isMedia + ) throws ContentNegotiatorException, ODataApplicationException, ODataLibraryException, + ODataHandlerException { + final HttpMethod method = request.getMethod(); + if (method == HttpMethod.GET) { + final ContentType requestedContentType = ContentNegotiator. + doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), RepresentationType.COLLECTION_ENTITY); + handler.selectProcessor(EntityCollectionProcessor.class) + .readEntityCollection(request, response, uriInfo, requestedContentType); + } else if (method == HttpMethod.POST) { + final ContentType responseFormat = ContentNegotiator. + doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY); + if (isMedia) { + final ContentType requestFormat = ContentType.parse( + request.getHeader(HttpHeader.CONTENT_TYPE)); + handler.selectProcessor(MediaEntityProcessor.class) + .createMediaEntity(request, response, uriInfo, requestFormat, responseFormat); + } else { + final ContentType requestFormat = getSupportedContentType( + request.getHeader(HttpHeader.CONTENT_TYPE), + RepresentationType.ENTITY, true); + handler.selectProcessor(EntityProcessor.class) + .createEntity(request, response, uriInfo, requestFormat, responseFormat); } } else { + throwMethodNotAllowed(method); + } + } + + private boolean isSingletonMedia(final UriResource pathSegment) { + return pathSegment instanceof UriResourceSingleton + && ((UriResourceSingleton) pathSegment).getEntityType().hasStream(); + } + + + + private void handleSingleEntityDispatching(final ODataRequest request, final ODataResponse response, + final boolean isMedia, final boolean isSingleton) throws + ContentNegotiatorException, ODataApplicationException, + ODataLibraryException, ODataHandlerException, PreconditionException { + final HttpMethod method = request.getMethod(); if (method == HttpMethod.GET) { - final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + final ContentType requestedContentType = ContentNegotiator. + doContentNegotiation(uriInfo.getFormatOption(), request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY); handler.selectProcessor(EntityProcessor.class) .readEntity(request, response, uriInfo, requestedContentType); } else if (method == HttpMethod.PUT || method == HttpMethod.PATCH) { validatePreconditions(request, false); - final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE), + final ContentType requestFormat = getSupportedContentType( + request.getHeader(HttpHeader.CONTENT_TYPE), RepresentationType.ENTITY, true); - final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + final ContentType responseFormat = ContentNegotiator. + doContentNegotiation(uriInfo.getFormatOption(), request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY); handler.selectProcessor(EntityProcessor.class) .updateEntity(request, response, uriInfo, requestFormat, responseFormat); - } else if (method == HttpMethod.DELETE) { + } else if (method == HttpMethod.DELETE && !isSingleton) { + validateIsSingleton(method); validatePreconditions(request, false); handler.selectProcessor(isMedia ? MediaEntityProcessor.class : EntityProcessor.class) .deleteEntity(request, response, uriInfo); @@ -511,8 +567,23 @@ public class ODataDispatcher { throwMethodNotAllowed(method); } } + + /*Delete method is not allowed for Entities navigating to Singleton*/ + private void validateIsSingleton(HttpMethod method) throws ODataHandlerException { + final int lastPathSegmentIndex = uriInfo.getUriResourceParts().size() - 1; + final UriResource pathSegment = uriInfo.getUriResourceParts().get(lastPathSegmentIndex); + if(pathSegment instanceof UriResourceNavigation){ + if(uriInfo.getUriResourceParts().get(lastPathSegmentIndex-1) instanceof UriResourceEntitySet){ + if(((UriResourceEntitySet)uriInfo.getUriResourceParts(). + get(lastPathSegmentIndex-1)).getEntitySet().getRelatedBindingTarget( + pathSegment.getSegmentValue())instanceof EdmSingleton){ + throwMethodNotAllowed(method); + } + } + } } + private void validatePreconditions(final ODataRequest request, final boolean isMediaValue) throws PreconditionException { // If needed perform preconditions validation. http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/84a052dc/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java index dfc1d78..39f7c81 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java @@ -88,13 +88,27 @@ public class DataCreator { data.put("ESStream", createESStream(edm, odata)); data.put("ESWithStream", createESWithStream(edm, odata)); data.put("ESPeople", createESPeople(edm, odata)); + data.put("SINav", createSINav(edm, odata)); + data.put("SI", createESTwoPrim(edm, odata)); + linkSINav(data); linkESTwoPrim(data); linkESAllPrim(data); linkESKeyNav(data); linkESTwoKeyNav(data); linkESPeople(data); } + + private EntityCollection createSINav(Edm edm, OData odata) { + final EntityCollection entityCollection = new EntityCollection(); + + entityCollection.getEntities().add(createESTwoKeyNavEntity((short) 1, "1")); + + setEntityType(entityCollection, edm.getEntityType(EntityTypeProvider.nameETTwoKeyNav)); + createEntityId(edm, odata, "ESTwoKeyNav", entityCollection); + createOperations("ESTwoKeyNav", entityCollection, EntityTypeProvider.nameETTwoKeyNav); + return entityCollection; +} private EntityCollection createESMixEnumDefCollComp(Edm edm, OData odata) { final EntityCollection entityCollection = new EntityCollection(); @@ -1362,7 +1376,8 @@ public class DataCreator { final EntityCollection entityCollection = data.get("ESTwoKeyNav"); final List<Entity> esKeyNavTargets = data.get("ESKeyNav").getEntities(); final List<Entity> esTwoKeyNavTargets = data.get("ESTwoKeyNav").getEntities(); - + final List<Entity> SINav = data.get("SINav").getEntities(); + // NavPropertyETKeyNavOne setLink(entityCollection.getEntities().get(0), "NavPropertyETKeyNavOne", esKeyNavTargets.get(0)); setLink(entityCollection.getEntities().get(1), "NavPropertyETKeyNavOne", esKeyNavTargets.get(0)); @@ -1387,6 +1402,27 @@ public class DataCreator { esTwoKeyNavTargets.get(1)); setLinks(entityCollection.getEntities().get(1), "NavPropertyETTwoKeyNavMany", esTwoKeyNavTargets.get(0)); setLinks(entityCollection.getEntities().get(2), "NavPropertyETTwoKeyNavMany", esTwoKeyNavTargets.get(1)); + // NavPropertySINav + setLink(entityCollection.getEntities().get(0), "NavPropertySINav", SINav.get(0)); + setLink(entityCollection.getEntities().get(1), "NavPropertySINav", SINav.get(0)); + setLink(entityCollection.getEntities().get(2), "NavPropertySINav", SINav.get(0)); + + } + + private void linkSINav(final Map<String, EntityCollection> data) { + final EntityCollection entityCollection = data.get("SINav"); + final List<Entity> esKeyNavTargets = data.get("ESKeyNav").getEntities(); + final List<Entity> esTwoKeyNavTargets = data.get("ESTwoKeyNav").getEntities(); + + // NavPropertyETKeyNavOne + setLink(entityCollection.getEntities().get(0), "NavPropertyETKeyNavOne", esKeyNavTargets.get(0)); + + // NavPropertyETTwoKeyNavOne + setLink(entityCollection.getEntities().get(0), "NavPropertyETTwoKeyNavOne", esTwoKeyNavTargets.get(0)); + + // NavPropertyETTwoKeyNavMany + setLinks(entityCollection.getEntities().get(0), "NavPropertyETTwoKeyNavMany", esTwoKeyNavTargets.get(0), + esTwoKeyNavTargets.get(1)); } protected static Property createPrimitive(final String name, final Object value) { http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/84a052dc/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 465ab80..b526887 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 @@ -51,6 +51,7 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.edm.EdmProperty; import org.apache.olingo.commons.api.edm.EdmStructuredType; +import org.apache.olingo.commons.api.edm.EdmSingleton; import org.apache.olingo.commons.api.edm.EdmType; import org.apache.olingo.commons.api.edm.EdmTypeDefinition; import org.apache.olingo.commons.api.edm.FullQualifiedName; @@ -700,4 +701,12 @@ public class DataProvider { super(message, statusCode.getStatusCode(), Locale.ROOT, throwable); } } + + public Entity read(EdmSingleton singleton) { + if (data.containsKey(singleton.getName())) { + EntityCollection entitySet = data.get(singleton.getName()); + return entitySet.getEntities().get(0); + } + return null; + } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/84a052dc/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 e6b625e..da2e936 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 @@ -64,6 +64,7 @@ import org.apache.olingo.server.api.uri.UriInfo; import org.apache.olingo.server.api.uri.UriResourceEntitySet; import org.apache.olingo.server.api.uri.UriResourceNavigation; import org.apache.olingo.server.api.uri.UriResourcePartTyped; +import org.apache.olingo.server.api.uri.UriResourceSingleton; import org.apache.olingo.server.api.uri.queryoption.CountOption; import org.apache.olingo.server.api.uri.queryoption.ExpandOption; import org.apache.olingo.server.api.uri.queryoption.IdOption; @@ -421,10 +422,11 @@ public class TechnicalEntityProcessor extends TechnicalProcessor // final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo); - final EdmEntityType edmEntityType = edmEntitySet == null ? - (EdmEntityType) ((UriResourcePartTyped) uriInfo.getUriResourceParts() - .get(uriInfo.getUriResourceParts().size() - 1)).getType() : - edmEntitySet.getEntityType(); + + //for Singleton/$ref edmEntityset will be null throw error + validateSingletonRef(isReference,edmEntitySet); + + final EdmEntityType edmEntityType = getEdmType(uriInfo, edmEntitySet); final Entity entity = readEntity(uriInfo); @@ -456,6 +458,35 @@ public class TechnicalEntityProcessor extends TechnicalProcessor response.setHeader(HttpHeader.CONTENT_TYPE, requestedFormat.toContentTypeString()); } + /*This method validates if the $ref is called directly on Singleton + * Error is thrown when $ref is called on a Singleton as it is not implemented*/ + private void validateSingletonRef(boolean isReference, EdmEntitySet edmEntitySet) throws + ODataApplicationException { + if(isReference && edmEntitySet==null){ + throw new ODataApplicationException("$ref not implemented on singleton", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + + } + + } + + /*This method returns edmType of the entityset or Singleton*/ + private EdmEntityType getEdmType(UriInfo uriInfo, EdmEntitySet edmEntitySet) { + if(edmEntitySet!=null){ + return edmEntitySet.getEntityType(); + }else if(edmEntitySet==null && uriInfo.getUriResourceParts() + .get(uriInfo.getUriResourceParts().size() - 1) instanceof UriResourcePartTyped){ + return (EdmEntityType) ((UriResourcePartTyped) uriInfo.getUriResourceParts() + .get(uriInfo.getUriResourceParts().size() - 1)).getType(); + }else if((UriResourceSingleton) uriInfo.getUriResourceParts() + .get(0) instanceof UriResourceSingleton){ + return (EdmEntityType)((UriResourceSingleton) uriInfo.getUriResourceParts() + .get(0)).getType(); + } + + return null; + } + private void readEntityCollection(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo, final ContentType requestedContentType, final boolean isReference) throws ODataApplicationException, ODataLibraryException { http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/84a052dc/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java index c59bec9..47a9a63 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java @@ -29,6 +29,7 @@ import org.apache.olingo.commons.api.edm.EdmEntitySet; import org.apache.olingo.commons.api.edm.EdmEntityType; import org.apache.olingo.commons.api.edm.EdmFunction; import org.apache.olingo.commons.api.edm.EdmNavigationProperty; +import org.apache.olingo.commons.api.edm.EdmSingleton; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.http.HttpStatusCode; import org.apache.olingo.server.api.OData; @@ -43,6 +44,7 @@ import org.apache.olingo.server.api.uri.UriResourceAction; import org.apache.olingo.server.api.uri.UriResourceEntitySet; import org.apache.olingo.server.api.uri.UriResourceFunction; import org.apache.olingo.server.api.uri.UriResourceNavigation; +import org.apache.olingo.server.api.uri.UriResourceSingleton; import org.apache.olingo.server.tecsvc.data.DataProvider; /** @@ -72,7 +74,8 @@ public abstract class TechnicalProcessor implements Processor { protected EdmEntitySet getEdmEntitySet(final UriInfoResource uriInfo) throws ODataApplicationException { EdmEntitySet entitySet = null; final List<UriResource> resourcePaths = uriInfo.getUriResourceParts(); - + EdmSingleton singleton = null; + // First must be an entity, an entity collection, a function import, or an action import. blockTypeFilters(resourcePaths.get(0)); if (resourcePaths.get(0) instanceof UriResourceEntitySet) { @@ -81,30 +84,41 @@ public abstract class TechnicalProcessor implements Processor { entitySet = ((UriResourceFunction) resourcePaths.get(0)).getFunctionImport().getReturnedEntitySet(); } else if (resourcePaths.get(0) instanceof UriResourceAction) { entitySet = ((UriResourceAction) resourcePaths.get(0)).getActionImport().getReturnedEntitySet(); + }else if (resourcePaths.get(0) instanceof UriResourceSingleton ) { + singleton =((UriResourceSingleton) resourcePaths.get(0)).getSingleton(); } else { throw new ODataApplicationException("Invalid resource type.", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); } + entitySet = (EdmEntitySet) getEntitySetForNavigation(entitySet, singleton, resourcePaths); + + return entitySet; + } + + private EdmBindingTarget getEntitySetForNavigation(EdmEntitySet entitySet, EdmSingleton singleton, + List<UriResource> resourcePaths) throws ODataApplicationException { int navigationCount = 0; - while (entitySet != null - && ++navigationCount < resourcePaths.size() - && resourcePaths.get(navigationCount) instanceof UriResourceNavigation) { - final UriResourceNavigation uriResourceNavigation = (UriResourceNavigation) resourcePaths.get(navigationCount); - blockTypeFilters(uriResourceNavigation); - if (uriResourceNavigation.getProperty().containsTarget()) { - throw new ODataApplicationException("Containment navigation is not supported.", - HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); - } - final EdmBindingTarget target = entitySet.getRelatedBindingTarget(uriResourceNavigation.getProperty().getName()); - if (target instanceof EdmEntitySet) { - entitySet = (EdmEntitySet) target; - } else { - throw new ODataApplicationException("Singletons are not supported.", - HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + while ((entitySet != null || singleton!=null) + && ++navigationCount < resourcePaths.size() + && resourcePaths.get(navigationCount) instanceof UriResourceNavigation) { + final UriResourceNavigation uriResourceNavigation = + (UriResourceNavigation) resourcePaths.get(navigationCount); + blockTypeFilters(uriResourceNavigation); + if (uriResourceNavigation.getProperty().containsTarget()) { + throw new ODataApplicationException("Containment navigation is not supported.", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + } + EdmBindingTarget target = null ; + if(entitySet!=null){ + target = entitySet.getRelatedBindingTarget(uriResourceNavigation.getProperty().getName()); + }else if(singleton != null){ + target = singleton.getRelatedBindingTarget(uriResourceNavigation.getProperty().getName()); + } + if (target instanceof EdmEntitySet) { + entitySet = (EdmEntitySet) target; + } } - } - return entitySet; } @@ -130,6 +144,9 @@ public abstract class TechnicalProcessor implements Processor { if (resourcePaths.get(0) instanceof UriResourceEntitySet) { final UriResourceEntitySet uriResource = (UriResourceEntitySet) resourcePaths.get(0); entity = dataProvider.read(uriResource.getEntitySet(), uriResource.getKeyPredicates()); + }else if (resourcePaths.get(0) instanceof UriResourceSingleton) { + final UriResourceSingleton uriResource = (UriResourceSingleton) resourcePaths.get(0); + entity = dataProvider.read( uriResource.getSingleton()); } else if (resourcePaths.get(0) instanceof UriResourceFunction) { final UriResourceFunction uriResource = (UriResourceFunction) resourcePaths.get(0); final EdmFunction function = uriResource.getFunction(); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/84a052dc/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java index 591e8e2..bacb700 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java @@ -624,6 +624,112 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } + + @Test + public void dispatchSingleton() throws Exception { + final String uri = "SI"; + final EntityProcessor processor = mock(EntityProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PATCH, uri, processor); + verify(processor).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor, times(2)).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + } + + @Test + public void dispatchSingletonMedia() throws Exception { + final String uri = "SIMedia/$value"; + final MediaEntityProcessor processor = mock(MediaEntityProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readMediaEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor).updateMediaEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + } + + @Test + public void dispatchSingletonNavigation() throws Exception { + final String uri = "SINav/NavPropertyETTwoKeyNavOne"; + final String sigletonNavUri = "ESTwoKeyNav(PropertyInt16=1,PropertyString='1')/NavPropertySINav"; + final String sigletonManyNavUri = "SINav/NavPropertyETTwoKeyNavMany"; + final EntityProcessor processor = mock(EntityProcessor.class); + final EntityCollectionProcessor collectionProcessor = mock(EntityCollectionProcessor.class); + + dispatch(HttpMethod.GET, sigletonNavUri, processor); + verify(processor).readEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PATCH, sigletonNavUri, processor); + verify(processor).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, sigletonNavUri, processor); + verify(processor, times(2)).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, sigletonNavUri, processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, sigletonNavUri, processor); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor, times(2)).readEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PATCH, uri, processor); + verify(processor, times(3)).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor, times(4)).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deleteEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + + dispatch(HttpMethod.GET, sigletonManyNavUri, collectionProcessor); + verify(collectionProcessor).readEntityCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.PATCH, sigletonManyNavUri, processor); + + dispatchMethodNotAllowed(HttpMethod.PUT, sigletonManyNavUri, processor); + + dispatch(HttpMethod.POST, sigletonManyNavUri, processor); + verify(processor).createEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + + dispatchMethodNotAllowed(HttpMethod.DELETE, sigletonManyNavUri, processor); + } + @Test public void dispatchMedia() throws Exception { final String uri = "ESMedia(1)/$value"; @@ -859,6 +965,9 @@ public class ODataHandlerImplTest { public void dispatchReference() throws Exception { final String uri = "ESAllPrim(0)/NavPropertyETTwoPrimOne/$ref"; final String uriMany = "ESAllPrim(0)/NavPropertyETTwoPrimMany/$ref"; + final String singletonUri = "SINav/NavPropertyETKeyNavOne/$ref"; + final String singletonUriMany = "SINav/NavPropertyETTwoKeyNavMany/$ref"; + final String singleUri = "SINav/$ref"; final ReferenceProcessor processor = mock(ReferenceProcessor.class); dispatch(HttpMethod.GET, uri, processor); @@ -882,12 +991,52 @@ public class ODataHandlerImplTest { dispatch(HttpMethod.DELETE, uriMany, "$id=ESTwoPrim(1)", null, null, processor); verify(processor).deleteReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); - dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + + //singleton URIs + + dispatch(HttpMethod.GET, singletonUri, processor); + verify(processor, times(2)).readReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.PATCH, singletonUri, processor); + verify(processor, times(3)).updateReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, singletonUri, processor); + verify(processor, times(4)).updateReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, singletonUri, processor); + + dispatch(HttpMethod.GET, singleUri, processor); + verify(processor, times(3)).readReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.PATCH, singleUri, processor); + verify(processor, times(5)).updateReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, singleUri, processor); + verify(processor, times(6)).updateReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, singleUri, processor); + + dispatch(HttpMethod.POST, singletonUriMany, processor); + verify(processor, times(2)).createReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, singletonUriMany, "$id=ESTwoPrim(1)", null, null, processor); + verify(processor, times(2)).deleteReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.HEAD, singletonUriMany, processor); } @Test public void dispatchReferenceCollection() throws Exception { final String uri = "ESAllPrim(0)/NavPropertyETTwoPrimMany/$ref"; + final String singletonUri = "SINav/NavPropertyETTwoKeyNavMany/$ref"; final ReferenceCollectionProcessor processor = mock(ReferenceCollectionProcessor.class); dispatch(HttpMethod.GET, uri, processor); @@ -896,7 +1045,17 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); - dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + + //singleton ref + dispatch(HttpMethod.GET, singletonUri, processor); + verify(processor, times(2)).readReferenceCollection(any(ODataRequest.class), + any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.PATCH, singletonUri, processor); + dispatchMethodNotAllowed(HttpMethod.PUT, singletonUri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, singletonUri, processor); } @Test
