From 69201a8a7878902bea10f30f4d31ccc3a75451fe Mon Sep 17 00:00:00 2001 From: Borisov Zakhar Date: Tue, 27 Apr 2021 13:04:15 +0300 Subject: [PATCH] feature (#51) * fix use Guid in filter * feature ToRelativeUri * review use contaner model --- README.md | 21 ++- .../Builders/AbstractODataQueryBuilder.cs | 32 ++++ .../Builders/ODataQueryBuilder.cs | 26 ++-- .../Builders/ODataQueryBuilder{T}.cs | 31 ++++ ...andBase.cs => AbstractODataQueryExpand.cs} | 4 +- .../Query/Expand/ODataQueryExpand.cs | 2 +- .../AddressingEntities/Query/IODataQuery.cs | 4 +- .../AddressingEntities/Query/ODataQuery.cs | 4 +- .../Resources/Expand/ODataExpandResource.cs | 12 +- .../Resources/IODataResource.cs | 9 +- .../Resources/IODataResource{T}.cs | 10 ++ .../Resources/ODataResource.cs | 19 +-- .../Resources/ODataResource{T}.cs | 29 ++++ .../ODataOptionFilterExpressionVisitor.cs | 7 +- .../Extensions/StringExtensions.cs | 2 + .../ODataQueryBenchmark.cs | 4 +- .../ODataTypeEntity.cs | 2 + .../Builders/ODataQueryBuilderTest.cs | 47 ++++++ test/OData.QueryBuilder.Test/CommonFixture.cs | 2 +- ...istTest.cs => ODataQueryCollectionTest.cs} | 140 ++++++++++++------ ...yOptionKeyTest.cs => ODataQueryKeyTest.cs} | 54 +++---- 21 files changed, 329 insertions(+), 132 deletions(-) create mode 100644 src/OData.QueryBuilder/Builders/AbstractODataQueryBuilder.cs create mode 100644 src/OData.QueryBuilder/Builders/ODataQueryBuilder{T}.cs rename src/OData.QueryBuilder/Conventions/AddressingEntities/Query/Expand/{ODataQueryExpandBase.cs => AbstractODataQueryExpand.cs} (76%) create mode 100644 src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/IODataResource{T}.cs create mode 100644 src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/ODataResource{T}.cs create mode 100644 test/OData.QueryBuilder.Test/Builders/ODataQueryBuilderTest.cs rename test/OData.QueryBuilder.Test/{ODataQueryOptionListTest.cs => ODataQueryCollectionTest.cs} (85%) rename test/OData.QueryBuilder.Test/{ODataQueryOptionKeyTest.cs => ODataQueryKeyTest.cs} (72%) diff --git a/README.md b/README.md index b3e3925f..032e6ff4 100644 --- a/README.md +++ b/README.md @@ -40,19 +40,24 @@ dotnet add package OData.QueryBuilder 1. Build instance - As soon as possible, create a new instance of the OData.QueryBuilder object indicating the data models and the base path: + As soon as possible, create a new instance of the OData.QueryBuilder object indicating the data models and the optional base path: ```csharp - var odataQueryBuilder = new ODataQueryBuilder(); + var odataQueryBuilder = new ODataQueryBuilder(?); + + // specify the resource for which the request will be built + odataQueryBuilder.For(s => s.ODataEntity) ``` - -2. Specify the resource for which the request will be built - + :information_source: __OData.Query Builder assumes you are using [OData Connected Service](https://marketplace.visualstudio.com/items?itemName=marketplace.ODataConnectedService) and you have a root model otherwise use:__ + ```csharp - odataQueryBuilder.For(s => s.ODataEntity) + var odataQueryBuilder = new ODataQueryBuilder(?); + + // specify the resource for which the request will be built + odataQueryBuilder.For("ODataEntity") ``` -3. Select request type +2. Select request type The builder allows you to build queries on the key and the list: * [ByKey](#ByKey) @@ -77,7 +82,7 @@ dotnet add package OData.QueryBuilder * [top](#top) * [skip](#skip) * [count](#count) -4. Get Uri request or collection of operators from the builder +3. Get Uri request or collection of operators from the builder ```csharp odataQueryBuilder.ToUri() odataQueryBuilder.ToDictionary() diff --git a/src/OData.QueryBuilder/Builders/AbstractODataQueryBuilder.cs b/src/OData.QueryBuilder/Builders/AbstractODataQueryBuilder.cs new file mode 100644 index 00000000..95a299ba --- /dev/null +++ b/src/OData.QueryBuilder/Builders/AbstractODataQueryBuilder.cs @@ -0,0 +1,32 @@ +using OData.QueryBuilder.Conventions.Constants; +using OData.QueryBuilder.Options; +using System; + +namespace OData.QueryBuilder.Builders +{ + public abstract class AbstractODataQueryBuilder + { + protected readonly string _baseUrl; + protected readonly ODataQueryBuilderOptions _odataQueryBuilderOptions; + + public AbstractODataQueryBuilder(ODataQueryBuilderOptions odataQueryBuilderOptions = default) + { + _baseUrl = string.Empty; + _odataQueryBuilderOptions = odataQueryBuilderOptions ?? new ODataQueryBuilderOptions(); + } + + public AbstractODataQueryBuilder(string baseUrl, ODataQueryBuilderOptions odataQueryBuilderOptions = default) + { + _baseUrl = !string.IsNullOrEmpty(baseUrl) ? + $"{baseUrl.TrimEnd(QuerySeparators.Slash)}{QuerySeparators.Slash}" + : + throw new ArgumentException($"{nameof(baseUrl)} is null"); + _odataQueryBuilderOptions = odataQueryBuilderOptions ?? new ODataQueryBuilderOptions(); + } + + public AbstractODataQueryBuilder(Uri baseUrl, ODataQueryBuilderOptions odataQueryBuilderOptions = default) + : this(baseUrl?.OriginalString, odataQueryBuilderOptions) + { + } + } +} diff --git a/src/OData.QueryBuilder/Builders/ODataQueryBuilder.cs b/src/OData.QueryBuilder/Builders/ODataQueryBuilder.cs index e8a6276c..06ac0d10 100644 --- a/src/OData.QueryBuilder/Builders/ODataQueryBuilder.cs +++ b/src/OData.QueryBuilder/Builders/ODataQueryBuilder.cs @@ -1,22 +1,30 @@ -using OData.QueryBuilder.Conventions.AddressingEntities.Resources; -using OData.QueryBuilder.Conventions.Constants; +using OData.QueryBuilder.Conventions.AddressingEntities; +using OData.QueryBuilder.Conventions.AddressingEntities.Resources; using OData.QueryBuilder.Options; using System; +using System.Text; namespace OData.QueryBuilder.Builders { - public class ODataQueryBuilder : ODataResource + public sealed class ODataQueryBuilder : AbstractODataQueryBuilder { - public ODataQueryBuilder(Uri baseUrl, ODataQueryBuilderOptions odataQueryBuilderOptions = default) - : base($"{baseUrl.OriginalString.TrimEnd(QuerySeparators.Slash)}{QuerySeparators.Slash}", - odataQueryBuilderOptions ?? new ODataQueryBuilderOptions()) + public ODataQueryBuilder(ODataQueryBuilderOptions odataQueryBuilderOptions = default) + : base(odataQueryBuilderOptions) { } public ODataQueryBuilder(string baseUrl, ODataQueryBuilderOptions odataQueryBuilderOptions = default) - : base($"{baseUrl.TrimEnd(QuerySeparators.Slash)}{QuerySeparators.Slash}", - odataQueryBuilderOptions ?? new ODataQueryBuilderOptions()) + : base(baseUrl, odataQueryBuilderOptions) + { + } + + public ODataQueryBuilder(Uri baseUrl, ODataQueryBuilderOptions odataQueryBuilderOptions = default) + : base(baseUrl, odataQueryBuilderOptions) { } + + public IAddressingEntries For(string resource) => + new ODataResource(new StringBuilder(_baseUrl), _odataQueryBuilderOptions) + .For(resource); } -} \ No newline at end of file +} diff --git a/src/OData.QueryBuilder/Builders/ODataQueryBuilder{T}.cs b/src/OData.QueryBuilder/Builders/ODataQueryBuilder{T}.cs new file mode 100644 index 00000000..24d6e9b0 --- /dev/null +++ b/src/OData.QueryBuilder/Builders/ODataQueryBuilder{T}.cs @@ -0,0 +1,31 @@ +using OData.QueryBuilder.Conventions.AddressingEntities; +using OData.QueryBuilder.Conventions.AddressingEntities.Resources; +using OData.QueryBuilder.Options; +using System; +using System.Linq.Expressions; +using System.Text; + +namespace OData.QueryBuilder.Builders +{ + public sealed class ODataQueryBuilder : AbstractODataQueryBuilder + { + public ODataQueryBuilder(ODataQueryBuilderOptions odataQueryBuilderOptions = default) + : base(odataQueryBuilderOptions) + { + } + + public ODataQueryBuilder(string baseUrl, ODataQueryBuilderOptions odataQueryBuilderOptions = default) + : base(baseUrl, odataQueryBuilderOptions) + { + } + + public ODataQueryBuilder(Uri baseUrl, ODataQueryBuilderOptions odataQueryBuilderOptions = default) + : base(baseUrl, odataQueryBuilderOptions) + { + } + + public IAddressingEntries For(Expression> resource) => + new ODataResource(new StringBuilder(_baseUrl), _odataQueryBuilderOptions) + .For(resource); + } +} \ No newline at end of file diff --git a/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/Expand/ODataQueryExpandBase.cs b/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/Expand/AbstractODataQueryExpand.cs similarity index 76% rename from src/OData.QueryBuilder/Conventions/AddressingEntities/Query/Expand/ODataQueryExpandBase.cs rename to src/OData.QueryBuilder/Conventions/AddressingEntities/Query/Expand/AbstractODataQueryExpand.cs index 896a5613..2a8f59d6 100644 --- a/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/Expand/ODataQueryExpandBase.cs +++ b/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/Expand/AbstractODataQueryExpand.cs @@ -4,12 +4,12 @@ namespace OData.QueryBuilder.Conventions.AddressingEntities.Query.Expand { - internal class ODataQueryExpandBase + internal abstract class AbstractODataQueryExpand { protected readonly ODataQueryBuilderOptions _odataQueryBuilderOptions; protected readonly StringBuilder _stringBuilder; - public ODataQueryExpandBase(StringBuilder stringBuilder, ODataQueryBuilderOptions odataQueryBuilderOptions) + public AbstractODataQueryExpand(StringBuilder stringBuilder, ODataQueryBuilderOptions odataQueryBuilderOptions) { _stringBuilder = stringBuilder; _odataQueryBuilderOptions = odataQueryBuilderOptions; diff --git a/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/Expand/ODataQueryExpand.cs b/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/Expand/ODataQueryExpand.cs index 1b652e28..0cd6d146 100644 --- a/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/Expand/ODataQueryExpand.cs +++ b/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/Expand/ODataQueryExpand.cs @@ -10,7 +10,7 @@ namespace OData.QueryBuilder.Conventions.AddressingEntities.Query.Expand { - internal class ODataQueryExpand : ODataQueryExpandBase, IODataQueryExpand + internal class ODataQueryExpand : AbstractODataQueryExpand, IODataQueryExpand { public ODataQueryExpand(ODataQueryBuilderOptions odataQueryBuilderOptions) : base(new StringBuilder(), odataQueryBuilderOptions) diff --git a/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/IODataQuery.cs b/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/IODataQuery.cs index d046d96f..5a36e846 100644 --- a/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/IODataQuery.cs +++ b/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/IODataQuery.cs @@ -5,8 +5,8 @@ namespace OData.QueryBuilder.Conventions.AddressingEntities.Query { public interface IODataQuery { - Uri ToUri(); + Uri ToUri(UriKind uriKind = UriKind.RelativeOrAbsolute); - Dictionary ToDictionary(); + IDictionary ToDictionary(); } } diff --git a/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/ODataQuery.cs b/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/ODataQuery.cs index 3daf5217..0157095a 100644 --- a/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/ODataQuery.cs +++ b/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/ODataQuery.cs @@ -17,7 +17,7 @@ public ODataQuery(StringBuilder stringBuilder, ODataQueryBuilderOptions odataQue _odataQueryBuilderOptions = odataQueryBuilderOptions; } - public Dictionary ToDictionary() + public IDictionary ToDictionary() { var odataOperators = _stringBuilder.ToString() .Split(new char[2] { QuerySeparators.Begin, QuerySeparators.Main }, StringSplitOptions.RemoveEmptyEntries); @@ -34,6 +34,6 @@ public Dictionary ToDictionary() return dictionary; } - public Uri ToUri() => new Uri(_stringBuilder.ToString().TrimEnd(QuerySeparators.Main)); + public Uri ToUri(UriKind uriKind = UriKind.RelativeOrAbsolute) => new Uri(_stringBuilder.ToString().TrimEnd(QuerySeparators.Main), uriKind); } } diff --git a/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/Expand/ODataExpandResource.cs b/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/Expand/ODataExpandResource.cs index 6d5a33c3..c765a0ae 100644 --- a/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/Expand/ODataExpandResource.cs +++ b/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/Expand/ODataExpandResource.cs @@ -11,9 +11,9 @@ internal class ODataExpandResource : IODataExpandResource { private readonly ODataQueryBuilderOptions _odataQueryBuilderOptions; private readonly StringBuilder _stringBuilder; - private ODataQueryExpandBase _odataOptionNestedBase; + private AbstractODataQueryExpand _odataQueryExpand; - public string Query => $"{_stringBuilder}({_odataOptionNestedBase.Query})"; + public string Query => $"{_stringBuilder}({_odataQueryExpand.Query})"; public ODataExpandResource(ODataQueryBuilderOptions odataQueryBuilderOptions) { @@ -25,18 +25,18 @@ public IODataQueryExpand For(Expression(_odataQueryBuilderOptions); + _odataQueryExpand = new ODataQueryExpand(_odataQueryBuilderOptions); - return _odataOptionNestedBase as ODataQueryExpand; + return _odataQueryExpand as ODataQueryExpand; } } } diff --git a/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/IODataResource.cs b/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/IODataResource.cs index c38aa25a..23dae3d5 100644 --- a/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/IODataResource.cs +++ b/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/IODataResource.cs @@ -1,10 +1,7 @@ -using System; -using System.Linq.Expressions; - -namespace OData.QueryBuilder.Conventions.AddressingEntities.Resources +namespace OData.QueryBuilder.Conventions.AddressingEntities.Resources { - public interface IODataResource + internal interface IODataResource { - IAddressingEntries For(Expression> resource); + IAddressingEntries For(string resource); } } diff --git a/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/IODataResource{T}.cs b/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/IODataResource{T}.cs new file mode 100644 index 00000000..da520764 --- /dev/null +++ b/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/IODataResource{T}.cs @@ -0,0 +1,10 @@ +using System; +using System.Linq.Expressions; + +namespace OData.QueryBuilder.Conventions.AddressingEntities.Resources +{ + internal interface IODataResource + { + IAddressingEntries For(Expression> resource); + } +} diff --git a/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/ODataResource.cs b/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/ODataResource.cs index 8613570a..d790de6a 100644 --- a/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/ODataResource.cs +++ b/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/ODataResource.cs @@ -1,27 +1,24 @@ -using OData.QueryBuilder.Expressions.Visitors; -using OData.QueryBuilder.Options; -using System; -using System.Linq.Expressions; +using OData.QueryBuilder.Options; using System.Text; namespace OData.QueryBuilder.Conventions.AddressingEntities.Resources { - public class ODataResource : IODataResource + internal class ODataResource : IODataResource { private readonly ODataQueryBuilderOptions _odataQueryBuilderOptions; - private readonly string _resourse; + private readonly StringBuilder _stringBuilder; - public ODataResource(string resourse, ODataQueryBuilderOptions odataQueryBuilderOptions) + public ODataResource(StringBuilder stringBuilder, ODataQueryBuilderOptions odataQueryBuilderOptions) { _odataQueryBuilderOptions = odataQueryBuilderOptions; - _resourse = resourse; + _stringBuilder = stringBuilder; } - public IAddressingEntries For(Expression> resource) + public IAddressingEntries For(string resource) { - var query = new ODataResourceExpressionVisitor().ToQuery(resource.Body); + _stringBuilder.Append(resource); - return new AddressingEntries(new StringBuilder($"{_resourse}{query}"), _odataQueryBuilderOptions); + return new AddressingEntries(_stringBuilder, _odataQueryBuilderOptions); } } } diff --git a/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/ODataResource{T}.cs b/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/ODataResource{T}.cs new file mode 100644 index 00000000..a525e58c --- /dev/null +++ b/src/OData.QueryBuilder/Conventions/AddressingEntities/Resources/ODataResource{T}.cs @@ -0,0 +1,29 @@ +using OData.QueryBuilder.Expressions.Visitors; +using OData.QueryBuilder.Options; +using System; +using System.Linq.Expressions; +using System.Text; + +namespace OData.QueryBuilder.Conventions.AddressingEntities.Resources +{ + internal class ODataResource : IODataResource + { + private readonly ODataQueryBuilderOptions _odataQueryBuilderOptions; + private readonly StringBuilder _stringBuilder; + + public ODataResource(StringBuilder stringBuilder, ODataQueryBuilderOptions odataQueryBuilderOptions) + { + _odataQueryBuilderOptions = odataQueryBuilderOptions; + _stringBuilder = stringBuilder; + } + + public IAddressingEntries For(Expression> resource) + { + var query = new ODataResourceExpressionVisitor().ToQuery(resource.Body); + + _stringBuilder.Append(query); + + return new AddressingEntries(_stringBuilder, _odataQueryBuilderOptions); + } + } +} diff --git a/src/OData.QueryBuilder/Expressions/Visitors/ODataOptionFilterExpressionVisitor.cs b/src/OData.QueryBuilder/Expressions/Visitors/ODataOptionFilterExpressionVisitor.cs index 0618362b..c4c2aeae 100644 --- a/src/OData.QueryBuilder/Expressions/Visitors/ODataOptionFilterExpressionVisitor.cs +++ b/src/OData.QueryBuilder/Expressions/Visitors/ODataOptionFilterExpressionVisitor.cs @@ -181,12 +181,7 @@ protected override string VisitNewExpression(NewExpression newExpression) arguments[i] = _valueExpression.GetValue(newExpression.Arguments[i]); } - if (newExpression.Type == typeof(DateTime) || newExpression.Type == typeof(DateTimeOffset)) - { - return newExpression.Constructor.Invoke(arguments).ToQuery(); - } - - return default; + return (arguments.Length == 0 ? Activator.CreateInstance(newExpression.Type) : newExpression.Constructor.Invoke(arguments)).ToQuery(); } return base.VisitNewExpression(newExpression); diff --git a/src/OData.QueryBuilder/Extensions/StringExtensions.cs b/src/OData.QueryBuilder/Extensions/StringExtensions.cs index cb5d19c1..1e4e876c 100644 --- a/src/OData.QueryBuilder/Extensions/StringExtensions.cs +++ b/src/OData.QueryBuilder/Extensions/StringExtensions.cs @@ -59,6 +59,8 @@ public static string ToQuery(this object @object) var stringValuesString = string.Join($"'{QuerySeparators.Comma}'", stringValues); return !string.IsNullOrEmpty(stringValuesString) ? $"'{stringValuesString}'" : string.Empty; + case Guid @guid: + return $"{@guid}"; default: return $"'{@object}'"; } diff --git a/test/OData.QueryBuilder.Benchmark/ODataQueryBenchmark.cs b/test/OData.QueryBuilder.Benchmark/ODataQueryBenchmark.cs index 21590cec..6900f487 100644 --- a/test/OData.QueryBuilder.Benchmark/ODataQueryBenchmark.cs +++ b/test/OData.QueryBuilder.Benchmark/ODataQueryBenchmark.cs @@ -132,7 +132,7 @@ public Uri ODataQueryBuilderKey_Expand_Nested_Filter() => _odataQueryBuilder .ToUri(); [Benchmark] - public Dictionary ODataQueryBuilderKey_ToDicionary() => _odataQueryBuilder + public IDictionary ODataQueryBuilderKey_ToDicionary() => _odataQueryBuilder .For(s => s.ODataType) .ByKey("223123123") .Expand(s => s.ODataKind) @@ -584,7 +584,7 @@ public Uri FilterEnumTest() => _odataQueryBuilder private ODataTypeEntity ODataQueryBuilderList_ToDicionary_NewObject = new ODataTypeEntity { IsOpen = false }; [Benchmark] - public Dictionary ToDicionary() => _odataQueryBuilder + public IDictionary ToDicionary() => _odataQueryBuilder .For(s => s.ODataType) .ByList() .Filter(s => s.IsActive diff --git a/test/OData.QueryBuilder.Fakes/ODataTypeEntity.cs b/test/OData.QueryBuilder.Fakes/ODataTypeEntity.cs index a676b957..f1abd071 100644 --- a/test/OData.QueryBuilder.Fakes/ODataTypeEntity.cs +++ b/test/OData.QueryBuilder.Fakes/ODataTypeEntity.cs @@ -5,6 +5,8 @@ namespace OData.QueryBuilder.Fakes { public class ODataTypeEntity { + public Guid Id { get; set; } + public int IdType { get; set; } public int? IdRule { get; set; } diff --git a/test/OData.QueryBuilder.Test/Builders/ODataQueryBuilderTest.cs b/test/OData.QueryBuilder.Test/Builders/ODataQueryBuilderTest.cs new file mode 100644 index 00000000..f7c74aff --- /dev/null +++ b/test/OData.QueryBuilder.Test/Builders/ODataQueryBuilderTest.cs @@ -0,0 +1,47 @@ +using FluentAssertions; +using OData.QueryBuilder.Builders; +using OData.QueryBuilder.Fakes; +using System; +using Xunit; + +namespace OData.QueryBuilder.Test.Builders +{ + public class ODataQueryBuilderTest + { + [Theory(DisplayName = "new instance ODataQueryBuilder with baseUrl => Exception")] + [InlineData("")] + [InlineData(null)] + public void ODataQueryBuilder_New_Instance_BaseUrl_Exception(string baseUrl) + { + var ex = Assert.Throws(() => new ODataQueryBuilder(baseUrl)); + ex.Message.Should().Be($"{nameof(baseUrl)} is null"); + } + + [Fact(DisplayName = "new instance ODataQueryBuilder with baseUrl Uri => Exception")] + public void ODataQueryBuilder_New_Instance_BaseUrl_Uri_Exception() + { + var uri = default(System.Uri); + + var ex = Assert.Throws(() => new ODataQueryBuilder(uri)); + ex.Message.Should().Be($"baseUrl is null"); + } + + [Theory(DisplayName = "new instance generic ODataQueryBuilder with baseUrl => Exception")] + [InlineData("")] + [InlineData(null)] + public void Generic_ODataQueryBuilder_New_Instance_BaseUrl_Exception(string baseUrl) + { + var ex = Assert.Throws(() => new ODataQueryBuilder(baseUrl)); + ex.Message.Should().Be($"{nameof(baseUrl)} is null"); + } + + [Fact(DisplayName = "new instance generic ODataQueryBuilder with baseUrl Uri => Exception")] + public void Generic_ODataQueryBuilder_New_Instance_BaseUrl_Uri_Exception() + { + var uri = default(System.Uri); + + var ex = Assert.Throws(() => new ODataQueryBuilder(uri)); + ex.Message.Should().Be($"baseUrl is null"); + } + } +} diff --git a/test/OData.QueryBuilder.Test/CommonFixture.cs b/test/OData.QueryBuilder.Test/CommonFixture.cs index 6208e9ef..c88e45a9 100644 --- a/test/OData.QueryBuilder.Test/CommonFixture.cs +++ b/test/OData.QueryBuilder.Test/CommonFixture.cs @@ -8,7 +8,7 @@ public CommonFixture() { } - public string BaseUrl => "http://mock/odata/"; + public string BaseUrl => "http://mock/odata"; public Uri BaseUri => new Uri("http://mock/odata/"); } diff --git a/test/OData.QueryBuilder.Test/ODataQueryOptionListTest.cs b/test/OData.QueryBuilder.Test/ODataQueryCollectionTest.cs similarity index 85% rename from test/OData.QueryBuilder.Test/ODataQueryOptionListTest.cs rename to test/OData.QueryBuilder.Test/ODataQueryCollectionTest.cs index 5323d810..595c6abe 100644 --- a/test/OData.QueryBuilder.Test/ODataQueryOptionListTest.cs +++ b/test/OData.QueryBuilder.Test/ODataQueryCollectionTest.cs @@ -9,14 +9,14 @@ namespace OData.QueryBuilder.Test { - public class ODataQueryOptionListTest : IClassFixture + public class ODataQueryCollectionTest : IClassFixture { private readonly CommonFixture _commonFixture; private readonly ODataQueryBuilder _odataQueryBuilderDefault; public static string IdCodeStatic => "testCode"; - public ODataQueryOptionListTest(CommonFixture commonFixture) + public ODataQueryCollectionTest(CommonFixture commonFixture) { _commonFixture = commonFixture; _odataQueryBuilderDefault = new ODataQueryBuilder( @@ -32,7 +32,7 @@ public void ODataQueryBuilderList_Expand_Simple_Success() .Expand(s => new { s.ODataKind }) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$expand=ODataKind"); + uri.Should().Be("http://mock/odata/ODataType?$expand=ODataKind"); } [Fact(DisplayName = "Expand nested => Success")] @@ -68,7 +68,7 @@ public void ODataQueryBuilderList_ExpandNested_Success() }) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$expand=ODataKind($expand=ODataCode($select=IdCode);$select=IdKind),ODataKindNew($select=IdKind),ODataKindNew($select=IdKind)"); + uri.Should().Be("http://mock/odata/ODataType?$expand=ODataKind($expand=ODataCode($select=IdCode);$select=IdKind),ODataKindNew($select=IdKind),ODataKindNew($select=IdKind)"); } [Fact(DisplayName = "Expand nested orderby => Success")] @@ -85,7 +85,7 @@ public void ODataQueryBuilderList_ExpandNested_OrderBy_Success() }) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$expand=ODataKindNew($select=IdKind;$orderby=EndDate asc)"); + uri.Should().Be("http://mock/odata/ODataType?$expand=ODataKindNew($select=IdKind;$orderby=EndDate asc)"); } [Fact(DisplayName = "Expand nested top => Success")] @@ -103,7 +103,7 @@ public void ODataQueryBuilderList_ExpandNested_Top_Success() }) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$expand=ODataKindNew($select=IdKind;$top=1;$orderby=EndDate asc)"); + uri.Should().Be("http://mock/odata/ODataType?$expand=ODataKindNew($select=IdKind;$top=1;$orderby=EndDate asc)"); } [Fact(DisplayName = "Expand orderBy multiple sort => Success")] @@ -123,7 +123,7 @@ public void ODataQueryBuilderList_Expand_OrderBy_Multiple_Sort_Success() }) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$expand=ODataKindNew($select=IdKind;$orderby=OpenDate asc,ODataCode/Code desc,IdKind asc)"); + uri.Should().Be("http://mock/odata/ODataType?$expand=ODataKindNew($select=IdKind;$orderby=OpenDate asc,ODataCode/Code desc,IdKind asc)"); } [Fact(DisplayName = "Expand nested orderby desc => Success")] @@ -140,7 +140,7 @@ public void ODataQueryBuilderList_ExpandNested_OrderByDescending_Success() }) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$expand=ODataKindNew($select=IdKind;$orderby=EndDate desc)"); + uri.Should().Be("http://mock/odata/ODataType?$expand=ODataKindNew($select=IdKind;$orderby=EndDate desc)"); } [Fact(DisplayName = "Select simple => Success")] @@ -152,7 +152,7 @@ public void ODataQueryBuilderList_Select_Simple_Success() .Select(s => s.IdType) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$select=IdType"); + uri.Should().Be("http://mock/odata/ODataType?$select=IdType"); } [Fact(DisplayName = "OrderBy simple => Success")] @@ -164,7 +164,7 @@ public void ODataQueryBuilderList_OrderBy_Simple_Success() .OrderBy(s => s.IdType) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$orderby=IdType asc"); + uri.Should().Be("http://mock/odata/ODataType?$orderby=IdType asc"); } [Fact(DisplayName = "Filter orderBy multiple sort => Success")] @@ -181,7 +181,7 @@ public void ODataQueryBuilderList_Filter_OrderBy_Multiple_Sort_Success() .Descending(entity.ODataKind.OpenDate)) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$orderby=BeginDate asc,EndDate desc,IdRule asc,Sum asc,ODataKind/OpenDate desc"); + uri.Should().Be("http://mock/odata/ODataType?$orderby=BeginDate asc,EndDate desc,IdRule asc,Sum asc,ODataKind/OpenDate desc"); } [Fact(DisplayName = "Filter orderBy multiple sort => NotSupportedException")] @@ -206,7 +206,7 @@ public void ODataQueryBuilderList_OrderByDescending_Simple_Success() .OrderByDescending(s => s.IdType) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$orderby=IdType desc"); + uri.Should().Be("http://mock/odata/ODataType?$orderby=IdType desc"); } [Fact(DisplayName = "Count simple => Success")] @@ -218,7 +218,7 @@ public void ODataQueryBuilderList_Count_Simple_Success() .Count() .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$count=true"); + uri.Should().Be("http://mock/odata/ODataType?$count=true"); } [Fact(DisplayName = "Skip and Top simple => Success")] @@ -231,7 +231,7 @@ public void ODataQueryBuilderList_Skip_Top_Simple_Success() .Top(1) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$skip=1&$top=1"); + uri.Should().Be("http://mock/odata/ODataType?$skip=1&$top=1"); } [Fact(DisplayName = "Filter call ToString => Success")] @@ -243,7 +243,7 @@ public void ODataQueryBuilderList_Filter_Call_ToString_Success() .Filter(s => s.TypeCode == 44.ToString()) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=TypeCode eq '44'"); + uri.Should().Be("http://mock/odata/ODataType?$filter=TypeCode eq '44'"); } [Fact(DisplayName = "Filter string with ReplaceCharacters => Success")] @@ -266,7 +266,7 @@ public void ODataQueryBuilderList_Filter_string_with_ReplaceCharacters_Success() .Filter((s, f) => s.ODataKind.ODataCode.Code == f.ReplaceCharacters(constValue, dictionary)) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCode/Code eq '3 %26 4 %2f 7 %3f 8 %25 9 %23 1'"); + uri.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCode/Code eq '3 %26 4 %2f 7 %3f 8 %25 9 %23 1'"); } [Fact(DisplayName = "Filter enumerable string with ReplaceCharacters => Success")] @@ -283,7 +283,7 @@ public void ODataQueryBuilderList_Filter_enumerable_string_with_ReplaceCharacter .Filter((s, f, o) => o.In(s.ODataKind.ODataCode.Code, f.ReplaceCharacters(strings, new Dictionary(0) { { @"\", "%5C" } }))) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCode/Code in ('test%5C%5CYUYYUT','test1%5C%5CYUYY123')"); + uri.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCode/Code in ('test%5C%5CYUYYUT','test1%5C%5CYUYY123')"); } [Fact(DisplayName = "Filter call ReplaceCharacters in operator In => ArgumentException")] @@ -330,7 +330,7 @@ public void ODataQueryBuilderList_Filter_With_ReplaceCharacters_new_dictionary_S new Dictionary { { "&", "%26" } })) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCode/Code eq '3 %26 4 / 7 ? 8 % 9 # 1'"); + uri.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCode/Code eq '3 %26 4 / 7 ? 8 % 9 # 1'"); } [Fact(DisplayName = "Filter string with ReplaceCharacters Value => Success")] @@ -346,7 +346,7 @@ public void ODataQueryBuilderList_Filter_With_ReplaceCharacters_Value_Success() new Dictionary { { "&", "%26" } })) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCode/Code eq null"); + uri.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCode/Code eq null"); } [Fact(DisplayName = "Filter string with ReplaceCharacters KeyValuePairs => ArgumentException")] @@ -374,7 +374,7 @@ public void ODataQueryBuilderList_Filter_Simple_Const_Int_Success() .Filter(s => s.ODataKind.ODataCode.IdCode >= 3 || s.IdType == 5) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCode/IdCode ge 3 or IdType eq 5"); + uri.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCode/IdCode ge 3 or IdType eq 5"); } [Fact(DisplayName = "Filter simple const string => Success")] @@ -389,7 +389,7 @@ public void ODataQueryBuilderList_Filter_Simple_Const_String_Success() && s.ODataKind.ODataCode.Code == IdCodeStatic) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCode/Code eq '3' or ODataKind/ODataCode/Code eq '5' and ODataKind/ODataCode/Code eq 'testCode'"); + uri.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCode/Code eq '3' or ODataKind/ODataCode/Code eq '5' and ODataKind/ODataCode/Code eq 'testCode'"); } [Fact(DisplayName = "Filter operators All/Any => Success")] @@ -402,7 +402,7 @@ public void ODataQueryBuilderList_Filter_All_Any_Success() && o.All(s.ODataKind.ODataCodes, v => v.IdActive)) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCodes/any(v:v/IdCode eq 1) and ODataKind/ODataCodes/all(v:v/IdActive)"); + uri.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCodes/any(v:v/IdCode eq 1) and ODataKind/ODataCodes/all(v:v/IdActive)"); } [Fact(DisplayName = "(ODataQueryBuilderList) Filter Any => Success")] @@ -414,7 +414,7 @@ public void ODataQueryBuilderList_Filter_Any_Success1() .Filter((s, f, o) => o.Any(s.Tags, t => t == "testTag")) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=Tags/any(t:t eq 'testTag')"); + uri.Should().Be("http://mock/odata/ODataType?$filter=Tags/any(t:t eq 'testTag')"); } [Fact(DisplayName = "(ODataQueryBuilderList) Filter Any with or => Success")] @@ -426,7 +426,7 @@ public void ODataQueryBuilderList_Filter_Any_with_or_Success() .Filter((s, f, o) => o.Any(s.Labels, label => label == "lb1" || label == "lb2")) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=Labels/any(label:label eq 'lb1' or label eq 'lb2')"); + uri.Should().Be("http://mock/odata/ODataType?$filter=Labels/any(label:label eq 'lb1' or label eq 'lb2')"); } [Fact(DisplayName = "Filter operators Any with func => Success")] @@ -438,7 +438,7 @@ public void ODataQueryBuilderList_Filter_Any_With_Func_Success() .Filter((s, f, o) => o.Any(s.ODataKind.ODataCodes, v => f.Date(v.Created) == new DateTime(2019, 2, 9))) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCodes/any(v:date(v/Created) eq 2019-02-09T00:00:00Z)"); + uri.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCodes/any(v:date(v/Created) eq 2019-02-09T00:00:00Z)"); } [Fact(DisplayName = "Expand,Filter,Select,OrderBy,OrderByDescending,Skip,Top,Count => Success")] @@ -463,7 +463,7 @@ public void ODataQueryBuilderList_Expand_Filter_Select_OrderBy_OrderByDescending .Count() .ToUri(); - uri.OriginalString.Should().Be($"http://mock/odata/ODataType?$expand=ODataKind&$filter=IdType lt 2 and 3 le ODataKind/ODataCode/IdCode or IdType eq 5 and IdRule ne null and IdRule eq null&$select=ODataKind,Sum&$orderby=IdType asc&$skip=1&$top=1&$count=true"); + uri.Should().Be($"http://mock/odata/ODataType?$expand=ODataKind&$filter=IdType lt 2 and 3 le ODataKind/ODataCode/IdCode or IdType eq 5 and IdRule ne null and IdRule eq null&$select=ODataKind,Sum&$orderby=IdType asc&$skip=1&$top=1&$count=true"); } [Fact(DisplayName = "Filter nullable bool eq null => Success")] @@ -477,7 +477,7 @@ public void ODataQueryBuilderList_filter_nullable_bool_eq_null_success() .Filter(s => s.IsOpen == constValue) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=IsOpen eq null"); + uri.Should().Be("http://mock/odata/ODataType?$filter=IsOpen eq null"); } [Fact(DisplayName = "Function Date => Success")] @@ -494,6 +494,8 @@ public void ODataQueryBuilderList_Function_Date_Success() f.Date(s.ODataKind.OpenDate) == currentDateNow && s.ODataKind.OpenDate == currentDateToday && s.ODataKind.OpenDate == DateTime.Today + && s.ODataKind.OpenDate == new DateTimeOffset() + && s.Open == new DateTime() && f.Date(s.Open) == DateTime.Today && f.Date(s.Open) == DateTimeOffset.Now && s.Open == DateTime.Today @@ -504,10 +506,12 @@ public void ODataQueryBuilderList_Function_Date_Success() && f.Date((DateTimeOffset)s.BeginDate) == DateTime.Today) .ToUri(); - uri.OriginalString.Should().Be($"http://mock/odata/ODataType?$filter=" + + uri.Should().Be($"http://mock/odata/ODataType?$filter=" + $"date(ODataKind/OpenDate) eq 2019-02-09T01:02:04Z " + $"and ODataKind/OpenDate eq 2019-02-09T00:00:00Z " + $"and ODataKind/OpenDate eq {DateTime.Today:s}Z " + + $"and ODataKind/OpenDate eq {new DateTimeOffset():s}Z " + + $"and Open eq {new DateTime():s}Z " + $"and date(Open) eq {DateTime.Today:s}Z " + $"and date(Open) eq {DateTimeOffset.Now:s}Z " + $"and Open eq {DateTime.Today:s}Z " + @@ -530,7 +534,7 @@ public void ODataQueryBuilderList_Function_Datetime_convert_Success() f.Date(s.ODataKind.OpenDate) == f.ConvertDateTimeToString(currentDateToday.Value, "yyyy-MM-dd")) .ToUri(); - uri.OriginalString.Should().Be($"http://mock/odata/ODataType?$filter=date(ODataKind/OpenDate) eq 2019-02-09"); + uri.Should().Be($"http://mock/odata/ODataType?$filter=date(ODataKind/OpenDate) eq 2019-02-09"); } [Fact(DisplayName = "Function Datetime convert => Exception")] @@ -559,7 +563,7 @@ public void ODataQueryBuilderList_Function_Datetimeoffset_convert_Success() f.Date(s.ODataKind.OpenDate) == f.ConvertDateTimeOffsetToString(currentDateToday.Value, "yyyy-MM-dd")) .ToUri(); - uri.OriginalString.Should().Be($"http://mock/odata/ODataType?$filter=date(ODataKind/OpenDate) eq 2019-02-09"); + uri.Should().Be($"http://mock/odata/ODataType?$filter=date(ODataKind/OpenDate) eq 2019-02-09"); } [Fact(DisplayName = "Function Datetimeoffset convert => Exception")] @@ -591,7 +595,7 @@ public void ODataQueryBuilderList_Test_Substringof_Simple() || f.SubstringOf("55", s.ODataKindNew.ODataCode.Code)) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=substringof('W',toupper(ODataKind/ODataCode/Code)) or substringof('P',ODataKind/ODataCode/Code) or substringof('TYPECODEVALUE',ODataKindNew/ODataCode/Code) or substringof('55',ODataKindNew/ODataCode/Code)"); + uri.Should().Be("http://mock/odata/ODataType?$filter=substringof('W',toupper(ODataKind/ODataCode/Code)) or substringof('P',ODataKind/ODataCode/Code) or substringof('TYPECODEVALUE',ODataKindNew/ODataCode/Code) or substringof('55',ODataKindNew/ODataCode/Code)"); } [Fact(DisplayName = "SubstringOf is null or empty value => Success")] @@ -613,7 +617,7 @@ public void ODataQueryBuilderList_Test_SubstringOf_is_null_or_empty_value_Succes || f.SubstringOf(null, s.ODataKindNew.ODataCode.Code)) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=substringof('P',ODataKind/ODataCode/Code)"); + uri.Should().Be("http://mock/odata/ODataType?$filter=substringof('P',ODataKind/ODataCode/Code)"); } @@ -651,7 +655,7 @@ public void ODataQueryBuilderList_Test_Contains_Simple() || f.Contains(s.ODataKindNew.ODataCode.Code, "55")) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=contains(tolower(ODataKind/ODataCode/Code),'W') or contains(ODataKind/ODataCode/Code,'p') or contains(ODataKindNew/ODataCode/Code,'typecodevalue') or contains(ODataKindNew/ODataCode/Code,'55')"); + uri.Should().Be("http://mock/odata/ODataType?$filter=contains(tolower(ODataKind/ODataCode/Code),'W') or contains(ODataKind/ODataCode/Code,'p') or contains(ODataKindNew/ODataCode/Code,'typecodevalue') or contains(ODataKindNew/ODataCode/Code,'55')"); } [Fact(DisplayName = "Contains is null or empty value => ArgumentException")] @@ -692,7 +696,7 @@ public void ODataQueryBuilderList_Test_Contains_is_null_or_empty_value_Success() || f.Contains(s.ODataKindNew.ODataCode.Code, string.Empty)) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=contains(ODataKind/ODataCode/Code,'P')"); + uri.Should().Be("http://mock/odata/ODataType?$filter=contains(ODataKind/ODataCode/Code,'P')"); } [Fact(DisplayName = "Concat string simple => Success")] @@ -704,7 +708,7 @@ public void ODataQueryBuilderList_concat_string_simple_success() .Filter((s, f) => f.Concat(s.TypeCode, ";") == "typeCodeTest;") .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=concat(TypeCode,';') eq 'typeCodeTest;'"); + uri.Should().Be("http://mock/odata/ODataType?$filter=concat(TypeCode,';') eq 'typeCodeTest;'"); } [Fact(DisplayName = "Nested Concat string => Success")] @@ -716,7 +720,7 @@ public void ODataQueryBuilderList_nested_concat_string_simple_success1() .Filter((s, f) => f.Concat(f.Concat(s.TypeCode, ", "), s.ODataKind.ODataCode.Code) == "testTypeCode1, testTypeCode2") .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=concat(concat(TypeCode,', '),ODataKind/ODataCode/Code) eq 'testTypeCode1, testTypeCode2'"); + uri.Should().Be("http://mock/odata/ODataType?$filter=concat(concat(TypeCode,', '),ODataKind/ODataCode/Code) eq 'testTypeCode1, testTypeCode2'"); } [Fact(DisplayName = "Nested Concat string => Success")] @@ -730,7 +734,7 @@ public void ODataQueryBuilderList_nested_concat_string_simple_success2() .Filter((s, f) => f.Concat(f.Concat(s.TypeCode, constParam), s.ODataKind.ODataCode.Code) == "testTypeCode1, testTypeCode2") .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=concat(concat(TypeCode,', '),ODataKind/ODataCode/Code) eq 'testTypeCode1, testTypeCode2'"); + uri.Should().Be("http://mock/odata/ODataType?$filter=concat(concat(TypeCode,', '),ODataKind/ODataCode/Code) eq 'testTypeCode1, testTypeCode2'"); } [Fact(DisplayName = "Nested Concat string => Success")] @@ -745,7 +749,7 @@ public void ODataQueryBuilderList_nested_concat_string_simple_success3() .Filter((s, f) => f.Concat(f.Concat(s.TypeCode, constParamObject.ODataKind.ODataCode.Code), s.ODataKind.ODataCode.Code) == "testTypeCode1, testTypeCode2") .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=concat(concat(TypeCode,', '),ODataKind/ODataCode/Code) eq 'testTypeCode1, testTypeCode2'"); + uri.Should().Be("http://mock/odata/ODataType?$filter=concat(concat(TypeCode,', '),ODataKind/ODataCode/Code) eq 'testTypeCode1, testTypeCode2'"); } [Fact(DisplayName = "Concat string is null or empty value argument1 => Exception")] @@ -793,7 +797,7 @@ public void ODataQueryBuilderList_concat_is_null_or_empty_value_agr1_success(str .Filter((s, f) => f.Concat(value, s.TypeCode) == "typeCodeTest;") .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter='typeCodeTest;'"); + uri.Should().Be("http://mock/odata/ODataType?$filter='typeCodeTest;'"); } [Theory(DisplayName = "Concat is null empty value agr2 => Success")] @@ -811,7 +815,7 @@ public void ODataQueryBuilderList_concat_is_null_or_empty_value_agr2_success(str .Filter((s, f) => f.Concat(s.TypeCode, value) == "typeCodeTest;") .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter='typeCodeTest;'"); + uri.Should().Be("http://mock/odata/ODataType?$filter='typeCodeTest;'"); } [Fact(DisplayName = "Operator IN => Success")] @@ -838,7 +842,7 @@ public void ODataQueryBuilderList_Operator_In_Success() && o.In(s.ODataKind.ODataCode.IdCode, newObjectSequenceArray.ODataKind.SequenceArray)) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCode/Code in ('123','512') and ODataKind/ODataCode/Code in ('123','512') and ODataKind/ODataCode/Code in ('123','512') and IdType in (123,512) and IdType in (123,512) and IdRule in (123,512) and IdRule in (123,512) and ODataKind/IdKind in (123,512) and ODataKind/ODataCode/IdCode in (123,512)"); + uri.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCode/Code in ('123','512') and ODataKind/ODataCode/Code in ('123','512') and ODataKind/ODataCode/Code in ('123','512') and IdType in (123,512) and IdType in (123,512) and IdRule in (123,512) and IdRule in (123,512) and ODataKind/IdKind in (123,512) and ODataKind/ODataCode/IdCode in (123,512)"); } [Fact(DisplayName = "(ODataQueryBuilderList) Operator IN empty => Success")] @@ -870,7 +874,7 @@ public void ODataQueryBuilderList_Operator_In_Empty_Success() && o.In(s.ODataKind.ODataCode.IdCode, newObjectSequenceArray.ODataKind.SequenceArray)) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=IdType in (123,512) and IdRule in (123,512) and ODataKind/IdKind in (123,512)"); + uri.Should().Be("http://mock/odata/ODataType?$filter=IdType in (123,512) and IdRule in (123,512) and ODataKind/IdKind in (123,512)"); } [Fact(DisplayName = "Operator IN is null => ArgumentException 1")] @@ -969,7 +973,7 @@ public void ODataQueryBuilderList_Filter_In_with_new_Success() .Filter((s, f, o) => o.In(s.ODataKind.ODataCode.Code, new[] { "123", "512" }) && o.In(s.IdType, new[] { 123, 512 })) .ToUri(); - uri.OriginalString.Should().Be($"http://mock/odata/ODataType?$filter=ODataKind/ODataCode/Code in ('123','512') and IdType in (123,512)"); + uri.Should().Be($"http://mock/odata/ODataType?$filter=ODataKind/ODataCode/Code in ('123','512') and IdType in (123,512)"); } [Fact(DisplayName = "Filter boolean values => Success")] @@ -987,7 +991,7 @@ public void ODataQueryBuilderList_Filter_Boolean_Values_Success() && s.ODataKind.ODataCode.IdActive == newObject.IsOpen) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=IsActive and IsOpen eq false and IsOpen eq true and ODataKind/ODataCode/IdActive eq false"); + uri.Should().Be("http://mock/odata/ODataType?$filter=IsActive and IsOpen eq false and IsOpen eq true and ODataKind/ODataCode/IdActive eq false"); } [Fact(DisplayName = "Filter support parentheses => Success")] @@ -1006,7 +1010,7 @@ public void ODataQueryBuilderList_Filter_support_parentheses_Success() && o.In(s.ODataKind.ODataCode.Code, constStrIds), useParenthesis: true) .ToUri(); - uri.OriginalString.Should().Be($"http://mock/odata/ODataType?$filter=(((IdRule eq 3" + + uri.Should().Be($"http://mock/odata/ODataType?$filter=(((IdRule eq 3" + $" and IsActive)" + $" and (date(EndDate) eq null or EndDate gt {DateTime.Today:s}Z))" + $" and (date(BeginDate) ne null or date(BeginDate) le {DateTime.Now:s}Z))" + @@ -1024,7 +1028,7 @@ public void ODataQueryBuilderList_Count_Value_Success(bool value) .Count(value) .ToUri(); - uri.OriginalString.Should().Be($"http://mock/odata/ODataType?$count={value.ToString().ToLower()}"); + uri.Should().Be($"http://mock/odata/ODataType?$count={value.ToString().ToLower()}"); } [Fact(DisplayName = "Filter not bool => Success")] @@ -1036,7 +1040,7 @@ public void ODataQueryBuilderList_Filter_Not__Bool_Success() .Filter(s => s.IsActive && !(bool)s.IsOpen) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=IsActive and not IsOpen"); + uri.Should().Be("http://mock/odata/ODataType?$filter=IsActive and not IsOpen"); } [Fact(DisplayName = "ToDicionary => Success")] @@ -1078,7 +1082,7 @@ public void ODataQueryBuilderList_Filter_Enum_Success() .Top(10) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/Color eq 'Blue' and ODataKind/Color eq 2&$skip=1&$top=10"); + uri.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/Color eq 'Blue' and ODataKind/Color eq 2&$skip=1&$top=10"); } [Fact(DisplayName = "Filter method not supported => NotSupportedException")] @@ -1104,7 +1108,45 @@ public void ODataQueryBuilderList_Test_IndexOf() .Filter((s, f) => f.IndexOf(s.ODataKind.ODataCode.Code, "testCode") == 1) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType?$filter=indexof(ODataKind/ODataCode/Code,'testCode') eq 1"); + uri.Should().Be("http://mock/odata/ODataType?$filter=indexof(ODataKind/ODataCode/Code,'testCode') eq 1"); + } + + [Fact(DisplayName = "Filter Guid Test => Success")] + public void ODataQueryBuilderList_Test_Filter_Guid() + { + var newGuid = Guid.NewGuid(); + + var uri = _odataQueryBuilderDefault + .For(s => s.ODataType) + .ByList() + .Filter(s => s.Id == new Guid() || s.Id == newGuid) + .ToUri(); + + uri.Should().Be($"http://mock/odata/ODataType?$filter=Id eq 00000000-0000-0000-0000-000000000000 or Id eq {newGuid}"); + } + + [Fact(DisplayName = "Without base url => Success")] + public void ODataQueryBuilder_Test_without_base_url() + { + var uri = new ODataQueryBuilder() + .For(s => s.ODataType) + .ByList() + .Filter(s => s.IdRule == 1) + .ToUri(); + + uri.Should().Be("ODataType?$filter=IdRule eq 1"); + } + + [Fact(DisplayName = "Without base url and root model => Success")] + public void ODataQueryBuilder_Test_without_base_url_and_root_model() + { + var uri = new ODataQueryBuilder() + .For("ODataType") + .ByList() + .Filter(s => s.IdRule == 1) + .ToUri(); + + uri.Should().Be("ODataType?$filter=IdRule eq 1"); } } } diff --git a/test/OData.QueryBuilder.Test/ODataQueryOptionKeyTest.cs b/test/OData.QueryBuilder.Test/ODataQueryKeyTest.cs similarity index 72% rename from test/OData.QueryBuilder.Test/ODataQueryOptionKeyTest.cs rename to test/OData.QueryBuilder.Test/ODataQueryKeyTest.cs index 3feb552b..e5289c78 100644 --- a/test/OData.QueryBuilder.Test/ODataQueryOptionKeyTest.cs +++ b/test/OData.QueryBuilder.Test/ODataQueryKeyTest.cs @@ -8,67 +8,67 @@ namespace OData.QueryBuilder.Test { - public class ODataQueryOptionKeyTest : IClassFixture + public class ODataQueryKeyTest : IClassFixture { - private readonly ODataQueryBuilder _odataQueryBuilder; + private readonly ODataQueryBuilder _odataQueryBuilderDefault; - public ODataQueryOptionKeyTest(CommonFixture commonFixture) => - _odataQueryBuilder = new ODataQueryBuilder( + public ODataQueryKeyTest(CommonFixture commonFixture) => + _odataQueryBuilderDefault = new ODataQueryBuilder( commonFixture.BaseUrl, new ODataQueryBuilderOptions()); [Fact(DisplayName = "Expand simple => Success")] public void ODataQueryBuilderKey_Expand_Simple_Success() { - var uri = _odataQueryBuilder + var uri = _odataQueryBuilderDefault .For(s => s.ODataType) .ByKey(223123123) .Expand(s => s.ODataKind) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType(223123123)?$expand=ODataKind"); + uri.Should().Be("http://mock/odata/ODataType(223123123)?$expand=ODataKind"); } [Fact(DisplayName = "Expand simple with key string => Success")] public void ODataQueryBuilderKey_Expand_Simple_With_Key_String_Success() { - var uri = _odataQueryBuilder + var uri = _odataQueryBuilderDefault .For(s => s.ODataType) .ByKey("223123123") .Expand(s => s.ODataKind) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType('223123123')?$expand=ODataKind"); + uri.Should().Be("http://mock/odata/ODataType('223123123')?$expand=ODataKind"); } [Fact(DisplayName = "Select simple => Success")] public void ODataQueryBuilderKey_Select_Simple_Success() { - var uri = _odataQueryBuilder + var uri = _odataQueryBuilderDefault .For(s => s.ODataType) .ByKey(223123123) .Select(s => s.IdType) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType(223123123)?$select=IdType"); + uri.Should().Be("http://mock/odata/ODataType(223123123)?$select=IdType"); } [Fact(DisplayName = "Expand and Select => Success")] public void ODataQueryBuilderKey_Expand_Select_Success() { - var uri = _odataQueryBuilder + var uri = _odataQueryBuilderDefault .For(s => s.ODataType) .ByKey(223123123) .Expand(f => f.ODataKind) .Select(s => new { s.IdType, s.Sum }) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType(223123123)?$expand=ODataKind&$select=IdType,Sum"); + uri.Should().Be("http://mock/odata/ODataType(223123123)?$expand=ODataKind&$select=IdType,Sum"); } [Fact(DisplayName = "Expand nested and Select => Success")] public void ODataQueryBuilderKey_ExpandNested_Select_Success() { - var uri = _odataQueryBuilder + var uri = _odataQueryBuilderDefault .For(s => s.ODataType) .ByKey(223123123) .Expand(f => @@ -86,14 +86,14 @@ public void ODataQueryBuilderKey_ExpandNested_Select_Success() .Select(s => new { s.IdType, s.Sum }) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType(223123123)?$expand=ODataKind($expand=ODataCode($select=IdCode)),ODataKindNew($expand=ODataCode;$select=IdKind),ODataKindNew($select=IdKind)&$select=IdType,Sum"); + uri.Should().Be("http://mock/odata/ODataType(223123123)?$expand=ODataKind($expand=ODataCode($select=IdCode)),ODataKindNew($expand=ODataCode;$select=IdKind),ODataKindNew($select=IdKind)&$select=IdType,Sum"); } [Fact(DisplayName = "Expand nested orderby => Success")] public void ODataQueryBuilderList_ExpandNested_OrderBy_Success() { - var uri = _odataQueryBuilder + var uri = _odataQueryBuilderDefault .For(s => s.ODataType) .ByKey(223123123) .Expand(f => @@ -104,13 +104,13 @@ public void ODataQueryBuilderList_ExpandNested_OrderBy_Success() }) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType(223123123)?$expand=ODataKindNew($select=IdKind;$orderby=EndDate asc)"); + uri.Should().Be("http://mock/odata/ODataType(223123123)?$expand=ODataKindNew($select=IdKind;$orderby=EndDate asc)"); } [Fact(DisplayName = "Expand nested orderby desc => Success")] public void ODataQueryBuilderList_ExpandNested_OrderByDescending_Success() { - var uri = _odataQueryBuilder + var uri = _odataQueryBuilderDefault .For(s => s.ODataType) .ByKey(223123123) .Expand(f => @@ -121,13 +121,13 @@ public void ODataQueryBuilderList_ExpandNested_OrderByDescending_Success() }) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType(223123123)?$expand=ODataKindNew($select=IdKind;$orderby=EndDate desc)"); + uri.Should().Be("http://mock/odata/ODataType(223123123)?$expand=ODataKindNew($select=IdKind;$orderby=EndDate desc)"); } [Fact(DisplayName = "Expand nested top => Success")] public void ODataQueryBuilderList_ExpandNested_Top_Success() { - var uri = _odataQueryBuilder + var uri = _odataQueryBuilderDefault .For(s => s.ODataType) .ByKey(223123123) .Expand(f => @@ -139,13 +139,13 @@ public void ODataQueryBuilderList_ExpandNested_Top_Success() }) .ToUri(); - uri.OriginalString.Should().Be("http://mock/odata/ODataType(223123123)?$expand=ODataKindNew($select=IdKind;$orderby=EndDate desc;$top=1)"); + uri.Should().Be("http://mock/odata/ODataType(223123123)?$expand=ODataKindNew($select=IdKind;$orderby=EndDate desc;$top=1)"); } [Fact(DisplayName = "Expand nested Filter1 => Success")] public void ODataQueryBuilderKey_Expand_Nested_Filter1_Success() { - var uri = _odataQueryBuilder + var uri = _odataQueryBuilderDefault .For(s => s.ODataType) .ByKey(223123123) .Expand(f => @@ -157,13 +157,13 @@ public void ODataQueryBuilderKey_Expand_Nested_Filter1_Success() .Select(s => new { s.IdType, s.Sum }) .ToUri(); - uri.OriginalString.Should().Be($"http://mock/odata/ODataType(223123123)?$expand=ODataKind($filter=IdKind eq 1 and date(EndDate) eq {DateTime.Today:s}Z and IdKind in (1);$select=IdKind)&$select=IdType,Sum"); + uri.Should().Be($"http://mock/odata/ODataType(223123123)?$expand=ODataKind($filter=IdKind eq 1 and date(EndDate) eq {DateTime.Today:s}Z and IdKind in (1);$select=IdKind)&$select=IdType,Sum"); } [Fact(DisplayName = "Expand nested Filter2 => Success")] public void ODataQueryBuilderKey_Expand_Nested_Filter2_Success() { - var uri = _odataQueryBuilder + var uri = _odataQueryBuilderDefault .For(s => s.ODataType) .ByKey(223123123) .Expand(f => @@ -175,13 +175,13 @@ public void ODataQueryBuilderKey_Expand_Nested_Filter2_Success() .Select(s => new { s.IdType, s.Sum }) .ToUri(); - uri.OriginalString.Should().Be($"http://mock/odata/ODataType(223123123)?$expand=ODataKind($filter=IdKind eq 1 and date(EndDate) eq {DateTime.Today:s}Z;$select=IdKind)&$select=IdType,Sum"); + uri.Should().Be($"http://mock/odata/ODataType(223123123)?$expand=ODataKind($filter=IdKind eq 1 and date(EndDate) eq {DateTime.Today:s}Z;$select=IdKind)&$select=IdType,Sum"); } [Fact(DisplayName = "Expand nested Filter3 => Success")] public void ODataQueryBuilderKey_Expand_Nested_Filter3_Success() { - var uri = _odataQueryBuilder + var uri = _odataQueryBuilderDefault .For(s => s.ODataType) .ByKey(223123123) .Expand(f => @@ -193,13 +193,13 @@ public void ODataQueryBuilderKey_Expand_Nested_Filter3_Success() .Select(s => new { s.IdType, s.Sum }) .ToUri(); - uri.OriginalString.Should().Be($"http://mock/odata/ODataType(223123123)?$expand=ODataKind($filter=IdKind eq 1;$select=IdKind)&$select=IdType,Sum"); + uri.Should().Be($"http://mock/odata/ODataType(223123123)?$expand=ODataKind($filter=IdKind eq 1;$select=IdKind)&$select=IdType,Sum"); } [Fact(DisplayName = "ToDicionary => Success")] public void ToDicionaryTest() { - var uri = _odataQueryBuilder + var uri = _odataQueryBuilderDefault .For(s => s.ODataType) .ByKey("223123123") .Expand(s => s.ODataKind)