Skip to content
This repository has been archived by the owner on Aug 2, 2024. It is now read-only.

(#685) URI Escaping of the Filter property when converting to an OData string #696

Merged
merged 1 commit into from
Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public QueryToken NextToken()
Token.Kind = tokenKind;
Token.Text = Text.Substring(tokenPosition, TextPosition - tokenPosition);
Token.Position = tokenPosition;

if (Token.Kind == QueryTokenKind.Identifier && identifierClassification.ContainsKey(Token.Text))
{
Token.Kind = identifierClassification[Token.Text];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static string ToODataString(this QueryDescription value, IDictionary<stri
if (value.Filter != null)
{
string filterStr = ODataExpressionVisitor.ToODataString(value.Filter);
queryFragments.Add($"{ODataOptions.Filter}={filterStr}");
queryFragments.Add($"{ODataOptions.Filter}={Uri.EscapeDataString(filterStr)}");
}

if (value.Ordering.Count > 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ public async Task PeekAsync_GeneratesCorrectQuery(long sequenceId, string[] tabl
Assert.Equal(SystemTables.OperationsQueue, query.TableName);

var odata = query.ToODataString();
var expected = $"$filter={filter}&$orderby=sequence&$top=1";
var expected = $"$filter={Uri.EscapeDataString(filter)}&$orderby=sequence&$top=1";
Assert.Equal(expected, odata);
}

Expand Down
32 changes: 16 additions & 16 deletions sdk/dotnet/test/Microsoft.Datasync.Client.Test/Query/Linq.Test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class Linq_Tests : ClientBaseTest
private readonly TableQuery<KitchenSink> _query;

public Linq_Tests()
{
{
_client = GetMockClient();
_table = _client.GetRemoteTable<KitchenSink>("kitchensink") as RemoteTable<KitchenSink>;
_query = new TableQuery<KitchenSink>(_table);
Expand All @@ -41,7 +41,7 @@ public void Linq_EndsWith_NoStringComparison()
{
ExecuteWhereQuery(
m => m.StringProperty.EndsWith("abc"),
"$filter=endswith(stringProperty,'abc')"
"$filter=endswith%28stringProperty%2C%27abc%27%29"
);
}

Expand All @@ -50,7 +50,7 @@ public void Linq_EndsWith_Ordinal()
{
ExecuteWhereQuery(
m => m.StringProperty.EndsWith("abc", StringComparison.Ordinal),
"$filter=endswith(stringProperty,'abc')"
"$filter=endswith%28stringProperty%2C%27abc%27%29"
);
}

Expand All @@ -59,7 +59,7 @@ public void Linq_EndsWith_Invariant()
{
ExecuteWhereQuery(
m => m.StringProperty.EndsWith("abc", StringComparison.InvariantCulture),
"$filter=endswith(stringProperty,'abc')"
"$filter=endswith%28stringProperty%2C%27abc%27%29"
);
}

Expand All @@ -68,7 +68,7 @@ public void Linq_EndsWith_OrdinalIgnoreCase()
{
ExecuteWhereQuery(
m => m.StringProperty.EndsWith("abc", StringComparison.OrdinalIgnoreCase),
"$filter=endswith(tolower(stringProperty),tolower('abc'))"
"$filter=endswith%28tolower%28stringProperty%29%2Ctolower%28%27abc%27%29%29"
);
}

Expand All @@ -77,7 +77,7 @@ public void Linq_EndsWith_InvariantIgnoreCase()
{
ExecuteWhereQuery(
m => m.StringProperty.EndsWith("abc", StringComparison.InvariantCultureIgnoreCase),
"$filter=endswith(tolower(stringProperty),tolower('abc'))"
"$filter=endswith%28tolower%28stringProperty%29%2Ctolower%28%27abc%27%29%29"
);
}

Expand All @@ -86,7 +86,7 @@ public void Linq_Equals_NoStringComparison()
{
ExecuteWhereQuery(
m => m.StringProperty.Equals("abc"),
"$filter=(stringProperty eq 'abc')"
"$filter=%28stringProperty%20eq%20%27abc%27%29"
);
}

Expand All @@ -95,7 +95,7 @@ public void Linq_Equals_Ordinal()
{
ExecuteWhereQuery(
m => m.StringProperty.Equals("abc", StringComparison.Ordinal),
"$filter=(stringProperty eq 'abc')"
"$filter=%28stringProperty%20eq%20%27abc%27%29"
);
}

Expand All @@ -104,7 +104,7 @@ public void Linq_Equals_Invariant()
{
ExecuteWhereQuery(
m => m.StringProperty.Equals("abc", StringComparison.InvariantCulture),
"$filter=(stringProperty eq 'abc')"
"$filter=%28stringProperty%20eq%20%27abc%27%29"
);
}

Expand All @@ -113,7 +113,7 @@ public void Linq_Equals_OrdinalIgnoreCase()
{
ExecuteWhereQuery(
m => m.StringProperty.Equals("abc", StringComparison.OrdinalIgnoreCase),
"$filter=(tolower(stringProperty) eq tolower('abc'))"
"$filter=%28tolower%28stringProperty%29%20eq%20tolower%28%27abc%27%29%29"
);
}

Expand All @@ -122,7 +122,7 @@ public void Linq_Equals_InvariantIgnoreCase()
{
ExecuteWhereQuery(
m => m.StringProperty.Equals("abc", StringComparison.InvariantCultureIgnoreCase),
"$filter=(tolower(stringProperty) eq tolower('abc'))"
"$filter=%28tolower%28stringProperty%29%20eq%20tolower%28%27abc%27%29%29"
);
}

Expand All @@ -131,7 +131,7 @@ public void Linq_StartsWith_NoStringComparison()
{
ExecuteWhereQuery(
m => m.StringProperty.StartsWith("abc"),
"$filter=startswith(stringProperty,'abc')"
"$filter=startswith%28stringProperty%2C%27abc%27%29"
);
}

Expand All @@ -140,7 +140,7 @@ public void Linq_StartsWith_Ordinal()
{
ExecuteWhereQuery(
m => m.StringProperty.StartsWith("abc", StringComparison.Ordinal),
"$filter=startswith(stringProperty,'abc')"
"$filter=startswith%28stringProperty%2C%27abc%27%29"
);
}

Expand All @@ -149,7 +149,7 @@ public void Linq_StartsWith_Invariant()
{
ExecuteWhereQuery(
m => m.StringProperty.StartsWith("abc", StringComparison.InvariantCulture),
"$filter=startswith(stringProperty,'abc')"
"$filter=startswith%28stringProperty%2C%27abc%27%29"
);
}

Expand All @@ -158,7 +158,7 @@ public void Linq_StartsWith_OrdinalIgnoreCase()
{
ExecuteWhereQuery(
m => m.StringProperty.StartsWith("abc", StringComparison.OrdinalIgnoreCase),
"$filter=startswith(tolower(stringProperty),tolower('abc'))"
"$filter=startswith%28tolower%28stringProperty%29%2Ctolower%28%27abc%27%29%29"
);
}

Expand All @@ -167,7 +167,7 @@ public void Linq_StartsWith_InvariantIgnoreCase()
{
ExecuteWhereQuery(
m => m.StringProperty.StartsWith("abc", StringComparison.InvariantCultureIgnoreCase),
"$filter=startswith(tolower(stringProperty),tolower('abc'))"
"$filter=startswith%28tolower%28stringProperty%29%2Ctolower%28%27abc%27%29%29"
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,97 +262,110 @@ public void QueryTokenKind_ToBinaryOperatorKind_ThrowsOnInvalid(QueryTokenKind k
[Theory]
[InlineData("")]
[InlineData("$count=true")]
[InlineData("$orderby=bestPictureWinner")]
[InlineData("$orderby=bestPictureWinner desc")]
[InlineData("$orderby=rating desc,title desc")]
[InlineData("$skip=100")]
[InlineData("$top=100")]
[InlineData("$skip=100&$top=50")]
[InlineData("$filter=bestPictureWinner")]
[InlineData("$filter=not(bestPictureWinner)")]
[InlineData("$filter=(bestPictureWinner eq true)")]
[InlineData("$filter=(bestPictureWinner eq false)")]
[InlineData("$filter=(bestPictureWinner ne true)")]
[InlineData("$filter=(bestPictureWinner ne false)")]
[InlineData("$filter=not((bestPictureWinner eq true))")]
[InlineData("$filter=not((bestPictureWinner ne true))")]
[InlineData("$filter=not((bestPictureWinner eq false))")]
[InlineData("$filter=not((bestPictureWinner ne false))")]
[InlineData("$filter=(duration eq 100)")]
[InlineData("$filter=(duration lt 100)")]
[InlineData("$filter=(duration le 100)")]
[InlineData("$filter=(duration gt 90)")]
[InlineData("$filter=(duration ge 90)")]
[InlineData("$filter=(duration ne 100)")]
[InlineData("$filter=not((duration eq 100))")]
[InlineData("$filter=not((duration lt 100))")]
[InlineData("$filter=not((duration le 100))")]
[InlineData("$filter=not((duration gt 90))")]
[InlineData("$filter=not((duration ge 90))")]
[InlineData("$filter=not((duration ne 100))")]
[InlineData($"$filter=(releaseDate eq cast(1994-10-14T00:00:00.000Z,Edm.DateTimeOffset))")]
[InlineData($"$filter=(releaseDate gt cast(1999-12-31T00:00:00.000Z,Edm.DateTimeOffset))")]
[InlineData($"$filter=(releaseDate ge cast(1999-12-31T00:00:00.000Z,Edm.DateTimeOffset))")]
[InlineData($"$filter=(releaseDate lt cast(2000-01-01T00:00:00.000Z,Edm.DateTimeOffset))")]
[InlineData($"$filter=(releaseDate le cast(2000-01-01T00:00:00.000Z,Edm.DateTimeOffset))")]
[InlineData($"$filter=(releaseDate eq cast(1994-10-14,Edm.Date))")]
[InlineData($"$filter=(releaseDate ge cast(1994-10-14,Edm.Date))")]
[InlineData($"$filter=(releaseDate gt cast(1994-10-14,Edm.Date))")]
[InlineData($"$filter=(releaseDate le cast(1994-10-14,Edm.Date))")]
[InlineData($"$filter=(releaseDate lt cast(1994-10-14,Edm.Date))")]
[InlineData($"$filter=(releaseDate ne cast(1994-10-14,Edm.Date))")]
[InlineData($"$filter=(performance eq cast(12:24:00,Edm.TimeOfDay))")]
[InlineData($"$filter=(performance ge cast(12:24:00,Edm.TimeOfDay))")]
[InlineData($"$filter=(performance gt cast(12:24:00,Edm.TimeOfDay))")]
[InlineData($"$filter=(performance le cast(12:24:00,Edm.TimeOfDay))")]
[InlineData($"$filter=(performance lt cast(12:24:00,Edm.TimeOfDay))")]
[InlineData($"$filter=(performance ne cast(12:24:00,Edm.TimeOfDay))")]
[InlineData("$filter=(title eq 'The Godfather')")]
[InlineData("$filter=(title ne 'The Godfather')")]
[InlineData("$filter=(rating ne null)")]
[InlineData("$filter=(rating eq null)")]
[InlineData("$filter=((year gt 1929) and (year lt 1940))")]
[InlineData("$filter=((year ge 1930) and (year le 1939))")]
[InlineData("$filter=((year gt 2000) or (year lt 1940))")]
[InlineData("$filter=((year gt 2000) or not(bestPictureWinner))")]
[InlineData("$filter=(((year ge 1930) and (year le 1940)) or ((year ge 1950) and (year le 1960)))")]
[InlineData("$filter=((year sub 1900) gt 80)")]
[InlineData("$filter=((year add duration) lt 2100)")]
[InlineData("$filter=((year sub 1900) lt duration)")]
[InlineData("$filter=((duration mul 2) lt 180)")]
[InlineData("$filter=((year div 1000.5) eq 2.0)")]
[InlineData("$filter=((duration mod 2) eq 1)")]
[InlineData("$filter=((((year sub 1900) ge 80) and ((year add 10) le 2000)) and (duration le 120))")]
[InlineData("$filter=(day(releaseDate) eq 1)")]
[InlineData("$filter=(month(releaseDate) eq 11)")]
[InlineData("$filter=(year(releaseDate) ne year)")]
[InlineData("$filter=endswith(title,'er')")]
[InlineData("$filter=endswith(tolower(title),'er')")]
[InlineData("$filter=endswith(toupper(title),'ER')")]
[InlineData("$filter=startswith(rating,'PG')")]
[InlineData("$filter=startswith(tolower(rating),'pg')")]
[InlineData("$filter=startswith(toupper(rating),'PG')")]
[InlineData("$filter=(indexof(rating,'-') gt 0)")]
[InlineData("$filter=contains(rating,'PG')")]
[InlineData("$filter=(substring(rating,0,2) eq 'PG')")]
[InlineData("$filter=(length(trim(title)) gt 10)")]
[InlineData("$filter=(concat(title,rating) eq 'Fight ClubR')")]
[InlineData("$filter=(round((duration div 60.0)) eq 2.0)")]
[InlineData("$filter=(ceiling((duration div 60.0)) eq 2.0)")]
[InlineData("$filter=(floor((duration div 60.0)) eq 2.0)")]
[InlineData("$filter=(not(bestPictureWinner) and (round((duration div 60.0)) eq 2.0))")]
[InlineData("$filter=(not(bestPictureWinner) and (ceiling((duration div 60.0)) eq 2.0))")]
[InlineData("$filter=(not(bestPictureWinner) and (floor((duration div 60.0)) eq 2.0))")]
[InlineData("$filter=(year lt 1990.5f)")]
[InlineData("$filter=(nullableValue le -0.5)")]
public void ODataExpressionParser_Roundtrips(string original)
[InlineData("$orderby=bestPictureWinner")]
[InlineData("$orderby=bestPictureWinner desc")]
[InlineData("$orderby=rating desc,title desc")]
[InlineData("$skip=100")]
[InlineData("$top=100")]
[InlineData("$skip=100&$top=50")]
public void ODataExpressionParser_Roundtrips(string original)
{
var query = QueryDescription.Parse("movies", original);
Assert.NotNull(query);

// Numbers are converted to LONG during the conversion, which isn't an issue. We translate [0-9]+L to [0-9]+
string odataString = Regex.Replace(query.ToODataString(), "([0-9]+)L", "$1");

Assert.Equal(original, odataString);
}

[Theory]
[InlineData("bestPictureWinner")]
[InlineData("not(bestPictureWinner)")]
[InlineData("(bestPictureWinner eq true)")]
[InlineData("(bestPictureWinner eq false)")]
[InlineData("(bestPictureWinner ne true)")]
[InlineData("(bestPictureWinner ne false)")]
[InlineData("not((bestPictureWinner eq true))")]
[InlineData("not((bestPictureWinner ne true))")]
[InlineData("not((bestPictureWinner eq false))")]
[InlineData("not((bestPictureWinner ne false))")]
[InlineData("(duration eq 100)")]
[InlineData("(duration lt 100)")]
[InlineData("(duration le 100)")]
[InlineData("(duration gt 90)")]
[InlineData("(duration ge 90)")]
[InlineData("(duration ne 100)")]
[InlineData("not((duration eq 100))")]
[InlineData("not((duration lt 100))")]
[InlineData("not((duration le 100))")]
[InlineData("not((duration gt 90))")]
[InlineData("not((duration ge 90))")]
[InlineData("not((duration ne 100))")]
[InlineData("(releaseDate eq cast(1994-10-14T00:00:00.000Z,Edm.DateTimeOffset))")]
[InlineData("(releaseDate gt cast(1999-12-31T00:00:00.000Z,Edm.DateTimeOffset))")]
[InlineData("(releaseDate ge cast(1999-12-31T00:00:00.000Z,Edm.DateTimeOffset))")]
[InlineData("(releaseDate lt cast(2000-01-01T00:00:00.000Z,Edm.DateTimeOffset))")]
[InlineData("(releaseDate le cast(2000-01-01T00:00:00.000Z,Edm.DateTimeOffset))")]
[InlineData("(releaseDate eq cast(1994-10-14,Edm.Date))")]
[InlineData("(releaseDate ge cast(1994-10-14,Edm.Date))")]
[InlineData("(releaseDate gt cast(1994-10-14,Edm.Date))")]
[InlineData("(releaseDate le cast(1994-10-14,Edm.Date))")]
[InlineData("(releaseDate lt cast(1994-10-14,Edm.Date))")]
[InlineData("(releaseDate ne cast(1994-10-14,Edm.Date))")]
[InlineData("(performance eq cast(12:24:00,Edm.TimeOfDay))")]
[InlineData("(performance ge cast(12:24:00,Edm.TimeOfDay))")]
[InlineData("(performance gt cast(12:24:00,Edm.TimeOfDay))")]
[InlineData("(performance le cast(12:24:00,Edm.TimeOfDay))")]
[InlineData("(performance lt cast(12:24:00,Edm.TimeOfDay))")]
[InlineData("(performance ne cast(12:24:00,Edm.TimeOfDay))")]
[InlineData("(title eq 'The Godfather')")]
[InlineData("(title ne 'The Godfather')")]
[InlineData("(rating ne null)")]
[InlineData("(rating eq null)")]
[InlineData("((year gt 1929) and (year lt 1940))")]
[InlineData("((year ge 1930) and (year le 1939))")]
[InlineData("((year gt 2000) or (year lt 1940))")]
[InlineData("((year gt 2000) or not(bestPictureWinner))")]
[InlineData("(((year ge 1930) and (year le 1940)) or ((year ge 1950) and (year le 1960)))")]
[InlineData("((year sub 1900) gt 80)")]
[InlineData("((year add duration) lt 2100)")]
[InlineData("((year sub 1900) lt duration)")]
[InlineData("((duration mul 2) lt 180)")]
[InlineData("((year div 1000.5) eq 2.0)")]
[InlineData("((duration mod 2) eq 1)")]
[InlineData("((((year sub 1900) ge 80) and ((year add 10) le 2000)) and (duration le 120))")]
[InlineData("(day(releaseDate) eq 1)")]
[InlineData("(month(releaseDate) eq 11)")]
[InlineData("(year(releaseDate) ne year)")]
[InlineData("endswith(title,'er')")]
[InlineData("endswith(tolower(title),'er')")]
[InlineData("endswith(toupper(title),'ER')")]
[InlineData("startswith(rating,'PG')")]
[InlineData("startswith(tolower(rating),'pg')")]
[InlineData("startswith(toupper(rating),'PG')")]
[InlineData("(indexof(rating,'-') gt 0)")]
[InlineData("contains(rating,'PG')")]
[InlineData("(substring(rating,0,2) eq 'PG')")]
[InlineData("(length(trim(title)) gt 10)")]
[InlineData("(concat(title,rating) eq 'Fight ClubR')")]
[InlineData("(round((duration div 60.0)) eq 2.0)")]
[InlineData("(ceiling((duration div 60.0)) eq 2.0)")]
[InlineData("(floor((duration div 60.0)) eq 2.0)")]
[InlineData("(not(bestPictureWinner) and (round((duration div 60.0)) eq 2.0))")]
[InlineData("(not(bestPictureWinner) and (ceiling((duration div 60.0)) eq 2.0))")]
[InlineData("(not(bestPictureWinner) and (floor((duration div 60.0)) eq 2.0))")]
[InlineData("(year lt 1990.5f)")]
[InlineData("(nullableValue le -0.5)")]
public void ODataExpressionParser_RoundtripsFilter(string original)
{
var query = QueryDescription.Parse("movies", original);
var queryString = $"$filter={Uri.EscapeDataString(original)}";
var query = QueryDescription.Parse("movies", queryString);
Assert.NotNull(query);

// Numbers are converted to LONG during the conversion, which isn't an issue. We translate [0-9]+L to [0-9]+
string odataString = Regex.Replace(query.ToODataString(), "([0-9]+)L", "$1");

Assert.Equal(original, odataString);
Assert.Equal(queryString, odataString);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ public void ToODataString_Where_IsWellFormed()
RemoteTable<IdEntity> table = client.GetRemoteTable<IdEntity>("movies") as RemoteTable<IdEntity>;
var query = new TableQuery<IdEntity>(table).Where(m => m.Id == "foo") as TableQuery<IdEntity>;
var odata = query.ToODataString();
Assert.Equal("$filter=(id eq 'foo')", odata);
Assert.Equal("$filter=%28id%20eq%20%27foo%27%29", odata);
}

[Fact]
Expand Down Expand Up @@ -766,7 +766,7 @@ public void ToODataString_NegativeDouble_Works()
var table = new RemoteTable<KSV>("ksv", client);
var query = new TableQuery<KSV>(table).Where(x => x.Value <= -0.5) as TableQuery<KSV>;
var actual = query.ToODataString();
Assert.Equal("$filter=(value le -0.5)", actual);
Assert.Equal("$filter=%28value%20le%20-0.5%29", actual);
}

[Fact]
Expand All @@ -776,7 +776,7 @@ public void ToODataString_NegativeNullableDouble_Works()
var table = new RemoteTable<KSV>("ksv", client);
var query = new TableQuery<KSV>(table).Where(x => x.NullableValue <= -0.5) as TableQuery<KSV>;
var actual = query.ToODataString();
Assert.Equal("$filter=(nullableValue le -0.5)", actual);
Assert.Equal("$filter=%28nullableValue%20le%20-0.5%29", actual);
}

#region Models
Expand Down
Loading