diff --git a/abnf/odata-abnf-construction-rules.txt b/abnf/odata-abnf-construction-rules.txt index ced5037..59a53f4 100644 --- a/abnf/odata-abnf-construction-rules.txt +++ b/abnf/odata-abnf-construction-rules.txt @@ -442,16 +442,34 @@ contextFragment = %s"Collection($ref)" / singletonEntity [ navigation *( containmentNavigation ) [ "/" qualifiedEntityTypeName ] ] [ selectList ] / qualifiedTypeName [ selectList ] / entitySet ( %s"/$deletedEntity" / %s"/$link" / %s"/$deletedLink" ) - / entitySet keyPredicate "/" contextPropertyPath [ selectList ] + / entitySet contextKeyPredicate "/" contextPropertyPath [ selectList ] / entitySet [ selectList ] [ %s"/$entity" / %s"/$delta" ] entitySet = entitySetName *( containmentNavigation ) [ "/" qualifiedEntityTypeName ] -containmentNavigation = keyPredicate [ "/" qualifiedEntityTypeName ] navigation +containmentNavigation = contextKeyPredicate [ "/" qualifiedEntityTypeName ] navigation navigation = *( "/" complexProperty [ "/" qualifiedComplexTypeName ] ) "/" navigationProperty -selectList = OPEN [ selectListItem *( COMMA selectListItem ) ] CLOSE -selectListItem = STAR ; all structural properties +contextKeyPredicate = "(" ( contextKeyValue / contextKeyValuePair *( "," contextKeyValuePair ) ) ")" +contextKeyValuePair = ( primitiveKeyProperty / keyPropertyAlias ) EQ contextKeyValue +contextKeyValue = boolean + / guid + / dateTimeOffsetValue + / date + / timeOfDayValue + / decimalValue + / sbyteValue + / byte + / int16Value + / int32Value + / int64Value + / stringValue + / durationValue + / enumValue +stringValue = %x27 *( %x00-26 / %x27 %x27 / %x28-FF) %x27 ; SQUOTE = %x27 + +selectList = "(" [ selectListItem *( "," selectListItem ) ] ")" +selectListItem = "*" ; all structural properties / allOperationsInSchema / [ ( qualifiedEntityTypeName / qualifiedComplexTypeName ) "/" ] ( qualifiedActionName @@ -469,7 +487,7 @@ contextPropertyPath = primitiveProperty / complexProperty [ [ "/" qualifiedComplexTypeName ] "/" contextPropertyPath ] qualifiedActionName = namespace "." action -qualifiedFunctionName = namespace "." function [ OPEN parameterNames CLOSE ] +qualifiedFunctionName = namespace "." function [ "(" parameterName *( "," parameterName ) ")" ] complexAnnotationInFragment = annotationInFragment ; complex-valued annotation entityAnnotationInFragment = annotationInFragment ; entity-valued annotation diff --git a/abnf/odata-abnf-testcases.yaml b/abnf/odata-abnf-testcases.yaml index fa8cc5e..73a4da3 100644 --- a/abnf/odata-abnf-testcases.yaml +++ b/abnf/odata-abnf-testcases.yaml @@ -102,6 +102,7 @@ Constraints: - ProductsByCustomer - ProductsOrderedBy entityColNavigationProperty: + - Details - DirectReports - Items - Orders @@ -3363,6 +3364,21 @@ TestCases: Rule: context Input: "#Customers(Address/Country)" + - Name: Context URL - no percent-encoding allowed + Rule: context + FailAt: 18 + Input: "#Customers(Address%2FStreet)" + + - Name: Context URL - no percent-encoding allowed + Rule: context + FailAt: 18 + Input: "#Customers(Address%2COrders)" + + - Name: Context URL - no percent-encoding allowed + Rule: context + FailAt: 10 + Input: "#Customers%28Address,Orders)" + - Name: Context URL - Entity set with $select and type-cast Rule: context Input: "#Customers(Address/Model.AddressWithLocation,Orders)" @@ -3459,14 +3475,31 @@ TestCases: Rule: context Input: "#SingletonEntity/Orders(3)/Items" + - Name: Context URL - Forbidden key-as-segment + Rule: context + FailAt: 8 + Input: "#Orders/3/Items" + - Name: Context URL - collection with containment Rule: context Input: "#Customers('ALFKI')/Orders" + - Name: Context URL - collection with containment, key value without percent-encoding + Rule: context + Input: "#Customers('ALF/KI')/Orders" + + - Name: Context URL - collection with containment, key value without percent-encoding + Rule: context + Input: "#Customers('%')/Orders" + - Name: Context URL - containment Rule: context Input: "#Customers('ALFKI')/Orders/$entity" + - Name: Context URL - containment - multi-part key + Rule: context + Input: "#OrderItems(OrderID=1,ItemID='a/b')/Details/$entity" + - Name: Context URL - collection with containment - multi-level Rule: context Input: "#Customers('ALFKI')/Orders(1)/Items"