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 2016/02/24 15:28:46 UTC
olingo-odata4 git commit: [OLINGO-884] Allow PUT on navigation for
media resource values
Repository: olingo-odata4
Updated Branches:
refs/heads/master 0879bfbe5 -> 73f465102
[OLINGO-884] Allow PUT on navigation for media resource 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/73f46510
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/73f46510
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/73f46510
Branch: refs/heads/master
Commit: 73f465102a8ea74a4ca6eb3ecc0c8fffbcc3a039
Parents: 0879bfb
Author: Christian Amend <ch...@sap.com>
Authored: Wed Feb 24 15:26:52 2016 +0100
Committer: Christian Amend <ch...@sap.com>
Committed: Wed Feb 24 15:26:52 2016 +0100
----------------------------------------------------------------------
.../olingo/server/core/ODataDispatcher.java | 92 +++++++--------
.../core/uri/parser/ResourcePathParser.java | 30 ++++-
.../uri/parser/UriParserSemanticException.java | 4 +-
.../server-core-exceptions-i18n.properties | 1 +
.../olingo/server/core/ODataHandlerTest.java | 54 ++++++++-
.../core/uri/parser/TestFullResourcePath.java | 113 ++++++++++---------
.../core/uri/testutil/TestUriValidator.java | 1 +
7 files changed, 189 insertions(+), 106 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/73f46510/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 4971c8e..ceacd10 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
@@ -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
@@ -83,26 +83,26 @@ public class ODataDispatcher {
}
public void dispatch(final ODataRequest request, final ODataResponse response) throws ODataApplicationException,
- ODataLibraryException {
+ ODataLibraryException {
switch (uriInfo.getKind()) {
case metadata:
checkMethod(request.getMethod(), HttpMethod.GET);
final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
request, handler.getCustomContentTypeSupport(), RepresentationType.METADATA);
handler.selectProcessor(MetadataProcessor.class)
- .readMetadata(request, response, uriInfo, requestedContentType);
+ .readMetadata(request, response, uriInfo, requestedContentType);
break;
case service:
checkMethod(request.getMethod(), HttpMethod.GET);
if ("".equals(request.getRawODataPath())) {
handler.selectProcessor(RedirectProcessor.class)
- .redirect(request, response);
+ .redirect(request, response);
} else {
final ContentType serviceContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
request, handler.getCustomContentTypeSupport(), RepresentationType.SERVICE);
handler.selectProcessor(ServiceDocumentProcessor.class)
- .readServiceDocument(request, response, uriInfo, serviceContentType);
+ .readServiceDocument(request, response, uriInfo, serviceContentType);
}
break;
@@ -113,7 +113,7 @@ public class ODataDispatcher {
case batch:
checkMethod(request.getMethod(), HttpMethod.POST);
new BatchHandler(handler, handler.selectProcessor(BatchProcessor.class))
- .process(request, response, true);
+ .process(request, response, true);
break;
default:
@@ -142,7 +142,7 @@ public class ODataDispatcher {
case entitySet:
case navigationProperty:
handleEntityDispatching(request, response,
- ((UriResourcePartTyped) lastPathSegment).isCollection(), isMedia(lastPathSegment));
+ ((UriResourcePartTyped) lastPathSegment).isCollection(), isEntityOrNavigationMedia(lastPathSegment));
break;
case count:
@@ -211,7 +211,7 @@ public class ODataDispatcher {
final EdmReturnType returnType = action.getReturnType();
if (returnType == null) {
handler.selectProcessor(ActionVoidProcessor.class)
- .processActionVoid(request, response, uriInfo, requestFormat);
+ .processActionVoid(request, response, uriInfo, requestFormat);
} else {
final boolean isCollection = returnType.isCollection();
ContentType responseFormat;
@@ -222,10 +222,10 @@ public class ODataDispatcher {
isCollection ? RepresentationType.COLLECTION_ENTITY : RepresentationType.ENTITY);
if (isCollection) {
handler.selectProcessor(ActionEntityCollectionProcessor.class)
- .processActionEntityCollection(request, response, uriInfo, requestFormat, responseFormat);
+ .processActionEntityCollection(request, response, uriInfo, requestFormat, responseFormat);
} else {
handler.selectProcessor(ActionEntityProcessor.class)
- .processActionEntity(request, response, uriInfo, requestFormat, responseFormat);
+ .processActionEntity(request, response, uriInfo, requestFormat, responseFormat);
}
break;
@@ -235,10 +235,10 @@ public class ODataDispatcher {
isCollection ? RepresentationType.COLLECTION_PRIMITIVE : RepresentationType.PRIMITIVE);
if (isCollection) {
handler.selectProcessor(ActionPrimitiveCollectionProcessor.class)
- .processActionPrimitiveCollection(request, response, uriInfo, requestFormat, responseFormat);
+ .processActionPrimitiveCollection(request, response, uriInfo, requestFormat, responseFormat);
} else {
handler.selectProcessor(ActionPrimitiveProcessor.class)
- .processActionPrimitive(request, response, uriInfo, requestFormat, responseFormat);
+ .processActionPrimitive(request, response, uriInfo, requestFormat, responseFormat);
}
break;
@@ -248,10 +248,10 @@ public class ODataDispatcher {
isCollection ? RepresentationType.COLLECTION_COMPLEX : RepresentationType.COMPLEX);
if (isCollection) {
handler.selectProcessor(ActionComplexCollectionProcessor.class)
- .processActionComplexCollection(request, response, uriInfo, requestFormat, responseFormat);
+ .processActionComplexCollection(request, response, uriInfo, requestFormat, responseFormat);
} else {
handler.selectProcessor(ActionComplexProcessor.class)
- .processActionComplex(request, response, uriInfo, requestFormat, responseFormat);
+ .processActionComplex(request, response, uriInfo, requestFormat, responseFormat);
}
break;
@@ -273,13 +273,13 @@ public class ODataDispatcher {
final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
request, handler.getCustomContentTypeSupport(), RepresentationType.COLLECTION_REFERENCE);
handler.selectProcessor(ReferenceCollectionProcessor.class)
- .readReferenceCollection(request, response, uriInfo, responseFormat);
+ .readReferenceCollection(request, response, uriInfo, responseFormat);
} else if (isCollection && httpMethod == HttpMethod.POST) {
final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE),
RepresentationType.REFERENCE, true);
handler.selectProcessor(ReferenceProcessor.class)
- .createReference(request, response, uriInfo, requestFormat);
+ .createReference(request, response, uriInfo, requestFormat);
} else if (!isCollection && httpMethod == HttpMethod.GET) {
final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
@@ -290,11 +290,11 @@ public class ODataDispatcher {
final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE),
RepresentationType.REFERENCE, true);
handler.selectProcessor(ReferenceProcessor.class)
- .updateReference(request, response, uriInfo, requestFormat);
+ .updateReference(request, response, uriInfo, requestFormat);
} else if (httpMethod == HttpMethod.DELETE) {
handler.selectProcessor(ReferenceProcessor.class)
- .deleteReference(request, response, uriInfo);
+ .deleteReference(request, response, uriInfo);
} else {
throwMethodNotAllowed(httpMethod);
@@ -303,6 +303,7 @@ 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
@@ -336,18 +337,20 @@ public class ODataDispatcher {
}
} 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);
- } else if (method == HttpMethod.PUT && resource instanceof UriResourceEntitySet) {
+ //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 && resource instanceof UriResourceEntitySet) {
+ } else if (method == HttpMethod.DELETE && isEntityOrNavigationMedia(resource)) {
validatePreconditions(request, true);
handler.selectProcessor(MediaEntityProcessor.class)
.deleteMediaEntity(request, response, uriInfo);
@@ -367,10 +370,10 @@ public class ODataDispatcher {
request, handler.getCustomContentTypeSupport(), complexRepresentationType);
if (isCollection) {
handler.selectProcessor(ComplexCollectionProcessor.class)
- .readComplexCollection(request, response, uriInfo, requestedContentType);
+ .readComplexCollection(request, response, uriInfo, requestedContentType);
} else {
handler.selectProcessor(ComplexProcessor.class)
- .readComplex(request, response, uriInfo, requestedContentType);
+ .readComplex(request, response, uriInfo, requestedContentType);
}
} else if (method == HttpMethod.PUT || method == HttpMethod.PATCH) {
validatePreconditions(request, false);
@@ -380,19 +383,19 @@ public class ODataDispatcher {
request, handler.getCustomContentTypeSupport(), complexRepresentationType);
if (isCollection) {
handler.selectProcessor(ComplexCollectionProcessor.class)
- .updateComplexCollection(request, response, uriInfo, requestFormat, responseFormat);
+ .updateComplexCollection(request, response, uriInfo, requestFormat, responseFormat);
} else {
handler.selectProcessor(ComplexProcessor.class)
- .updateComplex(request, response, uriInfo, requestFormat, responseFormat);
+ .updateComplex(request, response, uriInfo, requestFormat, responseFormat);
}
} else if (method == HttpMethod.DELETE) {
validatePreconditions(request, false);
if (isCollection) {
handler.selectProcessor(ComplexCollectionProcessor.class)
- .deleteComplexCollection(request, response, uriInfo);
+ .deleteComplexCollection(request, response, uriInfo);
} else {
handler.selectProcessor(ComplexProcessor.class)
- .deleteComplex(request, response, uriInfo);
+ .deleteComplex(request, response, uriInfo);
}
} else {
throwMethodNotAllowed(method);
@@ -409,10 +412,10 @@ public class ODataDispatcher {
request, handler.getCustomContentTypeSupport(), representationType);
if (isCollection) {
handler.selectProcessor(PrimitiveCollectionProcessor.class)
- .readPrimitiveCollection(request, response, uriInfo, requestedContentType);
+ .readPrimitiveCollection(request, response, uriInfo, requestedContentType);
} else {
handler.selectProcessor(PrimitiveProcessor.class)
- .readPrimitive(request, response, uriInfo, requestedContentType);
+ .readPrimitive(request, response, uriInfo, requestedContentType);
}
} else if (method == HttpMethod.PUT || method == HttpMethod.PATCH) {
validatePreconditions(request, false);
@@ -422,19 +425,19 @@ public class ODataDispatcher {
request, handler.getCustomContentTypeSupport(), representationType);
if (isCollection) {
handler.selectProcessor(PrimitiveCollectionProcessor.class)
- .updatePrimitiveCollection(request, response, uriInfo, requestFormat, responseFormat);
+ .updatePrimitiveCollection(request, response, uriInfo, requestFormat, responseFormat);
} else {
handler.selectProcessor(PrimitiveProcessor.class)
- .updatePrimitive(request, response, uriInfo, requestFormat, responseFormat);
+ .updatePrimitive(request, response, uriInfo, requestFormat, responseFormat);
}
} else if (method == HttpMethod.DELETE) {
validatePreconditions(request, false);
if (isCollection) {
handler.selectProcessor(PrimitiveCollectionProcessor.class)
- .deletePrimitiveCollection(request, response, uriInfo);
+ .deletePrimitiveCollection(request, response, uriInfo);
} else {
handler.selectProcessor(PrimitiveProcessor.class)
- .deletePrimitive(request, response, uriInfo);
+ .deletePrimitive(request, response, uriInfo);
}
} else {
throwMethodNotAllowed(method);
@@ -449,15 +452,15 @@ public class ODataDispatcher {
|| resource instanceof UriResourceFunction
&& ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.ENTITY) {
handler.selectProcessor(CountEntityCollectionProcessor.class)
- .countEntityCollection(request, response, uriInfo);
+ .countEntityCollection(request, response, uriInfo);
} else if (resource instanceof UriResourcePrimitiveProperty
|| resource instanceof UriResourceFunction
&& ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.PRIMITIVE) {
handler.selectProcessor(CountPrimitiveCollectionProcessor.class)
- .countPrimitiveCollection(request, response, uriInfo);
+ .countPrimitiveCollection(request, response, uriInfo);
} else {
handler.selectProcessor(CountComplexCollectionProcessor.class)
- .countComplexCollection(request, response, uriInfo);
+ .countComplexCollection(request, response, uriInfo);
}
}
@@ -469,19 +472,19 @@ public class ODataDispatcher {
final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
request, handler.getCustomContentTypeSupport(), RepresentationType.COLLECTION_ENTITY);
handler.selectProcessor(EntityCollectionProcessor.class)
- .readEntityCollection(request, response, uriInfo, requestedContentType);
+ .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);
+ .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);
+ .createEntity(request, response, uriInfo, requestFormat, responseFormat);
}
} else {
throwMethodNotAllowed(method);
@@ -491,7 +494,7 @@ public class ODataDispatcher {
final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY);
handler.selectProcessor(EntityProcessor.class)
- .readEntity(request, response, uriInfo, requestedContentType);
+ .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),
@@ -499,11 +502,11 @@ public class ODataDispatcher {
final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY);
handler.selectProcessor(EntityProcessor.class)
- .updateEntity(request, response, uriInfo, requestFormat, responseFormat);
+ .updateEntity(request, response, uriInfo, requestFormat, responseFormat);
} else if (method == HttpMethod.DELETE) {
validatePreconditions(request, false);
handler.selectProcessor(isMedia ? MediaEntityProcessor.class : EntityProcessor.class)
- .deleteEntity(request, response, uriInfo);
+ .deleteEntity(request, response, uriInfo);
} else {
throwMethodNotAllowed(method);
}
@@ -537,7 +540,7 @@ public class ODataDispatcher {
private ContentType getSupportedContentType(final String contentTypeHeader,
final RepresentationType representationType, final boolean mustNotBeNull)
- throws ODataHandlerException, ContentNegotiatorException {
+ throws ODataHandlerException, ContentNegotiatorException {
if (contentTypeHeader == null) {
if (mustNotBeNull) {
throw new ODataHandlerException(contentTypeHeader, ODataHandlerException.MessageKeys.MISSING_CONTENT_TYPE);
@@ -555,7 +558,8 @@ public class ODataDispatcher {
return contentType;
}
- private boolean isMedia(final UriResource pathSegment) {
+ private boolean isEntityOrNavigationMedia(final UriResource pathSegment) {
+ //This method MUST NOT check if the resource is of type function since these are handled differently
return pathSegment instanceof UriResourceEntitySet
&& ((UriResourceEntitySet) pathSegment).getEntityType().hasStream()
|| pathSegment instanceof UriResourceNavigation
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/73f46510/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java
index 8d6d52d..5c280c8 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.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
@@ -39,6 +39,9 @@ import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
import org.apache.olingo.server.api.uri.UriParameter;
import org.apache.olingo.server.api.uri.UriResource;
+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.UriResourcePartTyped;
import org.apache.olingo.server.api.uri.queryoption.AliasQueryOption;
import org.apache.olingo.server.core.uri.UriResourceActionImpl;
@@ -120,7 +123,7 @@ public class ResourcePathParser {
tokenizer = new UriTokenizer(pathSegment);
ParserHelper.requireNext(tokenizer, TokenKind.CROSSJOIN);
ParserHelper.requireNext(tokenizer, TokenKind.OPEN);
- // At least one entity-set name is mandatory. Try to fetch all.
+ // At least one entity-set name is mandatory. Try to fetch all.
List<String> entitySetNames = new ArrayList<String>();
do {
ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
@@ -153,6 +156,7 @@ public class ResourcePathParser {
ParserHelper.requireTokenEnd(tokenizer);
requireTyped(previous, "$value");
if (!((UriResourcePartTyped) previous).isCollection()) {
+ requireMediaResourceInCaseOfEntity(previous);
return new UriResourceValueImpl();
} else {
throw new UriParserSemanticException("$value is only allowed on typed path segments.",
@@ -160,6 +164,26 @@ public class ResourcePathParser {
}
}
+ private void requireMediaResourceInCaseOfEntity(UriResource resource) throws UriParserSemanticException {
+ // If the resource is an entity or navigatio
+ if (resource instanceof UriResourceEntitySet && !((UriResourceEntitySet) resource).getEntityType().hasStream()
+ || resource instanceof UriResourceNavigation
+ && !((EdmEntityType) ((UriResourceNavigation) resource).getType()).hasStream()) {
+ throw new UriParserSemanticException("$value on entity is only allowed on media resources.",
+ UriParserSemanticException.MessageKeys.NOT_A_MEDIA_RESOURCE, resource.getSegmentValue());
+ }
+
+ // Functions can also deliver an entity. In this case we have to check if the returned entity is a media resource
+ if (resource instanceof UriResourceFunction) {
+ EdmType returnType = ((UriResourceFunction) resource).getFunction().getReturnType().getType();
+ //Collection check is above so not needed here
+ if (returnType instanceof EdmEntityType && !((EdmEntityType) returnType).hasStream()) {
+ throw new UriParserSemanticException("$value on returned entity is only allowed on media resources.",
+ UriParserSemanticException.MessageKeys.NOT_A_MEDIA_RESOURCE, resource.getSegmentValue());
+ }
+ }
+ }
+
private UriResource count(final UriResource previous) throws UriParserException {
ParserHelper.requireTokenEnd(tokenizer);
requireTyped(previous, "$count");
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/73f46510/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSemanticException.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSemanticException.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSemanticException.java
index 423c875..db33a6a 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSemanticException.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSemanticException.java
@@ -66,7 +66,9 @@ public class UriParserSemanticException extends UriParserException {
/** parameter: complex parameter value */
COMPLEX_PARAMETER_IN_RESOURCE_PATH,
/** parameters: left type, right type */
- TYPES_NOT_COMPATIBLE;
+ TYPES_NOT_COMPATIBLE,
+ /** parameter: addressed resource name*/
+ NOT_A_MEDIA_RESOURCE;
@Override
public String getKey() {
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/73f46510/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties b/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
index ef5e744..c563a67 100644
--- a/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
+++ b/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
@@ -72,6 +72,7 @@ UriParserSemanticException.NOT_IMPLEMENTED='%1$s' is not implemented!
UriParserSemanticException.NAMESPACE_NOT_ALLOWED_AT_FIRST_ELEMENT=Namespace is not allowed for Entity Sets, Singletons, Action Imports and Function Imports; found '%1$s'.
UriParserSemanticException.COMPLEX_PARAMETER_IN_RESOURCE_PATH=Complex parameters must not appear in resource path segments; found: '%1$s'.
UriParserSemanticException.TYPES_NOT_COMPATIBLE=The types '%1$s' and '%2$s' are not compatible.
+UriParserSemanticException.NOT_A_MEDIA_RESOURCE=The resource '%1$s' is not a media resource. $value can only be applied on media resources.
UriValidationException.UNSUPPORTED_QUERY_OPTION=The query option '%1$s' is not supported.
UriValidationException.UNSUPPORTED_URI_KIND=The URI kind '%1$s' is not supported.
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/73f46510/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java
index d4b87d4..a228e40 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java
@@ -318,7 +318,7 @@ public class ODataHandlerTest {
dispatch(HttpMethod.GET, "FICRTString()", primitiveProcessor);
verify(primitiveProcessor).readPrimitive(
any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class));
-
+
// FINRTInt16 is not composable so /$value is not allowed
final String valueUri = "FINRTInt16()/$value";
final PrimitiveValueProcessor primitiveValueProcessor = mock(PrimitiveValueProcessor.class);
@@ -327,7 +327,7 @@ public class ODataHandlerTest {
dispatchMethodWithError(HttpMethod.PATCH, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST);
dispatchMethodWithError(HttpMethod.PUT, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST);
dispatchMethodWithError(HttpMethod.DELETE, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST);
-
+
final String primitiveCollectionUri = "FICRTCollString()";
PrimitiveCollectionProcessor primitiveCollectionProcessor = mock(PrimitiveCollectionProcessor.class);
dispatch(HttpMethod.GET, primitiveCollectionUri, primitiveCollectionProcessor);
@@ -495,6 +495,52 @@ public class ODataHandlerTest {
dispatchMethodNotAllowed(HttpMethod.POST, uri, processor);
dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor);
}
+
+ @Test
+ public void dispatchValueOnNoMedia() throws Exception {
+ final String uri = "ESAllPrim(1)/$value";
+ final MediaEntityProcessor processor = mock(MediaEntityProcessor.class);
+
+ dispatch(HttpMethod.GET, uri, processor);
+ verifyZeroInteractions(processor);
+
+ dispatch(HttpMethod.POST, uri, processor);
+ verifyZeroInteractions(processor);
+
+ dispatch(HttpMethod.PUT, uri, processor);
+ verifyZeroInteractions(processor);
+
+ dispatch(HttpMethod.DELETE, uri, processor);
+ verifyZeroInteractions(processor);
+ }
+
+ @Test
+ public void dispatchMediaWithNavigation() throws Exception {
+ /*
+ * In Java we decided that any kind of navigation will be accepted. This means that a $value on a media resource
+ * must be dispatched as well
+ */
+ final String uri = "ESKeyNav(1)/NavPropertyETMediaOne/$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));
+
+ dispatchMethodNotAllowed(HttpMethod.POST, "ESKeyNav(1)/NavPropertyETMediaOne", processor);
+
+ dispatchMethodNotAllowed(HttpMethod.POST, "ESKeyNav(1)/NavPropertyETMediaOne/$value", processor);
+
+ dispatch(HttpMethod.PUT, uri, processor);
+ verify(processor).updateMediaEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class),
+ any(ContentType.class), any(ContentType.class));
+
+ dispatch(HttpMethod.DELETE, uri, processor);
+ verify(processor).deleteMediaEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class));
+
+ dispatchMethodNotAllowed(HttpMethod.POST, uri, processor);
+ dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor);
+ }
@Test
public void dispatchMediaDeleteIndirect() throws Exception {
@@ -758,8 +804,8 @@ public class ODataHandlerTest {
assertEquals(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatusCode());
assertNotNull(response.getContent());
}
-
- private void dispatchMethodWithError(final HttpMethod method, final String path, final Processor processor,
+
+ private void dispatchMethodWithError(final HttpMethod method, final String path, final Processor processor,
final HttpStatusCode statusCode) {
final ODataResponse response = dispatch(method, path, processor);
assertEquals(statusCode.getStatusCode(), response.getStatusCode());
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/73f46510/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java
index fc9a0b8..32c56b4 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java
@@ -58,6 +58,20 @@ public class TestFullResourcePath {
private final FilterValidator testFilter = new FilterValidator().setEdm(edm);
@Test
+ public void valueOnNonMediaEntity() throws Exception {
+ testUri.runEx("ESAllPrim/$value").isExSemantic(UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS);
+ testUri.runEx("ESAllPrim(1)/NavPropertyETTwoPrimMany/$value").isExSemantic(
+ UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS);
+ testUri.runEx("FICRTCollESMedia()/$value")
+ .isExSemantic(UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS);
+
+ testUri.runEx("ESAllPrim(1)/$value").isExSemantic(UriParserSemanticException.MessageKeys.NOT_A_MEDIA_RESOURCE);
+ testUri.runEx("ESAllPrim(1)/NavPropertyETTwoPrimOne/$value").isExSemantic(
+ UriParserSemanticException.MessageKeys.NOT_A_MEDIA_RESOURCE);
+ testUri.runEx("FICRTETKeyNav()/$value").isExSemantic(UriParserSemanticException.MessageKeys.NOT_A_MEDIA_RESOURCE);
+ }
+
+ @Test
public void enumAndTypeDefAsKey() throws Exception {
testUri
.run("ESMixEnumDefCollComp(PropertyEnumString=olingo.odata.test1.ENString'String1',PropertyDefString='abc')")
@@ -3205,15 +3219,6 @@ public class TestFullResourcePath {
.isType(EntityTypeProvider.nameETTwoKeyNav)
.isTypeFilterOnEntry(EntityTypeProvider.nameETBaseTwoKeyNav)
.n().isRef();
-
- testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')/olingo.odata.test1.ETBaseTwoKeyNav/$value")
- .goPath().first()
- .isEntitySet("ESTwoKeyNav")
- .isKeyPredicate(0, "PropertyInt16", "1")
- .isKeyPredicate(1, "PropertyString", "'2'")
- .isType(EntityTypeProvider.nameETTwoKeyNav)
- .isTypeFilterOnEntry(EntityTypeProvider.nameETBaseTwoKeyNav)
- .n().isValue();
}
@Test
@@ -3768,7 +3773,7 @@ public class TestFullResourcePath {
public void filterFunctions() throws Exception {
testFilter.runOnETAllPrim(
"olingo.odata.test1.UFCRTETTwoKeyNavParamCTTwoPrim(ParameterCTTwoPrim=@ParamAlias) eq null"
- + "&@ParamAlias={}")
+ + "&@ParamAlias={}")
.is("<<UFCRTETTwoKeyNavParamCTTwoPrim> eq <null>>")
.left().goPath()
.first()
@@ -4687,7 +4692,7 @@ public class TestFullResourcePath {
testFilter.runOnETKeyNav("NavPropertyETTwoKeyNavMany/any(d:d/PropertyInt16 eq 1 or "
+ "d/CollPropertyString/any(e:e eq 'SomeString'))")
- .is("<NavPropertyETTwoKeyNavMany/<ANY;<<<d/PropertyInt16> eq <1>>"
+ .is("<NavPropertyETTwoKeyNavMany/<ANY;<<<d/PropertyInt16> eq <1>>"
+ " or <d/CollPropertyString/<ANY;<<e> eq <'SomeString'>>>>>>>")
.root().goPath()
.first().isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
@@ -4708,7 +4713,7 @@ public class TestFullResourcePath {
.isType(EntityTypeProvider.nameETTwoKeyNav, false)
.n().isUriPathInfoKind(UriResourceKind.primitiveProperty)
.isPrimitiveProperty("CollPropertyString", PropertyProvider.nameString, true)
- .at(2).isUriPathInfoKind(UriResourceKind.lambdaAny)
+ .at(2).isUriPathInfoKind(UriResourceKind.lambdaAny)
.goLambdaExpression()
.root().left().goPath()
.first().isUriPathInfoKind(UriResourceKind.lambdaVariable)
@@ -4743,7 +4748,7 @@ public class TestFullResourcePath {
.isType(PropertyProvider.nameString, false);
testFilter.runOnETKeyNav("NavPropertyETTwoKeyNavMany/any(d:d/PropertyString eq 'SomeString' and "
- + "d/CollPropertyString/any(e:e eq d/PropertyString))")
+ + "d/CollPropertyString/any(e:e eq d/PropertyString))")
.is("<NavPropertyETTwoKeyNavMany/<ANY;<<<d/PropertyString> eq <'SomeString'>> and "
+ "<d/CollPropertyString/<ANY;<<e> eq <d/PropertyString>>>>>>>")
.root().goPath()
@@ -5415,82 +5420,82 @@ public class TestFullResourcePath {
*/
@Test
public void searchQueryPhraseAbnfTestcases() throws Exception {
- // <TestCase Name="5.1.7 Search - simple phrase" Rule="queryOptions">
+ // <TestCase Name="5.1.7 Search - simple phrase" Rule="queryOptions">
testUri.run("ESTwoKeyNav", "$search=\"blue%20green\"");
- // <TestCase Name="5.1.7 Search - simple phrase" Rule="queryOptions">
+ // <TestCase Name="5.1.7 Search - simple phrase" Rule="queryOptions">
testUri.run("ESTwoKeyNav", "$search=\"blue%20green%22");
- // <TestCase Name="5.1.7 Search - phrase with escaped double-quote" Rule="queryOptions">
- // <Input>$search="blue\"green"</Input>
+ // <TestCase Name="5.1.7 Search - phrase with escaped double-quote" Rule="queryOptions">
+ // <Input>$search="blue\"green"</Input>
testUri.run("ESTwoKeyNav", "$search=\"blue\\\"green\"");
- // <TestCase Name="5.1.7 Search - phrase with escaped backslash" Rule="queryOptions">
- // <Input>$search="blue\\green"</Input>
+ // <TestCase Name="5.1.7 Search - phrase with escaped backslash" Rule="queryOptions">
+ // <Input>$search="blue\\green"</Input>
testUri.run("ESTwoKeyNav", "$search=\"blue\\\\green\"");
- // <TestCase Name="5.1.7 Search - phrase with unescaped double-quote" Rule="queryOptions" FailAt="14">
+ // <TestCase Name="5.1.7 Search - phrase with unescaped double-quote" Rule="queryOptions" FailAt="14">
testUri.runEx("ESTwoKeyNav", "$search=\"blue\"green\"")
- .isExceptionMessage(SearchParserException.MessageKeys.TOKENIZER_EXCEPTION);
- // <TestCase Name="5.1.7 Search - phrase with unescaped double-quote" Rule="queryOptions" FailAt="16">
+ .isExceptionMessage(SearchParserException.MessageKeys.TOKENIZER_EXCEPTION);
+ // <TestCase Name="5.1.7 Search - phrase with unescaped double-quote" Rule="queryOptions" FailAt="16">
testUri.runEx("ESTwoKeyNav", "$search=\"blue%22green\"")
- .isExceptionMessage(SearchParserException.MessageKeys.TOKENIZER_EXCEPTION);
+ .isExceptionMessage(SearchParserException.MessageKeys.TOKENIZER_EXCEPTION);
- // <TestCase Name="5.1.7 Search - implicit AND" Rule="queryOptions">
- // <Input>$search=blue green</Input>
- // SearchassertQuery("\"blue%20green\"").resultsIn();
+ // <TestCase Name="5.1.7 Search - implicit AND" Rule="queryOptions">
+ // <Input>$search=blue green</Input>
+ // SearchassertQuery("\"blue%20green\"").resultsIn();
testUri.run("ESTwoKeyNav", "$search=blue green");
- // <TestCase Name="5.1.7 Search - implicit AND, encoced" Rule="queryOptions">
- // SearchassertQuery("blue%20green").resultsIn();
+ // <TestCase Name="5.1.7 Search - implicit AND, encoced" Rule="queryOptions">
+ // SearchassertQuery("blue%20green").resultsIn();
testUri.run("ESTwoKeyNav", "$search=blue%20green");
- // <TestCase Name="5.1.7 Search - AND" Rule="queryOptions">
- // <Input>$search=blue AND green</Input>
+ // <TestCase Name="5.1.7 Search - AND" Rule="queryOptions">
+ // <Input>$search=blue AND green</Input>
testUri.run("ESTwoKeyNav", "$search=blue AND green");
- // <TestCase Name="5.1.7 Search - OR" Rule="queryOptions">
- // <Input>$search=blue OR green</Input>
+ // <TestCase Name="5.1.7 Search - OR" Rule="queryOptions">
+ // <Input>$search=blue OR green</Input>
testUri.run("ESTwoKeyNav", "$search=blue OR green");
- // <TestCase Name="5.1.7 Search - NOT" Rule="queryOptions">
- // <Input>$search=blue NOT green</Input>
+ // <TestCase Name="5.1.7 Search - NOT" Rule="queryOptions">
+ // <Input>$search=blue NOT green</Input>
testUri.run("ESTwoKeyNav", "$search=blue NOT green");
- // <TestCase Name="5.1.7 Search - only NOT" Rule="queryOptions">
- // <Input>$search=NOT blue</Input>
+ // <TestCase Name="5.1.7 Search - only NOT" Rule="queryOptions">
+ // <Input>$search=NOT blue</Input>
testUri.run("ESTwoKeyNav", "$search=NOT blue");
- // <TestCase Name="5.1.7 Search - multiple" Rule="queryOptions">
- // <Input>$search=foo AND bar OR foo AND baz OR that AND bar OR that AND baz</Input>
+ // <TestCase Name="5.1.7 Search - multiple" Rule="queryOptions">
+ // <Input>$search=foo AND bar OR foo AND baz OR that AND bar OR that AND baz</Input>
testUri.run("ESTwoKeyNav", "$search=foo AND bar OR foo AND baz OR that AND bar OR that AND baz");
- // <TestCase Name="5.1.7 Search - multiple" Rule="queryOptions">
- // <Input>$search=(foo OR that) AND (bar OR baz)</Input>
+ // <TestCase Name="5.1.7 Search - multiple" Rule="queryOptions">
+ // <Input>$search=(foo OR that) AND (bar OR baz)</Input>
testUri.run("ESTwoKeyNav", "$search=(foo OR that) AND (bar OR baz)");
- // <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
- // <Input>$search=foo AND (bar OR baz)</Input>
+ // <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
+ // <Input>$search=foo AND (bar OR baz)</Input>
testUri.run("ESTwoKeyNav", "$search=foo AND (bar OR baz)");
- // <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
- // <Input>$search=(foo AND bar) OR baz</Input>
+ // <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
+ // <Input>$search=(foo AND bar) OR baz</Input>
testUri.run("ESTwoKeyNav", "$search=(foo AND bar) OR baz");
- // <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
- // <Input>$search=(NOT foo) OR baz</Input>
+ // <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
+ // <Input>$search=(NOT foo) OR baz</Input>
testUri.run("ESTwoKeyNav", "$search=(NOT foo) OR baz");
- // <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
- // <Input>$search=(NOT foo)</Input>
+ // <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
+ // <Input>$search=(NOT foo)</Input>
testUri.run("ESTwoKeyNav", "$search=(NOT foo)");
- // <TestCase Name="5.1.7 Search - on entity set" Rule="odataUri">
- // <Input>http://serviceRoot/Products?$search=blue</Input>
+ // <TestCase Name="5.1.7 Search - on entity set" Rule="odataUri">
+ // <Input>http://serviceRoot/Products?$search=blue</Input>
testUri.run("ESTwoKeyNav", "$search=blue");
- // <TestCase Name="5.1.7 Search - on entity container" Rule="odataUri">
- // <Input>http://serviceRoot/Model.Container/$all?$search=blue</Input>
+ // <TestCase Name="5.1.7 Search - on entity container" Rule="odataUri">
+ // <Input>http://serviceRoot/Model.Container/$all?$search=blue</Input>
testUri.run("$all", "$search=blue");
- // <TestCase Name="5.1.7 Search - on service" Rule="odataUri">
- // <Input>http://serviceRoot/$all?$search=blue</Input>
+ // <TestCase Name="5.1.7 Search - on service" Rule="odataUri">
+ // <Input>http://serviceRoot/$all?$search=blue</Input>
testUri.run("$all", "$search=blue");
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/73f46510/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
index fbce5c9..fa1e440 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
@@ -90,6 +90,7 @@ public class TestUriValidator implements TestValidator {
exception = e;
} catch (UriValidationException e) {
exception = e;
+ e.printStackTrace();
}
return this;
}