diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java index f97b7e0d0..9c2906d6a 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java @@ -377,8 +377,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor { source = getTypeInformation(lastResourcePart); if (source.type == null) { - throw wrap(new UriParserSemanticException("Resource part '" + odi + "' can only applied on typed " - + "resource parts", UriParserSemanticException.MessageKeys.RESOURCE_PART_ONLY_FOR_TYPED_PARTS, odi)); + throw wrap(new UriParserSemanticException("Resource part '" + odi + "' can only be applied on typed " + + "resource parts.", UriParserSemanticException.MessageKeys.RESOURCE_PART_ONLY_FOR_TYPED_PARTS, odi)); } } @@ -396,18 +396,24 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor { } if (!(source.type instanceof EdmStructuredType)) { - throw wrap(new UriParserSemanticException("Can not parse'" + odi - + "'Previous path segment not a structural type.", + throw wrap(new UriParserSemanticException("Cannot parse '" + odi + + "'; previous path segment is not a structural type.", UriParserSemanticException.MessageKeys.RESOURCE_PART_MUST_BE_PRECEDED_BY_STRUCTURAL_TYPE, odi)); } + if (ctx.depth() <= 2 // path evaluation for the resource path + && source.isCollection) { + throw wrap(new UriParserSemanticException("Property '" + odi + "' is not allowed after collection.", + UriParserSemanticException.MessageKeys.PROPERTY_AFTER_COLLECTION, odi)); + } + EdmStructuredType structType = (EdmStructuredType) source.type; EdmElement property = structType.getProperty(odi); if (property == null) { throw wrap(new UriParserSemanticException("Property '" + odi + "' not found in type '" + structType.getNamespace() + "." + structType.getName() + "'", - ctx.depth() > 2 ? // resource path or path evaluation inside an expression? + ctx.depth() > 2 ? // path evaluation inside an expression or for the resource path? UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE : UriParserSemanticException.MessageKeys.PROPERTY_NOT_IN_TYPE, structType.getFullQualifiedName().toString(), odi)); 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 64715669e..78a32cf2d 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 @@ -27,6 +27,7 @@ public class UriParserSemanticException extends UriParserException { /** parameters: function-import name, function parameters */ FUNCTION_NOT_FOUND, /** parameter: resource part */ RESOURCE_PART_ONLY_FOR_TYPED_PARTS, /** parameter: resource part */ RESOURCE_PART_MUST_BE_PRECEDED_BY_STRUCTURAL_TYPE, + /** parameter: property name */ PROPERTY_AFTER_COLLECTION, /** parameters: type name, property name */ PROPERTY_NOT_IN_TYPE, /** parameters: type name, property name */ EXPRESSION_PROPERTY_NOT_IN_TYPE, /** parameter: property name */ UNKNOWN_PROPERTY_TYPE, 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 07d9250d5..06fa51ca9 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 @@ -36,6 +36,7 @@ UriParserSyntaxException.SYNTAX=The URI is malformed. UriParserSemanticException.FUNCTION_NOT_FOUND=The function import '%1$s' has no function with parameters '%2$s'. UriParserSemanticException.RESOURCE_PART_ONLY_FOR_TYPED_PARTS='%1$s' is only allowed for typed parts. UriParserSemanticException.RESOURCE_PART_MUST_BE_PRECEDED_BY_STRUCTURAL_TYPE=The resource part '%1$s' must be preceded by a structural type. +UriParserSemanticException.PROPERTY_AFTER_COLLECTION=The property '%1$s' must not follow a collection. UriParserSemanticException.PROPERTY_NOT_IN_TYPE=The type '%1$s' has no property '%2$s'. UriParserSemanticException.EXPRESSION_PROPERTY_NOT_IN_TYPE=The property '%2$s', used in a query expression, is not defined in type '%1$s'. UriParserSemanticException.UNKNOWN_PROPERTY_TYPE=The type of the property '%1$s' is unknown. 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 d99c9ae0c..176918dd2 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 @@ -269,7 +269,7 @@ public class ODataHandlerTest { ODataRequest request = new ODataRequest(); request.setMethod(HttpMethod.GET); - request.setRawODataPath("ESAllPrim/NavPropertyETTwoPrimMany/$count"); + request.setRawODataPath("ESAllPrim(0)/NavPropertyETTwoPrimMany/$count"); EntitySetProcessor processor = mock(EntitySetProcessor.class); handler.register(processor); @@ -287,7 +287,7 @@ public class ODataHandlerTest { ODataRequest request = new ODataRequest(); request.setMethod(HttpMethod.GET); - request.setRawODataPath("ESAllPrim/PropertyInt16"); + request.setRawODataPath("ESAllPrim(0)/PropertyInt16"); PropertyProcessor processor = mock(PropertyProcessor.class); handler.register(processor); @@ -307,7 +307,7 @@ public class ODataHandlerTest { ODataRequest request = new ODataRequest(); request.setMethod(HttpMethod.GET); - request.setRawODataPath("ESAllPrim/PropertyInt16/$value"); + request.setRawODataPath("ESAllPrim(0)/PropertyInt16/$value"); PropertyProcessor processor = mock(PropertyProcessor.class); handler.register(processor); @@ -327,7 +327,7 @@ public class ODataHandlerTest { ODataRequest request = new ODataRequest(); request.setMethod(HttpMethod.GET); - request.setRawODataPath("ESMixPrimCollComp/PropertyComp"); + request.setRawODataPath("ESMixPrimCollComp(7)/PropertyComp"); PropertyProcessor processor = mock(PropertyProcessor.class); handler.register(processor); diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java index bbb9e4513..3c237daa7 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java @@ -955,6 +955,8 @@ public class TestFullResourcePath { .isExSemantic(UriParserSemanticException.MessageKeys.RESOURCE_PART_ONLY_FOR_TYPED_PARTS); testUri.runEx("ESAllPrim/$count/invalid") .isExSemantic(UriParserSemanticException.MessageKeys.RESOURCE_PART_ONLY_FOR_TYPED_PARTS); + testUri.runEx("ESAllPrim/PropertyString") + .isExSemantic(UriParserSemanticException.MessageKeys.PROPERTY_AFTER_COLLECTION); testUri.runEx("ESAllPrim(1)/whatever") .isExSemantic(UriParserSemanticException.MessageKeys.PROPERTY_NOT_IN_TYPE); // testUri.runEx("ESAllPrim(PropertyInt16='1')") @@ -966,6 +968,9 @@ public class TestFullResourcePath { testUri.runEx("ESAllPrim(PropertyInt16=1,Invalid='1')") .isExSemantic(UriParserSemanticException.MessageKeys.NOT_ENOUGH_KEY_PROPERTIES); + testUri.runEx("ESBase/olingo.odata.test1.ETBase/PropertyInt16") + .isExSemantic(UriParserSemanticException.MessageKeys.PROPERTY_AFTER_COLLECTION); + testUri.runEx("ETBaseTwoKeyTwoPrim/olingo.odata.test1.ETBaseTwoKeyTwoPrim" + "/olingo.odata.test1.ETTwoBaseTwoKeyTwoPrim") .isExSemantic(UriParserSemanticException.MessageKeys.RESOURCE_NOT_FOUND); @@ -2735,12 +2740,6 @@ public class TestFullResourcePath { .isEntitySet("ESKeyNav") .n().isRef(); - testUri.run("ESKeyNav/$count") - .isKind(UriInfoKind.resource) - .goPath().first() - .isEntitySet("ESKeyNav") - .n().isCount(); - testUri.run("ESTwoKeyNav/olingo.odata.test1.BFCESTwoKeyNavRTESTwoKeyNav()") .isKind(UriInfoKind.resource) .goPath().first() diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java index 9fdbbdd01..a9a05d471 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java @@ -1135,7 +1135,8 @@ public class TestUriParserImpl { testUri.run("ESTwoKeyNav", "$select=olingo.odata.test1.ETBaseTwoKeyNav") .isSelectStartType(0, EntityTypeProvider.nameETBaseTwoKeyNav); - testUri.run("ESTwoKeyNav/PropertyCompNav", "$select=olingo.odata.test1.CTTwoBasePrimCompNav") + testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='1')/PropertyCompNav", + "$select=olingo.odata.test1.CTTwoBasePrimCompNav") .isSelectStartType(0, ComplexTypeProvider.nameCTTwoBasePrimCompNav); testUri.run("ESTwoKeyNav", "$select=PropertyCompNav/olingo.odata.test1.CTTwoBasePrimCompNav") diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java index dd169f094..d9dc5e1ba 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java @@ -55,13 +55,13 @@ public class UriValidatorTest { private static final String URI_PROPERTY_COMPLEX_COLLECTION_COUNT = "/ESCompCollComp(1)/PropertyComp/CollPropertyComp/$count"; private static final String URI_PROPERTY_PRIMITIVE = "/ESAllPrim(1)/PropertyString"; - private static final String URI_PROPERTY_PRIMITIVE_COLLECTION = "/ESCollAllPrim/CollPropertyString"; + private static final String URI_PROPERTY_PRIMITIVE_COLLECTION = "/ESCollAllPrim(1)/CollPropertyString"; private static final String URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT = - "/ESCollAllPrim/CollPropertyString/$count"; + "/ESCollAllPrim(1)/CollPropertyString/$count"; private static final String URI_PROPERTY_PRIMITIVE_VALUE = "/ESAllPrim(1)/PropertyString/$value"; private static final String URI_SINGLETON = "/SI"; - private static final String URI_NAV_ENTITY = "/ESKeyNav/NavPropertyETKeyNavOne"; - private static final String URI_NAV_ENTITY_SET = "/ESKeyNav/NavPropertyETKeyNavMany"; + private static final String URI_NAV_ENTITY = "/ESKeyNav(1)/NavPropertyETKeyNavOne"; + private static final String URI_NAV_ENTITY_SET = "/ESKeyNav(1)/NavPropertyETKeyNavMany"; private static final String QO_FILTER = "$filter='1' eq '1'"; private static final String QO_FORMAT = "$format=bla/bla"; @@ -341,9 +341,9 @@ public class UriValidatorTest { try { parseAndValidate(uri[0], uri[1], HttpMethod.GET); } catch (final UriParserException e) { - fail("Failed for uri: " + uri); + fail("Failed for uri: " + uri[0] + '?' + uri[1]); } catch (final UriValidationException e) { - fail("Failed for uri: " + uri); + fail("Failed for uri: " + uri[0] + '?' + uri[1]); } } } @@ -355,7 +355,7 @@ public class UriValidatorTest { for (String[] uri : uris) { try { parseAndValidate(uri[0], uri[1], HttpMethod.GET); - fail("Validation Exception not thrown: " + uri); + fail("Validation Exception not thrown: " + uri[0] + '?' + uri[1]); } catch (UriParserSemanticException e) { } catch (UriValidationException e) { }