From 19a7cef8481fddbde61b35f7dc7e9d2df6b120bc Mon Sep 17 00:00:00 2001 From: lice Date: Mon, 29 Jan 2024 13:33:53 +0100 Subject: [PATCH] Added support for Filter when using ByKey --- .../Query/IODataQueryKey.cs | 5 +++ .../AddressingEntities/Query/ODataQueryKey.cs | 41 +++++++++++++++++++ .../ODataQueryKeyTest.cs | 35 ++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/IODataQueryKey.cs b/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/IODataQueryKey.cs index 55bc838d..a7df16fe 100644 --- a/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/IODataQueryKey.cs +++ b/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/IODataQueryKey.cs @@ -1,11 +1,16 @@ using OData.QueryBuilder.Conventions.AddressingEntities.Options; using System; using System.Linq.Expressions; +using OData.QueryBuilder.Conventions.Functions; +using OData.QueryBuilder.Conventions.Operators; namespace OData.QueryBuilder.Conventions.AddressingEntities.Query { public interface IODataQueryKey : IODataOptionKey, TEntity>, IODataQuery { public IAddressingEntries For(Expression> resource); + IODataQueryKey Filter(Expression> filter, bool useParenthesis = false); + IODataQueryKey Filter(Expression> filter, bool useParenthesis = false); + IODataQueryKey Filter(Expression> filter, bool useParenthesis = false); } } diff --git a/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/ODataQueryKey.cs b/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/ODataQueryKey.cs index 90fe2842..ca3671e4 100644 --- a/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/ODataQueryKey.cs +++ b/src/OData.QueryBuilder/Conventions/AddressingEntities/Query/ODataQueryKey.cs @@ -7,17 +7,21 @@ using System; using System.Linq.Expressions; using System.Text; +using OData.QueryBuilder.Conventions.Functions; +using OData.QueryBuilder.Conventions.Operators; namespace OData.QueryBuilder.Conventions.AddressingEntities.Query { internal class ODataQueryKey : ODataQuery, IODataQueryKey { private bool _hasMultyExpands; + private bool _hasMultyFilters; public ODataQueryKey(StringBuilder stringBuilder, ODataQueryBuilderOptions odataQueryBuilderOptions) : base(stringBuilder, odataQueryBuilderOptions) { _hasMultyExpands = false; + _hasMultyFilters = false; } public IAddressingEntries For(Expression> resource) @@ -51,6 +55,27 @@ public IODataQueryKey Select(Expression> select) return this; } + + public IODataQueryKey Filter(Expression> filter, bool useParenthesis = false) + { + var query = new ODataOptionFilterExpressionVisitor(_odataQueryBuilderOptions).ToQuery(filter, useParenthesis); + + return Filter(query); + } + + public IODataQueryKey Filter(Expression> filter, bool useParenthesis = false) + { + var query = new ODataOptionFilterExpressionVisitor(_odataQueryBuilderOptions).ToQuery(filter, useParenthesis); + + return Filter(query); + } + + public IODataQueryKey Filter(Expression> filter, bool useParenthesis = false) + { + var query = new ODataOptionFilterExpressionVisitor(_odataQueryBuilderOptions).ToQuery(filter, useParenthesis); + + return Filter(query); + } private IODataQueryKey Expand(T query) where T : class { @@ -67,5 +92,21 @@ private IODataQueryKey Expand(T query) where T : class return this; } + + private IODataQueryKey Filter(string query) + { + if (_hasMultyFilters) + { + _stringBuilder.Merge(ODataOptionNames.Filter, QuerySeparators.Main, $" {ODataLogicalOperations.And} {query}"); + } + else + { + _stringBuilder.Append($"{ODataOptionNames.Filter}{QuerySeparators.EqualSign}{query}{QuerySeparators.Main}"); + } + + _hasMultyFilters = true; + + return this; + } } } diff --git a/test/OData.QueryBuilder.Test/ODataQueryKeyTest.cs b/test/OData.QueryBuilder.Test/ODataQueryKeyTest.cs index 427dfb74..73d28ecf 100644 --- a/test/OData.QueryBuilder.Test/ODataQueryKeyTest.cs +++ b/test/OData.QueryBuilder.Test/ODataQueryKeyTest.cs @@ -167,6 +167,41 @@ public void ODataQueryBuilderKey_Expand_Union_Success() "&" + "$select=IdRule"); } + + [Fact(DisplayName = "Filter not bool => Success")] + public void ODataQueryBuilderKey_Filter_Not__Bool_Success() + { + var uri = _odataQueryBuilderDefault + .For(s => s.ODataType) + .ByKey(123) + .Filter(s => s.IsActive && !(bool)s.IsOpen) + .ToUri(); + + uri.Should().Be("http://mock/odata/ODataType(123)?$filter=IsActive and not IsOpen"); + } + + [Fact(DisplayName = "Filter string with ReplaceCharacters => Success")] + public void ODataQueryBuilderKey_Filter_string_with_ReplaceCharacters_Success() + { + var dictionary = new Dictionary() + { + { "%", "%25" }, + { "/", "%2f" }, + { "?", "%3f" }, + { "#", "%23" }, + { "&", "%26" } + }; + + var constValue = "3 & 4 / 7 ? 8 % 9 # 1"; + + var uri = _odataQueryBuilderDefault + .For(s => s.ODataType) + .ByKey(123) + .Filter((s, f) => s.ODataKind.ODataCode.Code == f.ReplaceCharacters(constValue, dictionary)) + .ToUri(); + + uri.Should().Be("http://mock/odata/ODataType(123)?$filter=ODataKind/ODataCode/Code eq '3 %26 4 %2f 7 %3f 8 %25 9 %23 1'"); + } [Fact(DisplayName = "ToDicionary => Success")] public void ToDicionaryTest()