From badfe8d8ef266bfb633630dd3f5c1c9e5e8ca2e6 Mon Sep 17 00:00:00 2001 From: Pijus Kamandulis Date: Thu, 7 Sep 2023 16:18:13 +0000 Subject: [PATCH] Implemented 'ODataPropertyName' attribute --- .../Attributes/ODataPropertyNameAttribute.cs | 15 ++++ .../Visitors/ODataExpressionVisitor.cs | 15 +++- .../OData.QueryBuilder.Fakes.csproj | 4 ++ .../ODataKindEntity.cs | 4 ++ .../ODataQueryCollectionTest.cs | 70 ++++++++++--------- 5 files changed, 71 insertions(+), 37 deletions(-) create mode 100644 src/OData.QueryBuilder/Attributes/ODataPropertyNameAttribute.cs diff --git a/src/OData.QueryBuilder/Attributes/ODataPropertyNameAttribute.cs b/src/OData.QueryBuilder/Attributes/ODataPropertyNameAttribute.cs new file mode 100644 index 00000000..655875fc --- /dev/null +++ b/src/OData.QueryBuilder/Attributes/ODataPropertyNameAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace OData.QueryBuilder.Attributes +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] + public class ODataPropertyNameAttribute : Attribute + { + public string Name; + + public ODataPropertyNameAttribute(string name) + { + Name = name; + } + } +} diff --git a/src/OData.QueryBuilder/Expressions/Visitors/ODataExpressionVisitor.cs b/src/OData.QueryBuilder/Expressions/Visitors/ODataExpressionVisitor.cs index 2e262318..9a64e303 100644 --- a/src/OData.QueryBuilder/Expressions/Visitors/ODataExpressionVisitor.cs +++ b/src/OData.QueryBuilder/Expressions/Visitors/ODataExpressionVisitor.cs @@ -2,6 +2,8 @@ using OData.QueryBuilder.Extensions; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; +using System.Reflection; +using OData.QueryBuilder.Attributes; namespace OData.QueryBuilder.Expressions.Visitors { @@ -53,7 +55,7 @@ protected virtual string VisitMethodCallExpression(LambdaExpression topExpressio if (genericMethodType != memberExpression.Type) throw new ArgumentException( $"The type '{genericMethodType.FullName}' specified when calling 'ODataProperty.FromPath(\"{propertyPath}\")' does not match the expected type '{memberExpression.Type.FullName}' defined by the model."); - + return VisitMemberExpression(topExpression, memberExpression); } } @@ -78,15 +80,22 @@ protected virtual string VisitMemberExpression(LambdaExpression topExpression, M { var memberName = VisitExpression(topExpression, memberExpression.Expression); + var reflectedMemberName = memberExpression.Member.Name; + var propertyNameAttribute = memberExpression.Member.GetCustomAttribute(); + if (propertyNameAttribute != null) + { + reflectedMemberName = propertyNameAttribute.Name; + } + if (string.IsNullOrEmpty(memberName)) { - return memberExpression.Member.Name; + return reflectedMemberName; } return memberExpression.Member.DeclaringType.IsNullableType() ? memberName : - $"{memberName}/{memberExpression.Member.Name}"; + $"{memberName}/{reflectedMemberName}"; } public virtual string ToQuery(LambdaExpression expression) => diff --git a/test/OData.QueryBuilder.Fakes/OData.QueryBuilder.Fakes.csproj b/test/OData.QueryBuilder.Fakes/OData.QueryBuilder.Fakes.csproj index 9f5c4f4a..6466b647 100644 --- a/test/OData.QueryBuilder.Fakes/OData.QueryBuilder.Fakes.csproj +++ b/test/OData.QueryBuilder.Fakes/OData.QueryBuilder.Fakes.csproj @@ -4,4 +4,8 @@ netstandard2.0 + + + + diff --git a/test/OData.QueryBuilder.Fakes/ODataKindEntity.cs b/test/OData.QueryBuilder.Fakes/ODataKindEntity.cs index 19decf28..0190095c 100644 --- a/test/OData.QueryBuilder.Fakes/ODataKindEntity.cs +++ b/test/OData.QueryBuilder.Fakes/ODataKindEntity.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using OData.QueryBuilder.Attributes; namespace OData.QueryBuilder.Fakes { @@ -23,5 +24,8 @@ public class ODataKindEntity public long[] SequenceLongArray { get; set; } public ColorEnum Color { get; set; } + + [ODataPropertyName("customName")] + public string CustomNamedProperty { get; set; } } } diff --git a/test/OData.QueryBuilder.Test/ODataQueryCollectionTest.cs b/test/OData.QueryBuilder.Test/ODataQueryCollectionTest.cs index 3f32f5e4..a33068df 100644 --- a/test/OData.QueryBuilder.Test/ODataQueryCollectionTest.cs +++ b/test/OData.QueryBuilder.Test/ODataQueryCollectionTest.cs @@ -45,8 +45,8 @@ public void ODataQueryBuilderList_Expand_DynamicProperty_Success() .ToUri(); uri.Should().Be("http://mock/odata/ODataType?$expand=ODataKind"); - } - + } + [Fact(DisplayName = "Select simple => Success")] public void ODataQueryBuilderList_Select_Simple_Success() { @@ -69,8 +69,8 @@ public void ODataQueryBuilderList_Select_DynamicProperty_Success() .ToUri(); uri.Should().Be("http://mock/odata/ODataType?$select=IdType"); - } - + } + [Fact(DisplayName = "OrderBy simple => Success")] public void ODataQueryBuilderList_OrderBy_Simple_Success() { @@ -93,8 +93,8 @@ public void ODataQueryBuilderList_OrderBy_DynamicProperty_Success() .ToUri(); uri.Should().Be("http://mock/odata/ODataType?$orderby=IdType asc"); - } - + } + [Fact(DisplayName = "Filter orderBy multiple sort => Success")] public void ODataQueryBuilderList_Filter_OrderBy_Multiple_Sort_Success() { @@ -354,8 +354,8 @@ public void ODataQueryBuilderList_Filter_With_ReplaceCharacters_KeyValuePairs_Ar [Fact(DisplayName = "Filter variable dynamic property int=> Success")] public void ODataQueryBuilderList_Filter_Simple_Variable_DynamicProperty_Success() { - string propertyName = "ODataKind.ODataCode.IdCode"; - + string propertyName = "ODataKind.ODataCode.IdCode"; + var uri = _odataQueryBuilderDefault .For(s => s.ODataType) .ByList() @@ -376,8 +376,8 @@ public void ODataQueryBuilderList_Filter_Simple_Variable_DynamicProperty_WrongTy .ByList() .Filter((s, f, _) => ODataProperty.FromPath(propertyName) == "test") .ToUri()).Should().Throw(); - } - + } + [Fact(DisplayName = "Filter const dynamic property int=> Success")] public void ODataQueryBuilderList_Filter_Simple_Const_DynamicProperty_Success() { @@ -388,8 +388,8 @@ public void ODataQueryBuilderList_Filter_Simple_Const_DynamicProperty_Success() .ToUri(); uri.Should().Be("http://mock/odata/ODataType?$filter=ODataKind/ODataCode/IdCode ge 3"); - } - + } + [Fact(DisplayName = "Filter simple const int=> Success")] public void ODataQueryBuilderList_Filter_Simple_Const_Int_Success() { @@ -576,6 +576,7 @@ public void ODataQueryBuilderList_Function_Date_Success() && s.ODataKind.OpenDate == currentDateToday && s.ODataKind.OpenDate == DateTime.Today && s.ODataKind.OpenDate == new DateTimeOffset() + && s.ODataKind.CustomNamedProperty == "test" && s.Open == new DateTime() && f.Date(s.Open) == DateTime.Today && f.Date(s.Open) == DateTimeOffset.Now @@ -592,6 +593,7 @@ public void ODataQueryBuilderList_Function_Date_Success() $"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 ODataKind/customName eq 'test' " + $"and Open eq {new DateTime():s}Z " + $"and date(Open) eq {DateTime.Today:s}Z " + $"and date(Open) eq {DateTimeOffset.Now:s}Z " + @@ -1560,39 +1562,39 @@ public void ODataQueryBuilder_Function_Cast_Skip_Exception(string value) .ToUri(); uri.Should().Be("http://mock/odata/ODataType?$filter=contains(,'55')"); - } - + } + [Fact(DisplayName = "UseCorrectDateTimeFormat Convert => Success")] public void ODataQueryBuilderList_UseCorrectDatetimeFormat_Convert_Success() - { - var builder = new ODataQueryBuilder( - _commonFixture.BaseUri, - new ODataQueryBuilderOptions { UseCorrectDateTimeFormat = true }); - - var dateTimeLocal = new DateTime( - year: 2023, month: 04, day: 07, hour: 12, minute: 30, second: 20, kind: DateTimeKind.Local); - var dateTimeUtc = new DateTime( - year: 2023, month: 04, day: 07, hour: 12, minute: 30, second: 20, kind: DateTimeKind.Utc); - var dateTimeOffset = new DateTimeOffset( - year: 2023, month: 04, day: 07, hour: 12, minute: 30, second: 20, offset: TimeSpan.FromHours(+7)); - var dateTimeOffset2 = new DateTimeOffset( - year: 2023, month: 04, day: 07, hour: 12, minute: 30, second: 20, offset: TimeSpan.FromHours(-7)); + { + var builder = new ODataQueryBuilder( + _commonFixture.BaseUri, + new ODataQueryBuilderOptions { UseCorrectDateTimeFormat = true }); + + var dateTimeLocal = new DateTime( + year: 2023, month: 04, day: 07, hour: 12, minute: 30, second: 20, kind: DateTimeKind.Local); + var dateTimeUtc = new DateTime( + year: 2023, month: 04, day: 07, hour: 12, minute: 30, second: 20, kind: DateTimeKind.Utc); + var dateTimeOffset = new DateTimeOffset( + year: 2023, month: 04, day: 07, hour: 12, minute: 30, second: 20, offset: TimeSpan.FromHours(+7)); + var dateTimeOffset2 = new DateTimeOffset( + year: 2023, month: 04, day: 07, hour: 12, minute: 30, second: 20, offset: TimeSpan.FromHours(-7)); var nowOffset = $"{DateTimeOffset.Now:zzz}".Replace("+", "%2B"); var uri = builder .For(s => s.ODataType) .ByList() - .Filter((o) => - o.DateTime == dateTimeLocal - && o.DateTime == dateTimeUtc - && o.DateTime == dateTimeOffset + .Filter((o) => + o.DateTime == dateTimeLocal + && o.DateTime == dateTimeUtc + && o.DateTime == dateTimeOffset && o.DateTime == dateTimeOffset2) .ToUri(); uri.Should().Be($"http://mock/odata/ODataType?$filter=" + - $"DateTime eq 2023-04-07T12:30:20{nowOffset} and " + - $"DateTime eq 2023-04-07T12:30:20%2B00:00 and " + - $"DateTime eq 2023-04-07T12:30:20%2B07:00 and " + + $"DateTime eq 2023-04-07T12:30:20{nowOffset} and " + + $"DateTime eq 2023-04-07T12:30:20%2B00:00 and " + + $"DateTime eq 2023-04-07T12:30:20%2B07:00 and " + $"DateTime eq 2023-04-07T12:30:20-07:00"); } }