From 5ac124db3c821e0290cbcb8c6f9be9af1c5d8bf3 Mon Sep 17 00:00:00 2001 From: Ralf Handl Date: Fri, 27 Oct 2023 10:33:41 +0200 Subject: [PATCH 01/12] ODATA-1583 --- abnf/odata-abnf-construction-rules.txt | 48 +++++++++++++++++++++----- abnf/odata-abnf-testcases.yaml | 9 +++++ 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/abnf/odata-abnf-construction-rules.txt b/abnf/odata-abnf-construction-rules.txt index 992c43f..2ec6d45 100644 --- a/abnf/odata-abnf-construction-rules.txt +++ b/abnf/odata-abnf-construction-rules.txt @@ -124,10 +124,23 @@ keyPredicate = simpleKey / compoundKey / keyPathSegments simpleKey = OPEN ( parameterAlias / keyPropertyValue ) CLOSE compoundKey = OPEN keyValuePair *( COMMA keyValuePair ) CLOSE keyValuePair = ( primitiveKeyProperty / keyPropertyAlias ) EQ ( parameterAlias / keyPropertyValue ) -keyPropertyValue = primitiveLiteral keyPropertyAlias = odataIdentifier keyPathSegments = 1*( "/" keyPathLiteral ) keyPathLiteral = *pchar +keyPropertyValue = booleanValue + / guidValue + / dateTimeOffsetValueInUrl + / dateValue + / timeOfDayValueInUrl + / decimalValue + / sbyteValue + / byteValue + / int16Value + / int32Value + / int64Value + / string ; single-quoted + / duration + / enum singleNavigation = singleNavPath / "/" optionallyQualifiedEntityTypeName [ singleNavPath ] @@ -429,13 +442,30 @@ 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 -navigation = *( "/" complexProperty [ "/" qualifiedComplexTypeName ] ) "/" navigationProperty +containmentNavigation = contextKeyPredicate [ "/" qualifiedEntityTypeName ] navigation +navigation = *( "/" complexProperty [ "/" qualifiedComplexTypeName ] ) "/" navigationProperty + +contextKeyPredicate = OPEN ( contextKeyValue / contextKeyValuePair *( COMMA contextKeyValuePair ) ) CLOSE +contextKeyValuePair = ( primitiveKeyProperty / keyPropertyAlias ) EQ contextKeyValue +contextKeyValue = booleanValue + / guidValue + / dateTimeOffsetValue ; TODO: no percent-encoding in SIGN + / dateValue + / timeOfDayValue + / decimalValue ; TODO: no percent-encoding in SIGN + / sbyteValue ; TODO: no percent-encoding in SIGN + / byteValue + / int16Value ; TODO: no percent-encoding in SIGN + / int32Value ; TODO: no percent-encoding in SIGN + / int64Value ; TODO: no percent-encoding in SIGN + / string ; TODO: no percent-encoding + / duration ; TODO: no percent-encoding in SIGN + / enum ; TODO: no percent-encoding in SIGN selectList = OPEN [ selectListItem *( COMMA selectListItem ) ] CLOSE selectListItem = STAR ; all structural properties @@ -893,7 +923,7 @@ primitiveLiteral = nullValue ; plain values up to int64Value / string ; single-quoted / duration / enum - / binary ; all others are quoted and prefixed + / binary ; all others are quoted and prefixed / geographyCollection / geographyLineString / geographyMultiLineString @@ -908,8 +938,8 @@ primitiveLiteral = nullValue ; plain values up to int64Value / geometryMultiPolygon / geometryPoint / geometryPolygon - -; in Atom and JSON message bodies and CSDL DefaultValue attributes + +; in Atom and JSON message bodies and CSDL DefaultValue attributes primitiveValue = booleanValue / guidValue / durationValue @@ -937,9 +967,9 @@ primitiveValue = booleanValue ; - any XML string for strings in Atom and CSDL documents ; - any JSON string for JSON documents -nullValue = %s"null" +nullValue = %s"null" -; base64url encoding according to http://tools.ietf.org/html/rfc4648#section-5 +; base64url encoding according to http://tools.ietf.org/html/rfc4648#section-5 binary = "binary" SQUOTE binaryValue SQUOTE binaryValue = *(4base64char) [ base64b16 / base64b8 ] base64b16 = 2base64char ( %s"A" / %s"E" / %s"I" / %s"M" / %s"Q" / %s"U" / %s"Y" / %s"c" / %s"g" / %s"k" / %s"o" / %s"s" / %s"w" / %s"0" / %s"4" / %s"8" ) [ "=" ] diff --git a/abnf/odata-abnf-testcases.yaml b/abnf/odata-abnf-testcases.yaml index b9265be..f309e8c 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 @@ -3375,10 +3376,18 @@ TestCases: 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 - containment Rule: context Input: "#Customers('ALFKI')/Orders/$entity" + - Name: Context URL - containment - multi-part key + Rule: context + Input: "#OrderItems(OrderID=1,ItemID='a')/Details/$entity" + - Name: Context URL - collection with containment - multi-level Rule: context Input: "#Customers('ALFKI')/Orders(1)/Items" From c0dc760e29ad6823ad90d59ced82e736e29ffcc1 Mon Sep 17 00:00:00 2001 From: Ralf Handl Date: Fri, 27 Oct 2023 11:33:09 +0200 Subject: [PATCH 02/12] ODATA-1540 --- abnf/odata-abnf-construction-rules.txt | 6 +++--- abnf/odata-abnf-testcases.yaml | 20 ++++++++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/abnf/odata-abnf-construction-rules.txt b/abnf/odata-abnf-construction-rules.txt index 992c43f..488cb80 100644 --- a/abnf/odata-abnf-construction-rules.txt +++ b/abnf/odata-abnf-construction-rules.txt @@ -359,7 +359,7 @@ searchPhrase = quotation-mark 1*( qchar-no-AMP-DQUOTE / SP ) quotation-mark ; Expressing this in ABNF is somewhat clumsy, so the following rule is overly generous. ; Note: the words AND, OR, and NOT are sometimes operators, depending on their position within a search expression. searchWord = searchChar *( searchChar / SQUOTE ) -searchChar = unreserved / pct-encoded-no-DQUOTE / "!" / "*" / "+" / "," / ":" / "@" / "/" / "?" / "$" / "=" +searchChar = unreserved / pct-encoded-no-DQUOTE / "!" / "*" /"+" / "," / ":" / "@" / "/" / "?" / "$" / "=" searchExpr-incomplete = SQUOTE *( SQUOTE-in-string / qchar-no-AMP-SQUOTE / quotation-mark / SP ) SQUOTE @@ -966,7 +966,7 @@ SQUOTE-in-string = SQUOTE SQUOTE ; two consecutive single quotes represent one w dateValue = year "-" month "-" day -dateTimeOffsetValue = year "-" month "-" day "T" timeOfDayValue ( "Z" / SIGN hour ":" minute ) +dateTimeOffsetValue = year "-" month "-" day "T" timeOfDayValue ( "Z" / ( "+" / "-" ) hour ":" minute ) dateTimeOffsetValueInUrl = year "-" month "-" day "T" timeOfDayValueInUrl ( "Z" / SIGN hour COLON minute ) duration = [ "duration" ] SQUOTE durationValue SQUOTE @@ -1146,7 +1146,7 @@ COLON = ":" / "%3A" COMMA = "," / "%2C" EQ = "=" HASH = "%23" ; the # character is not allowed in the query part -SIGN = "+" / "%2B" / "-" +SIGN = "%2B" / "-" SEMI = ";" / "%3B" STAR = "*" / "%2A" SQUOTE = "'" / "%27" diff --git a/abnf/odata-abnf-testcases.yaml b/abnf/odata-abnf-testcases.yaml index b9265be..d43f205 100644 --- a/abnf/odata-abnf-testcases.yaml +++ b/abnf/odata-abnf-testcases.yaml @@ -431,7 +431,7 @@ TestCases: - Name: "DateTimeOffset: with percent-encoding" Rule: dateTimeOffsetValueInUrl - Input: 2012-09-03T23%3A59+01%3A00 + Input: 2012-09-03T23%3A59%2B01%3A00 - Name: Decimal Rule: decimalValue @@ -479,14 +479,18 @@ TestCases: Rule: duration Input: "'P6DT23H59M59.9999S'" - - Name: "Decimal: integer" + - Name: Decimal - negative integer Rule: decimalValue Input: "-2" - - Name: "Decimal: integer" + - Name: Decimal - positive integer Rule: decimalValue - FailAt: 4 - Input: "+42." + Input: "%2B42" + + - Name: Decimal - trailing dot + Rule: decimalValue + FailAt: 3 + Input: "42." - Name: "Decimal: no digit before decimal point" Rule: decimalValue @@ -540,7 +544,11 @@ TestCases: - Name: Int16 Rule: int16Value - Input: "+32000" + Input: "32000" + + - Name: Int16 + Rule: int16Value + Input: "%2B32000" - Name: Int32 Rule: int32Value From 645b5c890d35a5e53976f1f898b4616a46e449f6 Mon Sep 17 00:00:00 2001 From: Ralf Handl Date: Fri, 27 Oct 2023 13:21:46 +0200 Subject: [PATCH 03/12] Plus in URL needs percent-encoding --- abnf/odata-abnf-construction-rules.txt | 139 +++++++++++++------------ abnf/odata-abnf-testcases.yaml | 135 ++++++++++++++---------- 2 files changed, 153 insertions(+), 121 deletions(-) diff --git a/abnf/odata-abnf-construction-rules.txt b/abnf/odata-abnf-construction-rules.txt index 488cb80..6ada77c 100644 --- a/abnf/odata-abnf-construction-rules.txt +++ b/abnf/odata-abnf-construction-rules.txt @@ -327,7 +327,7 @@ format = ( "$format" / "format" ) EQ ) ; format specific to the specific data service> or ; -inlinecount = ( "$count" / "count" ) EQ booleanValue +inlinecount = ( "$count" / "count" ) EQ boolean schemaversion = ( "$schemaversion" / "schemaversion" ) EQ ( STAR / 1*unreserved ) @@ -685,7 +685,7 @@ gtExpr = RWS "gt" RWS commonExpr geExpr = RWS "ge" RWS commonExpr inExpr = RWS "in" RWS ( listExpr / commonExpr ) -hasExpr = RWS "has" RWS enum +hasExpr = RWS "has" RWS enumLiteral addExpr = RWS "add" RWS commonExpr subExpr = RWS "sub" RWS commonExpr @@ -876,24 +876,24 @@ primitiveColFunctionImport = odataIdentifier ;------------------------------------------------------------------------------ ; in URLs -primitiveLiteral = nullValue ; plain values up to int64Value - / booleanValue - / guidValue - / dateTimeOffsetValueInUrl - / dateValue - / timeOfDayValueInUrl - / decimalValue - / doubleValue - / singleValue - / sbyteValue - / byteValue - / int16Value - / int32Value - / int64Value - / string ; single-quoted - / duration - / enum - / binary ; all others are quoted and prefixed +primitiveLiteral = null + / boolean + / guid + / dateTimeOffsetLiteral + / date + / timeOfDayLiteral + / decimalLiteral + / doubleLiteral + / singleLiteral + / sbyteLiteral + / byte + / int16Literal + / int32Literal + / int64Literal + / stringLiteral ; single-quoted + / durationLiteral + / enumLiteral + / binaryLiteral ; all others are quoted and prefixed / geographyCollection / geographyLineString / geographyMultiLineString @@ -910,11 +910,11 @@ primitiveLiteral = nullValue ; plain values up to int64Value / geometryPolygon ; in Atom and JSON message bodies and CSDL DefaultValue attributes -primitiveValue = booleanValue - / guidValue +primitiveValue = boolean + / guid / durationValue - / dateTimeOffsetValue - / dateValue + / dateTimeOffsetValue + / date / timeOfDayValue / enumValue / fullCollectionLiteral @@ -924,58 +924,65 @@ primitiveValue = booleanValue / fullMultiPolygonLiteral / fullPointLiteral / fullPolygonLiteral - / decimalValue - / doubleValue - / singleValue - / sbyteValue - / byteValue - / int16Value - / int32Value - / int64Value - / binaryValue + / decimalValue + / doubleValue + / singleValue + / sbyteValue + / byte + / int16Value + / int32Value + / int64Value + / binaryValue ; also valid are: - ; - any XML string for strings in Atom and CSDL documents + ; - any XML string for strings in Atom and CSDL XML documents ; - any JSON string for JSON documents -nullValue = %s"null" +null = %s"null" ; base64url encoding according to http://tools.ietf.org/html/rfc4648#section-5 -binary = "binary" SQUOTE binaryValue SQUOTE -binaryValue = *(4base64char) [ base64b16 / base64b8 ] -base64b16 = 2base64char ( %s"A" / %s"E" / %s"I" / %s"M" / %s"Q" / %s"U" / %s"Y" / %s"c" / %s"g" / %s"k" / %s"o" / %s"s" / %s"w" / %s"0" / %s"4" / %s"8" ) [ "=" ] -base64b8 = base64char ( %s"A" / %s"Q" / %s"g" / %s"w" ) [ "==" ] -base64char = ALPHA / DIGIT / "-" / "_" - -booleanValue = "true" / "false" - -decimalValue = [ SIGN ] 1*DIGIT [ "." 1*DIGIT ] [ "e" [ SIGN ] 1*DIGIT ] / nanInfinity -doubleValue = decimalValue ; IEEE 754 binary64 floating-point number (15-17 decimal digits) -singleValue = decimalValue ; IEEE 754 binary32 floating-point number (6-9 decimal digits) -nanInfinity = %s"NaN" / %s"-INF" / %s"INF" - -guidValue = 8HEXDIG "-" 4HEXDIG "-" 4HEXDIG "-" 4HEXDIG "-" 12HEXDIG - -byteValue = 1*3DIGIT ; numbers in the range from 0 to 255 -sbyteValue = [ SIGN ] 1*3DIGIT ; numbers in the range from -128 to 127 -int16Value = [ SIGN ] 1*5DIGIT ; numbers in the range from -32768 to 32767 -int32Value = [ SIGN ] 1*10DIGIT ; numbers in the range from -2147483648 to 2147483647 -int64Value = [ SIGN ] 1*19DIGIT ; numbers in the range from -9223372036854775808 to 9223372036854775807 - -string = SQUOTE *( SQUOTE-in-string / pchar-no-SQUOTE ) SQUOTE +binaryLiteral = "binary" SQUOTE binaryValue SQUOTE +binaryValue = *(4base64char) [ base64b16 / base64b8 ] +base64b16 = 2base64char ( %s"A" / %s"E" / %s"I" / %s"M" / %s"Q" / %s"U" / %s"Y" / %s"c" / %s"g" / %s"k" / %s"o" / %s"s" / %s"w" / %s"0" / %s"4" / %s"8" ) [ "=" ] +base64b8 = base64char ( %s"A" / %s"Q" / %s"g" / %s"w" ) [ "==" ] +base64char = ALPHA / DIGIT / "-" / "_" + +boolean = "true" / "false" + +decimalLiteral = [ SIGN ] 1*DIGIT [ "." 1*DIGIT ] [ "e" [ SIGN ] 1*DIGIT ] / nanInfinity +decimalValue = ["+"/"-"] 1*DIGIT [ "." 1*DIGIT ] [ "e" ["+"/"-"] 1*DIGIT ] / nanInfinity +doubleLiteral = decimalLiteral ; IEEE 754 binary64 floating-point number (15-17 decimal digits) +doubleValue = decimalValue ; IEEE 754 binary64 floating-point number (15-17 decimal digits) +singleLiteral = decimalLiteral ; IEEE 754 binary32 floating-point number (6-9 decimal digits) +singleValue = decimalValue ; IEEE 754 binary32 floating-point number (6-9 decimal digits) +nanInfinity = %s"NaN" / %s"-INF" / %s"INF" + +guid = 8HEXDIG "-" 4HEXDIG "-" 4HEXDIG "-" 4HEXDIG "-" 12HEXDIG + +byte = 1*3DIGIT ; numbers in the range from 0 to 255 +sbyteLiteral = [ SIGN ] 1*3DIGIT ; numbers in the range from -128 to 127 +sbyteValue = ["+"/"-"] 1*3DIGIT ; numbers in the range from -128 to 127 +int16Literal = [ SIGN ] 1*5DIGIT ; numbers in the range from -32768 to 32767 +int16Value = ["+"/"-"] 1*5DIGIT ; numbers in the range from -32768 to 32767 +int32Literal = [ SIGN ] 1*10DIGIT ; numbers in the range from -2147483648 to 2147483647 +int32Value = ["+"/"-"] 1*10DIGIT ; numbers in the range from -2147483648 to 2147483647 +int64Literal = [ SIGN ] 1*19DIGIT ; numbers in the range from -9223372036854775808 to 9223372036854775807 +int64Value = ["+"/"-"] 1*19DIGIT ; numbers in the range from -9223372036854775808 to 9223372036854775807 + +stringLiteral = SQUOTE *( SQUOTE-in-string / pchar-no-SQUOTE ) SQUOTE SQUOTE-in-string = SQUOTE SQUOTE ; two consecutive single quotes represent one within a string literal -dateValue = year "-" month "-" day +date = year "-" month "-" day -dateTimeOffsetValue = year "-" month "-" day "T" timeOfDayValue ( "Z" / ( "+" / "-" ) hour ":" minute ) -dateTimeOffsetValueInUrl = year "-" month "-" day "T" timeOfDayValueInUrl ( "Z" / SIGN hour COLON minute ) +dateTimeOffsetLiteral = date "T" timeOfDayLiteral ( "Z" / SIGN hour COLON minute ) +dateTimeOffsetValue = date "T" timeOfDayValue ( "Z" / ("+"/"-") hour ":" minute ) -duration = [ "duration" ] SQUOTE durationValue SQUOTE -durationValue = [ SIGN ] "P" [ 1*DIGIT "D" ] [ "T" [ 1*DIGIT "H" ] [ 1*DIGIT "M" ] [ 1*DIGIT [ "." 1*DIGIT ] "S" ] ] +durationLiteral = [ "duration" ] SQUOTE durationValue SQUOTE +durationValue = [ "-" ] "P" [ 1*DIGIT "D" ] [ "T" [ 1*DIGIT "H" ] [ 1*DIGIT "M" ] [ 1*DIGIT [ "." 1*DIGIT ] "S" ] ] ; the above is an approximation of the rules for an xml dayTimeDuration. ; see the lexical representation for dayTimeDuration in http://www.w3.org/TR/xmlschema11-2#dayTimeDuration for more information -timeOfDayValue = hour ":" minute [ ":" second [ "." fractionalSeconds ] ] -timeOfDayValueInUrl = hour COLON minute [ COLON second [ "." fractionalSeconds ] ] +timeOfDayLiteral = hour COLON minute [ COLON second [ "." fractionalSeconds ] ] +timeOfDayValue = hour ":" minute [ ":" second [ "." fractionalSeconds ] ] oneToNine = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" zeroToFiftyNine = ( "0" / "1" / "2" / "3" / "4" / "5" ) DIGIT @@ -991,7 +998,7 @@ minute = zeroToFiftyNine second = zeroToFiftyNine / "60" ; for leap seconds fractionalSeconds = 1*12DIGIT -enum = [ qualifiedEnumTypeName ] SQUOTE enumValue SQUOTE +enumLiteral = [ qualifiedEnumTypeName ] SQUOTE enumValue SQUOTE enumValue = singleEnumValue *( COMMA singleEnumValue ) singleEnumValue = enumerationMember / enumMemberValue enumMemberValue = int64Value @@ -1096,7 +1103,7 @@ allowEntityReferencesPreference = [ "odata." ] "allow-entityreferences" callbackPreference = [ "odata." ] "callback" OWS ";" OWS "url" EQ-h DQUOTE URI DQUOTE -continueOnErrorPreference = [ "odata." ] "continue-on-error" [ EQ-h booleanValue ] +continueOnErrorPreference = [ "odata." ] "continue-on-error" [ EQ-h boolean ] includeAnnotationsPreference = [ "odata." ] "include-annotations" EQ-h DQUOTE annotationsList DQUOTE annotationsList = annotationIdentifier *("," annotationIdentifier) diff --git a/abnf/odata-abnf-testcases.yaml b/abnf/odata-abnf-testcases.yaml index d43f205..780f6eb 100644 --- a/abnf/odata-abnf-testcases.yaml +++ b/abnf/odata-abnf-testcases.yaml @@ -276,89 +276,89 @@ TestCases: Input: http//My.Org/ - Name: Binary with X - Rule: binary + Rule: binaryLiteral FailAt: 0 Input: X'1a2B3c4D' - Name: Binary - empty - Rule: binary + Rule: binaryLiteral Input: binary'' - Name: Binary - f - Rule: binary + Rule: binaryLiteral Input: binary'Zg==' - Name: Binary - f (pad character is optional) - Rule: binary + Rule: binaryLiteral Input: binary'Zg' - Name: Binary - fo - Rule: binary + Rule: binaryLiteral Input: binary'Zm8=' - Name: Binary - fo (pad character is optional) - Rule: binary + Rule: binaryLiteral Input: binary'Zm8=' - Name: Binary - foo - Rule: binary + Rule: binaryLiteral Input: binary'Zm9v' - Name: Binary - foob - Rule: binary + Rule: binaryLiteral Input: binary'Zm9vYg==' - Name: Binary - fooba - Rule: binary + Rule: binaryLiteral Input: binary'Zm9vYmE=' - Name: Binary - foobar - Rule: binary + Rule: binaryLiteral Input: binary'Zm9vYmFy' - Name: Boolean - true - Rule: booleanValue + Rule: boolean Input: "true" - Name: Boolean - false - Rule: booleanValue + Rule: boolean Input: "false" - Name: Boolean - literals are case-insensitive - Rule: booleanValue + Rule: boolean Input: tRUe - Name: Date in URL or body - Rule: dateValue + Rule: date Input: 2012-09-03 - Name: Date - Rule: dateValue + Rule: date Input: 2012-09-10 - Name: Date - Rule: dateValue + Rule: date Input: 2012-09-20 - Name: Date - Rule: dateValue + Rule: date Input: 2012-09-03 - Name: "Date: year zero" - Rule: dateValue + Rule: date Input: 0000-01-01 - Name: "Date: negative" - Rule: dateValue + Rule: date Input: -10000-04-01 - Name: "Date: negative Infinity" - Rule: dateValue + Rule: date FailAt: 1 Input: -INF - Name: "Date: positive Infinity" - Rule: dateValue + Rule: date FailAt: 0 Input: INF @@ -430,7 +430,7 @@ TestCases: Input: 2012-09-03T23:59+01%3A00 - Name: "DateTimeOffset: with percent-encoding" - Rule: dateTimeOffsetValueInUrl + Rule: dateTimeOffsetLiteral Input: 2012-09-03T23%3A59%2B01%3A00 - Name: Decimal @@ -457,35 +457,44 @@ TestCases: Rule: decimalValue Input: NaN - - Name: Duration in body + - Name: Duration Rule: durationValue - Input: P6DT23H59M59.9999S + Input: -P6DT23H59M59.9999S - - Name: "Duration in body: no years allowed" + - Name: Duration - no plus allowed + Rule: durationValue + FailAt: 0 + Input: +P6DT23H59M59.9999S + + - Name: Duration in body - no years allowed Rule: durationValue FailAt: 2 Input: P1Y6DT23H59M59.9999S - - Name: "Duration in body: no months allowed" + - Name: Duration in body - no months allowed Rule: durationValue FailAt: 2 Input: P1M6DT23H59M59.9999S - Name: Duration in URL - Rule: duration + Rule: durationLiteral Input: duration'P6DT23H59M59.9999S' - Name: Duration in URL without prefix - Rule: duration + Rule: durationLiteral Input: "'P6DT23H59M59.9999S'" - Name: Decimal - negative integer Rule: decimalValue Input: "-2" + - Name: Decimal - positive integer in URL + Rule: primitiveLiteral + Input: "%2B42" + - Name: Decimal - positive integer Rule: decimalValue - Input: "%2B42" + Input: "+42" - Name: Decimal - trailing dot Rule: decimalValue @@ -498,7 +507,7 @@ TestCases: Input: ".1" - Name: Decimal in URL - Rule: decimalValue + Rule: decimalLiteral Input: "3.14" - Name: Double @@ -527,67 +536,83 @@ TestCases: Input: NaN - Name: Double in URL - Rule: doubleValue + Rule: doubleLiteral Input: "-0.314e1" - Name: Single in URL + Rule: singleLiteral + Input: "%2B0.314e%2B1" + + - Name: Single Rule: singleValue - Input: "-0.314e1" + Input: "+0.314e+1" - Name: Byte - Rule: byteValue + Rule: byte Input: "255" + - Name: SByte in URL + Rule: sbyteLiteral + Input: "%2B128" + - Name: SByte Rule: sbyteValue Input: "-128" - - Name: Int16 - Rule: int16Value - Input: "32000" + - Name: Int16 in URL + Rule: int16Literal + Input: "%2B32000" - Name: Int16 Rule: int16Value - Input: "%2B32000" + Input: "+32000" + + - Name: Int32 in URL + Rule: int32Literal + Input: "%2B2000000000" - Name: Int32 Rule: int32Value Input: "-2000000000" + - Name: Int64 in URL + Rule: int64Literal + Input: "%2B1234567890123456789" + - Name: Int64 Rule: int64Value Input: "1234567890123456789" - Name: "Null: unqualified" - Rule: nullValue + Rule: "null" Input: "null" - Name: String - Rule: string + Rule: stringLiteral Input: "'ABCDEFGHIHJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\ ('')*+,;=:@'" - Name: String - Rule: string + Rule: stringLiteral Input: "'O''Neil'" - Name: String - Rule: string + Rule: stringLiteral Input: "%27O'%27Neil'" - Name: String - Rule: string + Rule: stringLiteral FailAt: 3 Input: "'O'Neil'" - Name: String - Rule: string + Rule: stringLiteral FailAt: 5 Input: "'O%27Neil'" - Name: String - Rule: string + Rule: stringLiteral Input: "'%26%28'" - Name: primitive value in request body - enumeration member @@ -630,7 +655,7 @@ TestCases: Input: Categories(ID=1) - Name: Key - Rule: string + Rule: stringLiteral Input: "'Hugo''s%20Tavern'" - Name: Resource Path - Entity @@ -638,16 +663,16 @@ TestCases: Input: Categories(ID=1,Size=5) - Name: Correct Guid - Rule: guidValue + Rule: guid Input: 01234567-89ab-cdef-0123-456789abcdef - Name: Guid with wrong character - Rule: guidValue + Rule: guid FailAt: 5 Input: 01234g67-89ab-cdef-0123-456789abcdef - Name: Guid with less than 32 digits - Rule: guidValue + Rule: guid FailAt: 23 Input: 01234567-89ab-cdef-456789abcdef @@ -656,7 +681,7 @@ TestCases: Input: 11:22:33 - Name: TimeOfDay - percent-encoded colon - Rule: timeOfDayValueInUrl + Rule: timeOfDayLiteral Input: 11%3A22%3a33 - Name: TimeOfDay - no percent-encoding in payloads @@ -1609,12 +1634,12 @@ TestCases: Input: true eq false - Name: 5.1.1.1.1 boolean - only true and false - Rule: booleanValue + Rule: boolean FailAt: 0 Input: "0" - Name: 5.1.1.1.1 boolean - only true and false - Rule: booleanValue + Rule: boolean FailAt: 0 Input: "1" @@ -2954,19 +2979,19 @@ TestCases: Input: ProductsByCustomer(customer=@c)?@c={"Orders":[$root/Orders(1)]} - Name: Enumeration value in URI - Rule: enum + Rule: enumLiteral Input: Sales.Pattern'Yellow' - Name: Enumeration value in URI - without prefix - Rule: enum + Rule: enumLiteral Input: "'Yellow'" - Name: Enumeration value in URI - multiple flag values - Rule: enum + Rule: enumLiteral Input: Sales.Pattern'Solid,Yellow' - Name: Enumeration value in URI - multiple flag values without prefix - Rule: enum + Rule: enumLiteral Input: "'Solid,Yellow'" - Name: Enumeration value - in filter From af69ba44c7ddca1a65be6dfd476ceb08622892e6 Mon Sep 17 00:00:00 2001 From: Ralf Handl Date: Fri, 27 Oct 2023 13:26:26 +0200 Subject: [PATCH 04/12] Update odata-abnf-construction-rules.txt --- abnf/odata-abnf-construction-rules.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abnf/odata-abnf-construction-rules.txt b/abnf/odata-abnf-construction-rules.txt index f40e31a..094cf49 100644 --- a/abnf/odata-abnf-construction-rules.txt +++ b/abnf/odata-abnf-construction-rules.txt @@ -359,7 +359,7 @@ searchPhrase = quotation-mark 1*( qchar-no-AMP-DQUOTE / SP ) quotation-mark ; Expressing this in ABNF is somewhat clumsy, so the following rule is overly generous. ; Note: the words AND, OR, and NOT are sometimes operators, depending on their position within a search expression. searchWord = searchChar *( searchChar / SQUOTE ) -searchChar = unreserved / pct-encoded-no-DQUOTE / "!" / "*" /"+" / "," / ":" / "@" / "/" / "?" / "$" / "=" +searchChar = unreserved / pct-encoded-no-DQUOTE / "!" / "*" / "+" / "," / ":" / "@" / "/" / "?" / "$" / "=" searchExpr-incomplete = SQUOTE *( SQUOTE-in-string / qchar-no-AMP-SQUOTE / quotation-mark / SP ) SQUOTE From c68d55b4be753cca15b0c44d330cc0065a59a634 Mon Sep 17 00:00:00 2001 From: Ralf Handl Date: Fri, 27 Oct 2023 13:34:52 +0200 Subject: [PATCH 05/12] Update odata-abnf-construction-rules.txt --- abnf/odata-abnf-construction-rules.txt | 48 +++++++++++++------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/abnf/odata-abnf-construction-rules.txt b/abnf/odata-abnf-construction-rules.txt index 094cf49..92c68c3 100644 --- a/abnf/odata-abnf-construction-rules.txt +++ b/abnf/odata-abnf-construction-rules.txt @@ -893,23 +893,23 @@ primitiveLiteral = null / stringLiteral ; single-quoted / durationLiteral / enumLiteral - / binaryLiteral ; all others are quoted and prefixed - / geographyCollection - / geographyLineString - / geographyMultiLineString - / geographyMultiPoint - / geographyMultiPolygon - / geographyPoint - / geographyPolygon - / geometryCollection - / geometryLineString - / geometryMultiLineString - / geometryMultiPoint - / geometryMultiPolygon - / geometryPoint + / binaryLiteral ; all others are quoted and prefixed + / geographyCollection + / geographyLineString + / geographyMultiLineString + / geographyMultiPoint + / geographyMultiPolygon + / geographyPoint + / geographyPolygon + / geometryCollection + / geometryLineString + / geometryMultiLineString + / geometryMultiPoint + / geometryMultiPolygon + / geometryPoint / geometryPolygon - -; in Atom and JSON message bodies and CSDL DefaultValue attributes + +; in Atom and JSON message bodies and CSDL DefaultValue attributes primitiveValue = boolean / guid / durationValue @@ -935,11 +935,11 @@ primitiveValue = boolean / binaryValue ; also valid are: ; - any XML string for strings in Atom and CSDL XML documents - ; - any JSON string for JSON documents + ; - any JSON string for JSON documents -null = %s"null" +null = %s"null" -; base64url encoding according to http://tools.ietf.org/html/rfc4648#section-5 +; base64url encoding according to http://tools.ietf.org/html/rfc4648#section-5 binaryLiteral = "binary" SQUOTE binaryValue SQUOTE binaryValue = *(4base64char) [ base64b16 / base64b8 ] base64b16 = 2base64char ( %s"A" / %s"E" / %s"I" / %s"M" / %s"Q" / %s"U" / %s"Y" / %s"c" / %s"g" / %s"k" / %s"o" / %s"s" / %s"w" / %s"0" / %s"4" / %s"8" ) [ "=" ] @@ -956,13 +956,13 @@ singleLiteral = decimalLiteral ; IEEE 754 binary32 floating-point number (6-9 d singleValue = decimalValue ; IEEE 754 binary32 floating-point number (6-9 decimal digits) nanInfinity = %s"NaN" / %s"-INF" / %s"INF" -guid = 8HEXDIG "-" 4HEXDIG "-" 4HEXDIG "-" 4HEXDIG "-" 12HEXDIG +guid = 8HEXDIG "-" 4HEXDIG "-" 4HEXDIG "-" 4HEXDIG "-" 12HEXDIG byte = 1*3DIGIT ; numbers in the range from 0 to 255 sbyteLiteral = [ SIGN ] 1*3DIGIT ; numbers in the range from -128 to 127 sbyteValue = ["+"/"-"] 1*3DIGIT ; numbers in the range from -128 to 127 -int16Literal = [ SIGN ] 1*5DIGIT ; numbers in the range from -32768 to 32767 -int16Value = ["+"/"-"] 1*5DIGIT ; numbers in the range from -32768 to 32767 +int16Literal = [ SIGN ] 1*5DIGIT ; numbers in the range from -32768 to 32767 +int16Value = ["+"/"-"] 1*5DIGIT ; numbers in the range from -32768 to 32767 int32Literal = [ SIGN ] 1*10DIGIT ; numbers in the range from -2147483648 to 2147483647 int32Value = ["+"/"-"] 1*10DIGIT ; numbers in the range from -2147483648 to 2147483647 int64Literal = [ SIGN ] 1*19DIGIT ; numbers in the range from -9223372036854775808 to 9223372036854775807 @@ -983,8 +983,8 @@ durationValue = [ "-" ] "P" [ 1*DIGIT "D" ] [ "T" [ 1*DIGIT "H" ] [ 1*DIGIT "M" timeOfDayLiteral = hour COLON minute [ COLON second [ "." fractionalSeconds ] ] timeOfDayValue = hour ":" minute [ ":" second [ "." fractionalSeconds ] ] - -oneToNine = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" + +oneToNine = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" zeroToFiftyNine = ( "0" / "1" / "2" / "3" / "4" / "5" ) DIGIT year = [ "-" ] ( "0" 3DIGIT / oneToNine 3*DIGIT ) month = "0" oneToNine From 5e067641f572f7482d5a0cca5b79464e712bd422 Mon Sep 17 00:00:00 2001 From: Ralf Handl Date: Fri, 27 Oct 2023 14:25:00 +0200 Subject: [PATCH 06/12] Update odata-abnf-construction-rules.txt --- abnf/odata-abnf-construction-rules.txt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/abnf/odata-abnf-construction-rules.txt b/abnf/odata-abnf-construction-rules.txt index 327b48e..8706645 100644 --- a/abnf/odata-abnf-construction-rules.txt +++ b/abnf/odata-abnf-construction-rules.txt @@ -920,10 +920,10 @@ primitiveLiteral = null / int16Literal / int32Literal / int64Literal - / stringLiteral ; single-quoted + / stringLiteral / durationLiteral / enumLiteral - / binaryLiteral ; all others are quoted and prefixed + / binaryLiteral / geographyCollection / geographyLineString / geographyMultiLineString @@ -939,7 +939,7 @@ primitiveLiteral = null / geometryPoint / geometryPolygon -; in Atom and JSON message bodies and CSDL DefaultValue attributes +; in CSDL XML DefaultValue attributes primitiveValue = boolean / guid / durationValue @@ -963,9 +963,6 @@ primitiveValue = boolean / int32Value / int64Value / binaryValue - ; also valid are: - ; - any XML string for strings in Atom and CSDL XML documents - ; - any JSON string for JSON documents null = %s"null" From 05d8a28d5f93b89ff697ef9a2c5ad8d24ee29c18 Mon Sep 17 00:00:00 2001 From: Ralf Handl Date: Fri, 27 Oct 2023 14:28:22 +0200 Subject: [PATCH 07/12] Update odata-abnf-construction-rules.txt --- abnf/odata-abnf-construction-rules.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/abnf/odata-abnf-construction-rules.txt b/abnf/odata-abnf-construction-rules.txt index 92c68c3..2c554a2 100644 --- a/abnf/odata-abnf-construction-rules.txt +++ b/abnf/odata-abnf-construction-rules.txt @@ -909,7 +909,7 @@ primitiveLiteral = null / geometryPoint / geometryPolygon -; in Atom and JSON message bodies and CSDL DefaultValue attributes +; in CSDL XML DefaultValue attributes primitiveValue = boolean / guid / durationValue @@ -933,9 +933,6 @@ primitiveValue = boolean / int32Value / int64Value / binaryValue - ; also valid are: - ; - any XML string for strings in Atom and CSDL XML documents - ; - any JSON string for JSON documents null = %s"null" From bb204cee8545964e72d672ec370e86ba58d95658 Mon Sep 17 00:00:00 2001 From: Ralf Handl Date: Fri, 27 Oct 2023 14:55:12 +0200 Subject: [PATCH 08/12] No percent-encoding in context URLs --- abnf/odata-abnf-construction-rules.txt | 11 ++++++----- abnf/odata-abnf-testcases.yaml | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/abnf/odata-abnf-construction-rules.txt b/abnf/odata-abnf-construction-rules.txt index 8706645..81e67dd 100644 --- a/abnf/odata-abnf-construction-rules.txt +++ b/abnf/odata-abnf-construction-rules.txt @@ -450,7 +450,7 @@ entitySet = entitySetName *( containmentNavigation ) [ "/" qualifiedEntityTypeNa containmentNavigation = contextKeyPredicate [ "/" qualifiedEntityTypeName ] navigation navigation = *( "/" complexProperty [ "/" qualifiedComplexTypeName ] ) "/" navigationProperty -contextKeyPredicate = OPEN ( contextKeyValue / contextKeyValuePair *( COMMA contextKeyValuePair ) ) CLOSE +contextKeyPredicate = "(" ( contextKeyValue / contextKeyValuePair *( "," contextKeyValuePair ) ) ")" contextKeyValuePair = ( primitiveKeyProperty / keyPropertyAlias ) EQ contextKeyValue contextKeyValue = boolean / guid @@ -463,12 +463,13 @@ contextKeyValue = boolean / int16Value / int32Value / int64Value - / stringLiteral ; TODO: stringValue without percent-encoding + / stringValue / durationValue / enumValue +stringValue = %x27 *( %x00-26 / %x27 %x27 / %x28-FF) %x27 ; SQUOTE = %x27 -selectList = OPEN [ selectListItem *( COMMA selectListItem ) ] CLOSE -selectListItem = STAR ; all structural properties +selectList = "(" [ selectListItem *( "," selectListItem ) ] ")" +selectListItem = "*" ; all structural properties / allOperationsInSchema / [ ( qualifiedEntityTypeName / qualifiedComplexTypeName ) "/" ] ( qualifiedActionName @@ -486,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 e3b7f74..3753fdb 100644 --- a/abnf/odata-abnf-testcases.yaml +++ b/abnf/odata-abnf-testcases.yaml @@ -3309,6 +3309,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)" @@ -3413,6 +3428,10 @@ TestCases: 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" From 319440983611ab8f73b6aa997c8295f188dc6eeb Mon Sep 17 00:00:00 2001 From: Ralf Handl Date: Fri, 27 Oct 2023 17:46:50 +0200 Subject: [PATCH 09/12] enumLiteral and enumValue --- abnf/odata-abnf-construction-rules.txt | 12 ++++---- abnf/odata-abnf-testcases.yaml | 40 +++++++++++++++++++------- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/abnf/odata-abnf-construction-rules.txt b/abnf/odata-abnf-construction-rules.txt index 2c554a2..dd89ec0 100644 --- a/abnf/odata-abnf-construction-rules.txt +++ b/abnf/odata-abnf-construction-rules.txt @@ -890,10 +890,10 @@ primitiveLiteral = null / int16Literal / int32Literal / int64Literal - / stringLiteral ; single-quoted + / stringLiteral / durationLiteral / enumLiteral - / binaryLiteral ; all others are quoted and prefixed + / binaryLiteral / geographyCollection / geographyLineString / geographyMultiLineString @@ -995,10 +995,10 @@ minute = zeroToFiftyNine second = zeroToFiftyNine / "60" ; for leap seconds fractionalSeconds = 1*12DIGIT -enumLiteral = [ qualifiedEnumTypeName ] SQUOTE enumValue SQUOTE -enumValue = singleEnumValue *( COMMA singleEnumValue ) -singleEnumValue = enumerationMember / enumMemberValue -enumMemberValue = int64Value +enumLiteral = [ qualifiedEnumTypeName ] SQUOTE singleEnumLiteral *( COMMA singleEnumLiteral ) SQUOTE +singleEnumLiteral = enumerationMember / int64Literal +enumValue = singleEnumValue *( "," singleEnumValue ) +singleEnumValue = enumerationMember / int64Value geographyCollection = geographyPrefix SQUOTE fullCollectionLiteral SQUOTE fullCollectionLiteral = sridLiteral collectionLiteral diff --git a/abnf/odata-abnf-testcases.yaml b/abnf/odata-abnf-testcases.yaml index 780f6eb..bae82a7 100644 --- a/abnf/odata-abnf-testcases.yaml +++ b/abnf/odata-abnf-testcases.yaml @@ -2978,46 +2978,64 @@ TestCases: Rule: odataRelativeUri Input: ProductsByCustomer(customer=@c)?@c={"Orders":[$root/Orders(1)]} - - Name: Enumeration value in URI + - Name: Enumeration literal in URI Rule: enumLiteral Input: Sales.Pattern'Yellow' - - Name: Enumeration value in URI - without prefix + - Name: Enumeration literal in URI - without prefix Rule: enumLiteral Input: "'Yellow'" - - Name: Enumeration value in URI - multiple flag values + - Name: Enumeration literal in URI - multiple flag values Rule: enumLiteral Input: Sales.Pattern'Solid,Yellow' - - Name: Enumeration value in URI - multiple flag values without prefix + - Name: Enumeration literal in URI - multiple flag values Rule: enumLiteral - Input: "'Solid,Yellow'" + Input: Sales.Pattern'Solid%2CYellow,%2B42' - - Name: Enumeration value - in filter + - Name: Enumeration literal in URI - multiple flag values without prefix + Rule: enumLiteral + Input: "'Solid,Yellow,-42'" + + - Name: Enumeration literal - in filter Rule: filter Input: $filter=style eq Sales.Pattern'Yellow' - - Name: Enumeration value - in filter without prefix + - Name: Enumeration literal - in filter without prefix Rule: filter Input: $filter=style eq 'Yellow' - - Name: Enumeration value - in filter with has + - Name: Enumeration literal - in filter with has Rule: filter Input: $filter=style has Sales.Pattern'Yellow' - - Name: Enumeration value - undefined value as numeric constant + - Name: Enumeration literal - undefined value as numeric constant Rule: filter Input: $filter=style has Sales.Pattern'32' - - Name: Enumeration value - undefined value as numeric constant + - Name: Enumeration literal - undefined value as numeric constant Rule: odataRelativeUri Input: Products?$filter=style eq Sales.Pattern'Yellow,32' - - Name: Enumeration value - undefined value as numeric constant + - Name: Enumeration literal - undefined value as numeric constant Rule: odataRelativeUri Input: Products?$filter=style eq cast(eyeColor,Sales.Pattern) + - Name: Enumeration value - multiple flag values + Rule: enumValue + Input: "Solid,Yellow,+42" + + - Name: Enumeration value - no percent-encoding + Rule: enumValue + FailAt: 5 + Input: Solid%2CYellow + + - Name: Enumeration value - no percent-encoding + Rule: enumValue + FailAt: 0 + Input: "%2B42" + - Name: GeographyCollection Rule: geographyCollection Input: geography'SRID=0;GeometryCollection(LineString(142.1 64.1,3.14 2.78))' From a6aafed94a1bb7c3ef565d6f81b37ae7672a719f Mon Sep 17 00:00:00 2001 From: Ralf Handl Date: Fri, 27 Oct 2023 17:48:27 +0200 Subject: [PATCH 10/12] Update odata-abnf-construction-rules.txt --- abnf/odata-abnf-construction-rules.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abnf/odata-abnf-construction-rules.txt b/abnf/odata-abnf-construction-rules.txt index 472afc4..7d99ec5 100644 --- a/abnf/odata-abnf-construction-rules.txt +++ b/abnf/odata-abnf-construction-rules.txt @@ -446,7 +446,7 @@ contextFragment = %s"Collection($ref)" / entitySet [ selectList ] [ %s"/$entity" / %s"/$delta" ] entitySet = entitySetName *( containmentNavigation ) [ "/" qualifiedEntityTypeName ] - + containmentNavigation = contextKeyPredicate [ "/" qualifiedEntityTypeName ] navigation navigation = *( "/" complexProperty [ "/" qualifiedComplexTypeName ] ) "/" navigationProperty From 5bd43cf96d3ddc5a142b39c1c29bf64909e2125d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20Thei=C3=9Fen?= Date: Thu, 16 May 2024 09:01:53 +0200 Subject: [PATCH 11/12] Negative test case for key-as-segment --- abnf/odata-abnf-testcases.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/abnf/odata-abnf-testcases.yaml b/abnf/odata-abnf-testcases.yaml index 6094584..2f3c175 100644 --- a/abnf/odata-abnf-testcases.yaml +++ b/abnf/odata-abnf-testcases.yaml @@ -3475,6 +3475,11 @@ 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" From 438c173193b531ef3f3eff2deb0ee44d8bedbd03 Mon Sep 17 00:00:00 2001 From: Ralf Handl Date: Thu, 16 May 2024 14:51:11 +0200 Subject: [PATCH 12/12] Another unencoded slash, just to be sure --- abnf/odata-abnf-testcases.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abnf/odata-abnf-testcases.yaml b/abnf/odata-abnf-testcases.yaml index 2f3c175..73a4da3 100644 --- a/abnf/odata-abnf-testcases.yaml +++ b/abnf/odata-abnf-testcases.yaml @@ -3498,7 +3498,7 @@ TestCases: - Name: Context URL - containment - multi-part key Rule: context - Input: "#OrderItems(OrderID=1,ItemID='a')/Details/$entity" + Input: "#OrderItems(OrderID=1,ItemID='a/b')/Details/$entity" - Name: Context URL - collection with containment - multi-level Rule: context