From fd813c7f29b54a12fd523fb71e10a133f4bea85e Mon Sep 17 00:00:00 2001 From: dvolper Date: Fri, 31 Mar 2023 11:06:42 +0200 Subject: [PATCH] Allow using query filters for non-string types (#25) * implement filter support for all known data types --- .../ITS019QueryFilter.cs | 96 +++++++++++++++++++ .../Models/DemoEntityQuery.cs | 4 + .../UnittestStorageEnvironment.cs | 21 ++-- ...eHelpers.WindowsAzure.Storage.Table.csproj | 2 +- .../Extensions/QueryFilterExtensions.cs | 68 +++++-------- 5 files changed, 137 insertions(+), 54 deletions(-) diff --git a/CoreHelpers.WindowsAzure.Storage.Table.Tests/ITS019QueryFilter.cs b/CoreHelpers.WindowsAzure.Storage.Table.Tests/ITS019QueryFilter.cs index 11be044..19effac 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table.Tests/ITS019QueryFilter.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table.Tests/ITS019QueryFilter.cs @@ -134,5 +134,101 @@ public async Task VerifyQueryfilterBoolOnly() await storageContext.DropTableAsync(); } } + + [Fact] + public async Task VerifyQueryfilterLongOnly() + { + // Import from Blob + using (var storageContext = new StorageContext(env.ConnectionString)) + { + // set the tablename context + storageContext.SetTableContext(); + + var l = 2910892817298; + + // create the model + var models = new List() + { + new DemoEntityQuery() {R = "E6", StringField = "Demo03"}, + new DemoEntityQuery() {R = "E7", StringField = "Demo03", LongField = l - 1}, + new DemoEntityQuery() {R = "E8", StringField = "Demo03", LongField = l + 1} + }; + + // ensure we are using the attributes + storageContext.AddAttributeMapper(typeof(DemoEntityQuery)); + + // inser the model + await storageContext.EnableAutoCreateTable().MergeOrInsertAsync(models); + + // build the basic filter + var filterItem = new QueryFilter() + { + FilterType = QueryFilterType.And, + Property = nameof(DemoEntityQuery.LongField), + Value = l, + Operator = QueryFilterOperator.GreaterEqual + }; + + // query all elements with empty filter list + var result = (await storageContext.QueryAsync(null, new List())).ToList(); + Assert.Equal(3, result.Count()); + + result = (await storageContext.QueryAsync("P1", new List() { filterItem })).ToList(); + Assert.Single(result, i => i?.LongField == l + 1); + + // Clean up + var all = await storageContext.QueryAsync(); + await storageContext.DeleteAsync(all); + await storageContext.DropTableAsync(); + } + } + + [Fact] + public async Task VerifyQueryfilterDateTimeOnly() + { + // Import from Blob + using (var storageContext = new StorageContext(env.ConnectionString)) + { + // set the tablename context + storageContext.SetTableContext(); + + var now = DateTime.UtcNow; + + // create the model + var models = new List() + { + new DemoEntityQuery() {R = "E6", StringField = "Demo03"}, + new DemoEntityQuery() {R = "E7", StringField = "Demo03", DateTimeField = now.AddDays(-1)}, + new DemoEntityQuery() {R = "E8", StringField = "Demo03", DateTimeField = now.AddDays(1)} + }; + + // ensure we are using the attributes + storageContext.AddAttributeMapper(typeof(DemoEntityQuery)); + + // inser the model + await storageContext.EnableAutoCreateTable().MergeOrInsertAsync(models); + + // build the basic filter + var filterItem = new QueryFilter() + { + FilterType = QueryFilterType.And, + Property = nameof(DemoEntityQuery.DateTimeField), + Value = now, + Operator = QueryFilterOperator.GreaterEqual + }; + + // query all elements with empty filter list + var result = (await storageContext.QueryAsync(null, new List())).ToList(); + Assert.Equal(3, result.Count()); + + result = (await storageContext.QueryAsync("P1", new List() { filterItem })).ToList(); + Assert.Single(result, i => i?.DateTimeField == now.AddDays(1)); + + // Clean up + var all = await storageContext.QueryAsync(); + await storageContext.DeleteAsync(all); + await storageContext.DropTableAsync(); + } + } } } \ No newline at end of file diff --git a/CoreHelpers.WindowsAzure.Storage.Table.Tests/Models/DemoEntityQuery.cs b/CoreHelpers.WindowsAzure.Storage.Table.Tests/Models/DemoEntityQuery.cs index c977488..0ef4f96 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table.Tests/Models/DemoEntityQuery.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table.Tests/Models/DemoEntityQuery.cs @@ -13,6 +13,10 @@ public class DemoEntityQuery public string StringField { get; set; } = String.Empty; public bool BoolField { get; set; } + + public DateTime? DateTimeField { get; set; } + + public long LongField { get; set; } } } diff --git a/CoreHelpers.WindowsAzure.Storage.Table.Tests/TestEnvironments/UnittestStorageEnvironment.cs b/CoreHelpers.WindowsAzure.Storage.Table.Tests/TestEnvironments/UnittestStorageEnvironment.cs index c6711e8..bcfb7c4 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table.Tests/TestEnvironments/UnittestStorageEnvironment.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table.Tests/TestEnvironments/UnittestStorageEnvironment.cs @@ -1,25 +1,26 @@ -using System; -using CoreHelpers.WindowsAzure.Storage.Table.Tests.Contracts; +using CoreHelpers.WindowsAzure.Storage.Table.Tests.Contracts; namespace CoreHelpers.WindowsAzure.Storage.Table.Tests.TestEnvironments { public class UnittestStorageEnvironment : ITestEnvironment - { - public string ConnectionString { - get { - + { + public string ConnectionString + { + get + { var connectionString = Environment.GetEnvironmentVariable("STORAGE"); - if (!String.IsNullOrEmpty(connectionString)) + if (!string.IsNullOrEmpty(connectionString)) { Console.WriteLine("Using environment credentials"); return connectionString; } - var filePath = Environment.ExpandEnvironmentVariables(Path.Combine("%HOME%", ".corehelpers.credentials.txt")); + var filePath = Environment.ExpandEnvironmentVariables(Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".corehelpers.credentials.txt")); Console.WriteLine("Using filesystem credentials"); + return File.ReadLines(filePath).First(); } } } -} - +} \ No newline at end of file diff --git a/CoreHelpers.WindowsAzure.Storage.Table/CoreHelpers.WindowsAzure.Storage.Table.csproj b/CoreHelpers.WindowsAzure.Storage.Table/CoreHelpers.WindowsAzure.Storage.Table.csproj index 3dac931..e0bae5d 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table/CoreHelpers.WindowsAzure.Storage.Table.csproj +++ b/CoreHelpers.WindowsAzure.Storage.Table/CoreHelpers.WindowsAzure.Storage.Table.csproj @@ -34,7 +34,7 @@ - + diff --git a/CoreHelpers.WindowsAzure.Storage.Table/Extensions/QueryFilterExtensions.cs b/CoreHelpers.WindowsAzure.Storage.Table/Extensions/QueryFilterExtensions.cs index 4218758..3a7b082 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table/Extensions/QueryFilterExtensions.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table/Extensions/QueryFilterExtensions.cs @@ -1,55 +1,37 @@ using System; -using CoreHelpers.WindowsAzure.Storage.Table.Serialization; +using System.Globalization; namespace CoreHelpers.WindowsAzure.Storage.Table.Extensions { public static class QueryFilterExtensions - { + { public static string ToFilterString(this QueryFilter filter) { - var filterOperation = "eq"; - switch (filter.Operator) + var filterOperation = filter.Operator switch { - case QueryFilterOperator.Equal: - filterOperation = "eq"; - break; - case QueryFilterOperator.NotEqual: - filterOperation = "ne"; - break; - case QueryFilterOperator.Lower: - filterOperation = "lt"; - break; - case QueryFilterOperator.Greater: - filterOperation = "gt"; - break; - case QueryFilterOperator.LowerEqual: - filterOperation = "le"; - break; - case QueryFilterOperator.GreaterEqual: - filterOperation = "ge"; - break; - } + QueryFilterOperator.Equal => "eq", + QueryFilterOperator.NotEqual => "ne", + QueryFilterOperator.Lower => "lt", + QueryFilterOperator.Greater => "gt", + QueryFilterOperator.LowerEqual => "le", + QueryFilterOperator.GreaterEqual => "ge", + _ => "eq" + }; - var filterValueString = default(string); - - if (filter.Value is string) - filterValueString = $"'{(string)filter.Value}'"; - else if (filter.Value is bool) - filterValueString = ((bool)filter.Value) ? "true" : "false"; - else if (filter.Value is byte[]) - filterValueString = Convert.ToString((byte[]) filter.Value); - else if (filter.Value is DateTimeOffset) - filterValueString = Convert.ToString((DateTimeOffset) filter.Value); - else if (filter.Value is double) - filterValueString = Convert.ToString((double) filter.Value); - else if (filter.Value is Guid) - filterValueString = ((Guid) filter.Value).ToString(); - else if (filter.Value is int) - filterValueString = Convert.ToString((int) filter.Value); - else if (filter.Value is long) - filterValueString = Convert.ToString((long) filter.Value); - else - throw new NotSupportedException($"QueryFilter of Type \"{filter.Value?.GetType().FullName}\" is not supported."); + var filterValueString = filter.Value switch + { + string value => $"'{value}'", + bool b => b.ToString().ToLower(), + byte[] bytes => $"binary'{Convert.ToBase64String(bytes)}'", + DateTimeOffset offset => $"datetime'{offset.ToUniversalTime():s}Z'", + DateTime offset => $"datetime'{offset.ToUniversalTime():s}Z'", + double d => d.ToString(CultureInfo.InvariantCulture), + Guid guid => $"guid'{guid}'", + int i => i.ToString(), + long l => $"{l}L", + _ => throw new NotSupportedException( + $"QueryFilter of Type \"{filter.Value?.GetType().FullName}\" is not supported.") + }; return $"{filter.Property} {filterOperation} {filterValueString}"; }