< Summary - Code Coverage

Information
Class: Plainquire.Filter.EntityFilterExtensions
Assembly: Plainquire.Filter
File(s): /home/runner/work/plainquire/plainquire/Plainquire.Filter/Plainquire.Filter/Extensions/EntityFilterExtensions.cs
Tag: 74_23635074410
Line coverage
97%
Covered lines: 105
Uncovered lines: 3
Coverable lines: 108
Total lines: 309
Line coverage: 97.2%
Branch coverage
90%
Covered branches: 56
Total branches: 62
Branch coverage: 90.3%
Method coverage
100%
Covered methods: 13
Total methods: 13
Method coverage: 100%

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
Add(...)100%22100%
Add(...)100%44100%
Add(...)100%11100%
Add(...)100%1010100%
Replace(...)100%22100%
Replace(...)100%44100%
Replace(...)100%11100%
Replace(...)100%1010100%
Cast(...)100%44100%
ApplyFromSyntax(...)100%11100%
ApplyFromSyntax(...)78.57%141493.33%
ToQueryString(...)100%11100%
ToQueryParams(...)75%121289.47%

File(s)

/home/runner/work/plainquire/plainquire/Plainquire.Filter/Plainquire.Filter/Extensions/EntityFilterExtensions.cs

#LineLine coverage
 1using Plainquire.Filter.Abstractions;
 2using System;
 3using System.Collections.Generic;
 4using System.Linq;
 5using System.Linq.Expressions;
 6using System.Reflection;
 7using System.Text.Json;
 8using System.Web;
 9
 10namespace Plainquire.Filter;
 11
 12/// <summary>
 13/// Extension methods for <see cref="EntityFilter{TEntity}"/>
 14/// </summary>
 15public static class EntityFilterExtensions
 16{
 17    /// <summary>
 18    /// Adds a filter for the given property. Existing filters for the same property are preserved.
 19    /// </summary>
 20    /// <typeparam name="TEntity">The type of the entity.</typeparam>
 21    /// <typeparam name="TProperty">The type of the property.</typeparam>
 22    /// <param name="entityFilter">The entity filter.</param>
 23    /// <param name="property">The property to filter.</param>
 24    /// <param name="filterSyntax">Description of the filter using micro syntax.</param>
 25    public static EntityFilter<TEntity> Add<TEntity, TProperty>(this EntityFilter<TEntity> entityFilter, Expression<Func
 26    {
 7427        if (filterSyntax == null)
 128            return entityFilter;
 29
 7330        var filters = ValueFilterFactory.Create(filterSyntax, entityFilter.Configuration);
 7331        entityFilter.Add(property, filters);
 7232        return entityFilter;
 33    }
 34
 35    /// <summary>
 36    /// Adds a filter for the given property using the default filter operator. Existing filters for the same property a
 37    /// </summary>
 38    /// <typeparam name="TEntity">The type of the entity.</typeparam>
 39    /// <typeparam name="TProperty">The type of the property.</typeparam>
 40    /// <typeparam name="TValue">The type of the value.</typeparam>
 41    /// <param name="entityFilter">The entity filter.</param>
 42    /// <param name="property">The property to filter.</param>
 43    /// <param name="values">The values to filter for. Multiple values are combined with conditional OR.</param>
 44    public static EntityFilter<TEntity> Add<TEntity, TProperty, TValue>(this EntityFilter<TEntity> entityFilter, Express
 45    {
 2546        if (values == null || values.Length == 0)
 447            return entityFilter;
 48
 2149        var valueFilters = values
 2150            .Select(value => ValueFilter.Create(
 2151                FilterOperator.Default,
 2152                value,
 2153                entityFilter.Configuration
 2154            ))
 2155            .ToArray();
 56
 2057        return entityFilter.Add(property, valueFilters);
 58    }
 59
 60    /// <summary>
 61    /// Adds a filter for the given property. Existing filters for the same property are preserved.
 62    /// </summary>
 63    /// <typeparam name="TEntity">The type of the entity.</typeparam>
 64    /// <typeparam name="TProperty">The type of the property.</typeparam>
 65    /// <param name="entityFilter">The entity filter.</param>
 66    /// <param name="property">The property to filter.</param>
 67    /// <param name="filterOperator">The filter operator to use.</param>
 68    public static EntityFilter<TEntity> Add<TEntity, TProperty>(this EntityFilter<TEntity> entityFilter, Expression<Func
 669        => entityFilter.Add(property, ValueFilter.Create(filterOperator, entityFilter.Configuration));
 70
 71    /// <summary>
 72    /// Adds a filter for the given property. Existing filters for the same property are preserved.
 73    /// </summary>
 74    /// <typeparam name="TEntity">The type of the entity.</typeparam>
 75    /// <typeparam name="TProperty">The type of the property.</typeparam>
 76    /// <typeparam name="TValue">The type of the value.</typeparam>
 77    /// <param name="entityFilter">The entity filter.</param>
 78    /// <param name="property">The property to filter.</param>
 79    /// <param name="filterOperator">The filter operator to use.</param>
 80    /// <param name="values">The values to filter for. Multiple values are combined with conditional OR.</param>
 81    public static EntityFilter<TEntity> Add<TEntity, TProperty, TValue>(this EntityFilter<TEntity> entityFilter, Express
 82    {
 1583        var isNullableFilterOperator = filterOperator is FilterOperator.IsNull or FilterOperator.NotNull;
 1584        if ((values == null || values.Length == 0) && !isNullableFilterOperator)
 285            return entityFilter;
 86
 1387        var valueFilters = values?
 1388            .Select(value => ValueFilter.Create(
 1389                filterOperator,
 1390                value,
 1391                entityFilter.Configuration
 1392            ))
 1393            .ToArray();
 94
 1395        entityFilter.Add(property, valueFilters);
 1396        return entityFilter;
 97    }
 98
 99    /// <summary>
 100    /// Replaces the filter for the given property. Existing filters for the same property are removed.
 101    /// </summary>
 102    /// <typeparam name="TEntity">The type of the entity.</typeparam>
 103    /// <typeparam name="TProperty">The type of the property.</typeparam>
 104    /// <param name="entityFilter">The entity filter.</param>
 105    /// <param name="property">The property to filter.</param>
 106    /// <param name="filterSyntax">Description of the filter using micro syntax.</param>
 107    public static EntityFilter<TEntity> Replace<TEntity, TProperty>(this EntityFilter<TEntity> entityFilter, Expression<
 108    {
 6504109        if (filterSyntax == null)
 1110            return entityFilter.Remove(property);
 111
 6503112        var valueFilters = ValueFilterFactory.Create(filterSyntax, entityFilter.Configuration);
 6503113        entityFilter.Replace(property, valueFilters);
 6503114        return entityFilter;
 115    }
 116
 117    /// <summary>
 118    /// Replaces the filter for the given property using the default filter operator. Existing filters for the same prop
 119    /// </summary>
 120    /// <typeparam name="TEntity">The type of the entity.</typeparam>
 121    /// <typeparam name="TProperty">The type of the t property.</typeparam>
 122    /// <typeparam name="TValue">The type of the t value.</typeparam>
 123    /// <param name="entityFilter">The entity filter.</param>
 124    /// <param name="property">The property to filter.</param>
 125    /// <param name="values">The values to filter for. Multiple values are combined with conditional OR.</param>
 126    public static EntityFilter<TEntity> Replace<TEntity, TProperty, TValue>(this EntityFilter<TEntity> entityFilter, Exp
 127    {
 4128        if (values == null || values.Length == 0)
 2129            return entityFilter.Remove(property);
 130
 2131        var valueFilters = values
 2132            .Select(value => ValueFilter.Create(
 2133                FilterOperator.Default,
 2134                value,
 2135                entityFilter.Configuration
 2136                ))
 2137            .ToArray();
 138
 2139        return entityFilter.Replace(property, valueFilters);
 140    }
 141
 142    /// <summary>
 143    /// Replaces the filter for the given property. Existing filters for the same property are removed.
 144    /// </summary>
 145    /// <typeparam name="TEntity">The type of the entity.</typeparam>
 146    /// <typeparam name="TProperty">The type of the property.</typeparam>
 147    /// <param name="entityFilter">The entity filter.</param>
 148    /// <param name="property">The property to filter.</param>
 149    /// <param name="filterOperator">The filter operator to use.</param>
 150    public static EntityFilter<TEntity> Replace<TEntity, TProperty>(this EntityFilter<TEntity> entityFilter, Expression<
 6151        => entityFilter.Replace(property, ValueFilter.Create(filterOperator, entityFilter.Configuration));
 152
 153    /// <summary>
 154    /// Replaces the filter for the given property. Existing filters for the same property are removed.
 155    /// </summary>
 156    /// <typeparam name="TEntity">The type of the entity.</typeparam>
 157    /// <typeparam name="TProperty">The type of the property.</typeparam>
 158    /// <typeparam name="TValue">The type of the value.</typeparam>
 159    /// <param name="entityFilter">The entity filter.</param>
 160    /// <param name="property">The property to filter.</param>
 161    /// <param name="filterOperator">The filter operator to use.</param>
 162    /// <param name="values">The values to filter for. Multiple values are combined with conditional OR.</param>
 163    public static EntityFilter<TEntity> Replace<TEntity, TProperty, TValue>(this EntityFilter<TEntity> entityFilter, Exp
 164    {
 3549165        var isNullableFilterOperator = filterOperator is FilterOperator.IsNull or FilterOperator.NotNull;
 3549166        if ((values == null || values.Length == 0) && !isNullableFilterOperator)
 2167            return entityFilter.Remove(property);
 168
 3547169        var valueFilters = values?
 3547170            .Select(value => ValueFilter.Create(
 3547171                filterOperator,
 3547172                value,
 3547173                entityFilter.Configuration
 3547174                ))
 3547175            .ToArray();
 176
 3546177        entityFilter.Replace(property, valueFilters);
 3546178        return entityFilter;
 179    }
 180
 181    /// <summary>
 182    /// Casts this filter to a different type (by creating a deep clone).
 183    /// Filtered properties are matched by type (check if assignable) and name (case-sensitive).
 184    /// </summary>
 185    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 186    /// <typeparam name="TDestination">The type of the destination entity to filter.</typeparam>
 187    public static EntityFilter<TDestination> Cast<TEntity, TDestination>(this EntityFilter<TEntity> filter)
 188    {
 8189        var castFilter = JsonSerializer.Deserialize<EntityFilter<TDestination>>(JsonSerializer.Serialize(filter))!;
 8190        var sourceProperties = typeof(TEntity).GetProperties();
 8191        var destinationProperties = typeof(TDestination).GetProperties().ToList();
 192
 80193        foreach (var sourceProperty in sourceProperties)
 194        {
 32195            var sameDestinationPropertyExists = destinationProperties
 32196                .Exists(x =>
 32197                            x.Name.EqualsOrdinal(sourceProperty.Name) &&
 32198                            x.PropertyType.IsAssignableFrom(sourceProperty.PropertyType)
 32199                );
 200
 32201            if (!sameDestinationPropertyExists)
 202            {
 9203                castFilter.PropertyFilters.RemoveAll(x => x.PropertyName.EqualsOrdinal(sourceProperty.Name));
 9204                castFilter.NestedFilters.RemoveAll(x => x.PropertyName.EqualsOrdinal(sourceProperty.Name));
 205            }
 206        }
 207
 8208        return castFilter;
 209    }
 210
 211    /// <summary>
 212    /// Applies given filters to <see cref="EntityFilter{TEntity}"/>.
 213    /// </summary>
 214    /// <typeparam name="TEntity">The type of the entity being filtered.</typeparam>
 215    /// <typeparam name="TValue">The type of the filter value.</typeparam>
 216    /// <param name="entityFilter">The <see cref="EntityFilter{TEntity}"/> to apply filters to.</param>
 217    /// <param name="filters">Property names mapped to their filter expressions in micro-syntax (e.g., {"customer", "~Jo
 218    /// <returns></returns>
 219    public static void ApplyFromSyntax<TEntity, TValue>(this EntityFilter<TEntity> entityFilter, IEnumerable<KeyValuePai
 220    {
 1221        var filteredType = typeof(TEntity);
 1222        entityFilter.ApplyFromSyntax(filteredType, filters);
 1223    }
 224
 225    /// <summary>
 226    /// Apply given filters to <see cref="EntityFilter{TEntity}"/>.
 227    /// </summary>
 228    /// <typeparam name="TValue">The type of the filter value (string or string?).</typeparam>
 229    /// <param name="entityFilter">The <see cref="EntityFilter"/> to apply filters to.</param>
 230    /// <param name="filteredType">The type of the entity being filtered.</param>
 231    /// <param name="filters">Property names mapped to their filter expressions in micro-syntax (e.g., {"customer", "~Jo
 232    /// <returns></returns>
 233    public static void ApplyFromSyntax<TValue>(this EntityFilter entityFilter, Type filteredType, IEnumerable<KeyValuePa
 234    {
 13235        var filterList = filters?.ToList();
 13236        if (filterList == null || filterList.Count == 0)
 0237            return;
 238
 13239        var filterableProperties = filteredType.GetFilterableProperties().ToList();
 13240        var entityFilterAttribute = filteredType.GetCustomAttribute<EntityFilterAttribute>();
 241
 104242        foreach (var property in filterableProperties)
 243        {
 39244            var parameterName = property.GetFilterParameterName(entityFilterAttribute?.Prefix);
 39245            var parameterValues = filterList
 39246                .Where(filter => filter.Key.Equals(parameterName, StringComparison.InvariantCultureIgnoreCase))
 39247                .Select(filter => filter.Value?.ToString())
 39248                .WhereNotNull()
 39249                .ToList();
 250
 106251            foreach (var filterSyntax in parameterValues)
 14252                entityFilter.PropertyFilters.Add(new PropertyFilter(property.Name, ValueFilterFactory.Create(filterSynta
 253        }
 13254    }
 255
 256    /// <summary>
 257    /// Converts an entity filter to it's corresponding HTTP query string.
 258    /// </summary>
 259    /// <typeparam name="TEntity">Type of the entity.</typeparam>
 260    /// <param name="entityFilter">The filter to act on.</param>
 261    public static string ToQueryString<TEntity>(this EntityFilter<TEntity>? entityFilter)
 262    {
 2263        var queryParams = ToQueryParams((EntityFilter?)entityFilter);
 2264        return string.Join('&', queryParams);
 265    }
 266
 267    /// <inheritdoc cref="ToQueryString{TEntity}(EntityFilter{TEntity}?)"/>
 268    [Obsolete($"Use {nameof(ToQueryString)} instead.")]
 269    public static string ToQueryParams<TEntity>(this EntityFilter<TEntity>? entityFilter)
 270        => entityFilter.ToQueryString();
 271
 272    /// <summary>
 273    /// Converts an entity filter to it's corresponding HTTP query parameters.
 274    /// </summary>
 275    /// <param name="entityFilter">The filter to act on.</param>
 276    private static List<string> ToQueryParams(this EntityFilter? entityFilter)
 277    {
 3278        if (entityFilter == null)
 0279            return [];
 280
 3281        var entityFilterType = entityFilter.GetType();
 3282        if (!entityFilterType.IsGenericType)
 0283            throw new ArgumentException($"Given filter must be a generic {nameof(EntityFilter)}", nameof(entityFilter));
 284
 3285        var filteredType = entityFilterType.GenericTypeArguments[0];
 3286        var filterableProperties = filteredType.GetFilterableProperties().ToList();
 3287        var entityFilterAttribute = filteredType.GetCustomAttribute<EntityFilterAttribute>();
 288
 3289        var queryParams = new List<string>();
 22290        foreach (var property in filterableProperties)
 291        {
 8292            var parameterName = HttpUtility.UrlEncode(property.GetFilterParameterName(entityFilterAttribute?.Prefix));
 8293            var propertyFilters = entityFilter.PropertyFilters.Where(x => x.PropertyName.EqualsOrdinal(property.Name));
 28294            foreach (var filter in propertyFilters)
 295            {
 6296                var values = string.Join(',', filter.ValueFilters.Select(v => HttpUtility.UrlEncode(v.ToString())));
 6297                queryParams.Add($"{parameterName}={values}");
 298            }
 299        }
 300
 8301        foreach (var nestedFilter in entityFilter.NestedFilters)
 302        {
 1303            var nestedQueryParams = nestedFilter.EntityFilter.ToQueryParams();
 1304            queryParams.AddRange(nestedQueryParams);
 305        }
 306
 3307        return queryParams;
 308    }
 309}

Methods/Properties

Add(Plainquire.Filter.EntityFilter`1<TEntity>,System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>,System.String)
Add(Plainquire.Filter.EntityFilter`1<TEntity>,System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>,TValue[])
Add(Plainquire.Filter.EntityFilter`1<TEntity>,System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>,Plainquire.Filter.Abstractions.FilterOperator)
Add(Plainquire.Filter.EntityFilter`1<TEntity>,System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>,Plainquire.Filter.Abstractions.FilterOperator,TValue[])
Replace(Plainquire.Filter.EntityFilter`1<TEntity>,System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>,System.String)
Replace(Plainquire.Filter.EntityFilter`1<TEntity>,System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>,TValue[])
Replace(Plainquire.Filter.EntityFilter`1<TEntity>,System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>,Plainquire.Filter.Abstractions.FilterOperator)
Replace(Plainquire.Filter.EntityFilter`1<TEntity>,System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>,Plainquire.Filter.Abstractions.FilterOperator,TValue[])
Cast(Plainquire.Filter.EntityFilter`1<TEntity>)
ApplyFromSyntax(Plainquire.Filter.EntityFilter`1<TEntity>,System.Collections.Generic.IEnumerable`1<System.Collections.Generic.KeyValuePair`2<System.String,TValue>>)
ApplyFromSyntax(Plainquire.Filter.EntityFilter,System.Type,System.Collections.Generic.IEnumerable`1<System.Collections.Generic.KeyValuePair`2<System.String,TValue>>)
ToQueryString(Plainquire.Filter.EntityFilter`1<TEntity>)
ToQueryParams(Plainquire.Filter.EntityFilter)