From b5e40fdfa4e836ff3420af33fa72b5aca3bda988 Mon Sep 17 00:00:00 2001 From: Klaus Straubinger Date: Fri, 24 Oct 2014 15:18:24 +0200 Subject: [PATCH] better key validation in server URI parsing Change-Id: Iab5d223e518af369dad046b4ea499e61bfaab7d0 Signed-off-by: Michael Bolz --- .../core/uri/parser/UriParseTreeVisitor.java | 70 +++--- .../parser/UriParserSemanticException.java | 2 +- .../uri/validator/UriValidationException.java | 2 + .../core/uri/validator/UriValidator.java | 41 ++-- .../server-core-exceptions-i18n.properties | 3 +- .../core/uri/antlr/TestFullResourcePath.java | 8 +- .../core/uri/validator/UriValidatorTest.java | 216 +++++++++--------- 7 files changed, 175 insertions(+), 167 deletions(-) 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 9c2906d6a..4b6745a38 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 @@ -182,6 +182,7 @@ import org.apache.olingo.server.core.uri.queryoption.expression.TypeLiteralImpl; import org.apache.olingo.server.core.uri.queryoption.expression.UnaryImpl; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -1459,37 +1460,39 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor { // get list of keys for lastType List lastKeyPredicates = lastType.getKeyPredicateNames(); - // if there is exactly one key defined in the EDM, then this key the the key written in the URI, - // so fill the keylist with this key and return + // If there is exactly one key defined in the EDM, then this key is the key written in the URI, + // so fill the keylist with this key and return. if (lastKeyPredicates.size() == 1) { - String keyName = lastKeyPredicates.get(0); - List list = new ArrayList(); - list.add(new UriParameterImpl().setName(keyName).setText(valueText).setExpression(expression)); - return list; + return Collections.singletonList(new UriParameterImpl() + .setName(lastKeyPredicates.get(0)) + .setText(valueText) + .setExpression(expression)); } // There are more keys defined in the EDM, but only one is written in the URI. This is allowed only if - // referential constrains are defined on this navigation property which can be used to will up all required - // key. - // for using referential constrains the last resource part must be a navigation property + // referential constraints are defined on this navigation property which can be used to fill up all + // required keys. + // For using referential constraints the last resource part must be a navigation property. if (!(context.contextUriInfo.getLastResourcePart() instanceof UriResourceNavigationPropertyImpl)) { - throw wrap(new UriParserSemanticException("Not enough key properties defined", - UriParserSemanticException.MessageKeys.NOT_ENOUGH_KEY_PROPERTIES)); + throw wrap(new UriParserSemanticException("Wrong number of key properties.", + UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES, + Integer.toString(lastKeyPredicates.size()), "1")); } UriResourceNavigationPropertyImpl lastNav = (UriResourceNavigationPropertyImpl) last; // get the partner of the navigation property EdmNavigationProperty partner = lastNav.getProperty().getPartner(); if (partner == null) { - throw wrap(new UriParserSemanticException("Not enough key properties defined", - UriParserSemanticException.MessageKeys.NOT_ENOUGH_KEY_PROPERTIES)); + throw wrap(new UriParserSemanticException("Wrong number of key properties.", + UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES, + Integer.toString(lastKeyPredicates.size()), "1")); } // create the keylist List list = new ArrayList(); - // find the key not filled by referential constrains and collect the other keys filled by - // referential constrains + // Find the keys not filled by referential constraints + // and collect the other keys filled by referential constraints. String missedKey = null; for (String item : lastKeyPredicates) { String property = partner.getReferencingPropertyName(item); @@ -1548,22 +1551,25 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor { return list; } - // if not, check if the missing key predicates can be satisfied with help of the defined referential constrains - // for using referential constrains the last resource part must be a navigation property + // if not, check if the missing key predicates can be satisfied with help of the defined + // referential constraints + // for using referential constraints the last resource part must be a navigation property if (!(context.contextUriInfo.getLastResourcePart() instanceof UriResourceNavigationPropertyImpl)) { - throw wrap(new UriParserSemanticException("Not enough key properties defined", - UriParserSemanticException.MessageKeys.NOT_ENOUGH_KEY_PROPERTIES)); + throw wrap(new UriParserSemanticException("Wrong number of key properties.", + UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES, + Integer.toString(lastKeyPredicates.size()), Integer.toString(list.size()))); } UriResourceNavigationPropertyImpl lastNav = (UriResourceNavigationPropertyImpl) last; // get the partner of the navigation property EdmNavigationProperty partner = lastNav.getProperty().getPartner(); if (partner == null) { - throw wrap(new UriParserSemanticException("Not enough key properties defined", - UriParserSemanticException.MessageKeys.NOT_ENOUGH_KEY_PROPERTIES)); + throw wrap(new UriParserSemanticException("Wrong number of key properties.", + UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES, + Integer.toString(lastKeyPredicates.size()), Integer.toString(list.size()))); } - // fill missing keys from referential constrains + // fill missing keys from referential constraints for (String key : lastKeyPredicates) { boolean found = false; for (UriParameterImpl item : list) { @@ -1582,15 +1588,25 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor { } } - // check again if all keyPredicate are filled from the URI + // check again if all key predicates are filled from the URI if (list.size() == lastKeyPredicates.size()) { return list; + } else { + throw wrap(new UriParserSemanticException("Wrong number of key properties.", + UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES, + Integer.toString(lastKeyPredicates.size()), Integer.toString(list.size()))); + } + } else { + if (context.contextReadingFunctionParameters) { + return Collections.emptyList(); + } else { + final UriResource last = context.contextUriInfo.getLastResourcePart(); + final int number = last instanceof UriResourcePartTyped ? + ((EdmEntityType) ((UriResourcePartTyped) last).getType()).getKeyPredicateNames().size() : 0; + throw wrap(new UriParserSemanticException("Wrong number of key properties.", + UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES, Integer.toString(number), "0")); } - - throw wrap(new UriParserSemanticException("Not enough key properties defined", - UriParserSemanticException.MessageKeys.NOT_ENOUGH_KEY_PROPERTIES)); } - return new ArrayList(); } @Override 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 78a32cf2d..77f975b79 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 @@ -44,7 +44,7 @@ public class UriParserSemanticException extends UriParserException { /** parameter: expression */ ONLY_FOR_TYPED_PROPERTIES, /** parameter: value */ INVALID_KEY_VALUE, PARAMETERS_LIST_ONLY_FOR_TYPED_PARTS, - NOT_ENOUGH_KEY_PROPERTIES, + /** parameters: expected number, actual number */ WRONG_NUMBER_OF_KEY_PROPERTIES, NOT_ENOUGH_REFERENTIAL_CONSTRAINTS, KEY_NOT_ALLOWED, RESOURCE_PATH_NOT_TYPED, diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidationException.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidationException.java index f7156cdee..9bba0c513 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidationException.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidationException.java @@ -43,6 +43,8 @@ public class UriValidationException extends ODataTranslatedException { SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD, /** parameter: invalid key property */ INVALID_KEY_PROPERTY, + /** parameter: key property */ + DOUBLE_KEY_PROPERTY, /** parameter: untyped segment name */ LAST_SEGMENT_NOT_TYPED, /** parameter: untyped segment name */ diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.java index 9c03db420..32fc690c1 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.java @@ -176,7 +176,7 @@ public class UriValidator { public void validate(final UriInfo uriInfo, final HttpMethod httpMethod) throws UriValidationException { validateForHttpMethod(uriInfo, httpMethod); validateQueryOptions(uriInfo); - validateKeyPredicateTypes(uriInfo); + validateKeyPredicates(uriInfo); } private ColumnIndex colIndex(final SystemQueryOptionKind queryOptionKind) throws UriValidationException { @@ -613,38 +613,40 @@ public class UriValidator { return idx; } - private void validateKeyPredicateTypes(final UriInfo uriInfo) throws UriValidationException { + private void validateKeyPredicates(final UriInfo uriInfo) throws UriValidationException { for (UriResource pathSegment : uriInfo.getUriResourceParts()) { if (pathSegment.getKind() == UriResourceKind.entitySet) { UriResourceEntitySet pathEntitySet = (UriResourceEntitySet) pathSegment; - - EdmEntityType type = pathEntitySet.getEntityType(); - List keys = type.getKeyPropertyRefs(); List keyPredicates = pathEntitySet.getKeyPredicates(); - if (null != keyPredicates) { + if (keyPredicates != null) { + final List keyPredicateNames = pathEntitySet.getEntityType().getKeyPredicateNames(); HashMap edmKeys = new HashMap(); - for (EdmKeyPropertyRef key : keys) { + for (EdmKeyPropertyRef key : pathEntitySet.getEntityType().getKeyPropertyRefs()) { edmKeys.put(key.getKeyPropertyName(), key); - String alias = key.getAlias(); - if (null != alias) { + final String alias = key.getAlias(); + if (alias != null) { edmKeys.put(alias, key); } } for (UriParameter keyPredicate : keyPredicates) { - String name = keyPredicate.getName(); - String alias = keyPredicate.getAlias(); - String value = keyPredicate.getText(); - if (alias != null) { - value = uriInfo.getValueForAlias(alias); - } - EdmKeyPropertyRef edmKey = edmKeys.get(name); + final String name = keyPredicate.getName(); + final String alias = keyPredicate.getAlias(); + final String value = alias == null ? + keyPredicate.getText() : + uriInfo.getValueForAlias(alias); + EdmKeyPropertyRef edmKey = edmKeys.get(name); if (edmKey == null) { - throw new UriValidationException("Unknown key property: " + name, - UriValidationException.MessageKeys.INVALID_KEY_PROPERTY, name); + if (keyPredicateNames.contains(name)) { + throw new UriValidationException("Double key property: " + name, + UriValidationException.MessageKeys.DOUBLE_KEY_PROPERTY, name); + } else { + throw new UriValidationException("Unknown key property: " + name, + UriValidationException.MessageKeys.INVALID_KEY_PROPERTY, name); + } } final EdmProperty property = edmKey.getProperty(); @@ -662,6 +664,9 @@ public class UriValidator { throw new UriValidationException("PrimitiveTypeException", e, UriValidationException.MessageKeys.INVALID_KEY_PROPERTY, name); } + + edmKeys.remove(name); + edmKeys.remove(alias); } } } 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 06fa51ca9..15f4ab8f9 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 @@ -53,7 +53,7 @@ UriParserSemanticException.ONLY_FOR_STRUCTURAL_TYPES='%1$s' is only allowed for UriParserSemanticException.ONLY_FOR_TYPED_PROPERTIES='%1$s' is only allowed for typed properties. UriParserSemanticException.INVALID_KEY_VALUE=The key value '%1$s' is invalid. UriParserSemanticException.PARAMETERS_LIST_ONLY_FOR_TYPED_PARTS=A list of parameters is only allowed for typed parts. -UriParserSemanticException.NOT_ENOUGH_KEY_PROPERTIES=There are not enough key properties. +UriParserSemanticException.WRONG_NUMBER_OF_KEY_PROPERTIES=There are %2$s key properties instead of the expected %1$s. UriParserSemanticException.NOT_ENOUGH_REFERENTIAL_CONSTRAINTS=There are not enough referential constraints. UriParserSemanticException.KEY_NOT_ALLOWED=A key is not allowed. UriParserSemanticException.RESOURCE_PATH_NOT_TYPED=The resource path is not typed. @@ -72,6 +72,7 @@ UriValidationException.UNSUPPORTED_HTTP_METHOD=The HTTP method '%1$s' is not sup UriValidationException.SYSTEM_QUERY_OPTION_NOT_ALLOWED=The system query option '%1$s' is not allowed. UriValidationException.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD=The system query option '%1$s' is not allowed for HTTP method '%2$s'. UriValidationException.INVALID_KEY_PROPERTY=The key property '%1$s' is invalid. +UriValidationException.DOUBLE_KEY_PROPERTY=The key property '%1$s' has been specified twice. UriValidationException.LAST_SEGMENT_NOT_TYPED=The last segment '%1$s' is not typed. UriValidationException.SECOND_LAST_SEGMENT_NOT_TYPED=The second last segment '%1$s' is not typed. UriValidationException.UNALLOWED_KIND_BEFORE_VALUE=The kind '%1$s' is not allowed before '$value'. 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 3c237daa7..d850edd98 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 @@ -31,7 +31,6 @@ import org.apache.olingo.server.core.edm.provider.EdmProviderImpl; import org.apache.olingo.server.core.uri.parser.UriParserException; import org.apache.olingo.server.core.uri.parser.UriParserSemanticException; import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException; -import org.apache.olingo.server.core.uri.validator.UriValidationException; import org.apache.olingo.server.core.uri.testutil.EdmTechTestProvider; import org.apache.olingo.server.core.uri.testutil.FilterValidator; import org.apache.olingo.server.core.uri.testutil.ResourceValidator; @@ -959,14 +958,12 @@ public class TestFullResourcePath { .isExSemantic(UriParserSemanticException.MessageKeys.PROPERTY_AFTER_COLLECTION); testUri.runEx("ESAllPrim(1)/whatever") .isExSemantic(UriParserSemanticException.MessageKeys.PROPERTY_NOT_IN_TYPE); - // testUri.runEx("ESAllPrim(PropertyInt16='1')") - // .isExSemantic(UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE); testUri.runEx("ESAllPrim(PropertyInt16)") .isExSemantic(UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE); testUri.runEx("ESAllPrim(PropertyInt16=)") .isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX); testUri.runEx("ESAllPrim(PropertyInt16=1,Invalid='1')") - .isExSemantic(UriParserSemanticException.MessageKeys.NOT_ENOUGH_KEY_PROPERTIES); + .isExSemantic(UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES); testUri.runEx("ESBase/olingo.odata.test1.ETBase/PropertyInt16") .isExSemantic(UriParserSemanticException.MessageKeys.PROPERTY_AFTER_COLLECTION); @@ -1087,8 +1084,6 @@ public class TestFullResourcePath { .isKeyPredicate(1, "KeyAlias1", "2") .isKeyPredicate(2, "KeyAlias2", "'3'") .isKeyPredicate(3, "KeyAlias3", "'4'"); - - testUri.runEx("ESCollAllPrim(null)").isExValidation(UriValidationException.MessageKeys.INVALID_KEY_PROPERTY); } @Test @@ -2660,6 +2655,7 @@ public class TestFullResourcePath { .goPath().first() .isEntitySet("ESKeyNav") .isKeyPredicate(0, "PropertyInt16", "1"); + testUri.runEx("ESKeyNav()").isExSemantic(UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES); testUri.run("SINav") .isKind(UriInfoKind.resource) 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 d9dc5e1ba..02b51b1bc 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 @@ -26,13 +26,11 @@ import org.apache.olingo.server.core.uri.parser.Parser; import org.apache.olingo.server.core.uri.parser.UriParserException; import org.apache.olingo.server.core.uri.parser.UriParserSemanticException; import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException; +import org.apache.olingo.server.core.uri.testutil.TestUriValidator; import org.apache.olingo.server.tecsvc.provider.EdmTechProvider; -import org.junit.Before; import org.junit.Test; -import java.util.ArrayList; -import java.util.List; - +import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public class UriValidatorTest { @@ -69,7 +67,7 @@ public class UriValidatorTest { private static final String QO_ID = "$id=Products(0)"; private static final String QO_COUNT = "$count=true"; private static final String QO_ORDERBY = "$orderby=true"; -// private static final String QO_SEARCH = "$search='bla'"; + // private static final String QO_SEARCH = "$search='bla'"; private static final String QO_SELECT = "$select=*"; private static final String QO_SKIP = "$skip=3"; private static final String QO_SKIPTOKEN = "$skiptoken=123"; @@ -77,23 +75,23 @@ public class UriValidatorTest { private static final String QO_TOP = "$top=1"; private String[][] urisWithValidSystemQueryOptions = { - { URI_ALL, QO_FILTER, }, { URI_ALL, QO_FORMAT }, { URI_ALL, QO_EXPAND }, { URI_ALL, QO_COUNT }, + { URI_ALL, QO_FILTER }, { URI_ALL, QO_FORMAT }, { URI_ALL, QO_EXPAND }, { URI_ALL, QO_COUNT }, { URI_ALL, QO_ORDERBY }, /* { URI_ALL, QO_SEARCH }, */{ URI_ALL, QO_SELECT }, { URI_ALL, QO_SKIP }, { URI_ALL, QO_SKIPTOKEN }, { URI_ALL, QO_LEVELS }, - { URI_CROSSJOIN, QO_FILTER, }, { URI_CROSSJOIN, QO_FORMAT }, + { URI_CROSSJOIN, QO_FILTER }, { URI_CROSSJOIN, QO_FORMAT }, { URI_CROSSJOIN, QO_EXPAND }, { URI_CROSSJOIN, QO_COUNT }, { URI_CROSSJOIN, QO_ORDERBY }, /* { URI_CROSSJOIN, QO_SEARCH }, */{ URI_CROSSJOIN, QO_SELECT }, { URI_CROSSJOIN, QO_SKIP }, { URI_CROSSJOIN, QO_SKIPTOKEN }, { URI_CROSSJOIN, QO_LEVELS }, { URI_CROSSJOIN, QO_TOP }, - { URI_ENTITY_ID, QO_ID, QO_FORMAT }, { URI_ENTITY_ID, QO_ID, }, { URI_ENTITY_ID, QO_ID, QO_EXPAND }, + { URI_ENTITY_ID, QO_ID, QO_FORMAT }, { URI_ENTITY_ID, QO_ID }, { URI_ENTITY_ID, QO_ID, QO_EXPAND }, { URI_ENTITY_ID, QO_ID, QO_SELECT }, { URI_ENTITY_ID, QO_ID, QO_LEVELS }, { URI_METADATA, QO_FORMAT }, { URI_SERVICE, QO_FORMAT }, - { URI_ENTITY_SET, QO_FILTER, }, { URI_ENTITY_SET, QO_FORMAT }, { URI_ENTITY_SET, QO_EXPAND }, + { URI_ENTITY_SET, QO_FILTER }, { URI_ENTITY_SET, QO_FORMAT }, { URI_ENTITY_SET, QO_EXPAND }, { URI_ENTITY_SET, QO_COUNT }, { URI_ENTITY_SET, QO_ORDERBY }, /* { URI_ENTITY_SET, QO_SEARCH }, */ { URI_ENTITY_SET, QO_SELECT }, { URI_ENTITY_SET, QO_SKIP }, { URI_ENTITY_SET, QO_SKIPTOKEN }, { URI_ENTITY_SET, QO_LEVELS }, @@ -139,7 +137,7 @@ public class UriValidatorTest { { URI_NAV_ENTITY, QO_FORMAT }, { URI_NAV_ENTITY, QO_EXPAND }, { URI_NAV_ENTITY, QO_SELECT }, { URI_NAV_ENTITY, QO_LEVELS }, - { URI_NAV_ENTITY_SET, QO_FILTER, }, { URI_NAV_ENTITY_SET, QO_FORMAT }, { URI_NAV_ENTITY_SET, QO_EXPAND }, + { URI_NAV_ENTITY_SET, QO_FILTER }, { URI_NAV_ENTITY_SET, QO_FORMAT }, { URI_NAV_ENTITY_SET, QO_EXPAND }, { URI_NAV_ENTITY_SET, QO_COUNT }, { URI_NAV_ENTITY_SET, QO_ORDERBY }, /* { URI_NAV_ENTITY_SET, QO_SEARCH }, */{ URI_NAV_ENTITY_SET, QO_SELECT }, { URI_NAV_ENTITY_SET, QO_SKIP }, { URI_NAV_ENTITY_SET, QO_SKIPTOKEN }, { URI_NAV_ENTITY_SET, QO_LEVELS }, { URI_NAV_ENTITY_SET, QO_TOP }, @@ -156,25 +154,24 @@ public class UriValidatorTest { { "ESAllPrim/olingo.odata.test1.BAESAllPrimRTETAllPrim" }, { "AIRTPrimCollParam" }, { "AIRTETParam" }, - { "AIRTPrimParam" }, - + { "AIRTPrimParam" } }; private String[][] urisWithNonValidSystemQueryOptions = { - { URI_ALL, QO_ID, }, { URI_ALL, QO_TOP }, + { URI_ALL, QO_ID }, { URI_ALL, QO_TOP }, - { URI_BATCH, QO_FILTER, }, { URI_BATCH, QO_FORMAT }, { URI_BATCH, QO_ID, }, { URI_BATCH, QO_EXPAND }, + { URI_BATCH, QO_FILTER }, { URI_BATCH, QO_FORMAT }, { URI_BATCH, QO_ID }, { URI_BATCH, QO_EXPAND }, { URI_BATCH, QO_COUNT }, { URI_BATCH, QO_ORDERBY }, /* { URI_BATCH, QO_SEARCH }, */{ URI_BATCH, QO_SELECT }, { URI_BATCH, QO_SKIP }, { URI_BATCH, QO_SKIPTOKEN }, { URI_BATCH, QO_LEVELS }, { URI_BATCH, QO_TOP }, - { URI_CROSSJOIN, QO_ID, }, + { URI_CROSSJOIN, QO_ID }, - { URI_ENTITY_ID, QO_ID, QO_FILTER, }, + { URI_ENTITY_ID, QO_ID, QO_FILTER }, { URI_ENTITY_ID, QO_ID, QO_COUNT }, { URI_ENTITY_ID, QO_ORDERBY }, /* { URI_ENTITY_ID, QO_SEARCH }, */ { URI_ENTITY_ID, QO_ID, QO_SKIP }, { URI_ENTITY_ID, QO_ID, QO_SKIPTOKEN }, { URI_ENTITY_ID, QO_ID, QO_TOP }, - { URI_METADATA, QO_FILTER, }, { URI_METADATA, QO_ID, }, { URI_METADATA, QO_EXPAND }, + { URI_METADATA, QO_FILTER }, { URI_METADATA, QO_ID }, { URI_METADATA, QO_EXPAND }, { URI_METADATA, QO_COUNT }, { URI_METADATA, QO_ORDERBY }, /* { URI_METADATA, QO_SEARCH }, */ { URI_METADATA, QO_SELECT }, { URI_METADATA, QO_SKIP }, { URI_METADATA, QO_SKIPTOKEN }, { URI_METADATA, QO_LEVELS }, { URI_METADATA, QO_TOP }, @@ -194,52 +191,52 @@ public class UriValidatorTest { { URI_ENTITY, QO_FILTER }, { URI_ENTITY, QO_ID }, { URI_ENTITY, QO_COUNT }, /* { URI_ENTITY, QO_ORDERBY }, */ /* { URI_ENTITY, QO_SEARCH }, */{ URI_ENTITY, QO_SKIP }, { URI_ENTITY, QO_SKIPTOKEN }, { URI_ENTITY, QO_TOP }, - { URI_MEDIA_STREAM, QO_FILTER }, { URI_MEDIA_STREAM, QO_ID, }, { URI_MEDIA_STREAM, QO_EXPAND }, + { URI_MEDIA_STREAM, QO_FILTER }, { URI_MEDIA_STREAM, QO_ID }, { URI_MEDIA_STREAM, QO_EXPAND }, { URI_MEDIA_STREAM, QO_COUNT }, { URI_MEDIA_STREAM, QO_ORDERBY }, /* { URI_MEDIA_STREAM, QO_SEARCH }, */ { URI_MEDIA_STREAM, QO_SELECT }, { URI_MEDIA_STREAM, QO_SKIP }, { URI_MEDIA_STREAM, QO_SKIPTOKEN }, { URI_MEDIA_STREAM, QO_LEVELS }, { URI_MEDIA_STREAM, QO_TOP }, - { URI_REFERENCES, QO_ID, }, { URI_REFERENCES, QO_EXPAND }, { URI_REFERENCES, QO_COUNT }, + { URI_REFERENCES, QO_ID }, { URI_REFERENCES, QO_EXPAND }, { URI_REFERENCES, QO_COUNT }, { URI_REFERENCES, QO_SELECT }, { URI_REFERENCES, QO_LEVELS }, - { URI_REFERENCE, QO_FILTER }, { URI_REFERENCE, QO_ID, }, { URI_REFERENCE, QO_EXPAND }, + { URI_REFERENCE, QO_FILTER }, { URI_REFERENCE, QO_ID }, { URI_REFERENCE, QO_EXPAND }, { URI_REFERENCE, QO_COUNT }, { URI_REFERENCE, QO_ORDERBY }, /* { URI_REFERENCE, QO_SEARCH }, */ { URI_REFERENCE, QO_SELECT }, { URI_REFERENCE, QO_SKIP }, { URI_REFERENCE, QO_SKIPTOKEN }, { URI_REFERENCE, QO_LEVELS }, { URI_REFERENCE, QO_TOP }, - { URI_PROPERTY_COMPLEX, QO_FILTER }, { URI_PROPERTY_COMPLEX, QO_ID, }, { URI_PROPERTY_COMPLEX, QO_COUNT }, + { URI_PROPERTY_COMPLEX, QO_FILTER }, { URI_PROPERTY_COMPLEX, QO_ID }, { URI_PROPERTY_COMPLEX, QO_COUNT }, { URI_PROPERTY_COMPLEX, QO_ORDERBY }, /* { URI_PROPERTY_COMPLEX, QO_SEARCH }, */ { URI_PROPERTY_COMPLEX, QO_SKIP }, { URI_PROPERTY_COMPLEX, QO_SKIPTOKEN }, { URI_PROPERTY_COMPLEX, QO_TOP }, - { URI_PROPERTY_COMPLEX_COLLECTION, QO_ID, }, + { URI_PROPERTY_COMPLEX_COLLECTION, QO_ID }, /* { URI_PROPERTY_COMPLEX_COLLECTION, QO_SEARCH }, */{ URI_PROPERTY_COMPLEX_COLLECTION, QO_SELECT }, { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_FORMAT }, - { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_ID, }, { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_EXPAND }, + { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_ID }, { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_EXPAND }, { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_COUNT }, { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_ORDERBY }, { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_SELECT }, { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_SKIP }, { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_SKIPTOKEN }, { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_LEVELS }, { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_TOP }, - { URI_PROPERTY_PRIMITIVE, QO_FILTER }, { URI_PROPERTY_PRIMITIVE, QO_ID, }, { URI_PROPERTY_PRIMITIVE, QO_EXPAND }, + { URI_PROPERTY_PRIMITIVE, QO_FILTER }, { URI_PROPERTY_PRIMITIVE, QO_ID }, { URI_PROPERTY_PRIMITIVE, QO_EXPAND }, { URI_PROPERTY_PRIMITIVE, QO_COUNT }, { URI_PROPERTY_PRIMITIVE, QO_ORDERBY }, /* { URI_PROPERTY_PRIMITIVE, QO_SEARCH }, */{ URI_PROPERTY_PRIMITIVE, QO_SELECT }, { URI_PROPERTY_PRIMITIVE, QO_SKIP }, { URI_PROPERTY_PRIMITIVE, QO_SKIPTOKEN }, { URI_PROPERTY_PRIMITIVE, QO_LEVELS }, { URI_PROPERTY_PRIMITIVE, QO_TOP }, - { URI_PROPERTY_PRIMITIVE_COLLECTION, QO_ID, }, { URI_PROPERTY_PRIMITIVE_COLLECTION, QO_EXPAND }, + { URI_PROPERTY_PRIMITIVE_COLLECTION, QO_ID }, { URI_PROPERTY_PRIMITIVE_COLLECTION, QO_EXPAND }, { URI_PROPERTY_PRIMITIVE_COLLECTION, QO_COUNT }, /* { URI_PROPERTY_PRIMITIVE_COLLECTION, QO_SEARCH }, */ { URI_PROPERTY_PRIMITIVE_COLLECTION, QO_SELECT }, { URI_PROPERTY_PRIMITIVE_COLLECTION, QO_LEVELS }, { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_FORMAT }, - { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_ID, }, { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_EXPAND }, + { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_ID }, { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_EXPAND }, { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_COUNT }, { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_ORDERBY }, { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_SELECT }, { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_SKIP }, { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_SKIPTOKEN }, { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_LEVELS }, { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_TOP }, - { URI_PROPERTY_PRIMITIVE_VALUE, QO_FILTER }, { URI_PROPERTY_PRIMITIVE_VALUE, QO_ID, }, + { URI_PROPERTY_PRIMITIVE_VALUE, QO_FILTER }, { URI_PROPERTY_PRIMITIVE_VALUE, QO_ID }, { URI_PROPERTY_PRIMITIVE_VALUE, QO_EXPAND }, { URI_PROPERTY_PRIMITIVE_VALUE, QO_COUNT }, { URI_PROPERTY_PRIMITIVE_VALUE, QO_ORDERBY },/* { URI_PROPERTY_PRIMITIVE_VALUE, QO_SEARCH }, */ { URI_PROPERTY_PRIMITIVE_VALUE, QO_SELECT }, { URI_PROPERTY_PRIMITIVE_VALUE, QO_SKIP }, @@ -254,92 +251,90 @@ public class UriValidatorTest { { URI_NAV_ENTITY, QO_ORDERBY }, /* { URI_NAV_ENTITY, QO_SEARCH }, */{ URI_NAV_ENTITY, QO_SKIP }, { URI_NAV_ENTITY, QO_SKIPTOKEN }, { URI_SINGLETON, QO_TOP }, - { URI_NAV_ENTITY_SET, QO_ID }, - + { URI_NAV_ENTITY_SET, QO_ID } }; - private Parser parser; - private Edm edm; + private static final Edm edm = new EdmProviderImpl(new EdmTechProvider()); - @Before - public void before() { - parser = new Parser(); - edm = new EdmProviderImpl(new EdmTechProvider()); + @Test + public void validateForHttpMethods() throws Exception { + final UriInfo uri = new Parser().parseUri(URI_ENTITY, null, null, edm); + final UriValidator validator = new UriValidator(); + + validator.validate(uri, HttpMethod.GET); + validator.validate(uri, HttpMethod.POST); + validator.validate(uri, HttpMethod.PUT); + validator.validate(uri, HttpMethod.DELETE); + validator.validate(uri, HttpMethod.PATCH); + validator.validate(uri, HttpMethod.MERGE); } @Test public void validateSelect() throws Exception { - parseAndValidate("/ESAllPrim(1)", "$select=PropertyString", HttpMethod.GET); - } - - @Test - public void validateForHttpMethods() throws Exception { - String uri = URI_ENTITY; - parseAndValidate(uri, null, HttpMethod.GET); - parseAndValidate(uri, null, HttpMethod.POST); - parseAndValidate(uri, null, HttpMethod.PUT); - parseAndValidate(uri, null, HttpMethod.DELETE); - parseAndValidate(uri, null, HttpMethod.PATCH); - parseAndValidate(uri, null, HttpMethod.MERGE); + new TestUriValidator().setEdm(edm).run(URI_ENTITY, "$select=PropertyString"); } @Test public void validateOrderBy() throws Exception { - parseAndValidate("/ESAllPrim", "$orderby=PropertyString", HttpMethod.GET); - } + final TestUriValidator testUri = new TestUriValidator().setEdm(edm); - @Test(expected = UriParserSemanticException.class) - public void validateOrderByInvalid() throws Exception { - parseAndValidate("/ESAllPrim(1)", "$orderby=XXXX", HttpMethod.GET); - } + testUri.run(URI_ENTITY_SET, "$orderby=PropertyString"); - @Test(expected = UriParserSyntaxException.class) - public void validateCountInvalid() throws Exception { - parseAndValidate("ESAllPrim", "$count=foo", HttpMethod.GET); - } - - @Test(expected = UriParserSyntaxException.class) - public void validateTopInvalid() throws Exception { - parseAndValidate("ESAllPrim", "$top=foo", HttpMethod.GET); - } - - @Test(expected = UriParserSyntaxException.class) - public void validateSkipInvalid() throws Exception { - parseAndValidate("ESAllPrim", "$skip=foo", HttpMethod.GET); - } - - @Test(expected = UriParserSyntaxException.class) - public void validateDoubleSystemOptions() throws Exception { - parseAndValidate("ESAllPrim", "$skip=1&$skip=2", HttpMethod.GET); - } - - @Test(expected = UriValidationException.class) - public void validateKeyPredicatesWrongKey() throws Exception { - parseAndValidate("ESTwoKeyNav(xxx=1, yyy='abc')", null, HttpMethod.GET); + testUri.runEx(URI_ENTITY, "$orderby=XXXX") + .isExSemantic(UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE); } @Test - public void validateKeyPredicates() throws Exception { - parseAndValidate("ESTwoKeyNav(PropertyInt16=1, PropertyString='abc')", null, HttpMethod.GET); + public void validateCountInvalid() throws Exception { + new TestUriValidator().setEdm(edm).runEx(URI_ENTITY_SET, "$count=foo") + .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION); } - @Test(expected = UriValidationException.class) - public void validateKeyPredicatesWrongValueType() throws Exception { - parseAndValidate("ESTwoKeyNav(PropertyInt16='abc', PropertyString=1)", null, HttpMethod.GET); + @Test + public void validateTopInvalid() throws Exception { + new TestUriValidator().setEdm(edm).runEx(URI_ENTITY_SET, "$top=foo") + .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION); } - @Test(expected = UriValidationException.class) - public void validateKeyPredicatesWrongValueTypeForValidateMethod() throws Exception { - parseAndValidate("ESTwoKeyNav(PropertyInt16='abc', PropertyString='abc')", null, HttpMethod.GET); + @Test + public void validateSkipInvalid() throws Exception { + new TestUriValidator().setEdm(edm).runEx(URI_ENTITY_SET, "$skip=foo") + .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION); } - + + @Test + public void validateDoubleSystemOptions() throws Exception { + new TestUriValidator().setEdm(edm).runEx(URI_ENTITY_SET, "$skip=1&$skip=2") + .isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION); + } + + @Test + public void checkKeys() throws Exception { + final TestUriValidator testUri = new TestUriValidator().setEdm(edm); + + testUri.run("ESTwoKeyNav(PropertyInt16=1, PropertyString='abc')"); + + testUri.runEx("ESTwoKeyNav(xxx=1, yyy='abc')") + .isExValidation(UriValidationException.MessageKeys.INVALID_KEY_PROPERTY); + testUri.runEx("ESCollAllPrim(null)").isExValidation(UriValidationException.MessageKeys.INVALID_KEY_PROPERTY); + testUri.runEx("ESAllPrim(PropertyInt16='1')") + .isExValidation(UriValidationException.MessageKeys.INVALID_KEY_PROPERTY); + testUri.runEx("ESAllPrim(12345678901234567890)") + .isExValidation(UriValidationException.MessageKeys.INVALID_KEY_PROPERTY); + testUri.runEx("ESTwoKeyNav(PropertyInt16=1,PropertyString=1)") + .isExValidation(UriValidationException.MessageKeys.INVALID_KEY_PROPERTY); + testUri.runEx("ESTwoKeyNav(PropertyInt16=1,PropertyInt16=1)") + .isExValidation(UriValidationException.MessageKeys.DOUBLE_KEY_PROPERTY); + } + @Test public void checkValidSystemQueryOption() throws Exception { - List uris = constructUri(urisWithValidSystemQueryOptions); - - for (String[] uri : uris) { + for (final String[] uriArray : urisWithValidSystemQueryOptions) { + final String[] uri = constructUri(uriArray); try { - parseAndValidate(uri[0], uri[1], HttpMethod.GET); + new UriValidator().validate( + new Parser().parseUri(uri[0], uri[1], null, edm), + HttpMethod.GET); } catch (final UriParserException e) { fail("Failed for uri: " + uri[0] + '?' + uri[1]); } catch (final UriValidationException e) { @@ -350,38 +345,31 @@ public class UriValidatorTest { @Test public void checkNonValidSystemQueryOption() throws Exception { - List uris = constructUri(urisWithNonValidSystemQueryOptions); - - for (String[] uri : uris) { + for (final String[] uriArray : urisWithNonValidSystemQueryOptions) { + final String[] uri = constructUri(uriArray); try { - parseAndValidate(uri[0], uri[1], HttpMethod.GET); + new UriValidator().validate( + new Parser().parseUri(uri[0], uri[1], null, edm), + HttpMethod.GET); fail("Validation Exception not thrown: " + uri[0] + '?' + uri[1]); - } catch (UriParserSemanticException e) { - } catch (UriValidationException e) { + } catch (final UriParserException e) { + fail("Wrong Exception thrown: " + uri[0] + '?' + uri[1]); + } catch (final UriValidationException e) { + assertEquals(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED, + e.getMessageKey()); } } } - private List constructUri(final String[][] uriParameterMatrix) { - List uris = new ArrayList(); - for (String[] uriParameter : uriParameterMatrix) { - String path = uriParameter[0]; - String query = ""; - for (int i = 1; i < uriParameter.length; i++) { - query += uriParameter[i]; - if (i < (uriParameter.length - 1)) { - query += "&"; - } + private String[] constructUri(final String[] uriParameterArray) { + final String path = uriParameterArray[0]; + String query = ""; + for (int i = 1; i < uriParameterArray.length; i++) { + if (i > 1) { + query += '&'; } - uris.add(new String[] { path, query }); + query += uriParameterArray[i]; } - return uris; + return new String[] { path, query }; } - - private void parseAndValidate(final String path, final String query, final HttpMethod method) - throws UriParserException, UriValidationException { - UriInfo uriInfo = parser.parseUri(path.trim(), query, null, edm); - new UriValidator().validate(uriInfo, method); - } - }