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 2015/08/14 12:27:03 UTC

[1/2] olingo-odata4 git commit: [OLINGO-730] UriParser fix

Repository: olingo-odata4
Updated Branches:
  refs/heads/master 4fd79b3c3 -> 4a8140924


[OLINGO-730] UriParser fix


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/9235cb2b
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/9235cb2b
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/9235cb2b

Branch: refs/heads/master
Commit: 9235cb2b056d142311bffd8b91d62b1fe7eab351
Parents: 4fd79b3
Author: Christian Holzer <c....@sap.com>
Authored: Mon Aug 10 16:58:28 2015 +0200
Committer: Christian Holzer <c....@sap.com>
Committed: Fri Aug 14 12:21:32 2015 +0200

----------------------------------------------------------------------
 .../olingo/server/core/uri/antlr/UriLexer.g4    | 50 +++++++++----------
 .../core/uri/parser/UriParseTreeVisitor.java    | 12 ++++-
 .../core/uri/antlr/TestFullResourcePath.java    | 52 +++++++++++++++++---
 3 files changed, 81 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9235cb2b/lib/server-core/src/main/antlr4/org/apache/olingo/server/core/uri/antlr/UriLexer.g4
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/antlr4/org/apache/olingo/server/core/uri/antlr/UriLexer.g4 b/lib/server-core/src/main/antlr4/org/apache/olingo/server/core/uri/antlr/UriLexer.g4
index f2ec378..ff3db0b 100644
--- a/lib/server-core/src/main/antlr4/org/apache/olingo/server/core/uri/antlr/UriLexer.g4
+++ b/lib/server-core/src/main/antlr4/org/apache/olingo/server/core/uri/antlr/UriLexer.g4
@@ -26,9 +26,9 @@ lexer grammar UriLexer;
 QM              : '?'                 ->        pushMode(MODE_QUERY);               //first query parameter
 AMP             : '&'                 ->        pushMode(MODE_QUERY);               //more query parameters
 STRING          : '\''                -> more,  pushMode(MODE_STRING);              //reads up to next single '
-QUOTATION_MARK  : ('\u0022' | '%22')  -> more,  pushMode(MODE_JSON_STRING);         //reads up to next unescaped "
+QUOTATION_MARK  : '\u0022'            -> more,  pushMode(MODE_JSON_STRING);         //reads up to next unescaped "
 SEARCH_INLINE   : '$search'           ->        pushMode(MODE_SYSTEM_QUERY_SEARCH); //
-FRAGMENT        : '#'                 ->        pushMode(MODE_FRAGMENT); //
+FRAGMENT        : '#'                 ->        pushMode(MODE_FRAGMENT);            //
 
 GEOGRAPHY    : G E O G R A P H Y SQUOTE         -> pushMode(MODE_ODATA_GEO); //TODO make case insensitive
 GEOMETRY     : G E O M E T R Y   SQUOTE         -> pushMode(MODE_ODATA_GEO);
@@ -55,28 +55,28 @@ fragment Y    : 'Y'|'y';
 fragment Z    : 'Z'|'z';
 
 //special chars
-OPEN            : '(' | '%28';
-CLOSE           : ')' | '%29';
-COMMA           : ',' | '%2C';
+OPEN            : '(';
+CLOSE           : ')';
+COMMA           : ',';
 SLASH           : '/';
 POINT           : '.';
 AT              : '@';
 EQ              : '=' ;
 STAR            : '*';
-SEMI            : ';' | '%3b';
+SEMI            : ';';
 COLON           : ':';
 
 EQ_sq           : '='           -> type(EQ);
 AMP_sq          : '&'           -> type(AMP), popMode;
-fragment WS     : ( ' ' | '%09' | '%20' | '%09' );
+fragment WS     : ( ' ' | '\u0009' );
 WSP             : WS+;
 
 //JSON support 
-BEGIN_OBJECT    : WS* ( '{' / '%7B' ) WS*;
-END_OBJECT      : WS* ( '}' / '%7D' ) WS*;
+BEGIN_OBJECT    : WS* '{' WS*;
+END_OBJECT      : WS* '}' WS*;
 
-BEGIN_ARRAY     : WS* ( '[' / '%5B' ) WS*;
-END_ARRAY       : WS* ( ']' / '%5D' ) WS*;
+BEGIN_ARRAY     : WS* '[' WS*;
+END_ARRAY       : WS* ']' WS*;
 
 
 //alpha stuff
@@ -132,7 +132,7 @@ BOOLEAN       :  T R U E |  F A L S E;
 PLUS          : '+';
 
 MINUS         : '-';
-SIGN          : PLUS  | '%2B' | '-';
+SIGN          : PLUS  | '-';
 INT           : SIGN? DIGITS;
 DECIMAL       : INT '.' DIGITS (('e'|'E') SIGN?  DIGITS)?;
 NANINFINITY   : 'NaN' | '-INF' | 'INF';
@@ -334,10 +334,10 @@ AND_sqc             : 'AND'   -> type(AND);
 OR_sqc              : 'OR'    -> type(OR);
 EQ_sqc              : '='     -> type(EQ);
 
-fragment WS_sqc     : ( ' ' | '\u0009' | '%20' | '%09' );
+fragment WS_sqc     : ( ' ' | '\u0009');
 WSP_sqc             : WS_sqc+ -> type(WSP);
 
-QUOTATION_MARK_sqc  : '\u0022' | '%22';
+QUOTATION_MARK_sqc  : '\u0022';
 
 SEARCHWORD          : ('a'..'z'|'A'..'Z')+;
 SEARCHPHRASE        : QUOTATION_MARK_sqc ~["]* QUOTATION_MARK_sqc -> popMode;
@@ -356,7 +356,7 @@ mode MODE_JSON_STRING;
 // Any """ characters inside a string are escaped with "\".
 //;==============================================================================
 
-STRING_IN_JSON      : ('\\"' | ~[\u0022] )* ('"' | '%22') -> popMode;
+STRING_IN_JSON      : ('\\"' | ~[\u0022] )* '"' -> popMode;
 
 //;==============================================================================
 mode MODE_ODATA_GEO;
@@ -379,21 +379,21 @@ fragment T_  : 't'|'T';
 fragment U_  : 'u'|'U';
 fragment Y_  : 'y'|'Y';
 
-fragment SP_g   : ' ';//'\u0020'; // a simple space
-fragment WS_g   : ( ' ' | '%20' | '%09' );
+fragment SP_g   : ' ';                  //'\u0020'; // a simple space
+fragment WS_g   : ( ' ' | '\u0009' );
 
-OPEN_g          : ('(' | '%28') -> type(OPEN);
-CLOSE_g         : (')' | '%29') -> type(CLOSE);
-COMMA_g         : (',' | '%2C') -> type(COMMA);
+OPEN_g          : '('   -> type(OPEN);
+CLOSE_g         : ')'   -> type(CLOSE);
+COMMA_g         : ','   -> type(COMMA);
 WSP_g           : WS_g+ -> type(WSP);
-POINT_g         : '.' -> type(POINT);
-AT_g            : '@' -> type(AT);
-SEMI_g            : (';' | '%3B') -> type(SEMI);
-EQ_g            : '=' -> type(EQ);
+POINT_g         : '.'   -> type(POINT);
+AT_g            : '@'   -> type(AT);
+SEMI_g          : ';'   -> type(SEMI);
+EQ_g            : '='   -> type(EQ);
 
 fragment DIGIT_g    : '0'..'9';
 fragment DIGITS_g   : DIGIT_g+;
-SIGN_g              : ('+' | '%2B' |'-') -> type(SIGN);
+SIGN_g              : ('+' | '-') -> type(SIGN);
 INT_g               : SIGN_g? DIGITS_g -> type(INT);
 DECIMAL_g           : 'SS' INT_g '.' DIGITS_g (('e'|'E') SIGN_g?  DIGITS_g)? -> type(DECIMAL);
 COLLECTION_g        : C_ O_ L_ L_ E_ C_ T_ I_ O_ N_ -> type(COLLECTION);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9235cb2b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java
----------------------------------------------------------------------
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 17da148..c405f70 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
@@ -457,6 +457,11 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
         }
       } else if (property instanceof EdmNavigationProperty) {
         // create navigation property
+        if(ctx.getParent() instanceof ExpandPathContext && ctx.vlNVO.size() > 0) {
+          throw wrap(new UriParserSemanticException("Navigation properties in expand system query options must not" 
+                + " be followed a an key", UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED));
+        }
+        
         UriResourceNavigationPropertyImpl navigationResource = new UriResourceNavigationPropertyImpl()
             .setNavigationProperty((EdmNavigationProperty) property);
         context.contextUriInfo.addResourcePart(navigationResource);
@@ -1215,7 +1220,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
     // set tmp context
     context.contextExpandItemPath = expandItem;
     context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.resource);
-
+    
     super.visitExpandPath(ctx);
 
     EdmType startType = removeUriResourceStartingTypeFilterImpl(context.contextUriInfo);
@@ -1769,6 +1774,11 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
     if (ctx.vlNVO.size() > 0) {
       // check for keyPredicates
       if (pathInfoSegment instanceof UriResourceWithKeysImpl) {
+        if(ctx.vlNVO.size() > 1) {
+          throw wrap(new UriParserSemanticException("More than one key predicates found", 
+              UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES));
+        }
+        
         @SuppressWarnings("unchecked")
         List<UriParameterImpl> list = (List<UriParameterImpl>) ctx.vlNVO.get(0).accept(this);
         ((UriResourceWithKeysImpl) pathInfoSegment)

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9235cb2b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
----------------------------------------------------------------------
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 66985ce..dc31ac9 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
@@ -22,11 +22,6 @@ import java.io.UnsupportedEncodingException;
 import java.util.Arrays;
 
 import org.apache.olingo.commons.api.edm.Edm;
-import org.apache.olingo.commons.api.edm.EdmEntityContainer;
-import org.apache.olingo.commons.api.edm.EdmEntitySet;
-import org.apache.olingo.commons.api.edm.EdmEntityType;
-import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
-import org.apache.olingo.commons.api.edm.FullQualifiedName;
 import org.apache.olingo.commons.api.http.HttpContentType;
 import org.apache.olingo.commons.core.Encoder;
 import org.apache.olingo.commons.core.edm.EdmProviderImpl;
@@ -51,7 +46,6 @@ import org.apache.olingo.server.tecsvc.provider.EnumTypeProvider;
 import org.apache.olingo.server.tecsvc.provider.PropertyProvider;
 import org.junit.Ignore;
 import org.junit.Test;
-import org.mockito.Mockito;
 
 public class TestFullResourcePath {
   Edm edm = null;
@@ -5235,7 +5229,51 @@ public class TestFullResourcePath {
     .goUpUriValidator()
     .isCustomParameter(0, "@A", "'2'");
   }
-
+  
+  @Test(expected=UriParserException.class)
+  public void testDoublePercentDecoding() throws Exception {
+    testUri.run("ESAllPrim%252832767%29");
+  }
+  
+  @Test(expected=UriParserException.class)
+  public void testMultipleKeysInResourcePath() throws Exception {
+    // See OLINGO-730
+    testUri.run("ESAllPrim(32767)(1)(2)");
+  }
+  
+  @Test(expected=UriParserException.class)
+  public void testSimpleKeyInExpandSystemQueryOption() throws Exception {
+    testUri.run("ESAllPrim(0)", "$expand=NavPropertyETTwoPrimMany(-365)($filter=PropertyString eq 'Test String1')");
+  }
+  
+  @Test(expected=UriParserException.class)
+  public void testCompountKeyInExpandSystemQueryOption() throws Exception {
+    testUri.run("ESAllPrim(0)", "$expand=NavPropertyETTwoPrimMany(PropertyInt16=1,PropertyString=2)" 
+             + "($filter=PropertyString eq 'Test String1')");
+  }
+  
+  @Test(expected=UriParserException.class)
+  public void testFilterSystemQueryOptionAnyWithKeyAny() throws Exception {
+    testUri.run("ESAllPrim", "$filter=NavPropertyETTwoPrimMany(1)" 
+              + "/any(d:d/PropertyInt16 eq 0)");
+  }
+  
+  @Test(expected=UriParserException.class)
+  public void testFilterSystemQueryOptionAnyWithKeyAll() throws Exception {
+    testUri.run("ESAllPrim", "$filter=NavPropertyETTwoPrimMany(1)" 
+              + "/all(d:d/PropertyInt16 eq 0)");
+  }
+  
+  @Test
+  public void testNavigationPropertyWithCount() throws Exception {
+    testUri.run("ESKeyNav(1)/NavPropertyETTwoKeyNavMany/$count")
+           .goPath().at(0).isEntitySet("ESKeyNav").isKeyPredicate(0, "PropertyInt16", "1")
+           .at(1).isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
+           .at(2).isCount();
+  }
+  
+  // ESKeyNav(1)/NavPropertyETTwoKeyNavMany/$count
+  
   public static String encode(final String decoded) throws UnsupportedEncodingException {
     return Encoder.encode(decoded);
   }


[2/2] olingo-odata4 git commit: [OLINGO-730] UriParser fix

Posted by ch...@apache.org.
[OLINGO-730] UriParser fix


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/4a814092
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/4a814092
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/4a814092

Branch: refs/heads/master
Commit: 4a8140924656e83114402ffc249ecf8dc8639caa
Parents: 9235cb2
Author: Christian Holzer <c....@sap.com>
Authored: Fri Aug 14 10:48:08 2015 +0200
Committer: Christian Holzer <c....@sap.com>
Committed: Fri Aug 14 12:22:05 2015 +0200

----------------------------------------------------------------------
 .../server/core/uri/parser/UriContext.java      | 13 ++++++++-
 .../core/uri/parser/UriParseTreeVisitor.java    | 18 +++++++++++--
 .../core/uri/antlr/TestFullResourcePath.java    | 28 +++++++++++++++++++-
 3 files changed, 55 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4a814092/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriContext.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriContext.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriContext.java
index 07eb1d3..348364f 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriContext.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriContext.java
@@ -63,7 +63,18 @@ public class UriContext {
    */
   public ExpandItemImpl contextExpandItemPath;
   // CHECKSTYLE:ON (Maven checkstyle)
-
+  
+  //CHECKSTYLE:OFF (Maven checkstyle)
+  /**
+   * Set to true in method xxx  right before calling {@link  org.apache.olingo.server.core.uri.parser.UriParseTreeVisitor#readResourcePathSegment}
+   * After reading the path the variable is set back to false
+   * 
+   * readResourcePathSegment handles all navigation properties, it depends on the context if key predicates are allowed or not.
+   * In case of expand 
+   */
+  public boolean contextVisitExpandResourcePath;
+  //CHECKSTYLE:ON (Maven checkstyle)
+  
   // CHECKSTYLE:OFF (Maven checkstyle)
   /**
    * Set within method

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4a814092/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java
----------------------------------------------------------------------
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 c405f70..c853c1a 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
@@ -45,6 +45,7 @@ import org.apache.olingo.server.api.uri.UriInfoKind;
 import org.apache.olingo.server.api.uri.UriInfoResource;
 import org.apache.olingo.server.api.uri.UriResource;
 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.queryoption.expression.BinaryOperatorKind;
 import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
@@ -457,7 +458,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
         }
       } else if (property instanceof EdmNavigationProperty) {
         // create navigation property
-        if(ctx.getParent() instanceof ExpandPathContext && ctx.vlNVO.size() > 0) {
+        if(context.contextVisitExpandResourcePath && ctx.vlNVO.size() > 0) {
+        //if(ctx.getParent() instanceof ExpandPathContext && ctx.vlNVO.size() > 0) {
           throw wrap(new UriParserSemanticException("Navigation properties in expand system query options must not" 
                 + " be followed a an key", UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED));
         }
@@ -701,6 +703,11 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
     if (!(obj instanceof UriResourcePartTyped)) {
       throw wrap(new UriParserSemanticException("all only allowed on typed path segments",
           UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS, "all"));
+    } else if(obj instanceof UriResourceNavigation) {
+      if(!((UriResourceNavigation) obj).getKeyPredicates().isEmpty()) {
+        throw wrap(new UriParserSemanticException("Any lamdba expression must not be following navigation properties"
+            + "with key predicates", UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED));
+      }
     }
 
     UriContext.LambdaVariables var = new UriContext.LambdaVariables();
@@ -926,6 +933,11 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
       if (!(lastResourcePart instanceof UriResourcePartTyped)) {
         throw wrap(new UriParserSemanticException("any only allowed on typed path segments",
             UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS, "any"));
+      } else if(lastResourcePart instanceof UriResourceNavigation) {
+        if(!((UriResourceNavigation) lastResourcePart).getKeyPredicates().isEmpty()) {
+          throw wrap(new UriParserSemanticException("Any lamdba expression must not be following navigation properties"
+              + "with key predicates", UriParserSemanticException.MessageKeys.KEY_NOT_ALLOWED));
+        }
       }
 
       UriContext.LambdaVariables var = new UriContext.LambdaVariables();
@@ -1221,8 +1233,10 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
     context.contextExpandItemPath = expandItem;
     context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.resource);
     
+    context.contextVisitExpandResourcePath = true;
     super.visitExpandPath(ctx);
-
+    context.contextVisitExpandResourcePath = false;
+    
     EdmType startType = removeUriResourceStartingTypeFilterImpl(context.contextUriInfo);
     expandItem.setResourcePath(context.contextUriInfo);
     if (startType != null) {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4a814092/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
----------------------------------------------------------------------
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 dc31ac9..fb231f0 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
@@ -26,6 +26,7 @@ import org.apache.olingo.commons.api.http.HttpContentType;
 import org.apache.olingo.commons.core.Encoder;
 import org.apache.olingo.commons.core.edm.EdmProviderImpl;
 import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.processor.EntityProcessor;
 import org.apache.olingo.server.api.uri.UriInfoKind;
 import org.apache.olingo.server.api.uri.UriResourceKind;
 import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
@@ -5252,6 +5253,27 @@ public class TestFullResourcePath {
              + "($filter=PropertyString eq 'Test String1')");
   }
   
+  @Test
+  public void testKeyPredicatesInExpandFilter() throws Exception {
+    testUri.run("ESKeyNav(0)", "$expand=NavPropertyETTwoKeyNavMany($filter=NavPropertyETTwoKeyNavMany" 
+        + "(PropertyInt16=1,PropertyString='2')/PropertyInt16 eq 1)").goPath().goExpand()
+        .first().goPath().isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
+        .goUpExpandValidator()
+        .isFilterSerialized("<<NavPropertyETTwoKeyNavMany/PropertyInt16> eq <1>>");
+  }
+  
+  @Test
+  public void testKeyPredicatesInDoubleExpandedFilter() throws Exception {
+    testUri.run("ESKeyNav(0)", "$expand=NavPropertyETTwoKeyNavMany($expand=NavPropertyETTwoKeyNavMany" 
+        + "($filter=NavPropertyETTwoKeyNavMany(PropertyInt16=1,PropertyString='2')/PropertyInt16 eq 1))")
+        .goPath().goExpand()
+        .first().goPath().isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
+        .goUpExpandValidator().goExpand()
+        .first().goPath().isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
+        .goUpExpandValidator()
+        .isFilterSerialized("<<NavPropertyETTwoKeyNavMany/PropertyInt16> eq <1>>");
+  }
+  
   @Test(expected=UriParserException.class)
   public void testFilterSystemQueryOptionAnyWithKeyAny() throws Exception {
     testUri.run("ESAllPrim", "$filter=NavPropertyETTwoPrimMany(1)" 
@@ -5272,7 +5294,11 @@ public class TestFullResourcePath {
            .at(2).isCount();
   }
   
-  // ESKeyNav(1)/NavPropertyETTwoKeyNavMany/$count
+  @Test(expected=UriParserException.class)
+  public void testNavigationWithMoreThanOneKey() throws Exception {
+    testUri.run("ESKeyNav(1)/NavPropertyETTwoKeyNavMany(PropertyInt=1,PropertyString='2')" 
+        + "(PropertyInt=1,PropertyString='2')");
+  }
   
   public static String encode(final String decoded) throws UnsupportedEncodingException {
     return Encoder.encode(decoded);