From b7296e73bd86dae7956d0447949c4d4e36dbf482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20Pavlovi=C4=87?= Date: Wed, 21 Feb 2024 23:13:47 +0100 Subject: [PATCH 1/9] Initial effort for async --- .../IOozeServiceCollectionBuilder.cs | 6 + .../OozeServiceCollectionBuilder.cs | 37 ++- .../Filters/Async/AsyncFilterBuilder.cs | 276 ++++++++++++++++++ .../Filters/Async/AsyncFilterDefinition.cs | 9 + .../Filters/Async/AsyncFilterHandler.cs | 39 +++ src/Ooze.Typed/Filters/Async/AsyncFilters.cs | 16 + .../Filters/Async/IAsyncFilterBuilder.cs | 22 ++ .../Filters/Async/IAsyncFilterHandler.cs | 8 + .../Filters/Async/IAsyncFilterProvider.cs | 17 ++ src/Ooze.Typed/Filters/FilterBuilder.cs | 2 +- src/Ooze.Typed/Filters/FilterDefinition.cs | 2 +- ...{OozeFilterHandler.cs => FilterHandler.cs} | 21 +- src/Ooze.Typed/Filters/Filters.cs | 2 +- src/Ooze.Typed/Filters/IFilterBuilder.cs | 172 +---------- ...OozeFilterHandler.cs => IFilterHandler.cs} | 4 +- ...zeFilterProvider.cs => IFilterProvider.cs} | 6 +- src/Ooze.Typed/Filters/IFilters.cs | 168 +++++++++++ src/Ooze.Typed/IOozeTypedAsyncResolver.cs | 109 +++++++ src/Ooze.Typed/OozeTypedAsyncResolver.cs | 180 ++++++++++++ src/Ooze.Typed/OozeTypedResolver.cs | 8 +- .../Sorters/Async/AsyncSortDefinition.cs | 10 + .../Sorters/Async/AsyncSorterBuilder.cs | 52 ++++ .../Sorters/Async/AsyncSorterHandler.cs | 78 +++++ src/Ooze.Typed/Sorters/Async/AsyncSorters.cs | 7 + .../Sorters/Async/IAsyncSorterBuilder.cs | 18 ++ .../Sorters/Async/IAsyncSorterHandler.cs | 8 + .../Sorters/Async/IAsyncSorterProvider.cs | 6 + src/Ooze.Typed/Sorters/ISorterBuilder.cs | 2 +- ...OozeSorterHandler.cs => ISorterHandler.cs} | 4 +- ...zeSorterProvider.cs => ISorterProvider.cs} | 6 +- src/Ooze.Typed/Sorters/SortDefinition.cs | 4 +- src/Ooze.Typed/Sorters/SorterBuilder.cs | 2 +- ...{OozeSorterHandler.cs => SorterHandler.cs} | 9 +- src/Ooze.Typed/Sorters/Sorters.cs | 2 +- 34 files changed, 1101 insertions(+), 211 deletions(-) create mode 100644 src/Ooze.Typed/Filters/Async/AsyncFilterBuilder.cs create mode 100644 src/Ooze.Typed/Filters/Async/AsyncFilterDefinition.cs create mode 100644 src/Ooze.Typed/Filters/Async/AsyncFilterHandler.cs create mode 100644 src/Ooze.Typed/Filters/Async/AsyncFilters.cs create mode 100644 src/Ooze.Typed/Filters/Async/IAsyncFilterBuilder.cs create mode 100644 src/Ooze.Typed/Filters/Async/IAsyncFilterHandler.cs create mode 100644 src/Ooze.Typed/Filters/Async/IAsyncFilterProvider.cs rename src/Ooze.Typed/Filters/{OozeFilterHandler.cs => FilterHandler.cs} (54%) rename src/Ooze.Typed/Filters/{IOozeFilterHandler.cs => IFilterHandler.cs} (67%) rename src/Ooze.Typed/Filters/{IOozeFilterProvider.cs => IFilterProvider.cs} (83%) create mode 100644 src/Ooze.Typed/Filters/IFilters.cs create mode 100644 src/Ooze.Typed/IOozeTypedAsyncResolver.cs create mode 100644 src/Ooze.Typed/OozeTypedAsyncResolver.cs create mode 100644 src/Ooze.Typed/Sorters/Async/AsyncSortDefinition.cs create mode 100644 src/Ooze.Typed/Sorters/Async/AsyncSorterBuilder.cs create mode 100644 src/Ooze.Typed/Sorters/Async/AsyncSorterHandler.cs create mode 100644 src/Ooze.Typed/Sorters/Async/AsyncSorters.cs create mode 100644 src/Ooze.Typed/Sorters/Async/IAsyncSorterBuilder.cs create mode 100644 src/Ooze.Typed/Sorters/Async/IAsyncSorterHandler.cs create mode 100644 src/Ooze.Typed/Sorters/Async/IAsyncSorterProvider.cs rename src/Ooze.Typed/Sorters/{IOozeSorterHandler.cs => ISorterHandler.cs} (69%) rename src/Ooze.Typed/Sorters/{IOozeSorterProvider.cs => ISorterProvider.cs} (83%) rename src/Ooze.Typed/Sorters/{OozeSorterHandler.cs => SorterHandler.cs} (87%) diff --git a/src/Ooze.Typed/Extensions/IOozeServiceCollectionBuilder.cs b/src/Ooze.Typed/Extensions/IOozeServiceCollectionBuilder.cs index 562e530..17f6062 100644 --- a/src/Ooze.Typed/Extensions/IOozeServiceCollectionBuilder.cs +++ b/src/Ooze.Typed/Extensions/IOozeServiceCollectionBuilder.cs @@ -14,4 +14,10 @@ public interface IOozeServiceCollectionBuilder /// Type of provider implementation /// Builder instance IOozeServiceCollectionBuilder Add(ServiceLifetime providerLifetime = ServiceLifetime.Singleton); + + /// + /// Enable usage of async resolvers and filter/sorter registrations + /// + /// Builder instance + IOozeServiceCollectionBuilder EnableAsyncResolvers(); } diff --git a/src/Ooze.Typed/Extensions/OozeServiceCollectionBuilder.cs b/src/Ooze.Typed/Extensions/OozeServiceCollectionBuilder.cs index 9fbaf42..6645ebf 100644 --- a/src/Ooze.Typed/Extensions/OozeServiceCollectionBuilder.cs +++ b/src/Ooze.Typed/Extensions/OozeServiceCollectionBuilder.cs @@ -1,15 +1,19 @@ using Microsoft.Extensions.DependencyInjection; using Ooze.Typed.Filters; +using Ooze.Typed.Filters.Async; using Ooze.Typed.Paging; using Ooze.Typed.Sorters; +using Ooze.Typed.Sorters.Async; namespace Ooze.Typed.Extensions; /// internal class OozeServiceCollectionBuilder(IServiceCollection services) : IOozeServiceCollectionBuilder { - private static readonly Type FilterProviderType = typeof(IOozeFilterProvider<,>); - private static readonly Type SorterProviderType = typeof(IOozeSorterProvider<,>); + private static readonly Type FilterProviderType = typeof(IFilterProvider<,>); + private static readonly Type SorterProviderType = typeof(ISorterProvider<,>); + private static readonly Type AsyncFilterProviderType = typeof(IAsyncFilterProvider<,>); + private static readonly Type AsyncSorterProviderType = typeof(IAsyncSorterProvider<,>); public IOozeServiceCollectionBuilder Add(ServiceLifetime providerLifetime = ServiceLifetime.Singleton) { @@ -19,8 +23,13 @@ public IOozeServiceCollectionBuilder Add(ServiceLifetime providerLife .ToList(); var filterProvider = implementedInterfaces.SingleOrDefault(@interface => CheckTypePredicate(@interface, FilterProviderType)); var sorterProvider = implementedInterfaces.SingleOrDefault(@interface => CheckTypePredicate(@interface, SorterProviderType)); + var asyncFilterProvider = implementedInterfaces.SingleOrDefault(@interface => CheckTypePredicate(@interface, AsyncFilterProviderType)); + var asyncSorterProvider = implementedInterfaces.SingleOrDefault(@interface => CheckTypePredicate(@interface, AsyncSorterProviderType)); - if (filterProvider is null && sorterProvider is null) + if (filterProvider is null + && sorterProvider is null + && asyncFilterProvider is null + && asyncSorterProvider is null) { throw new ArgumentException("Passed Type is not valid Ooze provider", nameof(TProvider)); } @@ -29,10 +38,28 @@ public IOozeServiceCollectionBuilder Add(ServiceLifetime providerLife { services.Add(new ServiceDescriptor(filterProvider, providerType, providerLifetime)); } + if (asyncFilterProvider is not null) + { + services.Add(new ServiceDescriptor(asyncFilterProvider, providerType, providerLifetime)); + } if (sorterProvider is not null) { services.Add(new ServiceDescriptor(sorterProvider, providerType, providerLifetime)); } + if (asyncSorterProvider is not null) + { + services.Add(new ServiceDescriptor(asyncSorterProvider, providerType, providerLifetime)); + } + + return this; + } + + public IOozeServiceCollectionBuilder EnableAsyncResolvers() + { + services.AddScoped(); + services.AddScoped(typeof(IOozeTypedAsyncResolver<,,>), typeof(OozeTypedAsyncResolver<,,>)); + services.AddScoped(typeof(IAsyncFilterHandler<,>), typeof(AsyncFilterHandler<,>)); + services.AddScoped(typeof(IAsyncSorterHandler<,>), typeof(AsyncSorterHandler<,>)); return this; } @@ -44,8 +71,8 @@ internal IOozeServiceCollectionBuilder AddCommonServices() { services.AddScoped(); services.AddScoped(typeof(IOozeTypedResolver<,,>), typeof(OozeTypedResolver<,,>)); - services.AddScoped(typeof(IOozeFilterHandler<,>), typeof(OozeFilterHandler<,>)); - services.AddScoped(typeof(IOozeSorterHandler<,>), typeof(OozeSorterHandler<,>)); + services.AddScoped(typeof(IFilterHandler<,>), typeof(FilterHandler<,>)); + services.AddScoped(typeof(ISorterHandler<,>), typeof(SorterHandler<,>)); services.AddScoped(typeof(IOozePagingHandler<>), typeof(OozePagingHandler<>)); return this; diff --git a/src/Ooze.Typed/Filters/Async/AsyncFilterBuilder.cs b/src/Ooze.Typed/Filters/Async/AsyncFilterBuilder.cs new file mode 100644 index 0000000..aea3e09 --- /dev/null +++ b/src/Ooze.Typed/Filters/Async/AsyncFilterBuilder.cs @@ -0,0 +1,276 @@ +using System.Linq.Expressions; +using Ooze.Typed.Expressions; + +namespace Ooze.Typed.Filters.Async; + +/// +internal class AsyncFilterBuilder : IAsyncFilterBuilder +{ + private readonly IList> _filterDefinitions = + new List>(); + + public IAsyncFilterBuilder Equal( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null) + { + shouldRun ??= filter => filterFunc(filter) != null; + _filterDefinitions.Add(new AsyncFilterDefinition + { + ShouldRun = filter => Task.FromResult(shouldRun(filter)), + FilterExpressionFactory = filter => + Task.FromResult(BasicExpressions.Equal( + dataExpression, + filterFunc(filter))) + }); + + return this; + } + + public IAsyncFilterBuilder NotEqual( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null) + { + shouldRun ??= filter => filterFunc(filter) != null; + _filterDefinitions.Add(new AsyncFilterDefinition + { + ShouldRun = filters => Task.FromResult(shouldRun(filters)), + FilterExpressionFactory = filter => Task.FromResult(BasicExpressions.NotEqual( + dataExpression, + filterFunc(filter))) + }); + + return this; + } + + public IAsyncFilterBuilder GreaterThan( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null) + { + shouldRun ??= filter => filterFunc(filter) != null; + _filterDefinitions.Add(new AsyncFilterDefinition + { + ShouldRun = filters => Task.FromResult(shouldRun(filters)), + FilterExpressionFactory = filter => + Task.FromResult(BasicExpressions.GreaterThan( + dataExpression, + filterFunc(filter))) + }); + + return this; + } + + public IAsyncFilterBuilder LessThan( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null) + { + shouldRun ??= filter => filterFunc(filter) != null; + _filterDefinitions.Add(new AsyncFilterDefinition + { + ShouldRun = filters => Task.FromResult(shouldRun(filters)), + FilterExpressionFactory = filter => + Task.FromResult(BasicExpressions.LessThan( + dataExpression, + filterFunc(filter))) + }); + + return this; + } + + public IAsyncFilterBuilder In( + Expression> dataExpression, + Func?> filterFunc, + Func? shouldRun = null) + { + shouldRun ??= filter => + { + var value = filterFunc(filter); + return value != null && value.Any(); + }; + _filterDefinitions.Add(new AsyncFilterDefinition + { + ShouldRun = filters => Task.FromResult(shouldRun(filters)), + FilterExpressionFactory = filter => Task.FromResult(BasicExpressions.In( + dataExpression, + filterFunc(filter))) + }); + + return this; + } + + public IAsyncFilterBuilder NotIn( + Expression> dataExpression, + Func?> filterFunc, + Func? shouldRun = null) + { + shouldRun ??= filter => + { + var value = filterFunc(filter); + return value != null && value.Any(); + }; + _filterDefinitions.Add(new AsyncFilterDefinition + { + ShouldRun = filters => Task.FromResult(shouldRun(filters)), + FilterExpressionFactory = filter => + Task.FromResult(BasicExpressions.In( + dataExpression, + filterFunc(filter), + true)) + }); + + return this; + } + + public IAsyncFilterBuilder Range( + Expression> dataExpression, + Func?> filterFunc, + Func? shouldRun = null) + { + shouldRun ??= filter => + { + var value = filterFunc(filter); + return value != null && value.From != null && value.To != null; + }; + _filterDefinitions.Add(new AsyncFilterDefinition + { + ShouldRun = filters => Task.FromResult(shouldRun(filters)), + FilterExpressionFactory = filter => + Task.FromResult(BasicExpressions.Range( + dataExpression, + filterFunc(filter))) + }); + + return this; + } + + public IAsyncFilterBuilder OutOfRange( + Expression> dataExpression, + Func?> filterFunc, + Func? shouldRun = null) + { + shouldRun ??= filter => + { + var value = filterFunc(filter); + return value != null && value.From != null && value.To != null; + }; + _filterDefinitions.Add(new AsyncFilterDefinition + { + ShouldRun = filters => Task.FromResult(shouldRun(filters)), + FilterExpressionFactory = filter => + Task.FromResult(BasicExpressions.Range( + dataExpression, + filterFunc(filter), + true)) + }); + + return this; + } + + public IAsyncFilterBuilder StartsWith( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null) + { + shouldRun ??= filter => string.IsNullOrEmpty(filterFunc(filter)) == false; + _filterDefinitions.Add(new AsyncFilterDefinition + { + ShouldRun = filters => Task.FromResult(shouldRun(filters)), + FilterExpressionFactory = filter + => Task.FromResult(BasicExpressions.StringOperation( + dataExpression, + filterFunc(filter), + CommonMethods.StringStartsWith)) + }); + + return this; + } + + public IAsyncFilterBuilder DoesntStartWith( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null) + { + shouldRun ??= filter => string.IsNullOrEmpty(filterFunc(filter)) == false; + _filterDefinitions.Add(new AsyncFilterDefinition + { + ShouldRun = filters => Task.FromResult(shouldRun(filters)), + FilterExpressionFactory = filter => Task.FromResult(BasicExpressions.StringOperation( + dataExpression, + filterFunc(filter), + CommonMethods.StringStartsWith, + true)) + }); + + return this; + } + + public IAsyncFilterBuilder EndsWith( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null) + { + shouldRun ??= filter => string.IsNullOrEmpty(filterFunc(filter)) == false; + _filterDefinitions.Add(new AsyncFilterDefinition + { + ShouldRun = filters => Task.FromResult(shouldRun(filters)), + FilterExpressionFactory = filter + => Task.FromResult(BasicExpressions.StringOperation( + dataExpression, + filterFunc(filter), + CommonMethods.StringEndsWith)) + }); + + return this; + } + + public IAsyncFilterBuilder DoesntEndWith( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null) + { + shouldRun ??= filter => string.IsNullOrEmpty(filterFunc(filter)) == false; + _filterDefinitions.Add(new AsyncFilterDefinition + { + ShouldRun = filters => Task.FromResult(shouldRun(filters)), + FilterExpressionFactory = filter + => Task.FromResult(BasicExpressions.StringOperation( + dataExpression, + filterFunc(filter), + CommonMethods.StringEndsWith, + true)) + }); + + return this; + } + + public IAsyncFilterBuilder Add( + Func shouldRun, + Func>> filterExpressionFactory) + { + _filterDefinitions.Add(new AsyncFilterDefinition + { + ShouldRun = filters => Task.FromResult(shouldRun(filters)), + FilterExpressionFactory = filters => Task.FromResult(filterExpressionFactory(filters)) + }); + + return this; + } + + public IAsyncFilterBuilder AddAsync( + Func> shouldRun, + Func>>> filterExpressionFactory) + { + _filterDefinitions.Add(new AsyncFilterDefinition + { + ShouldRun = shouldRun, + FilterExpressionFactory = filterExpressionFactory + }); + + return this; + } + + public IEnumerable> Build() => _filterDefinitions; +} \ No newline at end of file diff --git a/src/Ooze.Typed/Filters/Async/AsyncFilterDefinition.cs b/src/Ooze.Typed/Filters/Async/AsyncFilterDefinition.cs new file mode 100644 index 0000000..8645c14 --- /dev/null +++ b/src/Ooze.Typed/Filters/Async/AsyncFilterDefinition.cs @@ -0,0 +1,9 @@ +using System.Linq.Expressions; + +namespace Ooze.Typed.Filters.Async; + +public class AsyncFilterDefinition +{ + public Func> ShouldRun { get; internal set; } = null!; + public Func>>> FilterExpressionFactory { get; internal set; } = null!; +} \ No newline at end of file diff --git a/src/Ooze.Typed/Filters/Async/AsyncFilterHandler.cs b/src/Ooze.Typed/Filters/Async/AsyncFilterHandler.cs new file mode 100644 index 0000000..0f61cf7 --- /dev/null +++ b/src/Ooze.Typed/Filters/Async/AsyncFilterHandler.cs @@ -0,0 +1,39 @@ +using Microsoft.Extensions.Logging; + +namespace Ooze.Typed.Filters.Async; + +internal class AsyncFilterHandler( + IEnumerable> filterProviders, + ILogger> log) + : IAsyncFilterHandler +{ + public async ValueTask> ApplyAsync( + IQueryable query, + TFilters filters) + { + log.LogDebug("Processing available filters!"); + + var filterDefinitions = new List>(); + foreach (var provider in filterProviders) + { + var definitions = await provider.GetFiltersAsync(); + filterDefinitions.AddRange(definitions); + } + + foreach (var filterDefinition in filterDefinitions) + { + var shouldRun = await filterDefinition.ShouldRun(filters); + if (shouldRun == false) + continue; + + var filterExpr = await filterDefinition.FilterExpressionFactory.Invoke(filters); + if (filterExpr is null) + continue; + + log.LogDebug("Applying filter: [{@filter}]", filterExpr); + query = query.Where(filterExpr); + } + + return query; + } +} \ No newline at end of file diff --git a/src/Ooze.Typed/Filters/Async/AsyncFilters.cs b/src/Ooze.Typed/Filters/Async/AsyncFilters.cs new file mode 100644 index 0000000..0cd70ba --- /dev/null +++ b/src/Ooze.Typed/Filters/Async/AsyncFilters.cs @@ -0,0 +1,16 @@ +namespace Ooze.Typed.Filters.Async; + +/// +/// Static class with helper methods to start building async filter definitions for specified entity type and filter type +/// +public static class AsyncFilters +{ + /// + /// Creates a new instance of async filter builder for passed entity type and filter type + /// + /// Entity type + /// Filter type + /// Filter builder instance for entity, filter type combination + public static IAsyncFilterBuilder CreateFor() + => new AsyncFilterBuilder(); +} \ No newline at end of file diff --git a/src/Ooze.Typed/Filters/Async/IAsyncFilterBuilder.cs b/src/Ooze.Typed/Filters/Async/IAsyncFilterBuilder.cs new file mode 100644 index 0000000..3af3486 --- /dev/null +++ b/src/Ooze.Typed/Filters/Async/IAsyncFilterBuilder.cs @@ -0,0 +1,22 @@ +using System.Linq.Expressions; + +namespace Ooze.Typed.Filters.Async; + +public interface IAsyncFilterBuilder : IFilters> +{ + /// + /// Fluently creates custom async filter definition + /// + /// Delegate showing if the filter should execute + /// Factory for creation of filter Expression + /// Instance of builder for fluent building of multiple filter definitions + IAsyncFilterBuilder AddAsync( + Func> shouldRun, + Func>>> filterExpressionFactory); + + /// + /// Return collection of created async filter definitions + /// + /// Collection of filter definitions + IEnumerable> Build(); +} \ No newline at end of file diff --git a/src/Ooze.Typed/Filters/Async/IAsyncFilterHandler.cs b/src/Ooze.Typed/Filters/Async/IAsyncFilterHandler.cs new file mode 100644 index 0000000..1227d48 --- /dev/null +++ b/src/Ooze.Typed/Filters/Async/IAsyncFilterHandler.cs @@ -0,0 +1,8 @@ +namespace Ooze.Typed.Filters.Async; + +internal interface IAsyncFilterHandler +{ + ValueTask> ApplyAsync( + IQueryable query, + TFilter filters); +} \ No newline at end of file diff --git a/src/Ooze.Typed/Filters/Async/IAsyncFilterProvider.cs b/src/Ooze.Typed/Filters/Async/IAsyncFilterProvider.cs new file mode 100644 index 0000000..f70fe24 --- /dev/null +++ b/src/Ooze.Typed/Filters/Async/IAsyncFilterProvider.cs @@ -0,0 +1,17 @@ +namespace Ooze.Typed.Filters.Async; + +/// +/// Async filter provider interface called internally by / +/// to fetch defined filters for provided Entity type. +/// +/// Entity type +/// Filter implementation type +public interface IAsyncFilterProvider +{ + /// + /// Method used for creation of definitions. These definitions are + /// used in filtering process. + /// + /// Collection of filter definitions + ValueTask>> GetFiltersAsync(); +} \ No newline at end of file diff --git a/src/Ooze.Typed/Filters/FilterBuilder.cs b/src/Ooze.Typed/Filters/FilterBuilder.cs index 9799541..d1ef9c7 100644 --- a/src/Ooze.Typed/Filters/FilterBuilder.cs +++ b/src/Ooze.Typed/Filters/FilterBuilder.cs @@ -224,5 +224,5 @@ public IFilterBuilder Add( return this; } - public IEnumerable> Build() => _filterDefinitions; + public IEnumerable> Build() => _filterDefinitions; } \ No newline at end of file diff --git a/src/Ooze.Typed/Filters/FilterDefinition.cs b/src/Ooze.Typed/Filters/FilterDefinition.cs index 6a219a3..eb15196 100644 --- a/src/Ooze.Typed/Filters/FilterDefinition.cs +++ b/src/Ooze.Typed/Filters/FilterDefinition.cs @@ -2,7 +2,7 @@ namespace Ooze.Typed.Filters; -internal class FilterDefinition : IFilterDefinition +public class FilterDefinition { public Func ShouldRun { get; internal set; } = null!; public Func>> FilterExpressionFactory { get; internal set; } = null!; diff --git a/src/Ooze.Typed/Filters/OozeFilterHandler.cs b/src/Ooze.Typed/Filters/FilterHandler.cs similarity index 54% rename from src/Ooze.Typed/Filters/OozeFilterHandler.cs rename to src/Ooze.Typed/Filters/FilterHandler.cs index 8d9430b..5fcc211 100644 --- a/src/Ooze.Typed/Filters/OozeFilterHandler.cs +++ b/src/Ooze.Typed/Filters/FilterHandler.cs @@ -2,10 +2,10 @@ namespace Ooze.Typed.Filters; -internal class OozeFilterHandler( - IEnumerable> filterProviders, - ILogger> log) - : IOozeFilterHandler +internal class FilterHandler( + IEnumerable> filterProviders, + ILogger> log) + : IFilterHandler { public IQueryable Apply( IQueryable query, @@ -14,19 +14,18 @@ public IQueryable Apply( log.LogDebug("Processing available filters!"); var validFilters = filterProviders.SelectMany(provider => provider.GetFilters()) - .Cast>() .Where(filter => filter.ShouldRun(filters)); foreach (var filterDefinition in validFilters) { var filterExpr = filterDefinition.FilterExpressionFactory?.Invoke(filters); - if (filterExpr is not null) - { - log.LogDebug("Applying filter: [{@filter}]", filterExpr); - query = query.Where(filterExpr); - } + if (filterExpr is null) + continue; + + log.LogDebug("Applying filter: [{@filter}]", filterExpr); + query = query.Where(filterExpr); } return query; } -} +} \ No newline at end of file diff --git a/src/Ooze.Typed/Filters/Filters.cs b/src/Ooze.Typed/Filters/Filters.cs index 687baca..57d3fe5 100644 --- a/src/Ooze.Typed/Filters/Filters.cs +++ b/src/Ooze.Typed/Filters/Filters.cs @@ -13,4 +13,4 @@ public static class Filters /// Filter builder instance for entity, filter type combination public static IFilterBuilder CreateFor() => new FilterBuilder(); -} +} \ No newline at end of file diff --git a/src/Ooze.Typed/Filters/IFilterBuilder.cs b/src/Ooze.Typed/Filters/IFilterBuilder.cs index b7458f6..f3ea1a3 100644 --- a/src/Ooze.Typed/Filters/IFilterBuilder.cs +++ b/src/Ooze.Typed/Filters/IFilterBuilder.cs @@ -1,179 +1,15 @@ -using System.Linq.Expressions; - -namespace Ooze.Typed.Filters; +namespace Ooze.Typed.Filters; /// /// Interface defining contract for filter builder implementation /// /// Entity type /// Filter type -public interface IFilterBuilder +public interface IFilterBuilder : IFilters> { - /// - /// Fluently creates "Equality" filter definition - /// - /// Expression targeting property of specified entity class - /// Delegate targeting property of filter class - /// Target type of entity property - /// Delegate returning bool value which denotes if filter should be applied - /// Instance of builder for fluent building of multiple filter definitions - IFilterBuilder Equal( - Expression> dataExpression, - Func filterFunc, - Func? shouldRun = null); - - /// - /// Fluently creates "Not Equal" filter definition - /// - /// Expression targeting property of specified entity class - /// Delegate targeting property of filter class - /// Target type of entity property - /// Delegate returning bool value which denotes if filter should be applied - /// Instance of builder for fluent building of multiple filter definitions - IFilterBuilder NotEqual( - Expression> dataExpression, - Func filterFunc, - Func? shouldRun = null); - - /// - /// Fluently creates "Greater Than" filter definition - /// - /// Expression targeting property of specified entity class - /// Delegate targeting property of filter class - /// Target type of entity property - /// Delegate returning bool value which denotes if filter should be applied - /// Instance of builder for fluent building of multiple filter definitions - IFilterBuilder GreaterThan( - Expression> dataExpression, - Func filterFunc, - Func? shouldRun = null); - - /// - /// Fluently creates "Less Than" filter definition - /// - /// Expression targeting property of specified entity class - /// Delegate targeting property of filter class - /// Target type of entity property - /// Delegate returning bool value which denotes if filter should be applied - /// Instance of builder for fluent building of multiple filter definitions - IFilterBuilder LessThan( - Expression> dataExpression, - Func filterFunc, - Func? shouldRun = null); - - /// - /// Fluently creates "In" filter definition - /// - /// Expression targeting property of specified entity class - /// Delegate targeting property of filter class - /// Target type of entity property - /// Delegate returning bool value which denotes if filter should be applied - /// Instance of builder for fluent building of multiple filter definitions - IFilterBuilder In( - Expression> dataExpression, - Func?> filterFunc, - Func? shouldRun = null); - - /// - /// Fluently creates "Not In" filter definition - /// - /// Expression targeting property of specified entity class - /// Delegate targeting property of filter class - /// Target type of entity property - /// Delegate returning bool value which denotes if filter should be applied - /// Instance of builder for fluent building of multiple filter definitions - IFilterBuilder NotIn( - Expression> dataExpression, - Func?> filterFunc, - Func? shouldRun = null); - - /// - /// Fluently creates "Range" filter definition - /// - /// Expression targeting property of specified entity class - /// Delegate targeting property of filter class - /// Target type of entity property - /// Delegate returning bool value which denotes if filter should be applied - /// Instance of builder for fluent building of multiple filter definitions - IFilterBuilder Range( - Expression> dataExpression, - Func?> filterFunc, - Func? shouldRun = null); - - /// - /// Fluently creates "Out of Range" filter definition - /// - /// Expression targeting property of specified entity class - /// Delegate targeting property of filter class - /// Target type of entity property - /// Delegate returning bool value which denotes if filter should be applied - /// Instance of builder for fluent building of multiple filter definitions - IFilterBuilder OutOfRange( - Expression> dataExpression, - Func?> filterFunc, - Func? shouldRun = null); - - /// - /// Fluently creates "Starts with" filter definition - /// - /// Expression targeting property of specified entity class - /// Delegate targeting property of filter class - /// Delegate returning bool value which denotes if filter should be applied - /// Instance of builder for fluent building of multiple filter definitions - IFilterBuilder StartsWith( - Expression> dataExpression, - Func filterFunc, - Func? shouldRun = null); - - /// - /// Fluently creates "Doesn't Start With" filter definition - /// - /// Expression targeting property of specified entity class - /// Delegate targeting property of filter class - /// Delegate returning bool value which denotes if filter should be applied - /// Instance of builder for fluent building of multiple filter definitions - IFilterBuilder DoesntStartWith( - Expression> dataExpression, - Func filterFunc, - Func? shouldRun = null); - - /// - /// Fluently creates "Ends With" filter definition - /// - /// Expression targeting property of specified entity class - /// Delegate targeting property of filter class - /// Delegate returning bool value which denotes if filter should be applied - /// Instance of builder for fluent building of multiple filter definitions - IFilterBuilder EndsWith( - Expression> dataExpression, - Func filterFunc, - Func? shouldRun = null); - - /// - /// Fluently creates "Doesn't End With" filter definition - /// - /// Expression targeting property of specified entity class - /// Delegate targeting property of filter class - /// Delegate returning bool value which denotes if filter should be applied - /// Instance of builder for fluent building of multiple filter definitions - IFilterBuilder DoesntEndWith( - Expression> dataExpression, - Func filterFunc, - Func? shouldRun = null); - - /// - /// Fluently creates custom filter definition - /// - /// Delegate showing if the filter should execute - /// Factory for creation of filter Expression - /// Instance of builder for fluent building of multiple filter definitions - IFilterBuilder Add( - Func shouldRun, - Func>> filterExpressionFactory); - /// /// Return collection of created filter definitions /// /// Collection of filter definitions - IEnumerable> Build(); -} + IEnumerable> Build(); +} \ No newline at end of file diff --git a/src/Ooze.Typed/Filters/IOozeFilterHandler.cs b/src/Ooze.Typed/Filters/IFilterHandler.cs similarity index 67% rename from src/Ooze.Typed/Filters/IOozeFilterHandler.cs rename to src/Ooze.Typed/Filters/IFilterHandler.cs index 5f05232..478f204 100644 --- a/src/Ooze.Typed/Filters/IOozeFilterHandler.cs +++ b/src/Ooze.Typed/Filters/IFilterHandler.cs @@ -1,8 +1,8 @@ namespace Ooze.Typed.Filters; -internal interface IOozeFilterHandler +internal interface IFilterHandler { IQueryable Apply( IQueryable query, TFilter filters); -} +} \ No newline at end of file diff --git a/src/Ooze.Typed/Filters/IOozeFilterProvider.cs b/src/Ooze.Typed/Filters/IFilterProvider.cs similarity index 83% rename from src/Ooze.Typed/Filters/IOozeFilterProvider.cs rename to src/Ooze.Typed/Filters/IFilterProvider.cs index de2f409..20d4ead 100644 --- a/src/Ooze.Typed/Filters/IOozeFilterProvider.cs +++ b/src/Ooze.Typed/Filters/IFilterProvider.cs @@ -6,12 +6,12 @@ /// /// Entity type /// Filter implementation type -public interface IOozeFilterProvider +public interface IFilterProvider { /// /// Method used for creation of definitions. These definitions are /// used in filtering process. /// /// Collection of filter definitions - IEnumerable> GetFilters(); -} + IEnumerable> GetFilters(); +} \ No newline at end of file diff --git a/src/Ooze.Typed/Filters/IFilters.cs b/src/Ooze.Typed/Filters/IFilters.cs new file mode 100644 index 0000000..c88d352 --- /dev/null +++ b/src/Ooze.Typed/Filters/IFilters.cs @@ -0,0 +1,168 @@ +using System.Linq.Expressions; + +namespace Ooze.Typed.Filters; + +public interface IFilters +{ + /// + /// Fluently creates "Equality" filter definition + /// + /// Expression targeting property of specified entity class + /// Delegate targeting property of filter class + /// Target type of entity property + /// Delegate returning bool value which denotes if filter should be applied + /// Instance of builder for fluent building of multiple filter definitions + TReturn Equal( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null); + + /// + /// Fluently creates "Not Equal" filter definition + /// + /// Expression targeting property of specified entity class + /// Delegate targeting property of filter class + /// Target type of entity property + /// Delegate returning bool value which denotes if filter should be applied + /// Instance of builder for fluent building of multiple filter definitions + TReturn NotEqual( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null); + + /// + /// Fluently creates "Greater Than" filter definition + /// + /// Expression targeting property of specified entity class + /// Delegate targeting property of filter class + /// Target type of entity property + /// Delegate returning bool value which denotes if filter should be applied + /// Instance of builder for fluent building of multiple filter definitions + TReturn GreaterThan( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null); + + /// + /// Fluently creates "Less Than" filter definition + /// + /// Expression targeting property of specified entity class + /// Delegate targeting property of filter class + /// Target type of entity property + /// Delegate returning bool value which denotes if filter should be applied + /// Instance of builder for fluent building of multiple filter definitions + TReturn LessThan( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null); + + /// + /// Fluently creates "In" filter definition + /// + /// Expression targeting property of specified entity class + /// Delegate targeting property of filter class + /// Target type of entity property + /// Delegate returning bool value which denotes if filter should be applied + /// Instance of builder for fluent building of multiple filter definitions + TReturn In( + Expression> dataExpression, + Func?> filterFunc, + Func? shouldRun = null); + + /// + /// Fluently creates "Not In" filter definition + /// + /// Expression targeting property of specified entity class + /// Delegate targeting property of filter class + /// Target type of entity property + /// Delegate returning bool value which denotes if filter should be applied + /// Instance of builder for fluent building of multiple filter definitions + TReturn NotIn( + Expression> dataExpression, + Func?> filterFunc, + Func? shouldRun = null); + + /// + /// Fluently creates "Range" filter definition + /// + /// Expression targeting property of specified entity class + /// Delegate targeting property of filter class + /// Target type of entity property + /// Delegate returning bool value which denotes if filter should be applied + /// Instance of builder for fluent building of multiple filter definitions + TReturn Range( + Expression> dataExpression, + Func?> filterFunc, + Func? shouldRun = null); + + /// + /// Fluently creates "Out of Range" filter definition + /// + /// Expression targeting property of specified entity class + /// Delegate targeting property of filter class + /// Target type of entity property + /// Delegate returning bool value which denotes if filter should be applied + /// Instance of builder for fluent building of multiple filter definitions + TReturn OutOfRange( + Expression> dataExpression, + Func?> filterFunc, + Func? shouldRun = null); + + /// + /// Fluently creates "Starts with" filter definition + /// + /// Expression targeting property of specified entity class + /// Delegate targeting property of filter class + /// Delegate returning bool value which denotes if filter should be applied + /// Instance of builder for fluent building of multiple filter definitions + TReturn StartsWith( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null); + + /// + /// Fluently creates "Doesn't Start With" filter definition + /// + /// Expression targeting property of specified entity class + /// Delegate targeting property of filter class + /// Delegate returning bool value which denotes if filter should be applied + /// Instance of builder for fluent building of multiple filter definitions + TReturn DoesntStartWith( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null); + + /// + /// Fluently creates "Ends With" filter definition + /// + /// Expression targeting property of specified entity class + /// Delegate targeting property of filter class + /// Delegate returning bool value which denotes if filter should be applied + /// Instance of builder for fluent building of multiple filter definitions + TReturn EndsWith( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null); + + /// + /// Fluently creates "Doesn't End With" filter definition + /// + /// Expression targeting property of specified entity class + /// Delegate targeting property of filter class + /// Delegate returning bool value which denotes if filter should be applied + /// Instance of builder for fluent building of multiple filter definitions + TReturn DoesntEndWith( + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null); + + /// + /// Fluently creates custom filter definition + /// + /// Delegate showing if the filter should execute + /// Factory for creation of filter Expression + /// Instance of builder for fluent building of multiple filter definitions + TReturn Add( + Func shouldRun, + Func>> filterExpressionFactory); +} \ No newline at end of file diff --git a/src/Ooze.Typed/IOozeTypedAsyncResolver.cs b/src/Ooze.Typed/IOozeTypedAsyncResolver.cs new file mode 100644 index 0000000..9fbd89b --- /dev/null +++ b/src/Ooze.Typed/IOozeTypedAsyncResolver.cs @@ -0,0 +1,109 @@ +using System.Linq.Expressions; +using Ooze.Typed.Paging; + +namespace Ooze.Typed; + +/// +/// Ooze resolver instance, contains implementations of Filtering, Sorting and Paging methods used to update +/// instances. +/// +public interface IOozeTypedAsyncResolver +{ + /// + /// Applies valid sorters over instance. Sorters application is based of sorter provider + /// implementation for entity type + /// + /// Base instance + /// Sorter definitions to apply over instance + /// Entity type + /// Sorter implementation type + /// Updated instance + ValueTask> SortAsync( + IQueryable query, + IEnumerable sorters); + + /// + /// Applies valid filters over instance. Filter application is based of filter provider + /// implementation for entity type + /// + /// Base instance + /// Filter definitions to apply over instance + /// Entity type + /// Filter implementation type + /// Updated instance + ValueTask> FilterAsync( + IQueryable query, + TFilters filters); + + /// + /// Applies valid paging options over instance. + /// + /// Base instance + /// Instance of paging options + /// Entity type + /// Updated instance + ValueTask> PageAsync( + IQueryable query, + PagingOptions pagingOptions); + + /// + /// Applies valid cursor paging options over instance. + /// + /// Base instance + /// Expression targeting entity property to use for cursor paging + /// Instance of paging options + /// Entity type + /// After type + /// Property type + /// Updated instance + ValueTask> PageWithCursorAsync( + IQueryable query, + Expression> cursorPropertyExpression, + CursorPagingOptions? pagingOptions); +} + +/// +/// Ooze resolver instance, contains implementations of Filtering, Sorting and Paging methods used to update +/// instances. +/// +/// Entity type +/// Filter implementation type +/// Sorter implementation type +public interface IOozeTypedAsyncResolver +{ + /// + /// Registers passed instance to for upcoming operations + /// + /// Base instance + /// Resolver fluent instance + IOozeTypedAsyncResolver WithQuery(IQueryable query); + + /// + /// Applies valid sorters over instance. Sorters application is based of sorter provider + /// implementation for entity type + /// + /// Sorter definitions to apply over instance + /// Updated instance + IOozeTypedAsyncResolver Sort(IEnumerable sorters); + + /// + /// Applies valid filters over instance. Filter application is based of filter provider + /// implementation for entity type + /// + /// Filter definitions to apply over instance + /// Updated instance + IOozeTypedAsyncResolver Filter(TFilters filters); + + /// + /// Applies valid paging options over instance. + /// + /// Instance of paging options + /// Updated instance + IOozeTypedAsyncResolver Page(PagingOptions pagingOptions); + + /// + /// Apply and return update instance asynchronously + /// + /// Updated instance + ValueTask> ApplyAsync(); +} \ No newline at end of file diff --git a/src/Ooze.Typed/OozeTypedAsyncResolver.cs b/src/Ooze.Typed/OozeTypedAsyncResolver.cs new file mode 100644 index 0000000..187cc58 --- /dev/null +++ b/src/Ooze.Typed/OozeTypedAsyncResolver.cs @@ -0,0 +1,180 @@ +using System.Linq.Expressions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Ooze.Typed.Filters; +using Ooze.Typed.Filters.Async; +using Ooze.Typed.Paging; +using Ooze.Typed.Sorters; +using Ooze.Typed.Sorters.Async; + +namespace Ooze.Typed; + +/// +internal class OozeTypedAsyncResolver( + IServiceProvider serviceProvider, + ILogger log) + : IOozeTypedAsyncResolver +{ + public async ValueTask> FilterAsync( + IQueryable query, + TFilters? filters) + { + if (filters is null) + { + log.LogDebug("Filters of type: [{typeName}] are null", typeof(TFilters).Name); + return query; + } + + var filterHandler = serviceProvider.GetRequiredService>(); + query = await filterHandler.ApplyAsync(query, filters); + + return query; + } + + public async ValueTask> SortAsync( + IQueryable query, + IEnumerable? sorters) + { + sorters ??= Enumerable.Empty(); + if (sorters.Any() == false) + { + log.LogDebug("Sorters of type: [{typeName}] are not present", typeof(TSorters).Name); + return query; + } + + var sorterHandler = serviceProvider.GetRequiredService>(); + query = await sorterHandler.ApplyAsync(query, sorters); + + return query; + } + + public ValueTask> PageAsync( + IQueryable query, + PagingOptions? pagingOptions) + { + if (pagingOptions == null) + { + log.LogDebug("Pagination options are not present"); + return ValueTask.FromResult(query); + } + + var sorterHandler = serviceProvider.GetRequiredService>(); + query = sorterHandler.Apply(query, pagingOptions); + + return ValueTask.FromResult(query); + } + + public ValueTask> PageWithCursorAsync( + IQueryable query, + Expression> cursorPropertyExpression, + CursorPagingOptions? pagingOptions) + { + if (pagingOptions == null) + { + log.LogDebug("Pagination options are not present"); + return ValueTask.FromResult(query); + } + + var sorterHandler = serviceProvider.GetRequiredService>(); + query = sorterHandler.ApplyCursor(query, cursorPropertyExpression, pagingOptions); + + return ValueTask.FromResult(query); + } +} + +/// +internal class OozeTypedAsyncResolver( + IAsyncSorterHandler sorterHandler, + IAsyncFilterHandler filterHandler, + IOozePagingHandler pagingHandler, + ILogger> log) + : IOozeTypedAsyncResolver +{ + private OozeResolverData _resolverData = new(); + + public IOozeTypedAsyncResolver WithQuery(IQueryable query) + { + _resolverData = _resolverData with + { + Query = query + }; + + return this; + } + + public IOozeTypedAsyncResolver Sort(IEnumerable? sorters) + { + _resolverData = _resolverData with + { + Sorters = sorters + }; + + return this; + } + + public IOozeTypedAsyncResolver Filter(TFilters? filters) + { + _resolverData = _resolverData with + { + Filters = filters + }; + + return this; + } + + public IOozeTypedAsyncResolver Page(PagingOptions? pagingOptions) + { + _resolverData = _resolverData with + { + Paging = pagingOptions + }; + + return this; + } + + public async ValueTask> ApplyAsync() + { + if (_resolverData.Query is null) + throw new Exception("Queryable not defined/passed to the resolver!"); + + if (_resolverData.Sorters is { } sorters) + { + log.LogDebug("Applying sorters: [{typeName}]", typeof(TSorters).Name); + + _resolverData = _resolverData with + { + Query = await sorterHandler.ApplyAsync(_resolverData.Query, sorters) + }; + } + + if (_resolverData.Filters is { } filters) + { + log.LogDebug("Applying filters: [{typeName}]", typeof(TFilters).Name); + + _resolverData = _resolverData with + { + Query = await filterHandler.ApplyAsync(_resolverData.Query, filters) + }; + } + + + if (_resolverData.Paging is { } paging) + { + log.LogDebug("Applying pagination options"); + + _resolverData = _resolverData with + { + Query = pagingHandler.Apply(_resolverData.Query, paging) + }; + } + + + return _resolverData.Query; + } +} + +internal record OozeResolverData( + IQueryable? Query = default, + TFilter? Filters = default, + IEnumerable? Sorters = default, + PagingOptions? Paging = default); \ No newline at end of file diff --git a/src/Ooze.Typed/OozeTypedResolver.cs b/src/Ooze.Typed/OozeTypedResolver.cs index 441cc0b..e114d21 100644 --- a/src/Ooze.Typed/OozeTypedResolver.cs +++ b/src/Ooze.Typed/OozeTypedResolver.cs @@ -23,7 +23,7 @@ public IQueryable Filter( return query; } - var filterHandler = serviceProvider.GetRequiredService>(); + var filterHandler = serviceProvider.GetRequiredService>(); query = filterHandler.Apply(query, filters); return query; @@ -41,7 +41,7 @@ public IQueryable Sort( } - var sorterHandler = serviceProvider.GetRequiredService>(); + var sorterHandler = serviceProvider.GetRequiredService>(); query = sorterHandler.Apply(query, sorters); return query; @@ -84,8 +84,8 @@ public IQueryable PageWithCursor( /// internal class OozeTypedResolver( - IOozeSorterHandler sorterHandler, - IOozeFilterHandler filterHandler, + ISorterHandler sorterHandler, + IFilterHandler filterHandler, IOozePagingHandler pagingHandler, ILogger> log) : IOozeTypedResolver diff --git a/src/Ooze.Typed/Sorters/Async/AsyncSortDefinition.cs b/src/Ooze.Typed/Sorters/Async/AsyncSortDefinition.cs new file mode 100644 index 0000000..1c5cd5e --- /dev/null +++ b/src/Ooze.Typed/Sorters/Async/AsyncSortDefinition.cs @@ -0,0 +1,10 @@ +using System.Linq.Expressions; + +namespace Ooze.Typed.Sorters.Async; + +public class AsyncSortDefinition +{ + public LambdaExpression DataExpression { get; internal set; } = null!; + public Func> ShouldRun { get; set; } = null!; + public Func> GetSortDirection { get; set; } = null!; +} \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/Async/AsyncSorterBuilder.cs b/src/Ooze.Typed/Sorters/Async/AsyncSorterBuilder.cs new file mode 100644 index 0000000..11d3d57 --- /dev/null +++ b/src/Ooze.Typed/Sorters/Async/AsyncSorterBuilder.cs @@ -0,0 +1,52 @@ +using System.Linq.Expressions; + +namespace Ooze.Typed.Sorters.Async; + +internal class AsyncSorterBuilder : IAsyncSorterBuilder +{ + private readonly IList> _sortDefinitions = + new List>(); + + public IAsyncSorterBuilder SortBy( + Expression> dataExpression, + Func sorterFunc, + Func? shouldRun = null) + { + shouldRun ??= sorters => sorterFunc(sorters) != null; + _sortDefinitions.Add(new AsyncSortDefinition + { + DataExpression = dataExpression, + ShouldRun = sorters => Task.FromResult(shouldRun(sorters)), + GetSortDirection = sorters => + { + var sortDirection = sorterFunc(sorters); + return Task.FromResult(sortDirection); + } + }); + + return this; + } + + public IAsyncSorterBuilder SortByAsync( + Expression> dataExpression, + Func> sorterFunc, + Func>? shouldRun = null) + { + shouldRun ??= async sorters => await sorterFunc(sorters) != null; + _sortDefinitions.Add(new AsyncSortDefinition + { + DataExpression = dataExpression, + ShouldRun = shouldRun, + GetSortDirection = async sorters => + { + var sortDirection = await sorterFunc(sorters); + return sortDirection; + } + }); + + return this; + } + + public IEnumerable> Build() + => _sortDefinitions; +} \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/Async/AsyncSorterHandler.cs b/src/Ooze.Typed/Sorters/Async/AsyncSorterHandler.cs new file mode 100644 index 0000000..d138f32 --- /dev/null +++ b/src/Ooze.Typed/Sorters/Async/AsyncSorterHandler.cs @@ -0,0 +1,78 @@ +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Extensions.Logging; +using Ooze.Typed.Expressions; + +namespace Ooze.Typed.Sorters.Async; + +internal class AsyncSorterHandler( + IEnumerable> sortProviders, + ILogger> log) + : IAsyncSorterHandler +{ + public async ValueTask> ApplyAsync( + IQueryable query, + IEnumerable sorters) + { + log.LogDebug("Processing available sorters!"); + + if (query == null) throw new ArgumentNullException(nameof(query)); + var sortDefinitions = new List>(); + foreach (var provider in sortProviders) + { + var definitions = await provider.GetSortersAsync(); + sortDefinitions.AddRange(definitions); + } + + foreach (var sorter in sorters) + { + async Task?> GetSorterDefinition() + { + foreach (var sorterDefinition in sortDefinitions) + { + var shouldRun = await sorterDefinition.ShouldRun(sorter); + if (shouldRun == true) + return sorterDefinition; + } + + return default; + } + + var sortDefinition = await GetSorterDefinition(); + if (sortDefinition is null) + continue; + + var sorterType = BasicExpressions.GetMemberExpression(sortDefinition.DataExpression.Body).Type; + var direction = await sortDefinition.GetSortDirection(sorter); + MethodInfo? method; + + if (query.Expression.Type == typeof(IOrderedQueryable)) + { + method = direction == SortDirection.Ascending + ? CommonMethods.ThenBy + : CommonMethods.ThenByDescending; + } + else + { + method = direction == SortDirection.Ascending + ? CommonMethods.OrderBy + : CommonMethods.OrderByDescending; + } + + log.LogDebug("Applying sorter: [{@sorter}]", sortDefinition.DataExpression); + query = CreateSortedQueryable(query, method, sorterType, sortDefinition.DataExpression); + } + + return query; + } + + private static IQueryable CreateSortedQueryable( + IQueryable query, + MethodInfo method, + Type sorterType, + LambdaExpression dataExpression) + { + return (method.MakeGenericMethod(typeof(TEntity), sorterType) + .Invoke(null, [query, dataExpression]) as IQueryable)!; + } +} \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/Async/AsyncSorters.cs b/src/Ooze.Typed/Sorters/Async/AsyncSorters.cs new file mode 100644 index 0000000..6e014a6 --- /dev/null +++ b/src/Ooze.Typed/Sorters/Async/AsyncSorters.cs @@ -0,0 +1,7 @@ +namespace Ooze.Typed.Sorters.Async; + +public static class AsyncSorters +{ + public static IAsyncSorterBuilder CreateFor() + => new AsyncSorterBuilder(); +} \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/Async/IAsyncSorterBuilder.cs b/src/Ooze.Typed/Sorters/Async/IAsyncSorterBuilder.cs new file mode 100644 index 0000000..a9370f4 --- /dev/null +++ b/src/Ooze.Typed/Sorters/Async/IAsyncSorterBuilder.cs @@ -0,0 +1,18 @@ +using System.Linq.Expressions; + +namespace Ooze.Typed.Sorters.Async; + +public interface IAsyncSorterBuilder +{ + IAsyncSorterBuilder SortBy( + Expression> dataExpression, + Func sorterFunc, + Func? shouldRun = null); + + IAsyncSorterBuilder SortByAsync( + Expression> dataExpression, + Func> sorterFunc, + Func>? shouldRun = null); + + IEnumerable> Build(); +} \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/Async/IAsyncSorterHandler.cs b/src/Ooze.Typed/Sorters/Async/IAsyncSorterHandler.cs new file mode 100644 index 0000000..41b8cef --- /dev/null +++ b/src/Ooze.Typed/Sorters/Async/IAsyncSorterHandler.cs @@ -0,0 +1,8 @@ +namespace Ooze.Typed.Sorters.Async; + +internal interface IAsyncSorterHandler +{ + ValueTask> ApplyAsync( + IQueryable query, + IEnumerable sorters); +} \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/Async/IAsyncSorterProvider.cs b/src/Ooze.Typed/Sorters/Async/IAsyncSorterProvider.cs new file mode 100644 index 0000000..faaad6c --- /dev/null +++ b/src/Ooze.Typed/Sorters/Async/IAsyncSorterProvider.cs @@ -0,0 +1,6 @@ +namespace Ooze.Typed.Sorters.Async; + +public interface IAsyncSorterProvider +{ + ValueTask>> GetSortersAsync(); +} \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/ISorterBuilder.cs b/src/Ooze.Typed/Sorters/ISorterBuilder.cs index d9f9de6..6fd53f8 100644 --- a/src/Ooze.Typed/Sorters/ISorterBuilder.cs +++ b/src/Ooze.Typed/Sorters/ISorterBuilder.cs @@ -26,5 +26,5 @@ ISorterBuilder SortBy( /// Return collection of created sorter definitions /// /// Collection of sorter definitions - IEnumerable> Build(); + IEnumerable> Build(); } \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/IOozeSorterHandler.cs b/src/Ooze.Typed/Sorters/ISorterHandler.cs similarity index 69% rename from src/Ooze.Typed/Sorters/IOozeSorterHandler.cs rename to src/Ooze.Typed/Sorters/ISorterHandler.cs index 2efde6f..5407bfa 100644 --- a/src/Ooze.Typed/Sorters/IOozeSorterHandler.cs +++ b/src/Ooze.Typed/Sorters/ISorterHandler.cs @@ -1,8 +1,8 @@ namespace Ooze.Typed.Sorters; -internal interface IOozeSorterHandler +internal interface ISorterHandler { IQueryable Apply( IQueryable query, IEnumerable sorters); -} +} \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/IOozeSorterProvider.cs b/src/Ooze.Typed/Sorters/ISorterProvider.cs similarity index 83% rename from src/Ooze.Typed/Sorters/IOozeSorterProvider.cs rename to src/Ooze.Typed/Sorters/ISorterProvider.cs index 64a8134..bc2f7ae 100644 --- a/src/Ooze.Typed/Sorters/IOozeSorterProvider.cs +++ b/src/Ooze.Typed/Sorters/ISorterProvider.cs @@ -6,12 +6,12 @@ /// /// Entity type /// Sorter implementation type -public interface IOozeSorterProvider +public interface ISorterProvider { /// /// Method used for creation of definitions. These definitions are /// used in sorting process. /// /// Collection of sort definitions - IEnumerable> GetSorters(); -} + IEnumerable> GetSorters(); +} \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/SortDefinition.cs b/src/Ooze.Typed/Sorters/SortDefinition.cs index 5bbbe4b..acd5e2a 100644 --- a/src/Ooze.Typed/Sorters/SortDefinition.cs +++ b/src/Ooze.Typed/Sorters/SortDefinition.cs @@ -2,9 +2,9 @@ namespace Ooze.Typed.Sorters; -internal class SortDefinition : ISortDefinition +public class SortDefinition { public LambdaExpression DataExpression { get; internal set; } = null!; public Func ShouldRun { get; set; } = null!; public Func GetSortDirection { get; set; } = null!; -} +} \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/SorterBuilder.cs b/src/Ooze.Typed/Sorters/SorterBuilder.cs index eb25fa8..0e7c9c6 100644 --- a/src/Ooze.Typed/Sorters/SorterBuilder.cs +++ b/src/Ooze.Typed/Sorters/SorterBuilder.cs @@ -28,6 +28,6 @@ public ISorterBuilder SortBy( return this; } - public IEnumerable> Build() + public IEnumerable> Build() => _sortDefinitions; } \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/OozeSorterHandler.cs b/src/Ooze.Typed/Sorters/SorterHandler.cs similarity index 87% rename from src/Ooze.Typed/Sorters/OozeSorterHandler.cs rename to src/Ooze.Typed/Sorters/SorterHandler.cs index 85c691e..afe44e4 100644 --- a/src/Ooze.Typed/Sorters/OozeSorterHandler.cs +++ b/src/Ooze.Typed/Sorters/SorterHandler.cs @@ -5,10 +5,10 @@ namespace Ooze.Typed.Sorters; -internal class OozeSorterHandler( - IEnumerable> sortProviders, - ILogger> log) - : IOozeSorterHandler +internal class SorterHandler( + IEnumerable> sortProviders, + ILogger> log) + : ISorterHandler { public IQueryable Apply( IQueryable query, @@ -18,7 +18,6 @@ public IQueryable Apply( if (query == null) throw new ArgumentNullException(nameof(query)); var sortDefinitions = sortProviders.SelectMany(provider => provider.GetSorters()) - .Cast>() .ToList(); foreach (var sorter in sorters) diff --git a/src/Ooze.Typed/Sorters/Sorters.cs b/src/Ooze.Typed/Sorters/Sorters.cs index 87bc275..b687ac6 100644 --- a/src/Ooze.Typed/Sorters/Sorters.cs +++ b/src/Ooze.Typed/Sorters/Sorters.cs @@ -13,4 +13,4 @@ public static class Sorters /// Sorter builder instance for entity, sorter type combination public static ISorterBuilder CreateFor() => new SorterBuilder(); -} +} \ No newline at end of file From ae64d0baa66ae0fab5cd5dcf53a2b1d9bb3c56b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20Pavlovi=C4=87?= Date: Thu, 22 Feb 2024 20:59:43 +0100 Subject: [PATCH 2/9] Update naming of root resolvers, Add docs to exposed API --- ...cResolver.cs => AsyncOperationResolver.cs} | 20 +++++++-------- .../OozeServiceCollectionBuilder.cs | 8 +++--- .../Filters/Async/AsyncFilterDefinition.cs | 16 ++++++++++-- .../Filters/Async/IAsyncFilterBuilder.cs | 5 ++++ .../Filters/Async/IAsyncFilterProvider.cs | 4 +-- src/Ooze.Typed/Filters/FilterDefinition.cs | 16 ++++++++++-- src/Ooze.Typed/Filters/IFilterProvider.cs | 4 +-- src/Ooze.Typed/Filters/IFilters.cs | 8 +++++- ...Resolver.cs => IAsyncOperationResolver.cs} | 14 +++++------ ...TypedResolver.cs => IOperationResolver.cs} | 16 ++++++------ ...eTypedResolver.cs => OperationResolver.cs} | 22 ++++++++-------- .../Sorters/Async/AsyncSortDefinition.cs | 22 +++++++++++++--- src/Ooze.Typed/Sorters/Async/AsyncSorters.cs | 9 +++++++ .../Sorters/Async/IAsyncSorterBuilder.cs | 25 +++++++++++++++++++ .../Sorters/Async/IAsyncSorterProvider.cs | 11 ++++++++ src/Ooze.Typed/Sorters/ISorterProvider.cs | 6 ++--- src/Ooze.Typed/Sorters/SortDefinition.cs | 16 ++++++++++++ 17 files changed, 167 insertions(+), 55 deletions(-) rename src/Ooze.Typed/{OozeTypedAsyncResolver.cs => AsyncOperationResolver.cs} (90%) rename src/Ooze.Typed/{IOozeTypedAsyncResolver.cs => IAsyncOperationResolver.cs} (92%) rename src/Ooze.Typed/{IOozeTypedResolver.cs => IOperationResolver.cs} (93%) rename src/Ooze.Typed/{OozeTypedResolver.cs => OperationResolver.cs} (89%) diff --git a/src/Ooze.Typed/OozeTypedAsyncResolver.cs b/src/Ooze.Typed/AsyncOperationResolver.cs similarity index 90% rename from src/Ooze.Typed/OozeTypedAsyncResolver.cs rename to src/Ooze.Typed/AsyncOperationResolver.cs index 187cc58..f072125 100644 --- a/src/Ooze.Typed/OozeTypedAsyncResolver.cs +++ b/src/Ooze.Typed/AsyncOperationResolver.cs @@ -10,10 +10,10 @@ namespace Ooze.Typed; /// -internal class OozeTypedAsyncResolver( +internal class AsyncOperationResolver( IServiceProvider serviceProvider, - ILogger log) - : IOozeTypedAsyncResolver + ILogger log) + : IAsyncOperationResolver { public async ValueTask> FilterAsync( IQueryable query, @@ -83,16 +83,16 @@ public ValueTask> PageWithCursorAsync -internal class OozeTypedAsyncResolver( +internal class AsyncOperationResolver( IAsyncSorterHandler sorterHandler, IAsyncFilterHandler filterHandler, IOozePagingHandler pagingHandler, - ILogger> log) - : IOozeTypedAsyncResolver + ILogger> log) + : IAsyncOperationResolver { private OozeResolverData _resolverData = new(); - public IOozeTypedAsyncResolver WithQuery(IQueryable query) + public IAsyncOperationResolver WithQuery(IQueryable query) { _resolverData = _resolverData with { @@ -102,7 +102,7 @@ public IOozeTypedAsyncResolver WithQuery(IQueryable return this; } - public IOozeTypedAsyncResolver Sort(IEnumerable? sorters) + public IAsyncOperationResolver Sort(IEnumerable? sorters) { _resolverData = _resolverData with { @@ -112,7 +112,7 @@ public IOozeTypedAsyncResolver Sort(IEnumerable Filter(TFilters? filters) + public IAsyncOperationResolver Filter(TFilters? filters) { _resolverData = _resolverData with { @@ -122,7 +122,7 @@ public IOozeTypedAsyncResolver Filter(TFilters? fil return this; } - public IOozeTypedAsyncResolver Page(PagingOptions? pagingOptions) + public IAsyncOperationResolver Page(PagingOptions? pagingOptions) { _resolverData = _resolverData with { diff --git a/src/Ooze.Typed/Extensions/OozeServiceCollectionBuilder.cs b/src/Ooze.Typed/Extensions/OozeServiceCollectionBuilder.cs index 6645ebf..2577c10 100644 --- a/src/Ooze.Typed/Extensions/OozeServiceCollectionBuilder.cs +++ b/src/Ooze.Typed/Extensions/OozeServiceCollectionBuilder.cs @@ -56,8 +56,8 @@ public IOozeServiceCollectionBuilder Add(ServiceLifetime providerLife public IOozeServiceCollectionBuilder EnableAsyncResolvers() { - services.AddScoped(); - services.AddScoped(typeof(IOozeTypedAsyncResolver<,,>), typeof(OozeTypedAsyncResolver<,,>)); + services.AddScoped(); + services.AddScoped(typeof(IAsyncOperationResolver<,,>), typeof(AsyncOperationResolver<,,>)); services.AddScoped(typeof(IAsyncFilterHandler<,>), typeof(AsyncFilterHandler<,>)); services.AddScoped(typeof(IAsyncSorterHandler<,>), typeof(AsyncSorterHandler<,>)); @@ -69,8 +69,8 @@ private static bool CheckTypePredicate(Type interfaceType, Type providerType) internal IOozeServiceCollectionBuilder AddCommonServices() { - services.AddScoped(); - services.AddScoped(typeof(IOozeTypedResolver<,,>), typeof(OozeTypedResolver<,,>)); + services.AddScoped(); + services.AddScoped(typeof(IOperationResolver<,,>), typeof(OperationResolver<,,>)); services.AddScoped(typeof(IFilterHandler<,>), typeof(FilterHandler<,>)); services.AddScoped(typeof(ISorterHandler<,>), typeof(SorterHandler<,>)); services.AddScoped(typeof(IOozePagingHandler<>), typeof(OozePagingHandler<>)); diff --git a/src/Ooze.Typed/Filters/Async/AsyncFilterDefinition.cs b/src/Ooze.Typed/Filters/Async/AsyncFilterDefinition.cs index 8645c14..47b68eb 100644 --- a/src/Ooze.Typed/Filters/Async/AsyncFilterDefinition.cs +++ b/src/Ooze.Typed/Filters/Async/AsyncFilterDefinition.cs @@ -2,8 +2,20 @@ namespace Ooze.Typed.Filters.Async; +/// +/// Represents a single async filter definition +/// +/// Entity type +/// Filter type public class AsyncFilterDefinition { - public Func> ShouldRun { get; internal set; } = null!; - public Func>>> FilterExpressionFactory { get; internal set; } = null!; + /// + /// Delegate which decides if the filter should be applied + /// + public Func> ShouldRun { get; init; } = null!; + + /// + /// Delegate which creates final expression used for filtering + /// + public Func>>> FilterExpressionFactory { get; init; } = null!; } \ No newline at end of file diff --git a/src/Ooze.Typed/Filters/Async/IAsyncFilterBuilder.cs b/src/Ooze.Typed/Filters/Async/IAsyncFilterBuilder.cs index 3af3486..8200d50 100644 --- a/src/Ooze.Typed/Filters/Async/IAsyncFilterBuilder.cs +++ b/src/Ooze.Typed/Filters/Async/IAsyncFilterBuilder.cs @@ -2,6 +2,11 @@ namespace Ooze.Typed.Filters.Async; +/// +/// Interface defining contract for async filter builder implementation +/// +/// Entity type +/// Filter type public interface IAsyncFilterBuilder : IFilters> { /// diff --git a/src/Ooze.Typed/Filters/Async/IAsyncFilterProvider.cs b/src/Ooze.Typed/Filters/Async/IAsyncFilterProvider.cs index f70fe24..e98ee01 100644 --- a/src/Ooze.Typed/Filters/Async/IAsyncFilterProvider.cs +++ b/src/Ooze.Typed/Filters/Async/IAsyncFilterProvider.cs @@ -1,8 +1,8 @@ namespace Ooze.Typed.Filters.Async; /// -/// Async filter provider interface called internally by / -/// to fetch defined filters for provided Entity type. +/// Async filter provider interface called internally by / +/// to fetch defined filters for provided Entity type. /// /// Entity type /// Filter implementation type diff --git a/src/Ooze.Typed/Filters/FilterDefinition.cs b/src/Ooze.Typed/Filters/FilterDefinition.cs index eb15196..49bd4bb 100644 --- a/src/Ooze.Typed/Filters/FilterDefinition.cs +++ b/src/Ooze.Typed/Filters/FilterDefinition.cs @@ -2,8 +2,20 @@ namespace Ooze.Typed.Filters; +/// +/// Represents a single filter definition +/// +/// Entity type +/// Filter type public class FilterDefinition { - public Func ShouldRun { get; internal set; } = null!; - public Func>> FilterExpressionFactory { get; internal set; } = null!; + /// + /// Delegate which decides if the filter should be applied + /// + public Func ShouldRun { get; set; } = null!; + + /// + /// Delegate which creates final expression used for filtering + /// + public Func>> FilterExpressionFactory { get; set; } = null!; } \ No newline at end of file diff --git a/src/Ooze.Typed/Filters/IFilterProvider.cs b/src/Ooze.Typed/Filters/IFilterProvider.cs index 20d4ead..e7bbfce 100644 --- a/src/Ooze.Typed/Filters/IFilterProvider.cs +++ b/src/Ooze.Typed/Filters/IFilterProvider.cs @@ -1,8 +1,8 @@ namespace Ooze.Typed.Filters; /// -/// Filter provider interface called internally by / -/// to fetch defined filters for provided Entity type. +/// Filter provider interface called internally by / +/// to fetch defined filters for provided Entity type. /// /// Entity type /// Filter implementation type diff --git a/src/Ooze.Typed/Filters/IFilters.cs b/src/Ooze.Typed/Filters/IFilters.cs index c88d352..121ff5d 100644 --- a/src/Ooze.Typed/Filters/IFilters.cs +++ b/src/Ooze.Typed/Filters/IFilters.cs @@ -2,7 +2,13 @@ namespace Ooze.Typed.Filters; -public interface IFilters +/// +/// Interface with shared filter contracts +/// +/// Entity type +/// Filter type +/// Return type +public interface IFilters { /// /// Fluently creates "Equality" filter definition diff --git a/src/Ooze.Typed/IOozeTypedAsyncResolver.cs b/src/Ooze.Typed/IAsyncOperationResolver.cs similarity index 92% rename from src/Ooze.Typed/IOozeTypedAsyncResolver.cs rename to src/Ooze.Typed/IAsyncOperationResolver.cs index 9fbd89b..fe0204f 100644 --- a/src/Ooze.Typed/IOozeTypedAsyncResolver.cs +++ b/src/Ooze.Typed/IAsyncOperationResolver.cs @@ -7,7 +7,7 @@ namespace Ooze.Typed; /// Ooze resolver instance, contains implementations of Filtering, Sorting and Paging methods used to update /// instances. /// -public interface IOozeTypedAsyncResolver +public interface IAsyncOperationResolver { /// /// Applies valid sorters over instance. Sorters application is based of sorter provider @@ -69,14 +69,14 @@ ValueTask> PageWithCursorAsync( /// Entity type /// Filter implementation type /// Sorter implementation type -public interface IOozeTypedAsyncResolver +public interface IAsyncOperationResolver { /// - /// Registers passed instance to for upcoming operations + /// Registers passed instance to for upcoming operations /// /// Base instance /// Resolver fluent instance - IOozeTypedAsyncResolver WithQuery(IQueryable query); + IAsyncOperationResolver WithQuery(IQueryable query); /// /// Applies valid sorters over instance. Sorters application is based of sorter provider @@ -84,7 +84,7 @@ public interface IOozeTypedAsyncResolver /// /// Sorter definitions to apply over instance /// Updated instance - IOozeTypedAsyncResolver Sort(IEnumerable sorters); + IAsyncOperationResolver Sort(IEnumerable sorters); /// /// Applies valid filters over instance. Filter application is based of filter provider @@ -92,14 +92,14 @@ public interface IOozeTypedAsyncResolver /// /// Filter definitions to apply over instance /// Updated instance - IOozeTypedAsyncResolver Filter(TFilters filters); + IAsyncOperationResolver Filter(TFilters filters); /// /// Applies valid paging options over instance. /// /// Instance of paging options /// Updated instance - IOozeTypedAsyncResolver Page(PagingOptions pagingOptions); + IAsyncOperationResolver Page(PagingOptions pagingOptions); /// /// Apply and return update instance asynchronously diff --git a/src/Ooze.Typed/IOozeTypedResolver.cs b/src/Ooze.Typed/IOperationResolver.cs similarity index 93% rename from src/Ooze.Typed/IOozeTypedResolver.cs rename to src/Ooze.Typed/IOperationResolver.cs index b2efbf0..0fea555 100644 --- a/src/Ooze.Typed/IOozeTypedResolver.cs +++ b/src/Ooze.Typed/IOperationResolver.cs @@ -7,7 +7,7 @@ namespace Ooze.Typed; /// Ooze resolver instance, contains implementations of Filtering, Sorting and Paging methods used to update /// instances. /// -public interface IOozeTypedResolver +public interface IOperationResolver { /// /// Applies valid sorters over instance. Sorters application is based of sorter provider @@ -69,14 +69,14 @@ IQueryable PageWithCursor( /// Entity type /// Filter implementation type /// Sorter implementation type -public interface IOozeTypedResolver +public interface IOperationResolver { /// - /// Registers passed instance to for upcoming operations + /// Registers passed instance to for upcoming operations /// /// Base instance /// Resolver fluent instance - IOozeTypedResolver WithQuery(IQueryable query); + IOperationResolver WithQuery(IQueryable query); /// /// Applies valid sorters over instance. Sorters application is based of sorter provider @@ -84,7 +84,7 @@ public interface IOozeTypedResolver /// /// Sorter definitions to apply over instance /// Updated instance - IOozeTypedResolver Sort(IEnumerable sorters); + IOperationResolver Sort(IEnumerable sorters); /// /// Applies valid filters over instance. Filter application is based of filter provider @@ -92,14 +92,14 @@ public interface IOozeTypedResolver /// /// Filter definitions to apply over instance /// Updated instance - IOozeTypedResolver Filter(TFilters filters); + IOperationResolver Filter(TFilters filters); /// /// Applies valid paging options over instance. /// /// Instance of paging options /// Updated instance - IOozeTypedResolver Page(PagingOptions pagingOptions); + IOperationResolver Page(PagingOptions pagingOptions); /// /// Applies valid paging options over instance. @@ -109,7 +109,7 @@ public interface IOozeTypedResolver /// After type /// Property type /// Updated instance - IOozeTypedResolver PageWithCursor( + IOperationResolver PageWithCursor( Expression> cursorPropertyExpression, CursorPagingOptions? pagingOptions); diff --git a/src/Ooze.Typed/OozeTypedResolver.cs b/src/Ooze.Typed/OperationResolver.cs similarity index 89% rename from src/Ooze.Typed/OozeTypedResolver.cs rename to src/Ooze.Typed/OperationResolver.cs index e114d21..13fe65f 100644 --- a/src/Ooze.Typed/OozeTypedResolver.cs +++ b/src/Ooze.Typed/OperationResolver.cs @@ -8,10 +8,10 @@ namespace Ooze.Typed; /// -internal class OozeTypedResolver( +internal class OperationResolver( IServiceProvider serviceProvider, - ILogger log) - : IOozeTypedResolver + ILogger log) + : IOperationResolver { public IQueryable Filter( IQueryable query, @@ -83,22 +83,22 @@ public IQueryable PageWithCursor( } /// -internal class OozeTypedResolver( +internal class OperationResolver( ISorterHandler sorterHandler, IFilterHandler filterHandler, IOozePagingHandler pagingHandler, - ILogger> log) - : IOozeTypedResolver + ILogger> log) + : IOperationResolver { private IQueryable _query = null!; - public IOozeTypedResolver WithQuery(IQueryable query) + public IOperationResolver WithQuery(IQueryable query) { _query = query; return this; } - public IOozeTypedResolver Sort(IEnumerable? sorters) + public IOperationResolver Sort(IEnumerable? sorters) { sorters ??= Enumerable.Empty(); if (sorters.Any() == false) @@ -112,7 +112,7 @@ public IOozeTypedResolver Sort(IEnumerable Filter(TFilters? filters) + public IOperationResolver Filter(TFilters? filters) { if (filters is null) { @@ -125,7 +125,7 @@ public IOozeTypedResolver Filter(TFilters? filters) return this; } - public IOozeTypedResolver Page(PagingOptions? pagingOptions) + public IOperationResolver Page(PagingOptions? pagingOptions) { if (pagingOptions == null) { @@ -137,7 +137,7 @@ public IOozeTypedResolver Page(PagingOptions? pagin return this; } - public IOozeTypedResolver PageWithCursor( + public IOperationResolver PageWithCursor( Expression> cursorPropertyExpression, CursorPagingOptions? pagingOptions) { diff --git a/src/Ooze.Typed/Sorters/Async/AsyncSortDefinition.cs b/src/Ooze.Typed/Sorters/Async/AsyncSortDefinition.cs index 1c5cd5e..345bfc7 100644 --- a/src/Ooze.Typed/Sorters/Async/AsyncSortDefinition.cs +++ b/src/Ooze.Typed/Sorters/Async/AsyncSortDefinition.cs @@ -2,9 +2,25 @@ namespace Ooze.Typed.Sorters.Async; +/// +/// Represents a single async sorter definition +/// +/// +/// public class AsyncSortDefinition { - public LambdaExpression DataExpression { get; internal set; } = null!; - public Func> ShouldRun { get; set; } = null!; - public Func> GetSortDirection { get; set; } = null!; + /// + /// Sorting expression + /// + public LambdaExpression DataExpression { get; init; } = null!; + + /// + /// Delegate which decides if the sorter should be applied + /// + public Func> ShouldRun { get; init; } = null!; + + /// + /// Delegate which provides sorting direction + /// + public Func> GetSortDirection { get; init; } = null!; } \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/Async/AsyncSorters.cs b/src/Ooze.Typed/Sorters/Async/AsyncSorters.cs index 6e014a6..cd3bd5e 100644 --- a/src/Ooze.Typed/Sorters/Async/AsyncSorters.cs +++ b/src/Ooze.Typed/Sorters/Async/AsyncSorters.cs @@ -1,7 +1,16 @@ namespace Ooze.Typed.Sorters.Async; +/// +/// Static class with helper methods to start building async sorter definitions for specified entity type and sorters type +/// public static class AsyncSorters { + /// + /// Creates a new instance of async sorter builder for passed entity type and sorter type + /// + /// Entity type + /// Sorter type + /// Sorter builder instance for entity, sorter type combination public static IAsyncSorterBuilder CreateFor() => new AsyncSorterBuilder(); } \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/Async/IAsyncSorterBuilder.cs b/src/Ooze.Typed/Sorters/Async/IAsyncSorterBuilder.cs index a9370f4..4a56564 100644 --- a/src/Ooze.Typed/Sorters/Async/IAsyncSorterBuilder.cs +++ b/src/Ooze.Typed/Sorters/Async/IAsyncSorterBuilder.cs @@ -2,17 +2,42 @@ namespace Ooze.Typed.Sorters.Async; +/// +/// Interface defining contract for async sorter builder implementation +/// +/// Entity type +/// Sorter type public interface IAsyncSorterBuilder { + /// + /// Creates a new sort definition fluently for specified entity property and sorter property + /// + /// Expression targeting property of specified entity class + /// Delegate targeting property of sorter class + /// Delegate returning bool value which denotes if sorter should be applied + /// Target type of entity property + /// Instance of builder for fluent building of multiple sorter definitions IAsyncSorterBuilder SortBy( Expression> dataExpression, Func sorterFunc, Func? shouldRun = null); + /// + /// Creates a new async sort definition fluently for specified entity property and sorter property + /// + /// Expression targeting property of specified entity class + /// Delegate targeting property of sorter class + /// Delegate returning bool value which denotes if sorter should be applied + /// Target type of entity property + /// Instance of builder for fluent building of multiple sorter definitions IAsyncSorterBuilder SortByAsync( Expression> dataExpression, Func> sorterFunc, Func>? shouldRun = null); + /// + /// Return collection of created sorter definitions + /// + /// Collection of sorter definitions IEnumerable> Build(); } \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/Async/IAsyncSorterProvider.cs b/src/Ooze.Typed/Sorters/Async/IAsyncSorterProvider.cs index faaad6c..5a17167 100644 --- a/src/Ooze.Typed/Sorters/Async/IAsyncSorterProvider.cs +++ b/src/Ooze.Typed/Sorters/Async/IAsyncSorterProvider.cs @@ -1,6 +1,17 @@ namespace Ooze.Typed.Sorters.Async; +/// +/// Async sorter provider interface called internally by / +/// to fetch defined sorters for provided Entity type. +/// +/// Entity type +/// Sorter implementation type public interface IAsyncSorterProvider { + /// + /// Method used for creation of definitions. These definitions are + /// used in sorting process. + /// + /// Collection of sort definitions ValueTask>> GetSortersAsync(); } \ No newline at end of file diff --git a/src/Ooze.Typed/Sorters/ISorterProvider.cs b/src/Ooze.Typed/Sorters/ISorterProvider.cs index bc2f7ae..1dd8cbe 100644 --- a/src/Ooze.Typed/Sorters/ISorterProvider.cs +++ b/src/Ooze.Typed/Sorters/ISorterProvider.cs @@ -1,15 +1,15 @@ namespace Ooze.Typed.Sorters; /// -/// Sorter provider interface called internally by / -/// to fetch defined sorters for provided Entity type. +/// Sorter provider interface called internally by / +/// to fetch defined sorters for provided Entity type. /// /// Entity type /// Sorter implementation type public interface ISorterProvider { /// - /// Method used for creation of definitions. These definitions are + /// Method used for creation of definitions. These definitions are /// used in sorting process. /// /// Collection of sort definitions diff --git a/src/Ooze.Typed/Sorters/SortDefinition.cs b/src/Ooze.Typed/Sorters/SortDefinition.cs index acd5e2a..bcaff9b 100644 --- a/src/Ooze.Typed/Sorters/SortDefinition.cs +++ b/src/Ooze.Typed/Sorters/SortDefinition.cs @@ -2,9 +2,25 @@ namespace Ooze.Typed.Sorters; +/// +/// Represents a single async sorter definition +/// +/// +/// public class SortDefinition { + /// + /// Sorting expression + /// public LambdaExpression DataExpression { get; internal set; } = null!; + + /// + /// Delegate which decides if the sorter should be applied + /// public Func ShouldRun { get; set; } = null!; + + /// + /// Delegate which provides sorting direction + /// public Func GetSortDirection { get; set; } = null!; } \ No newline at end of file From 4bfac0322f2f27c7597e14196dda50481f7113c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20Pavlovi=C4=87?= Date: Thu, 22 Feb 2024 21:12:41 +0100 Subject: [PATCH 3/9] Update README --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 8d92b7a..4e665d3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Ooze.Typed 🌳💧🔧 -![Nuget](https://img.shields.io/nuget/v/Ooze.Typed) +[![Nuget](https://img.shields.io/nuget/v/Ooze.Typed)](https://www.nuget.org/packages/Ooze.Typed/) ![framework](https://img.shields.io/badge/framework-.net%206.0-green) ![framework](https://img.shields.io/badge/framework-.net%207.0-green) ![framework](https://img.shields.io/badge/framework-.net%208.0-green) @@ -43,11 +43,11 @@ public class Startup This call will register internal services needed by `Ooze` and will in turn return a related "builder" via which you can then register your `provider` implementations. ## Adding Filters 🗡️🧀 -After registering Ooze you need to create your filter definition. This can be done by implementing `IOozeFilterProvider` interface. After creating implementation you can use static `Filters` class to start of the builder which will in turn create your filter definitions. Example can be seen below: +After registering Ooze you need to create your filter definition. This can be done by implementing `IFilterProvider` interface. After creating implementation you can use static `Filters` class to start of the builder which will in turn create your filter definitions. Example can be seen below: ```csharp -public class MyClassFiltersProvider : IOozeFilterProvider +public class MyClassFiltersProvider : IFilterProvider { - public IEnumerable> GetFilters() + public IEnumerable> GetFilters() { return Filters.CreateFor() //add equality filter onto MyClass instance over Id property and use Id property from incoming filter instance in that operation @@ -59,7 +59,7 @@ public class MyClassFiltersProvider : IOozeFilterProvider> GetFilters() +public IEnumerable> GetFilters() { return Filters.CreateFor() //check if property is equal to filter @@ -105,11 +105,11 @@ By default all the provider implementations that you register via `.Add` interface, and using `Sorters` static class to start of builder for creating sorters. Example of this can be found below: +by `Ooze` to sort your queries. This is done by implementing `ISorterProvider` interface, and using `Sorters` static class to start of builder for creating sorters. Example of this can be found below: ```csharp -public class MyClassSortersProvider : IOozeSorterProvider +public class MyClassSortersProvider : ISorterProvider { - public IEnumerable> GetSorters() + public IEnumerable> GetSorters() { return Sorters.CreateFor() //add sorting on Id property in provided direction from sorter instance @@ -153,12 +153,12 @@ query = resolver ``` ## Applying definitions 🧪 -In order to apply filter/sorter definitions you need to get instance of `IOozeTypedResolver`/`IOozeTypedResolver` after that you can just call methods in order to change `IQueryable` instance. Here is a more elaborate example below: +In order to apply filter/sorter definitions you need to get instance of `IOperationResolver`/`IOperationResolver` after that you can just call methods in order to change `IQueryable` instance. Here is a more elaborate example below: ```csharp //lets say you have a route which gets filters/sorters from request body app.MapPost("/", ( DatabaseContext db, - IOozeTypedResolver resolver, + IOperationResolver resolver, Input model) => { IQueryable query = db.Set(); @@ -188,7 +188,7 @@ Filter builders have a special parameter called `shouldRun` which is by default Example of this can be seen below: ```csharp -public IEnumerable> GetFilters() +public IEnumerable> GetFilters() { return Filters.CreateFor() //common filter definition, shouldRun is not used here but is resolved internally @@ -199,20 +199,20 @@ public IEnumerable> GetFilters() .Build(); } ``` -Due to nature of how `OozeFilterProvider` implementation work you can even create a custom filter collection +Due to nature of how `FilterProvider` implementation works you can even create a custom filter collection which will depend on a specific parameter being passed in the request. For example you could do something like next example (but you don't have to and I'm not sure why would you): ```csharp -public class BlogFiltersProvider : IOozeFilterProvider +public class BlogFiltersProvider : IFilterProvider { private readonly IHttpContextAccessor _httpContextAccessor; public BlogFiltersProvider(IHttpContextAccessor httpContextAccessor) => _httpContextAccessor = httpContextAccessor; - public IEnumerable> GetFilters() + public IEnumerable> GetFilters() { var httpContext = _httpContextAccessor.HttpContext; var hasSecretParam = !httpContext?.Request.Query.ContainsKey("secret") ?? true; @@ -227,7 +227,7 @@ public class BlogFiltersProvider : IOozeFilterProvider } ``` -Similar can be applied to `OozeSorterProvider` implementations which also contain `shouldRun` parameter on +Similar can be applied to `SorterProvider` implementations which also contain `shouldRun` parameter on sorter builder extensions. Be careful when using `IHttpContextAccessor` in this way and be sure to read about how to correctly use it over on [this link](https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AspNetCoreGuidance.md#do-not-store-ihttpcontextaccessorhttpcontext-in-a-field). @@ -250,11 +250,11 @@ namespace Ooze.Typed.Web.Filters; public sealed class OozeFilter : IAsyncResultFilter where TEntity : class { - private readonly IOozeTypedResolver _resolver; + private readonly IOperationResolver _resolver; private readonly ILogger> _log; public OozeFilter( - IOozeTypedResolver resolver, + IOperationResolver resolver, ILogger> log) { _resolver = resolver; @@ -309,7 +309,7 @@ For more details look at sample project in `tests/Ooze.Typed.Web`.
Applying filters and sorters on IEnumerable collections -Due to nature of `IQueryable` and `IEnumerable` you can even use `OozeTypedResolver` on materialized or in memory collections for example `List`. You'll just need to convert it (cast it) to `IQueryable` via `.AsQueryable()` method. Notice that this can lead to exception since not all operations can be used this way. Some operations can't be used on `client side` and this can cause errors. +Due to nature of `IQueryable` and `IEnumerable` you can even use `OozeOperationResolver` on materialized or in memory collections for example `List`. You'll just need to convert it (cast it) to `IQueryable` via `.AsQueryable()` method. Notice that this can lead to exception since not all operations can be used this way. Some operations can't be used on `client side` and this can cause errors. Example of all this can be seen below: ```csharp From ce7dbd61231b3b6642b8a57c6d6cc00cbc402942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20Pavlovi=C4=87?= Date: Thu, 22 Feb 2024 21:23:03 +0100 Subject: [PATCH 4/9] Update test project, update sample project --- .../MySqlIntegrationTests.cs | 16 +++--- .../OozeConfiguration/PostFiltersProvider.cs | 4 +- .../OozeConfiguration/PostSortersProvider.cs | 4 +- .../NpgsqlIntegrationTests.cs | 32 ++++++------ .../OozeConfiguration/PostFiltersProvider.cs | 9 ++-- .../OozeConfiguration/PostSortersProvider.cs | 4 +- .../OozeConfiguration/PostFiltersProvider.cs | 4 +- .../OozeConfiguration/PostSortersProvider.cs | 4 +- .../SqlServerIntegrationTests.cs | 50 +++++++++---------- tests/Ooze.Typed.Tests/FilterHandlerTests.cs | 16 +++--- .../DatabaseFilterEqualIntegrationTests.cs | 8 +-- ...tabaseFilterGreaterThanIntegrationTests.cs | 8 +-- .../DatabaseFilterInIntegrationTests.cs | 6 +-- .../DatabaseFilterLessThanIntegrationTests.cs | 8 +-- .../DatabaseFilterNotEqualIntegrationTests.cs | 8 +-- .../DatabaseFilterNotInIntegrationTests.cs | 6 +-- ...atabaseFilterOutOfRangeIntegrationTests.cs | 6 +-- .../DatabaseFilterRangeIntegrationTests.cs | 6 +-- .../DatabaseSorterIntegrationTests.cs | 8 +-- .../Setup/PostEqualFiltersProvider.cs | 4 +- .../Setup/PostGreaterThanFiltersProvider.cs | 4 +- .../Setup/PostInFiltersProvider.cs | 4 +- .../Setup/PostLessThanFiltersProvider.cs | 4 +- .../Setup/PostNotEqualFiltersProvider.cs | 4 +- .../Setup/PostNotInFiltersProvider.cs | 4 +- .../Setup/PostOutOfRangeFiltersProvider.cs | 4 +- .../Setup/PostRangeFiltersProvider.cs | 4 +- .../Integration/Setup/PostSortersProvider.cs | 4 +- .../ServiceCollectionTests.cs | 38 +++++++------- tests/Ooze.Typed.Tests/SorterHandlerTests.cs | 16 +++--- .../Controllers/BlogController.cs | 22 +++++--- .../Controllers/CommentController.cs | 4 +- tests/Ooze.Typed.Web/Filters/OozeFilter.cs | 4 +- .../OozeConfiguration/BlogFiltersProvider.cs | 24 ++++++++- .../OozeConfiguration/BlogSortersProvider.cs | 12 ++--- tests/Ooze.Typed.Web/Program.cs | 1 + 36 files changed, 197 insertions(+), 167 deletions(-) diff --git a/tests/Ooze.Typed.Tests.MySql/MySqlIntegrationTests.cs b/tests/Ooze.Typed.Tests.MySql/MySqlIntegrationTests.cs index 2695d47..da82502 100644 --- a/tests/Ooze.Typed.Tests.MySql/MySqlIntegrationTests.cs +++ b/tests/Ooze.Typed.Tests.MySql/MySqlIntegrationTests.cs @@ -11,7 +11,7 @@ public async Task DateDiffDay_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffDayFilter: new DateTime(2022, 5, 20))); @@ -29,7 +29,7 @@ public async Task DateDiffMonth_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffMonthFilter: new DateTime(2022, 5, 20))); @@ -47,7 +47,7 @@ public async Task DateDiffYear_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffYearFilter: new DateTime(2022, 5, 20))); @@ -65,7 +65,7 @@ public async Task DateDiffHour_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffHourFilter: new DateTime(2022, 5, 20))); @@ -83,7 +83,7 @@ public async Task DateDiffMinute_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffMinuteFilter: new DateTime(2022, 5, 20))); @@ -101,7 +101,7 @@ public async Task DateDiffSecond_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffSecondFilter: new DateTime(2022, 5, 20))); @@ -119,7 +119,7 @@ public async Task DateDiffMicrosecond_Should_Update_Query_And_Return_Correct_Que { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffMicrosecondFilter: new DateTime(2022, 2, 2, 20, 20, 22))); @@ -134,7 +134,7 @@ public async Task Id_Sorter_Should_Update_Query_And_Return_Correctly_Ordered_Dat { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); var defaultIds = await query.Select(x => x.Id) .ToListAsync(); diff --git a/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/PostFiltersProvider.cs b/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/PostFiltersProvider.cs index 4212c0b..b0a2c6c 100644 --- a/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/PostFiltersProvider.cs +++ b/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/PostFiltersProvider.cs @@ -3,9 +3,9 @@ namespace Ooze.Typed.Tests.MySql.OozeConfiguration; -public class PostFiltersProvider : IOozeFilterProvider +public class PostFiltersProvider : IFilterProvider { - public IEnumerable> GetFilters() + public IEnumerable> GetFilters() => Filters.Filters.CreateFor() .IsDateDiffDay(post => post.Date, filter => filter.DateDiffDayFilter, DateDiffOperation.Equal) .IsDateDiffMonth(post => post.Date, filter => filter.DateDiffMonthFilter, DateDiffOperation.Equal) diff --git a/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/PostSortersProvider.cs b/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/PostSortersProvider.cs index 572e7b2..400e9a1 100644 --- a/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/PostSortersProvider.cs +++ b/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/PostSortersProvider.cs @@ -2,9 +2,9 @@ namespace Ooze.Typed.Tests.MySql.OozeConfiguration; -public class PostSortersProvider : IOozeSorterProvider +public class PostSortersProvider : ISorterProvider { - public IEnumerable> GetSorters() + public IEnumerable> GetSorters() => Sorters.Sorters.CreateFor() .SortBy(post => post.Id, sort => sort.Id) .Build(); diff --git a/tests/Ooze.Typed.Tests.Npgsql/NpgsqlIntegrationTests.cs b/tests/Ooze.Typed.Tests.Npgsql/NpgsqlIntegrationTests.cs index ca72537..7a2cfe0 100644 --- a/tests/Ooze.Typed.Tests.Npgsql/NpgsqlIntegrationTests.cs +++ b/tests/Ooze.Typed.Tests.Npgsql/NpgsqlIntegrationTests.cs @@ -15,7 +15,7 @@ public async Task Equal_Should_Update_Query_And_Return_Correct_Query(int postId) { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(PostId: postId)); @@ -31,7 +31,7 @@ public async Task NotEqual_Should_Update_Query_And_Return_Correct_Query(int post { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NotEqualPostId: postId)); @@ -47,7 +47,7 @@ public async Task GreaterThan_Should_Update_Query_And_Return_Correct_Query(int p { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(GreaterThanPostId: postId)); @@ -64,7 +64,7 @@ public async Task LessThan_Should_Update_Query_And_Return_Correct_Query(int post { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(LessThanPostId: postId)); @@ -79,7 +79,7 @@ public async Task In_Should_Update_Query_And_Return_Correct_Query() var postIds = new long[] { 1, 10, 100 }; await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdIn: postIds)); @@ -96,7 +96,7 @@ public async Task NotIn_Should_Update_Query_And_Return_Correct_Query() var postIds = new long[] { -1, 1000, -100 }; await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdIn: postIds)); @@ -118,7 +118,7 @@ public async Task Range_Should_Update_Query_And_Return_Correct_Query( { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdRange: new RangeFilter { @@ -144,7 +144,7 @@ public async Task OutOfRange_Should_Update_Query_And_Return_Correct_Query( { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdOutOfRange: new RangeFilter { @@ -167,7 +167,7 @@ public async Task StartsWith_Should_Update_Query_And_Return_Correct_Query(string { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameStartsWith: prefix)); @@ -185,7 +185,7 @@ public async Task DoesntStartWith_Should_Update_Query_And_Return_Correct_Query(s { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameDoesntWith: prefix)); @@ -203,7 +203,7 @@ public async Task EndsWith_Should_Update_Query_And_Return_Correct_Query(string s { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameEndsWith: suffix)); @@ -221,7 +221,7 @@ public async Task DoesntEndWith_Should_Update_Query_And_Return_Correct_Query(str { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameDoesntEndWith: suffix)); @@ -236,7 +236,7 @@ public async Task InsensitiveLike_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameLikeFilter: "%Sample%")); @@ -245,7 +245,7 @@ public async Task InsensitiveLike_Should_Update_Query_And_Return_Correct_Query() Assert.True(sqlContainsCall); var hasFilteredItems = await query.AnyAsync(); - Assert.True(hasFilteredItems == true); + Assert.True(hasFilteredItems == false); } [Fact] @@ -253,7 +253,7 @@ public async Task SoundexEqual_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameSoundexEqual: "%Sample%")); @@ -270,7 +270,7 @@ public async Task Id_Sorter_Should_Update_Query_And_Return_Correctly_Ordered_Dat { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); var defaultIds = await query.Select(x => x.Id) .ToListAsync(); diff --git a/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/PostFiltersProvider.cs b/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/PostFiltersProvider.cs index 88ae9c9..1f9aaca 100644 --- a/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/PostFiltersProvider.cs +++ b/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/PostFiltersProvider.cs @@ -3,11 +3,14 @@ namespace Ooze.Typed.Tests.Npgsql.OozeConfiguration; -public class PostFiltersProvider : IOozeFilterProvider +public class PostFiltersProvider : IFilterProvider { - public IEnumerable> GetFilters() + public IEnumerable> GetFilters() => Filters.Filters.CreateFor() - .Equal(post => post.Id, filter => filter.PostId) + .Equal(post => post.Id, filter => filter.PostId, _ => + { + return true; + }) .NotEqual(post => post.Id, filter => filter.NotEqualPostId) .GreaterThan(post => post.Id, filter => filter.GreaterThanPostId) .LessThan(post => post.Id, filter => filter.LessThanPostId) diff --git a/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/PostSortersProvider.cs b/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/PostSortersProvider.cs index 2c42ee7..9119e04 100644 --- a/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/PostSortersProvider.cs +++ b/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/PostSortersProvider.cs @@ -2,9 +2,9 @@ namespace Ooze.Typed.Tests.Npgsql.OozeConfiguration; -public class PostSortersProvider : IOozeSorterProvider +public class PostSortersProvider : ISorterProvider { - public IEnumerable> GetSorters() + public IEnumerable> GetSorters() => Sorters.Sorters.CreateFor() .SortBy(post => post.Id, sort => sort.Id) .Build(); diff --git a/tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/PostFiltersProvider.cs b/tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/PostFiltersProvider.cs index 4dd4fab..2fe974d 100644 --- a/tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/PostFiltersProvider.cs +++ b/tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/PostFiltersProvider.cs @@ -3,9 +3,9 @@ namespace Ooze.Typed.Tests.SqlServer.OozeConfiguration; -public class PostFiltersProvider : IOozeFilterProvider +public class PostFiltersProvider : IFilterProvider { - public IEnumerable> GetFilters() + public IEnumerable> GetFilters() => Filters.Filters.CreateFor() .Equal(post => post.Id, filter => filter.PostId) .NotEqual(post => post.Id, filter => filter.NotEqualPostId) diff --git a/tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/PostSortersProvider.cs b/tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/PostSortersProvider.cs index 8160430..e5b860e 100644 --- a/tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/PostSortersProvider.cs +++ b/tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/PostSortersProvider.cs @@ -2,9 +2,9 @@ namespace Ooze.Typed.Tests.SqlServer.OozeConfiguration; -public class PostSortersProvider : IOozeSorterProvider +public class PostSortersProvider : ISorterProvider { - public IEnumerable> GetSorters() + public IEnumerable> GetSorters() => Sorters.Sorters.CreateFor() .SortBy(post => post.Id, sort => sort.Id) .Build(); diff --git a/tests/Ooze.Typed.Tests.SqlServer/SqlServerIntegrationTests.cs b/tests/Ooze.Typed.Tests.SqlServer/SqlServerIntegrationTests.cs index 666b1f4..fdb9b38 100644 --- a/tests/Ooze.Typed.Tests.SqlServer/SqlServerIntegrationTests.cs +++ b/tests/Ooze.Typed.Tests.SqlServer/SqlServerIntegrationTests.cs @@ -15,7 +15,7 @@ public async Task Equal_Should_Update_Query_And_Return_Correct_Query(int postId) { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(PostId: postId)); @@ -31,7 +31,7 @@ public async Task NotEqual_Should_Update_Query_And_Return_Correct_Query(int post { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NotEqualPostId: postId)); @@ -47,7 +47,7 @@ public async Task GreaterThan_Should_Update_Query_And_Return_Correct_Query(int p { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(GreaterThanPostId: postId)); @@ -64,7 +64,7 @@ public async Task LessThan_Should_Update_Query_And_Return_Correct_Query(int post { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(LessThanPostId: postId)); @@ -79,7 +79,7 @@ public async Task In_Should_Update_Query_And_Return_Correct_Query() var postIds = new long[] { 1, 10, 100 }; await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdIn: postIds)); @@ -96,7 +96,7 @@ public async Task NotIn_Should_Update_Query_And_Return_Correct_Query() var postIds = new long[] { -1, 1000, -100 }; await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdIn: postIds)); @@ -118,7 +118,7 @@ public async Task Range_Should_Update_Query_And_Return_Correct_Query( { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdRange: new RangeFilter { @@ -144,7 +144,7 @@ public async Task OutOfRange_Should_Update_Query_And_Return_Correct_Query( { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdOutOfRange: new RangeFilter { @@ -167,7 +167,7 @@ public async Task StartsWith_Should_Update_Query_And_Return_Correct_Query(string { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameStartsWith: prefix)); @@ -185,7 +185,7 @@ public async Task DoesntStartWith_Should_Update_Query_And_Return_Correct_Query(s { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameDoesntWith: prefix)); @@ -203,7 +203,7 @@ public async Task EndsWith_Should_Update_Query_And_Return_Correct_Query(string s { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameEndsWith: suffix)); @@ -221,7 +221,7 @@ public async Task DoesntEndWith_Should_Update_Query_And_Return_Correct_Query(str { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameDoesntEndWith: suffix)); @@ -236,7 +236,7 @@ public async Task IsDate_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IsNameDate: true)); @@ -253,7 +253,7 @@ public async Task IsNumeric_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IsIdNumeric: true)); @@ -270,7 +270,7 @@ public async Task DateDiffDay_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffDay: new DateTime(2022, 5, 20))); @@ -288,7 +288,7 @@ public async Task DateDiffMonth_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffMonth: new DateTime(2022, 5, 20))); @@ -306,7 +306,7 @@ public async Task DateDiffYear_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffYear: new DateTime(2022, 5, 20))); @@ -324,7 +324,7 @@ public async Task DateDiffWeek_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffWeek: new DateTime(2022, 5, 20))); @@ -342,7 +342,7 @@ public async Task DateDiffHour_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffHour: new DateTime(2022, 5, 20))); @@ -360,7 +360,7 @@ public async Task DateDiffMinute_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffMinute: new DateTime(2022, 5, 20))); @@ -378,7 +378,7 @@ public async Task DateDiffSecond_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffSecond: new DateTime(2022, 5, 20))); @@ -396,7 +396,7 @@ public async Task DateDiffMillisecond_Should_Update_Query_And_Return_Correct_Que { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffMillisecond: new DateTime(2022, 2, 2, 20, 20, 22), DateDiffDayEqual: new DateTime(2022, 2, 2))); @@ -414,7 +414,7 @@ public async Task DateDiffMicrosecond_Should_Update_Query_And_Return_Correct_Que { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffMicrosecond: new DateTime(2022, 2, 2, 20, 20, 22), DateDiffDayEqual: new DateTime(2022, 2, 2, 20, 20, 22))); @@ -432,7 +432,7 @@ public async Task DateDiffNanosecond_Should_Update_Query_And_Return_Correct_Quer { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffDayEqual: new DateTime(2022, 2, 2, 20, 20, 22), DateDiffNanosecond: new DateTime(2022, 2, 2, 20, 20, 22))); @@ -450,7 +450,7 @@ public async Task Id_Sorter_Should_Update_Query_And_Return_Correctly_Ordered_Dat { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.ServiceProvider.GetRequiredService(); IQueryable query = context.Set(); var defaultIds = await query.Select(x => x.Id) .ToListAsync(); diff --git a/tests/Ooze.Typed.Tests/FilterHandlerTests.cs b/tests/Ooze.Typed.Tests/FilterHandlerTests.cs index c1e97d8..31d5685 100644 --- a/tests/Ooze.Typed.Tests/FilterHandlerTests.cs +++ b/tests/Ooze.Typed.Tests/FilterHandlerTests.cs @@ -76,20 +76,20 @@ public void Should_Call_Correct_FilterExpressionFactory() class SUT { - public IOozeFilterProvider Provider1 { get; } - public IOozeFilterProvider Provider2 { get; } + public IFilterProvider Provider1 { get; } + public IFilterProvider Provider2 { get; } public IQueryable Query { get; } - public IOozeFilterHandler Handler { get; } - private ILogger> Log { get; } + public IFilterHandler Handler { get; } + private ILogger> Log { get; } public SUT() { - Provider1 = Substitute.For>(); - Provider2 = Substitute.For>(); - Log = Substitute.For>>(); + Provider1 = Substitute.For>(); + Provider2 = Substitute.For>(); + Log = Substitute.For>>(); Query = Enumerable.Empty().AsQueryable(); - Handler = new OozeFilterHandler(new[] { Provider1, Provider2 }, Log); + Handler = new FilterHandler(new[] { Provider1, Provider2 }, Log); } } } \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterEqualIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterEqualIntegrationTests.cs index b2f51c7..4d289de 100644 --- a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterEqualIntegrationTests.cs +++ b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterEqualIntegrationTests.cs @@ -18,7 +18,7 @@ public async Task Should_Correctly_Filter_Data_By_Equal_Int_Filter(int postId) var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) @@ -41,7 +41,7 @@ public async Task Should_Correctly_Filter_Data_By_Equal_String_Filter(string pos var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) @@ -62,7 +62,7 @@ public async Task Should_Correctly_Filter_Data_By_Equal_Bool_Filter(bool enabled var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) @@ -87,7 +87,7 @@ public async Task Should_Correctly_Filter_Data_By_Equal_DateTime_Filter( var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); var filterDate = new DateTime(year, month, day); IQueryable query = context.Posts; diff --git a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterGreaterThanIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterGreaterThanIntegrationTests.cs index 8dc5344..3e26869 100644 --- a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterGreaterThanIntegrationTests.cs +++ b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterGreaterThanIntegrationTests.cs @@ -18,7 +18,7 @@ public async Task Should_Correctly_Filter_Data_By_Greater_Than_Int_Filter(int po var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) @@ -42,7 +42,7 @@ public async Task Should_Fail_To_Filter_Data_By_Greater_Than_String_Filter(strin var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; Assert.Throws(() => oozeResolver.WithQuery(query) @@ -59,7 +59,7 @@ public async Task Should_Fail_To_Filter_Data_By_Greater_Than_Bool_Filter(bool en var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; Assert.Throws(() => oozeResolver.WithQuery(query) @@ -80,7 +80,7 @@ public async Task Should_Correctly_Filter_Data_By_Greater_Than_DateTime_Filter( var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); var filterDate = new DateTime(year, month, day); IQueryable query = context.Posts; diff --git a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterInIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterInIntegrationTests.cs index 2ca8901..03c2eee 100644 --- a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterInIntegrationTests.cs +++ b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterInIntegrationTests.cs @@ -21,7 +21,7 @@ public async Task Should_Correctly_Filter_Data_By_In_Int_Filter(params int[] pos var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) @@ -45,7 +45,7 @@ public async Task Should_Correctly_Filter_Data_By_In_String_Filter(params string var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) @@ -66,7 +66,7 @@ public async Task Should_Correctly_Filter_Data_By_In_DateTime_Filter(params Date var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) diff --git a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterLessThanIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterLessThanIntegrationTests.cs index 8b13474..60a121a 100644 --- a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterLessThanIntegrationTests.cs +++ b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterLessThanIntegrationTests.cs @@ -18,7 +18,7 @@ public async Task Should_Correctly_Filter_Data_By_Less_Than_Int_Filter(int postI var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) @@ -42,7 +42,7 @@ public async Task Should_Fail_To_Filter_Data_By_Less_Than_String_Filter(string p var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; Assert.Throws(() => oozeResolver.WithQuery(query) @@ -59,7 +59,7 @@ public async Task Should_Fail_To_Filter_Data_By_Less_Than_Bool_Filter(bool enabl var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; Assert.Throws(() => oozeResolver.WithQuery(query) @@ -80,7 +80,7 @@ public async Task Should_Correctly_Filter_Data_By_Less_Than_DateTime_Filter( var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); var filterDate = new DateTime(year, month, day); IQueryable query = context.Posts; diff --git a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterNotEqualIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterNotEqualIntegrationTests.cs index aed3b8b..5b47c22 100644 --- a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterNotEqualIntegrationTests.cs +++ b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterNotEqualIntegrationTests.cs @@ -18,7 +18,7 @@ public async Task Should_Correctly_Filter_Data_By_Not_Equal_Int_Filter(int postI var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) @@ -41,7 +41,7 @@ public async Task Should_Correctly_Filter_Data_By_Not_Equal_String_Filter(string var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) @@ -62,7 +62,7 @@ public async Task Should_Correctly_Filter_Data_By_Not_Equal_Bool_Filter(bool ena var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) @@ -87,7 +87,7 @@ public async Task Should_Correctly_Filter_Data_By_Not_Equal_DateTime_Filter( var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); var filterDate = new DateTime(year, month, day); IQueryable query = context.Posts; diff --git a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterNotInIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterNotInIntegrationTests.cs index 8eec975..4fdce64 100644 --- a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterNotInIntegrationTests.cs +++ b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterNotInIntegrationTests.cs @@ -20,7 +20,7 @@ public async Task Should_Correctly_Filter_Data_By_Not_In_Int_Filter(params int[] var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) @@ -43,7 +43,7 @@ public async Task Should_Correctly_Filter_Data_By_Not_In_String_Filter(params st var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) @@ -63,7 +63,7 @@ public async Task Should_Correctly_Filter_Data_By_Not_In_DateTime_Filter(params var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) diff --git a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterOutOfRangeIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterOutOfRangeIntegrationTests.cs index 6f97149..ef6c284 100644 --- a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterOutOfRangeIntegrationTests.cs +++ b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterOutOfRangeIntegrationTests.cs @@ -22,7 +22,7 @@ public async Task Should_Correctly_Filter_Data_By_Out_Of_Range_Int_Filter( var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) @@ -44,7 +44,7 @@ public async Task Should_Correctly_Filter_Data_By_Out_Of_Range_String_Filter(str var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; Assert.Throws(() => oozeResolver.WithQuery(query) @@ -67,7 +67,7 @@ public async Task Should_Correctly_Filter_Data_By_Out_Of_Range_DateTime_Filter() var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) diff --git a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterRangeIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterRangeIntegrationTests.cs index f896529..5427f0d 100644 --- a/tests/Ooze.Typed.Tests/Integration/DatabaseFilterRangeIntegrationTests.cs +++ b/tests/Ooze.Typed.Tests/Integration/DatabaseFilterRangeIntegrationTests.cs @@ -22,7 +22,7 @@ public async Task Should_Correctly_Filter_Data_By_Range_Int_Filter( var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) @@ -44,7 +44,7 @@ public async Task Should_Correctly_Filter_Data_By_Range_String_Filter(string fro var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; Assert.Throws(() => oozeResolver.WithQuery(query) @@ -67,7 +67,7 @@ public async Task Should_Correctly_Filter_Data_By_Range_DateTime_Filter() var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); IQueryable query = context.Posts; query = oozeResolver.WithQuery(query) diff --git a/tests/Ooze.Typed.Tests/Integration/DatabaseSorterIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/DatabaseSorterIntegrationTests.cs index 8f96b76..fd727aa 100644 --- a/tests/Ooze.Typed.Tests/Integration/DatabaseSorterIntegrationTests.cs +++ b/tests/Ooze.Typed.Tests/Integration/DatabaseSorterIntegrationTests.cs @@ -15,7 +15,7 @@ public async Task Should_Correctly_Sort_Data_Descending_By_Single_Field() var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); var sorters = new[] { @@ -39,7 +39,7 @@ public async Task Should_Correctly_Sort_Data_Ascending_By_Single_Field() var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); var sorters = new[] { @@ -63,7 +63,7 @@ public async Task Should_Correctly_Sort_Data_Ascending_By_Multple_Fields() var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); var sorters = new[] { @@ -88,7 +88,7 @@ public async Task Should_Not_Sort_Data_If_Sort_Not_Provided() var provider = scope.ServiceProvider; await using var context = fixture.CreateContext(); - var oozeResolver = provider.GetRequiredService>(); + var oozeResolver = provider.GetRequiredService>(); var sorters = Enumerable.Empty(); IQueryable query = context.Posts; diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/PostEqualFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/PostEqualFiltersProvider.cs index bae7e09..8354722 100644 --- a/tests/Ooze.Typed.Tests/Integration/Setup/PostEqualFiltersProvider.cs +++ b/tests/Ooze.Typed.Tests/Integration/Setup/PostEqualFiltersProvider.cs @@ -2,9 +2,9 @@ namespace Ooze.Typed.Tests.Integration.Setup; -public class PostEqualFiltersProvider : IOozeFilterProvider +public class PostEqualFiltersProvider : IFilterProvider { - public IEnumerable> GetFilters() + public IEnumerable> GetFilters() => Filters.Filters.CreateFor() .Equal(post => post.Id, filter => filter.Id) .Equal(post => post.Name, filter => filter.Name) diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/PostGreaterThanFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/PostGreaterThanFiltersProvider.cs index 3c7802a..4a97483 100644 --- a/tests/Ooze.Typed.Tests/Integration/Setup/PostGreaterThanFiltersProvider.cs +++ b/tests/Ooze.Typed.Tests/Integration/Setup/PostGreaterThanFiltersProvider.cs @@ -2,9 +2,9 @@ namespace Ooze.Typed.Tests.Integration.Setup; -public class PostGreaterThanFiltersProvider : IOozeFilterProvider +public class PostGreaterThanFiltersProvider : IFilterProvider { - public IEnumerable> GetFilters() + public IEnumerable> GetFilters() => Filters.Filters.CreateFor() .GreaterThan(post => post.Id, filter => filter.Id) .GreaterThan(post => post.Name, filter => filter.Name) diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/PostInFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/PostInFiltersProvider.cs index 75e5451..b4c22ae 100644 --- a/tests/Ooze.Typed.Tests/Integration/Setup/PostInFiltersProvider.cs +++ b/tests/Ooze.Typed.Tests/Integration/Setup/PostInFiltersProvider.cs @@ -2,9 +2,9 @@ namespace Ooze.Typed.Tests.Integration.Setup; -public class PostInFiltersProvider : IOozeFilterProvider +public class PostInFiltersProvider : IFilterProvider { - public IEnumerable> GetFilters() + public IEnumerable> GetFilters() => Filters.Filters.CreateFor() .In(post => post.Id, filter => filter.Ids) .In(post => post.Name, filter => filter.Names) diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/PostLessThanFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/PostLessThanFiltersProvider.cs index 9e3c3a8..683d990 100644 --- a/tests/Ooze.Typed.Tests/Integration/Setup/PostLessThanFiltersProvider.cs +++ b/tests/Ooze.Typed.Tests/Integration/Setup/PostLessThanFiltersProvider.cs @@ -2,9 +2,9 @@ namespace Ooze.Typed.Tests.Integration.Setup; -public class PostLessThanFiltersProvider : IOozeFilterProvider +public class PostLessThanFiltersProvider : IFilterProvider { - public IEnumerable> GetFilters() + public IEnumerable> GetFilters() => Filters.Filters.CreateFor() .LessThan(post => post.Id, filter => filter.Id) .LessThan(post => post.Name, filter => filter.Name) diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/PostNotEqualFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/PostNotEqualFiltersProvider.cs index c6b67fd..e9c9aaa 100644 --- a/tests/Ooze.Typed.Tests/Integration/Setup/PostNotEqualFiltersProvider.cs +++ b/tests/Ooze.Typed.Tests/Integration/Setup/PostNotEqualFiltersProvider.cs @@ -2,9 +2,9 @@ namespace Ooze.Typed.Tests.Integration.Setup; -public class PostNotEqualFiltersProvider : IOozeFilterProvider +public class PostNotEqualFiltersProvider : IFilterProvider { - public IEnumerable> GetFilters() + public IEnumerable> GetFilters() => Filters.Filters.CreateFor() .NotEqual(post => post.Id, filter => filter.Id) .NotEqual(post => post.Name, filter => filter.Name) diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/PostNotInFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/PostNotInFiltersProvider.cs index 4556a65..1740647 100644 --- a/tests/Ooze.Typed.Tests/Integration/Setup/PostNotInFiltersProvider.cs +++ b/tests/Ooze.Typed.Tests/Integration/Setup/PostNotInFiltersProvider.cs @@ -2,9 +2,9 @@ namespace Ooze.Typed.Tests.Integration.Setup; -public class PostNotInFiltersProvider : IOozeFilterProvider +public class PostNotInFiltersProvider : IFilterProvider { - public IEnumerable> GetFilters() + public IEnumerable> GetFilters() => Filters.Filters.CreateFor() .NotIn(post => post.Id, filter => filter.Ids) .NotIn(post => post.Name, filter => filter.Names) diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/PostOutOfRangeFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/PostOutOfRangeFiltersProvider.cs index 1c50190..2a80f87 100644 --- a/tests/Ooze.Typed.Tests/Integration/Setup/PostOutOfRangeFiltersProvider.cs +++ b/tests/Ooze.Typed.Tests/Integration/Setup/PostOutOfRangeFiltersProvider.cs @@ -2,9 +2,9 @@ namespace Ooze.Typed.Tests.Integration.Setup; -public class PostOutOfRangeFiltersProvider : IOozeFilterProvider +public class PostOutOfRangeFiltersProvider : IFilterProvider { - public IEnumerable> GetFilters() + public IEnumerable> GetFilters() => Filters.Filters.CreateFor() .OutOfRange(post => post.Id, filter => filter.Ids) .OutOfRange(post => post.Name, filter => filter.Names) diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/PostRangeFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/PostRangeFiltersProvider.cs index 6dc2035..226f12b 100644 --- a/tests/Ooze.Typed.Tests/Integration/Setup/PostRangeFiltersProvider.cs +++ b/tests/Ooze.Typed.Tests/Integration/Setup/PostRangeFiltersProvider.cs @@ -2,9 +2,9 @@ namespace Ooze.Typed.Tests.Integration.Setup; -public class PostRangeFiltersProvider : IOozeFilterProvider +public class PostRangeFiltersProvider : IFilterProvider { - public IEnumerable> GetFilters() + public IEnumerable> GetFilters() => Filters.Filters.CreateFor() .Range(post => post.Id, filter => filter.Ids) .Range(post => post.Name, filter => filter.Names) diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/PostSortersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/PostSortersProvider.cs index 39e8492..1bd39bc 100644 --- a/tests/Ooze.Typed.Tests/Integration/Setup/PostSortersProvider.cs +++ b/tests/Ooze.Typed.Tests/Integration/Setup/PostSortersProvider.cs @@ -2,9 +2,9 @@ namespace Ooze.Typed.Tests.Integration.Setup; -public class PostSortersProvider : IOozeSorterProvider +public class PostSortersProvider : ISorterProvider { - public IEnumerable> GetSorters() + public IEnumerable> GetSorters() => Sorters.Sorters.CreateFor() .SortBy(post => post.Id, sort => sort.Id) .SortBy(post => post.Enabled, sort => sort.Enabled) diff --git a/tests/Ooze.Typed.Tests/ServiceCollectionTests.cs b/tests/Ooze.Typed.Tests/ServiceCollectionTests.cs index 83ea5dc..2888d84 100644 --- a/tests/Ooze.Typed.Tests/ServiceCollectionTests.cs +++ b/tests/Ooze.Typed.Tests/ServiceCollectionTests.cs @@ -15,10 +15,10 @@ public void Should_Have_Registered_Default_Ooze_Interfaces() services.AddOozeTyped(); var requiredInterfaces = new (Type ContractType, Type ImplementationType)[] { - (typeof(IOozeTypedResolver), typeof(OozeTypedResolver)), - (typeof(IOozeTypedResolver<,,>), typeof(OozeTypedResolver<,,>)), - (typeof(IOozeFilterHandler<,>), typeof(OozeFilterHandler<,>)), - (typeof(IOozeSorterHandler<,>), typeof(OozeSorterHandler<,>)), + (typeof(IOperationResolver), typeof(OperationResolver)), + (typeof(IOperationResolver<,,>), typeof(OperationResolver<,,>)), + (typeof(IFilterHandler<,>), typeof(FilterHandler<,>)), + (typeof(ISorterHandler<,>), typeof(SorterHandler<,>)), (typeof(IOozePagingHandler<>), typeof(OozePagingHandler<>)) }; @@ -42,8 +42,8 @@ public void Should_Have_Custom_Provider_Implementation() var requiredInterfaces = new (Type ContractType, Type ImplementationType)[] { - (typeof(IOozeFilterProvider), typeof(BlogFiltersProvider)), - (typeof(IOozeSorterProvider), typeof(BlogSortersProvider)), + (typeof(IFilterProvider), typeof(BlogFiltersProvider)), + (typeof(ISorterProvider), typeof(BlogSortersProvider)), }; foreach (var (contractType, implementationType) in requiredInterfaces) @@ -65,8 +65,8 @@ public void Should_Have_Custom_Provider_Implementations_If_Both_Providers_Are_Im var requiredInterfaces = new (Type ContractType, Type ImplementationType)[] { - (typeof(IOozeFilterProvider), typeof(BlogFiltersAndSortersProvider)), - (typeof(IOozeSorterProvider), typeof(BlogFiltersAndSortersProvider)), + (typeof(IFilterProvider), typeof(BlogFiltersAndSortersProvider)), + (typeof(ISorterProvider), typeof(BlogFiltersAndSortersProvider)), }; foreach (var (contractType, implementationType) in requiredInterfaces) @@ -89,22 +89,22 @@ public void Should_Fail_If_Non_Provider_Type_Is_Used() } } -public class BlogFiltersProvider : IOozeFilterProvider +public class BlogFiltersProvider : IFilterProvider { - public IEnumerable> GetFilters() - => Enumerable.Empty>(); + public IEnumerable> GetFilters() + => Enumerable.Empty>(); } -public class BlogSortersProvider : IOozeSorterProvider +public class BlogSortersProvider : ISorterProvider { - public IEnumerable> GetSorters() - => Enumerable.Empty>(); + public IEnumerable> GetSorters() + => Enumerable.Empty>(); } -public class BlogFiltersAndSortersProvider : IOozeFilterProvider, IOozeSorterProvider +public class BlogFiltersAndSortersProvider : IFilterProvider, ISorterProvider { - public IEnumerable> GetFilters() - => Enumerable.Empty>(); - public IEnumerable> GetSorters() - => Enumerable.Empty>(); + public IEnumerable> GetFilters() + => Enumerable.Empty>(); + public IEnumerable> GetSorters() + => Enumerable.Empty>(); } \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/SorterHandlerTests.cs b/tests/Ooze.Typed.Tests/SorterHandlerTests.cs index ade7aaf..aff5abb 100644 --- a/tests/Ooze.Typed.Tests/SorterHandlerTests.cs +++ b/tests/Ooze.Typed.Tests/SorterHandlerTests.cs @@ -48,20 +48,20 @@ public void Should_Update_Query_When_Sorters_Are_Present() class SUT { - public IOozeSorterProvider Provider1 { get; } - public IOozeSorterProvider Provider2 { get; } + public ISorterProvider Provider1 { get; } + public ISorterProvider Provider2 { get; } public IQueryable Query { get; } - public IOozeSorterHandler Handler { get; } - private ILogger> Log { get; } + public ISorterHandler Handler { get; } + private ILogger> Log { get; } public SUT() { - Provider1 = Substitute.For>(); - Provider2 = Substitute.For>(); - Log = Substitute.For>>(); + Provider1 = Substitute.For>(); + Provider2 = Substitute.For>(); + Log = Substitute.For>>(); Query = Enumerable.Empty().AsQueryable(); - Handler = new OozeSorterHandler(new[] { Provider1, Provider2 }, Log); + Handler = new SorterHandler(new[] { Provider1, Provider2 }, Log); } } } \ No newline at end of file diff --git a/tests/Ooze.Typed.Web/Controllers/BlogController.cs b/tests/Ooze.Typed.Web/Controllers/BlogController.cs index 3f31c28..9b94c79 100644 --- a/tests/Ooze.Typed.Web/Controllers/BlogController.cs +++ b/tests/Ooze.Typed.Web/Controllers/BlogController.cs @@ -13,16 +13,16 @@ public class BlogController : ControllerBase private readonly SqlServerDatabaseContext _sqlServerDb; private readonly PostgresDatabaseContext _postgresDb; private readonly MariaDbDatabaseContext _mariaDb; - private readonly IOozeTypedResolver _nonTypedResolver; - private readonly IOozeTypedResolver _resolver; + private readonly IOperationResolver _nonTypedResolver; + private readonly IOperationResolver _resolver; public BlogController( DatabaseContext db, SqlServerDatabaseContext sqlServerDb, PostgresDatabaseContext postgresDb, MariaDbDatabaseContext mariaDb, - IOozeTypedResolver nonTypedResolver, - IOozeTypedResolver resolver) + IOperationResolver nonTypedResolver, + IOperationResolver resolver) { _db = db; _sqlServerDb = sqlServerDb; @@ -73,13 +73,19 @@ public async Task PostTypedExpanded(Input model) } [HttpPost("/sql-server")] - public async Task PostSqlServer(Input model) + public async Task PostSqlServer( + [FromServices] IAsyncOperationResolver asyncResolver, + Input model) { IQueryable query = _sqlServerDb.Set(); - query = _nonTypedResolver - .Filter(query, model.Filters); - query = _nonTypedResolver.Sort(query, model.Sorters); + // query = _nonTypedResolver + // .Filter(query, model.Filters); + // query = _nonTypedResolver.Sort(query, model.Sorters); + query = await asyncResolver.WithQuery(query) + .Filter(model.Filters) + .Sort(model.Sorters) + .ApplyAsync(); var results = await query.ToListAsync(); return Ok(results); diff --git a/tests/Ooze.Typed.Web/Controllers/CommentController.cs b/tests/Ooze.Typed.Web/Controllers/CommentController.cs index ab6dc75..afe94c4 100644 --- a/tests/Ooze.Typed.Web/Controllers/CommentController.cs +++ b/tests/Ooze.Typed.Web/Controllers/CommentController.cs @@ -9,11 +9,11 @@ namespace Ooze.Typed.Web.Controllers; public class CommentController : ControllerBase { private readonly DatabaseContext _db; - private readonly IOozeTypedResolver _resolver; + private readonly IOperationResolver _resolver; public CommentController( DatabaseContext db, - IOozeTypedResolver resolver) + IOperationResolver resolver) { _db = db; _resolver = resolver; diff --git a/tests/Ooze.Typed.Web/Filters/OozeFilter.cs b/tests/Ooze.Typed.Web/Filters/OozeFilter.cs index 3f7a9be..153d84d 100644 --- a/tests/Ooze.Typed.Web/Filters/OozeFilter.cs +++ b/tests/Ooze.Typed.Web/Filters/OozeFilter.cs @@ -8,11 +8,11 @@ namespace Ooze.Typed.Web.Filters; public sealed class OozeFilter : IAsyncResultFilter where TEntity : class { - private readonly IOozeTypedResolver _resolver; + private readonly IOperationResolver _resolver; private readonly ILogger> _log; public OozeFilter( - IOozeTypedResolver resolver, + IOperationResolver resolver, ILogger> log) { _resolver = resolver; diff --git a/tests/Ooze.Typed.Web/OozeConfiguration/BlogFiltersProvider.cs b/tests/Ooze.Typed.Web/OozeConfiguration/BlogFiltersProvider.cs index 1de5057..e175de9 100644 --- a/tests/Ooze.Typed.Web/OozeConfiguration/BlogFiltersProvider.cs +++ b/tests/Ooze.Typed.Web/OozeConfiguration/BlogFiltersProvider.cs @@ -1,8 +1,9 @@ using Ooze.Typed.EntityFrameworkCore.Extensions; using Ooze.Typed.Filters; +using Ooze.Typed.Filters.Async; using Ooze.Typed.Web.Entities; -public class BlogFiltersProvider : IOozeFilterProvider +public class BlogFiltersProvider : IFilterProvider, IAsyncFilterProvider { private readonly IHttpContextAccessor _httpContextAccessor; @@ -11,7 +12,7 @@ public BlogFiltersProvider(IHttpContextAccessor httpContextAccessor) _httpContextAccessor = httpContextAccessor; } - public IEnumerable> GetFilters() + public IEnumerable> GetFilters() { var httpContext = _httpContextAccessor.HttpContext; var hasSecretParam = !httpContext?.Request.Query.ContainsKey("secret") ?? true; @@ -23,4 +24,23 @@ public IEnumerable> GetFilters() .Like(blog => blog.Name, filter => filter.Name) .Build(); } + + public ValueTask>> GetFiltersAsync() + { + var filters = AsyncFilters.CreateFor() + .Add(filter => string.IsNullOrEmpty(filter.Name) == false, filter => blog => blog.Name == filter.Name) + .AddAsync(async filter => + { + await Task.Delay(1); + return string.IsNullOrEmpty(filter.Name) == false; + }, + async filter => + { + await Task.Delay(1); + return blog => blog.Name == filter.Name; + }) + .Build(); + + return ValueTask.FromResult(filters); + } } diff --git a/tests/Ooze.Typed.Web/OozeConfiguration/BlogSortersProvider.cs b/tests/Ooze.Typed.Web/OozeConfiguration/BlogSortersProvider.cs index 7a3d56d..e41e4a7 100644 --- a/tests/Ooze.Typed.Web/OozeConfiguration/BlogSortersProvider.cs +++ b/tests/Ooze.Typed.Web/OozeConfiguration/BlogSortersProvider.cs @@ -2,9 +2,9 @@ using Ooze.Typed.Sorters; using Ooze.Typed.Web.Entities; -public class BlogSortersProvider : IOozeSorterProvider +public class BlogSortersProvider : ISorterProvider { - public IEnumerable> GetSorters() + public IEnumerable> GetSorters() { return Sorters.CreateFor() .SortBy(blog => blog.Id, sort => sort.BlogIdSort) @@ -21,9 +21,9 @@ public class CommentFilters public RangeFilter CommentIdsRange { get; set; } = default!; } -public class CommentFiltersProvider : IOozeFilterProvider +public class CommentFiltersProvider : IFilterProvider { - public IEnumerable> GetFilters() + public IEnumerable> GetFilters() { return Filters.CreateFor() .StartsWith(comment => comment.Text, filter => filter.Name) @@ -41,9 +41,9 @@ public class CommentSorters public SortDirection? NameSort { get; set; } } -public class CommentsSortersProvider : IOozeSorterProvider +public class CommentsSortersProvider : ISorterProvider { - public IEnumerable> GetSorters() + public IEnumerable> GetSorters() { return Sorters.CreateFor() .SortBy(comment => comment.Post.Id, sort => sort.IdSort) diff --git a/tests/Ooze.Typed.Web/Program.cs b/tests/Ooze.Typed.Web/Program.cs index 0cc1850..e55b25b 100644 --- a/tests/Ooze.Typed.Web/Program.cs +++ b/tests/Ooze.Typed.Web/Program.cs @@ -18,6 +18,7 @@ opts.UseMySql(builder.Configuration.GetConnectionString("MariaDb"), serverVersion).EnableSensitiveDataLogging()); builder.Services.AddHostedService(); builder.Services.AddOozeTyped() + .EnableAsyncResolvers() .Add() .Add() .Add() From 3e0828a8fc266d33febc4f8b23071ccfe79d9f83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20Pavlovi=C4=87?= Date: Thu, 29 Feb 2024 19:52:25 +0100 Subject: [PATCH 5/9] Add simple async tests --- ...syncDatabaseFilterEqualIntegrationTests.cs | 103 +++++++++++++++++ ...tabaseFilterGreaterThanIntegrationTests.cs | 96 ++++++++++++++++ .../AsyncDatabaseFilterInIntegrationTests.cs | 78 +++++++++++++ ...cDatabaseFilterLessThanIntegrationTests.cs | 96 ++++++++++++++++ ...cDatabaseFilterNotEqualIntegrationTests.cs | 103 +++++++++++++++++ ...syncDatabaseFilterNotInIntegrationTests.cs | 78 +++++++++++++ ...atabaseFilterOutOfRangeIntegrationTests.cs | 83 ++++++++++++++ ...syncDatabaseFilterRangeIntegrationTests.cs | 83 ++++++++++++++ .../AsyncDatabaseSorterIntegrationTests.cs | 105 ++++++++++++++++++ .../Async/AsyncPostEqualFiltersProvider.cs | 14 +++ .../AsyncPostGreaterThanFiltersProvider.cs | 14 +++ .../Setup/Async/AsyncPostInFiltersProvider.cs | 13 +++ .../Async/AsyncPostLessThanFiltersProvider.cs | 14 +++ .../Async/AsyncPostNotEqualFiltersProvider.cs | 14 +++ .../Async/AsyncPostNotInFiltersProvider.cs | 13 +++ .../AsyncPostOutOfRangeFiltersProvider.cs | 13 +++ .../Async/AsyncPostRangeFiltersProvider.cs | 13 +++ .../Setup/Async/AsyncPostSortersProvider.cs | 12 ++ .../Integration/Setup/DbFixture.cs | 1 + 19 files changed, 946 insertions(+) create mode 100644 tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterEqualIntegrationTests.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterGreaterThanIntegrationTests.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterInIntegrationTests.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterLessThanIntegrationTests.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterNotEqualIntegrationTests.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterNotInIntegrationTests.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterOutOfRangeIntegrationTests.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterRangeIntegrationTests.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/AsyncDatabaseSorterIntegrationTests.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostEqualFiltersProvider.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostGreaterThanFiltersProvider.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostInFiltersProvider.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostLessThanFiltersProvider.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostNotEqualFiltersProvider.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostNotInFiltersProvider.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostOutOfRangeFiltersProvider.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostRangeFiltersProvider.cs create mode 100644 tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostSortersProvider.cs diff --git a/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterEqualIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterEqualIntegrationTests.cs new file mode 100644 index 0000000..dd8ac98 --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterEqualIntegrationTests.cs @@ -0,0 +1,103 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Ooze.Typed.Tests.Integration.Setup; +using Ooze.Typed.Tests.Integration.Setup.Async; + +namespace Ooze.Typed.Tests.Integration; + +public class AsyncDatabaseFilterEqualIntegrationTests(DbFixture fixture) + : IClassFixture> +{ + [Theory] + [InlineData(1)] + [InlineData(5)] + [InlineData(10)] + [InlineData(100)] + public async Task Should_Correctly_Filter_Data_By_Equal_Int_Filter(int postId) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostFilters(postId, null, null, null)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.Count == 1); + Assert.True(results[0].Id == postId); + } + + [Theory] + [InlineData("1_Sample_post")] + [InlineData("5_Sample_post")] + [InlineData("10_Sample_post")] + [InlineData("100_Sample_post")] + public async Task Should_Correctly_Filter_Data_By_Equal_String_Filter(string postName) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostFilters(null, postName, null, null)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.Count == 1); + Assert.True(results[0].Name == postName); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Should_Correctly_Filter_Data_By_Equal_Bool_Filter(bool enabled) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostFilters(null, null, enabled, null)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.Count == 50); + Assert.True(results.All(x => x.Enabled == enabled) == true); + } + + [Theory] + [InlineData(2022, 1, 2)] + [InlineData(2022, 1, 3)] + [InlineData(2022, 2, 1)] + public async Task Should_Correctly_Filter_Data_By_Equal_DateTime_Filter( + int year, + int month, + int day) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + var filterDate = new DateTime(year, month, day); + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostFilters(null, null, null, filterDate)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.Count == 1); + Assert.True(results[0].Date == filterDate); + } +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterGreaterThanIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterGreaterThanIntegrationTests.cs new file mode 100644 index 0000000..437782b --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterGreaterThanIntegrationTests.cs @@ -0,0 +1,96 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Ooze.Typed.Tests.Integration.Setup; +using Ooze.Typed.Tests.Integration.Setup.Async; + +namespace Ooze.Typed.Tests.Integration; + +public class AsyncDatabaseFilterGreaterThanIntegrationTests(DbFixture fixture) + : IClassFixture> +{ + [Theory] + [InlineData(1)] + [InlineData(5)] + [InlineData(10)] + [InlineData(100)] + public async Task Should_Correctly_Filter_Data_By_Greater_Than_Int_Filter(int postId) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostFilters(postId, null, null, null)) + .ApplyAsync(); + var expectedCount = DatabaseContext.TotalCountOfFakes - postId; + + var results = await query.ToListAsync(); + Assert.True(results.Count == expectedCount); + Assert.True(results.All(x => x.Id > postId)); + } + + [Theory] + [InlineData("1_Sample_post")] + [InlineData("5_Sample_post")] + [InlineData("10_Sample_post")] + [InlineData("100_Sample_post")] + public async Task Should_Fail_To_Filter_Data_By_Greater_Than_String_Filter(string postName) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + await Assert.ThrowsAsync(async () => await oozeResolver.WithQuery(query) + .Filter(new PostFilters(null, postName, null, null)) + .ApplyAsync()); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Should_Fail_To_Filter_Data_By_Greater_Than_Bool_Filter(bool enabled) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + await Assert.ThrowsAsync(async () => await oozeResolver.WithQuery(query) + .Filter(new PostFilters(null, null, enabled, null)) + .ApplyAsync()); + } + + [Theory] + [InlineData(2022, 1, 2)] + [InlineData(2022, 1, 3)] + [InlineData(2022, 2, 1)] + public async Task Should_Correctly_Filter_Data_By_Greater_Than_DateTime_Filter( + int year, + int month, + int day) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + var filterDate = new DateTime(year, month, day); + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostFilters(null, null, null, filterDate)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.All(x => x.Date != filterDate)); + Assert.True(results.All(x => x.Date > filterDate)); + } +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterInIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterInIntegrationTests.cs new file mode 100644 index 0000000..ccf94e2 --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterInIntegrationTests.cs @@ -0,0 +1,78 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Ooze.Typed.Tests.Integration.Setup; +using Ooze.Typed.Tests.Integration.Setup.Async; + +namespace Ooze.Typed.Tests.Integration; + +public class AsyncDatabaseFilterInThanIntegrationTests(DbFixture fixture) + : IClassFixture> +{ + [Theory] + [InlineData(1, 2, 3, 4)] + [InlineData(5, 6, 7, 8)] + [InlineData(10, 11, 12, 13)] + [InlineData(100, 101, 102, 103)] + public async Task Should_Correctly_Filter_Data_By_In_Int_Filter(params int[] postIds) + { + var castedIds = postIds.Select(x => (long)x); + var validIds = castedIds.Where(x => x <= DatabaseContext.TotalCountOfFakes); + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostInFilters(castedIds, null, null)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.Count == validIds.Count()); + Assert.True(results.All(x => validIds.Contains(x.Id))); + } + + [Theory] + [InlineData("1_Sample_post", "2_Sample_post", "3_Sample_post", "4_Sample_post")] + [InlineData("5_Sample_post", "6_Sample_post", "7_Sample_post", "8_Sample_post")] + [InlineData("10_Sample_post", "11_Sample_post", "12_Sample_post", "13_Sample_post")] + [InlineData("100_Sample_post", "101_Sample_post", "102_Sample_post", "103")] + public async Task Should_Correctly_Filter_Data_By_In_String_Filter(params string[] postNames) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostInFilters(null, postNames, null)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.Count <= postNames.Length); + Assert.True(results.All(x => postNames.Contains(x.Name))); + } + + [Theory] + [ClassData(typeof(DateData))] + public async Task Should_Correctly_Filter_Data_By_In_DateTime_Filter(params DateTime[] dates) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostInFilters(null, null, dates)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.Count <= dates.Length); + Assert.True(results.All(x => dates.Contains(x.Date))); + } +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterLessThanIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterLessThanIntegrationTests.cs new file mode 100644 index 0000000..d630878 --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterLessThanIntegrationTests.cs @@ -0,0 +1,96 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Ooze.Typed.Tests.Integration.Setup; +using Ooze.Typed.Tests.Integration.Setup.Async; + +namespace Ooze.Typed.Tests.Integration; + +public class AsyncDatabaseFilterLessThanIntegrationTests(DbFixture fixture) + : IClassFixture> +{ + [Theory] + [InlineData(1)] + [InlineData(5)] + [InlineData(10)] + [InlineData(100)] + public async Task Should_Correctly_Filter_Data_By_Less_Than_Int_Filter(int postId) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostFilters(postId, null, null, null)) + .ApplyAsync(); + var expectedCount = postId - 1; + + var results = await query.ToListAsync(); + Assert.True(results.Count == expectedCount); + Assert.True(results.All(x => x.Id < postId)); + } + + [Theory] + [InlineData("1_Sample_post")] + [InlineData("5_Sample_post")] + [InlineData("10_Sample_post")] + [InlineData("100_Sample_post")] + public async Task Should_Fail_To_Filter_Data_By_Less_Than_String_Filter(string postName) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + await Assert.ThrowsAsync(async () => await oozeResolver.WithQuery(query) + .Filter(new PostFilters(null, postName, null, null)) + .ApplyAsync()); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Should_Fail_To_Filter_Data_By_Less_Than_Bool_Filter(bool enabled) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + await Assert.ThrowsAsync(async () => await oozeResolver.WithQuery(query) + .Filter(new PostFilters(null, null, enabled, null)) + .ApplyAsync()); + } + + [Theory] + [InlineData(2022, 1, 2)] + [InlineData(2022, 1, 3)] + [InlineData(2022, 2, 1)] + public async Task Should_Correctly_Filter_Data_By_Less_Than_DateTime_Filter( + int year, + int month, + int day) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + var filterDate = new DateTime(year, month, day); + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostFilters(null, null, null, filterDate)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.All(x => x.Date != filterDate)); + Assert.True(results.All(x => x.Date < filterDate)); + } +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterNotEqualIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterNotEqualIntegrationTests.cs new file mode 100644 index 0000000..eef4938 --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterNotEqualIntegrationTests.cs @@ -0,0 +1,103 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Ooze.Typed.Tests.Integration.Setup; +using Ooze.Typed.Tests.Integration.Setup.Async; + +namespace Ooze.Typed.Tests.Integration; + +public class AsyncDatabaseFilterNotEqualIntegrationTests(DbFixture fixture) + : IClassFixture> +{ + [Theory] + [InlineData(1)] + [InlineData(5)] + [InlineData(10)] + [InlineData(100)] + public async Task Should_Correctly_Filter_Data_By_Not_Equal_Int_Filter(int postId) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostFilters(postId, null, null, null)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.Count == 99); + Assert.True(results.All(x => x.Id != postId)); + } + + [Theory] + [InlineData("1_Sample_post")] + [InlineData("5_Sample_post")] + [InlineData("10_Sample_post")] + [InlineData("100_Sample_post")] + public async Task Should_Correctly_Filter_Data_By_Not_Equal_String_Filter(string postName) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostFilters(null, postName, null, null)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.Count == 99); + Assert.True(results.All(x => x.Name != postName)); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Should_Correctly_Filter_Data_By_Not_Equal_Bool_Filter(bool enabled) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostFilters(null, null, enabled, null)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.Count == 50); + Assert.True(results.All(x => x.Enabled != enabled)); + } + + [Theory] + [InlineData(2022, 1, 2)] + [InlineData(2022, 1, 3)] + [InlineData(2022, 2, 1)] + public async Task Should_Correctly_Filter_Data_By_Not_Equal_DateTime_Filter( + int year, + int month, + int day) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + var filterDate = new DateTime(year, month, day); + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostFilters(null, null, null, filterDate)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.Count == 99); + Assert.True(results.All(x => x.Date != filterDate)); + } +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterNotInIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterNotInIntegrationTests.cs new file mode 100644 index 0000000..55ee376 --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterNotInIntegrationTests.cs @@ -0,0 +1,78 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Ooze.Typed.Tests.Integration.Setup; +using Ooze.Typed.Tests.Integration.Setup.Async; + +namespace Ooze.Typed.Tests.Integration; + +public class AsyncDatabaseFilterNotInThanIntegrationTests(DbFixture fixture) + : IClassFixture> +{ + [Theory] + [InlineData(1, 2, 3, 4)] + [InlineData(5, 6, 7, 8)] + [InlineData(10, 11, 12, 13)] + [InlineData(100, 101, 102, 103)] + public async Task Should_Correctly_Filter_Data_By_Not_In_Int_Filter(params int[] postIds) + { + var castedIds = postIds.Select(x => (long)x); + var validIds = castedIds.Where(x => x <= DatabaseContext.TotalCountOfFakes); + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostInFilters(castedIds, null, null)) + .ApplyAsync(); + + + var results = await query.ToListAsync(); + Assert.True(results.All(x => validIds.Contains(x.Id) == false)); + } + + [Theory] + [InlineData("1_Sample_post", "2_Sample_post", "3_Sample_post", "4_Sample_post")] + [InlineData("5_Sample_post", "6_Sample_post", "7_Sample_post", "8_Sample_post")] + [InlineData("10_Sample_post", "11_Sample_post", "12_Sample_post", "13_Sample_post")] + [InlineData("100_Sample_post", "101_Sample_post", "102_Sample_post", "103")] + public async Task Should_Correctly_Filter_Data_By_Not_In_String_Filter(params string[] postNames) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostInFilters(null, postNames, null)) + .ApplyAsync(); + + + var results = await query.ToListAsync(); + Assert.True(results.All(x => postNames.Contains(x.Name) == false)); + } + + [Theory] + [ClassData(typeof(DateData))] + public async Task Should_Correctly_Filter_Data_By_Not_In_DateTime_Filter(params DateTime[] dates) + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostInFilters(null, null, dates)) + .ApplyAsync(); + + + var results = await query.ToListAsync(); + Assert.True(results.All(x => dates.Contains(x.Date) == false)); + } +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterOutOfRangeIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterOutOfRangeIntegrationTests.cs new file mode 100644 index 0000000..87ced26 --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterOutOfRangeIntegrationTests.cs @@ -0,0 +1,83 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Ooze.Typed.Filters; +using Ooze.Typed.Tests.Integration.Setup; +using Ooze.Typed.Tests.Integration.Setup.Async; + +namespace Ooze.Typed.Tests.Integration; + +public class AsyncDatabaseFilterOutOfRangeThanIntegrationTests(DbFixture fixture) + : IClassFixture> +{ + [Theory] + [InlineData(-10, 5)] + [InlineData(5, 20)] + [InlineData(25, 90)] + [InlineData(95, 120)] + public async Task Should_Correctly_Filter_Data_By_Out_Of_Range_Int_Filter( + int from, + int to) + { + var filter = new RangeFilter { From = from, To = to }; + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostRangeFilters(filter, null, null)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + var generatedRange = Enumerable.Range(from, to + 1).Select(x => (long)x); + Assert.Contains(results, x => generatedRange.Contains(x.Id) == false); + } + + [Theory] + [InlineData("-10", "5")] + [InlineData("postname", "endpostname")] + public async Task Should_Correctly_Filter_Data_By_Out_Of_Range_String_Filter(string from, string to) + { + var filter = new RangeFilter { From = from, To = to }; + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + await Assert.ThrowsAsync(async () => await oozeResolver.WithQuery(query) + .Filter(new PostRangeFilters(null, filter, null)) + .ApplyAsync()); + } + + [Fact] + public async Task Should_Correctly_Filter_Data_By_Out_Of_Range_DateTime_Filter() + { + var from = new DateTime(2022, 1, 1); + var to = new DateTime(2022, 2, 20); + var filter = new RangeFilter + { + From = from, + To = to + }; + + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostRangeFilters(null, null, filter)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + var diffDays = to.Subtract(from).Days; + var generatedRange = Enumerable.Range(0, diffDays + 1).Select(x => from.AddDays(x)); + Assert.Contains(results, x => generatedRange.Contains(x.Date) == false); + } +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterRangeIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterRangeIntegrationTests.cs new file mode 100644 index 0000000..359dd6d --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseFilterRangeIntegrationTests.cs @@ -0,0 +1,83 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Ooze.Typed.Filters; +using Ooze.Typed.Tests.Integration.Setup; +using Ooze.Typed.Tests.Integration.Setup.Async; + +namespace Ooze.Typed.Tests.Integration; + +public class AsyncDatabaseFilterRangeThanIntegrationTests(DbFixture fixture) + : IClassFixture> +{ + [Theory] + [InlineData(-10, 5)] + [InlineData(5, 20)] + [InlineData(25, 90)] + [InlineData(95, 120)] + public async Task Should_Correctly_Filter_Data_By_Range_Int_Filter( + int from, + int to) + { + var filter = new RangeFilter { From = from, To = to }; + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostRangeFilters(filter, null, null)) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.All(x => x.Id >= filter.From)); + Assert.True(results.All(x => x.Id <= filter.To)); + } + + [Theory] + [InlineData("-10", "5")] + [InlineData("postname", "endpostname")] + public async Task Should_Correctly_Filter_Data_By_Range_String_Filter(string from, string to) + { + var filter = new RangeFilter { From = from, To = to }; + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + await Assert.ThrowsAsync(async () => await oozeResolver.WithQuery(query) + .Filter(new PostRangeFilters(null, filter, null)) + .ApplyAsync()); + } + + [Fact] + public async Task Should_Correctly_Filter_Data_By_Range_DateTime_Filter() + { + var from = new DateTime(2022, 1, 1); + var to = new DateTime(2022, 2, 20); + var filter = new RangeFilter + { + From = from, + To = to + }; + + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Filter(new PostRangeFilters(null, null, filter)) + .ApplyAsync(); + + + var results = await query.ToListAsync(); + Assert.True(results.All(x => x.Date >= filter.From)); + Assert.True(results.All(x => x.Date <= filter.To)); + } +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseSorterIntegrationTests.cs b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseSorterIntegrationTests.cs new file mode 100644 index 0000000..948b761 --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/AsyncDatabaseSorterIntegrationTests.cs @@ -0,0 +1,105 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Ooze.Typed.Sorters; +using Ooze.Typed.Tests.Integration.Setup; +using Ooze.Typed.Tests.Integration.Setup.Async; + +namespace Ooze.Typed.Tests.Integration; + +public class AsyncDatabaseSorterEqualIntegrationTests(DbFixture fixture) + : IClassFixture> +{ + [Fact] + public async Task Should_Correctly_Sort_Data_Descending_By_Single_Field() + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + var sorters = new[] + { + new PostSorters(SortDirection.Descending, null, null) + }; + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Sort(sorters) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.Count == DatabaseContext.TotalCountOfFakes); + Assert.True(results[0].Id == 100); + Assert.True(results[99].Id == 1); + } + + [Fact] + public async Task Should_Correctly_Sort_Data_Ascending_By_Single_Field() + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + var sorters = new[] + { + new PostSorters(SortDirection.Ascending, null, null) + }; + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Sort(sorters) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.Count == DatabaseContext.TotalCountOfFakes); + Assert.True(results[0].Id == 1); + Assert.True(results[99].Id == 100); + } + + [Fact] + public async Task Should_Correctly_Sort_Data_Ascending_By_Multple_Fields() + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + var sorters = new[] + { + new PostSorters(SortDirection.Ascending, null, null), + new PostSorters(null, null, SortDirection.Ascending) + }; + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Sort(sorters) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.Count == DatabaseContext.TotalCountOfFakes); + Assert.True(results[0].Id == 1); + Assert.True(results[1].Id == 2); + } + + [Fact] + public async Task Should_Not_Sort_Data_If_Sort_Not_Provided() + { + using var scope = fixture.CreateServiceProvider().CreateScope(); + var provider = scope.ServiceProvider; + + await using var context = fixture.CreateContext(); + var oozeResolver = provider.GetRequiredService>(); + + var sorters = Enumerable.Empty(); + IQueryable query = context.Posts; + query = await oozeResolver.WithQuery(query) + .Sort(sorters) + .ApplyAsync(); + + var results = await query.ToListAsync(); + Assert.True(results.Count == DatabaseContext.TotalCountOfFakes); + Assert.True(results[0].Id == 1); + Assert.True(results[99].Id == 100); + } +} diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostEqualFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostEqualFiltersProvider.cs new file mode 100644 index 0000000..96a587e --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostEqualFiltersProvider.cs @@ -0,0 +1,14 @@ +using Ooze.Typed.Filters.Async; + +namespace Ooze.Typed.Tests.Integration.Setup.Async; + +public class AsyncPostEqualFiltersProvider : IAsyncFilterProvider +{ + public ValueTask>> GetFiltersAsync() + => ValueTask.FromResult(AsyncFilters.CreateFor() + .Equal(post => post.Id, filter => filter.Id) + .Equal(post => post.Name, filter => filter.Name) + .Equal(post => post.Enabled, filter => filter.Enabled) + .Equal(post => post.Date, filter => filter.Date) + .Build()); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostGreaterThanFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostGreaterThanFiltersProvider.cs new file mode 100644 index 0000000..2440a6b --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostGreaterThanFiltersProvider.cs @@ -0,0 +1,14 @@ +using Ooze.Typed.Filters.Async; + +namespace Ooze.Typed.Tests.Integration.Setup.Async; + +public class AsyncPostGreaterThanFiltersProvider : IAsyncFilterProvider +{ + public ValueTask>> GetFiltersAsync() + => ValueTask.FromResult(AsyncFilters.CreateFor() + .GreaterThan(post => post.Id, filter => filter.Id) + .GreaterThan(post => post.Name, filter => filter.Name) + .GreaterThan(post => post.Enabled, filter => filter.Enabled) + .GreaterThan(post => post.Date, filter => filter.Date) + .Build()); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostInFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostInFiltersProvider.cs new file mode 100644 index 0000000..f5eeb62 --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostInFiltersProvider.cs @@ -0,0 +1,13 @@ +using Ooze.Typed.Filters.Async; + +namespace Ooze.Typed.Tests.Integration.Setup.Async; + +public class AsyncPostInFiltersProvider : IAsyncFilterProvider +{ + public ValueTask>> GetFiltersAsync() + => ValueTask.FromResult(AsyncFilters.CreateFor() + .In(post => post.Id, filter => filter.Ids) + .In(post => post.Name, filter => filter.Names) + .In(post => post.Date, filter => filter.Dates) + .Build()); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostLessThanFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostLessThanFiltersProvider.cs new file mode 100644 index 0000000..7eb65f6 --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostLessThanFiltersProvider.cs @@ -0,0 +1,14 @@ +using Ooze.Typed.Filters.Async; + +namespace Ooze.Typed.Tests.Integration.Setup.Async; + +public class AsyncPostLessThanFiltersProvider : IAsyncFilterProvider +{ + public ValueTask>> GetFiltersAsync() + => ValueTask.FromResult(AsyncFilters.CreateFor() + .LessThan(post => post.Id, filter => filter.Id) + .LessThan(post => post.Name, filter => filter.Name) + .LessThan(post => post.Enabled, filter => filter.Enabled) + .LessThan(post => post.Date, filter => filter.Date) + .Build()); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostNotEqualFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostNotEqualFiltersProvider.cs new file mode 100644 index 0000000..100ec55 --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostNotEqualFiltersProvider.cs @@ -0,0 +1,14 @@ +using Ooze.Typed.Filters.Async; + +namespace Ooze.Typed.Tests.Integration.Setup.Async; + +public class AsyncPostNotEqualFiltersProvider : IAsyncFilterProvider +{ + public ValueTask>> GetFiltersAsync() + => ValueTask.FromResult(AsyncFilters.CreateFor() + .NotEqual(post => post.Id, filter => filter.Id) + .NotEqual(post => post.Name, filter => filter.Name) + .NotEqual(post => post.Enabled, filter => filter.Enabled) + .NotEqual(post => post.Date, filter => filter.Date) + .Build()); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostNotInFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostNotInFiltersProvider.cs new file mode 100644 index 0000000..2c8cebd --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostNotInFiltersProvider.cs @@ -0,0 +1,13 @@ +using Ooze.Typed.Filters.Async; + +namespace Ooze.Typed.Tests.Integration.Setup.Async; + +public class AsyncPostNotInFiltersProvider : IAsyncFilterProvider +{ + public ValueTask>> GetFiltersAsync() + => ValueTask.FromResult(AsyncFilters.CreateFor() + .NotIn(post => post.Id, filter => filter.Ids) + .NotIn(post => post.Name, filter => filter.Names) + .NotIn(post => post.Date, filter => filter.Dates) + .Build()); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostOutOfRangeFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostOutOfRangeFiltersProvider.cs new file mode 100644 index 0000000..49ab444 --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostOutOfRangeFiltersProvider.cs @@ -0,0 +1,13 @@ +using Ooze.Typed.Filters.Async; + +namespace Ooze.Typed.Tests.Integration.Setup.Async; + +public class AsyncPostOutOfRangeFiltersProvider : IAsyncFilterProvider +{ + public ValueTask>> GetFiltersAsync() + => ValueTask.FromResult(AsyncFilters.CreateFor() + .OutOfRange(post => post.Id, filter => filter.Ids) + .OutOfRange(post => post.Name, filter => filter.Names) + .OutOfRange(post => post.Date, filter => filter.Dates) + .Build()); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostRangeFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostRangeFiltersProvider.cs new file mode 100644 index 0000000..77751e3 --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostRangeFiltersProvider.cs @@ -0,0 +1,13 @@ +using Ooze.Typed.Filters.Async; + +namespace Ooze.Typed.Tests.Integration.Setup.Async; + +public class AsyncPostRangeFiltersProvider : IAsyncFilterProvider +{ + public ValueTask>> GetFiltersAsync() + => ValueTask.FromResult(AsyncFilters.CreateFor() + .Range(post => post.Id, filter => filter.Ids) + .Range(post => post.Name, filter => filter.Names) + .Range(post => post.Date, filter => filter.Dates) + .Build()); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostSortersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostSortersProvider.cs new file mode 100644 index 0000000..261803a --- /dev/null +++ b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostSortersProvider.cs @@ -0,0 +1,12 @@ +using Ooze.Typed.Sorters.Async; + +namespace Ooze.Typed.Tests.Integration.Setup.Async; + +public class AsyncPostSortersProvider : IAsyncSorterProvider +{ + public ValueTask>> GetSortersAsync() + => ValueTask.FromResult(AsyncSorters.CreateFor() + .SortBy(post => post.Id, sort => sort.Id) + .SortBy(post => post.Enabled, sort => sort.Enabled) + .Build()); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/DbFixture.cs b/tests/Ooze.Typed.Tests/Integration/Setup/DbFixture.cs index a4b0592..b3b94da 100644 --- a/tests/Ooze.Typed.Tests/Integration/Setup/DbFixture.cs +++ b/tests/Ooze.Typed.Tests/Integration/Setup/DbFixture.cs @@ -14,6 +14,7 @@ private static IServiceCollection CreateServiceCollection() .AddLogging(); services.AddOozeTyped() + .EnableAsyncResolvers() .Add(); return services; From ed140056c941b78ac85c6edc9da1af91597418eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20Pavlovi=C4=87?= Date: Wed, 3 Apr 2024 22:05:09 +0200 Subject: [PATCH 6/9] Add async extensions for provider specific packages --- .../AsyncFilterBuilderExtensions.cs | 240 ++++++++++ .../Extensions/FilterBuilderExtensions.cs | 29 +- .../Extensions/Shared.cs | 18 + .../AsyncFilterBuilderExtensions.cs | 86 ++++ .../Extensions/FilterBuilderExtensions.cs | 20 +- .../Extensions/Shared.cs | 14 + .../AsyncFilterBuilderExtensions.cs | 431 ++++++++++++++++++ .../Extensions/FilterBuilderExtensions.cs | 73 +-- .../Extensions/Shared.cs | 23 + .../AsyncFilterBuilderExtensions.cs | 49 ++ .../Extensions/FilterBuilderExtensions.cs | 12 +- .../Extensions/Shared.cs | 11 + .../AsyncFilterBuilderExtensions.cs | 48 ++ .../Extensions/FilterBuilderExtensions.cs | 12 +- .../Extensions/Shared.cs | 11 + .../Async/AsyncPostEqualFiltersProvider.cs | 9 + 16 files changed, 977 insertions(+), 109 deletions(-) create mode 100644 src/Ooze.Typed.EntityFrameworkCore.MySql/Extensions/AsyncFilterBuilderExtensions.cs create mode 100644 src/Ooze.Typed.EntityFrameworkCore.MySql/Extensions/Shared.cs create mode 100644 src/Ooze.Typed.EntityFrameworkCore.Npgsql/Extensions/AsyncFilterBuilderExtensions.cs create mode 100644 src/Ooze.Typed.EntityFrameworkCore.Npgsql/Extensions/Shared.cs create mode 100644 src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/AsyncFilterBuilderExtensions.cs create mode 100644 src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/Shared.cs create mode 100644 src/Ooze.Typed.EntityFrameworkCore.Sqlite/Extensions/AsyncFilterBuilderExtensions.cs create mode 100644 src/Ooze.Typed.EntityFrameworkCore.Sqlite/Extensions/Shared.cs create mode 100644 src/Ooze.Typed.EntityFrameworkCore/Extensions/AsyncFilterBuilderExtensions.cs create mode 100644 src/Ooze.Typed.EntityFrameworkCore/Extensions/Shared.cs diff --git a/src/Ooze.Typed.EntityFrameworkCore.MySql/Extensions/AsyncFilterBuilderExtensions.cs b/src/Ooze.Typed.EntityFrameworkCore.MySql/Extensions/AsyncFilterBuilderExtensions.cs new file mode 100644 index 0000000..0524dbd --- /dev/null +++ b/src/Ooze.Typed.EntityFrameworkCore.MySql/Extensions/AsyncFilterBuilderExtensions.cs @@ -0,0 +1,240 @@ +using System.Linq.Expressions; +using Ooze.Typed.Expressions; +using Ooze.Typed.Filters.Async; +using static System.Linq.Expressions.Expression; + +namespace Ooze.Typed.EntityFrameworkCore.MySql.Extensions; + +/// +/// MySql/MariaDb extensions for AsyncFilterBuilder +/// +public static class AsyncFilterBuilderExtensions +{ + /// + /// Creates DateDiffDay filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffDay( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0) + => filterBuilder.IsDateDiffFilter(Shared.DateDiffDayMethod, dataExpression, filterFunc, operation, diffConstant); + + /// + /// Creates DateDiffMonth filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffMonth( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter( + Shared.DateDiffMonthMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + /// + /// Creates DateDiffYear filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffYear( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter( + Shared.DateDiffYearMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + /// + /// Creates DateDiffHour filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffHour( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter( + Shared.DateDiffHourMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + /// + /// Creates DateDiffMinute filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffMinute( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter( + Shared.DateDiffMinuteMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + /// + /// Creates DateDiffSecond filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffSecond( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter( + Shared.DateDiffSecondMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + /// + /// Creates DateDiffMicrosecond filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffMicrosecond( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter( + Shared.DateDiffMicrosecondMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + private static IAsyncFilterBuilder IsDateDiffFilter( + this IAsyncFilterBuilder filterBuilder, + string methodName, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + { + bool FilterShouldRun(TFilter filter) => filterFunc(filter) != null; + + Expression> FilterExpressionFactory(TFilter filter) + { + var filterValue = filterFunc(filter).GetValueOrDefault(); + var memberAccessExpression = BasicExpressions.GetMemberExpression(dataExpression.Body); + var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); + var constantExpression = BasicExpressions.GetWrappedConstantExpression(filterValue); + var callExpression = Call( + Shared.DbFunctionsExtensionsType, + methodName, + Type.EmptyTypes, + Shared.EfPropertyExpression, + memberAccessExpression!, + constantExpression); + + var operationExpressionFactory = GetOperationFactory(operation); + var operationExpression = operationExpressionFactory(callExpression, Constant(diffConstant)); + return Lambda>(operationExpression, parameterExpression); + } + + shouldRun ??= FilterShouldRun; + filterBuilder.Add(shouldRun, FilterExpressionFactory); + return filterBuilder; + } + + private static Func GetOperationFactory(DateDiffOperation operation) + => operation switch + { + DateDiffOperation.GreaterThan => GreaterThan, + DateDiffOperation.LessThan => LessThan, + DateDiffOperation.Equal => Equal, + DateDiffOperation.NotEqual => (callExpr, constant) => Not(Equal(callExpr, constant)), + DateDiffOperation.NotGreaterThan => (callExpr, constant) => Not(GreaterThan(callExpr, constant)), + DateDiffOperation.NotLessThan => (callExpr, constant) => Not(LessThan(callExpr, constant)), + _ => throw new ArgumentOutOfRangeException(nameof(operation), operation, null) + }; +} \ No newline at end of file diff --git a/src/Ooze.Typed.EntityFrameworkCore.MySql/Extensions/FilterBuilderExtensions.cs b/src/Ooze.Typed.EntityFrameworkCore.MySql/Extensions/FilterBuilderExtensions.cs index 016e968..b643cb1 100644 --- a/src/Ooze.Typed.EntityFrameworkCore.MySql/Extensions/FilterBuilderExtensions.cs +++ b/src/Ooze.Typed.EntityFrameworkCore.MySql/Extensions/FilterBuilderExtensions.cs @@ -1,5 +1,4 @@ using System.Linq.Expressions; -using Microsoft.EntityFrameworkCore; using Ooze.Typed.Expressions; using Ooze.Typed.Filters; using static System.Linq.Expressions.Expression; @@ -11,16 +10,6 @@ namespace Ooze.Typed.EntityFrameworkCore.MySql.Extensions; ///
public static class FilterBuilderExtensions { - private static readonly Type DbFunctionsExtensionsType = typeof(MySqlDbFunctionsExtensions); - private const string DateDiffDayMethod = nameof(MySqlDbFunctionsExtensions.DateDiffDay); - private const string DateDiffMonthMethod = nameof(MySqlDbFunctionsExtensions.DateDiffMonth); - private const string DateDiffYearMethod = nameof(MySqlDbFunctionsExtensions.DateDiffYear); - private const string DateDiffHourMethod = nameof(MySqlDbFunctionsExtensions.DateDiffHour); - private const string DateDiffMinuteMethod = nameof(MySqlDbFunctionsExtensions.DateDiffMinute); - private const string DateDiffSecondMethod = nameof(MySqlDbFunctionsExtensions.DateDiffSecond); - private const string DateDiffMicrosecondMethod = nameof(MySqlDbFunctionsExtensions.DateDiffMicrosecond); - private static readonly MemberExpression EfPropertyExpression = Property(null, typeof(EF), nameof(EF.Functions)); - /// /// Creates DateDiffDay filter over entity property and filter value /// @@ -38,7 +27,7 @@ public static IFilterBuilder IsDateDiffDay( Func filterFunc, DateDiffOperation operation, int diffConstant = 0) - => filterBuilder.IsDateDiffFilter(DateDiffDayMethod, dataExpression, filterFunc, operation, diffConstant); + => filterBuilder.IsDateDiffFilter(Shared.DateDiffDayMethod, dataExpression, filterFunc, operation, diffConstant); /// /// Creates DateDiffMonth filter over entity property and filter value @@ -60,7 +49,7 @@ public static IFilterBuilder IsDateDiffMonth int diffConstant = 0, Func? shouldRun = null) => filterBuilder.IsDateDiffFilter( - DateDiffMonthMethod, + Shared.DateDiffMonthMethod, dataExpression, filterFunc, operation, @@ -87,7 +76,7 @@ public static IFilterBuilder IsDateDiffYear( int diffConstant = 0, Func? shouldRun = null) => filterBuilder.IsDateDiffFilter( - DateDiffYearMethod, + Shared.DateDiffYearMethod, dataExpression, filterFunc, operation, @@ -114,7 +103,7 @@ public static IFilterBuilder IsDateDiffHour( int diffConstant = 0, Func? shouldRun = null) => filterBuilder.IsDateDiffFilter( - DateDiffHourMethod, + Shared.DateDiffHourMethod, dataExpression, filterFunc, operation, @@ -141,7 +130,7 @@ public static IFilterBuilder IsDateDiffMinute? shouldRun = null) => filterBuilder.IsDateDiffFilter( - DateDiffMinuteMethod, + Shared.DateDiffMinuteMethod, dataExpression, filterFunc, operation, @@ -168,7 +157,7 @@ public static IFilterBuilder IsDateDiffSecond? shouldRun = null) => filterBuilder.IsDateDiffFilter( - DateDiffSecondMethod, + Shared.DateDiffSecondMethod, dataExpression, filterFunc, operation, @@ -195,7 +184,7 @@ public static IFilterBuilder IsDateDiffMicrosecond? shouldRun = null) => filterBuilder.IsDateDiffFilter( - DateDiffMicrosecondMethod, + Shared.DateDiffMicrosecondMethod, dataExpression, filterFunc, operation, @@ -220,10 +209,10 @@ Expression> FilterExpressionFactory(TFilter filter) var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); var constantExpression = BasicExpressions.GetWrappedConstantExpression(filterValue); var callExpression = Call( - DbFunctionsExtensionsType, + Shared.DbFunctionsExtensionsType, methodName, Type.EmptyTypes, - EfPropertyExpression, + Shared.EfPropertyExpression, memberAccessExpression!, constantExpression); diff --git a/src/Ooze.Typed.EntityFrameworkCore.MySql/Extensions/Shared.cs b/src/Ooze.Typed.EntityFrameworkCore.MySql/Extensions/Shared.cs new file mode 100644 index 0000000..32fd749 --- /dev/null +++ b/src/Ooze.Typed.EntityFrameworkCore.MySql/Extensions/Shared.cs @@ -0,0 +1,18 @@ +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; +using static System.Linq.Expressions.Expression; + +namespace Ooze.Typed.EntityFrameworkCore.MySql.Extensions; + +internal static class Shared +{ + public static readonly Type DbFunctionsExtensionsType = typeof(MySqlDbFunctionsExtensions); + public const string DateDiffDayMethod = nameof(MySqlDbFunctionsExtensions.DateDiffDay); + public const string DateDiffMonthMethod = nameof(MySqlDbFunctionsExtensions.DateDiffMonth); + public const string DateDiffYearMethod = nameof(MySqlDbFunctionsExtensions.DateDiffYear); + public const string DateDiffHourMethod = nameof(MySqlDbFunctionsExtensions.DateDiffHour); + public const string DateDiffMinuteMethod = nameof(MySqlDbFunctionsExtensions.DateDiffMinute); + public const string DateDiffSecondMethod = nameof(MySqlDbFunctionsExtensions.DateDiffSecond); + public const string DateDiffMicrosecondMethod = nameof(MySqlDbFunctionsExtensions.DateDiffMicrosecond); + public static readonly MemberExpression EfPropertyExpression = Property(null, typeof(EF), nameof(EF.Functions)); +} \ No newline at end of file diff --git a/src/Ooze.Typed.EntityFrameworkCore.Npgsql/Extensions/AsyncFilterBuilderExtensions.cs b/src/Ooze.Typed.EntityFrameworkCore.Npgsql/Extensions/AsyncFilterBuilderExtensions.cs new file mode 100644 index 0000000..1c1d632 --- /dev/null +++ b/src/Ooze.Typed.EntityFrameworkCore.Npgsql/Extensions/AsyncFilterBuilderExtensions.cs @@ -0,0 +1,86 @@ +using System.Linq.Expressions; +using Ooze.Typed.Expressions; +using Ooze.Typed.Filters.Async; +using static System.Linq.Expressions.Expression; + +namespace Ooze.Typed.EntityFrameworkCore.Npgsql.Extensions; + +/// +/// Postgres extensions for AsyncFilterBuilder +/// +public static class AsyncFilterBuilderExtensions +{ + /// + /// Creates a ILike filter over specified property and filter + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Targeted property type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder InsensitiveLike( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null) + { + bool FilterShouldRun(TFilter filter) => filterFunc(filter) != null; + + Expression> FilterExpressionFactory(TFilter filter) + { + var filterValue = filterFunc(filter); + var memberAccessExpression = BasicExpressions.GetMemberExpression(dataExpression.Body); + var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); + var constantExpression = BasicExpressions.GetWrappedConstantExpression(filterValue); + var callExpression = Call(Shared.DbFunctionsExtensionsType, Shared.ILikeMethod, + Type.EmptyTypes, Shared.EfPropertyExpression, + memberAccessExpression, + constantExpression); + + return Lambda>(callExpression, parameterExpression); + } + + shouldRun ??= FilterShouldRun; + filterBuilder.Add(shouldRun, FilterExpressionFactory); + return filterBuilder; + } + + /// + /// Creates a Soundex filter over specified property and filter + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder SoundexEqual( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null) + { + bool FilterShouldRun(TFilter filter) => filterFunc(filter) != null; + + Expression> FilterExpressionFactory(TFilter filter) + { + var filterValue = filterFunc(filter); + var memberAccessExpression = BasicExpressions.GetMemberExpression(dataExpression.Body); + var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); + var constantExpression = BasicExpressions.GetWrappedConstantExpression(filterValue); + var callExpression = Call(Shared.FuzzyStringMatchDbFunctionsExtensionsType, Shared.FuzzyStringMatchSoundexMethod, + Type.EmptyTypes, Shared.EfPropertyExpression, + memberAccessExpression); + var equalExpression = Equal(callExpression, constantExpression); + return Lambda>(equalExpression, parameterExpression); + } + + shouldRun ??= FilterShouldRun; + filterBuilder.Add(shouldRun, FilterExpressionFactory); + return filterBuilder; + } +} diff --git a/src/Ooze.Typed.EntityFrameworkCore.Npgsql/Extensions/FilterBuilderExtensions.cs b/src/Ooze.Typed.EntityFrameworkCore.Npgsql/Extensions/FilterBuilderExtensions.cs index 25f32bf..281cd5f 100644 --- a/src/Ooze.Typed.EntityFrameworkCore.Npgsql/Extensions/FilterBuilderExtensions.cs +++ b/src/Ooze.Typed.EntityFrameworkCore.Npgsql/Extensions/FilterBuilderExtensions.cs @@ -1,5 +1,4 @@ using System.Linq.Expressions; -using Microsoft.EntityFrameworkCore; using Ooze.Typed.Expressions; using Ooze.Typed.Filters; using static System.Linq.Expressions.Expression; @@ -11,13 +10,6 @@ namespace Ooze.Typed.EntityFrameworkCore.Npgsql.Extensions; /// public static class FilterBuilderExtensions { - private static readonly Type DbFunctionsExtensionsType = typeof(NpgsqlDbFunctionsExtensions); - private static readonly Type FuzzyStringMatchDbFunctionsExtensionsType = typeof(NpgsqlFuzzyStringMatchDbFunctionsExtensions); - // ReSharper disable once InconsistentNaming - private const string ILikeMethod = nameof(NpgsqlDbFunctionsExtensions.ILike); - private const string FuzzyStringMatchSoundexMethod = nameof(NpgsqlFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchSoundex); - private static readonly MemberExpression EfPropertyExpression = Property(null, typeof(EF), nameof(EF.Functions)); - /// /// Creates a ILike filter over specified property and filter /// @@ -44,10 +36,10 @@ Expression> FilterExpressionFactory(TFilter filter) var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); var constantExpression = BasicExpressions.GetWrappedConstantExpression(filterValue); var callExpression = Call( - DbFunctionsExtensionsType, - ILikeMethod, + Shared.DbFunctionsExtensionsType, + Shared.ILikeMethod, Type.EmptyTypes, - EfPropertyExpression, + Shared.EfPropertyExpression, memberAccessExpression, constantExpression); @@ -84,10 +76,10 @@ Expression> FilterExpressionFactory(TFilter filter) var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); var constantExpression = BasicExpressions.GetWrappedConstantExpression(filterValue); var callExpression = Call( - FuzzyStringMatchDbFunctionsExtensionsType, - FuzzyStringMatchSoundexMethod, + Shared.FuzzyStringMatchDbFunctionsExtensionsType, + Shared.FuzzyStringMatchSoundexMethod, Type.EmptyTypes, - EfPropertyExpression, + Shared.EfPropertyExpression, memberAccessExpression); var equalExpression = Equal(callExpression, constantExpression); return Lambda>(equalExpression, parameterExpression); diff --git a/src/Ooze.Typed.EntityFrameworkCore.Npgsql/Extensions/Shared.cs b/src/Ooze.Typed.EntityFrameworkCore.Npgsql/Extensions/Shared.cs new file mode 100644 index 0000000..c080d9f --- /dev/null +++ b/src/Ooze.Typed.EntityFrameworkCore.Npgsql/Extensions/Shared.cs @@ -0,0 +1,14 @@ +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; + +namespace Ooze.Typed.EntityFrameworkCore.Npgsql.Extensions; + +internal static class Shared +{ + // ReSharper disable once InconsistentNaming + public const string ILikeMethod = nameof(NpgsqlDbFunctionsExtensions.ILike); + public const string FuzzyStringMatchSoundexMethod = nameof(NpgsqlFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchSoundex); + public static readonly Type DbFunctionsExtensionsType = typeof(NpgsqlDbFunctionsExtensions); + public static readonly Type FuzzyStringMatchDbFunctionsExtensionsType = typeof(NpgsqlFuzzyStringMatchDbFunctionsExtensions); + public static readonly MemberExpression EfPropertyExpression = Expression.Property(null, typeof(EF), nameof(EF.Functions)); +} \ No newline at end of file diff --git a/src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/AsyncFilterBuilderExtensions.cs b/src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/AsyncFilterBuilderExtensions.cs new file mode 100644 index 0000000..039fffd --- /dev/null +++ b/src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/AsyncFilterBuilderExtensions.cs @@ -0,0 +1,431 @@ +using System.Linq.Expressions; +using Ooze.Typed.Expressions; +using Ooze.Typed.Filters.Async; +using static System.Linq.Expressions.Expression; + +namespace Ooze.Typed.EntityFrameworkCore.SqlServer.Extensions; + +/// +/// Sql Server extensions for AsyncFilterBuilder +/// +public static class AsyncFilterBuilderExtensions +{ + /// + /// Creates a IsDate filter over a string property if filter requests it + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDate( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null) + { + bool FilterShouldRun(TFilter filter) => filterFunc(filter).GetValueOrDefault() == true; + + Expression> FilterExpressionFactory(TFilter filter) + { + var filterValue = filterFunc(filter).GetValueOrDefault(); + var memberAccessExpression = BasicExpressions.GetMemberExpression(dataExpression.Body); + var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); + var callExpression = Call(Shared.DbFunctionsExtensionsType, Shared.IsDateMethod, + Type.EmptyTypes, Shared.EfPropertyExpression, + memberAccessExpression); + Expression notExpression = filterValue == true + ? callExpression + : Not(callExpression); + + return Lambda>(notExpression, parameterExpression); + } + + shouldRun ??= FilterShouldRun; + filterBuilder.Add(shouldRun, FilterExpressionFactory); + return filterBuilder; + } + + /// + /// Creates a IsNumeric filter over a string property if filter requests it + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsNumeric( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null) + { + bool FilterShouldRun(TFilter filter) => filterFunc(filter).GetValueOrDefault() == true; + + Expression> FilterExpressionFactory(TFilter filter) + { + var filterValue = filterFunc(filter).GetValueOrDefault(); + var memberAccessExpression = BasicExpressions.GetMemberExpression(dataExpression.Body); + var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); + var callExpression = Call(Shared.DbFunctionsExtensionsType, Shared.IsNumericMethod, + Type.EmptyTypes, Shared.EfPropertyExpression, + memberAccessExpression); + Expression notExpression = filterValue == true + ? callExpression + : Not(callExpression); + + return Lambda>(notExpression, parameterExpression); + } + + shouldRun ??= FilterShouldRun; + filterBuilder.Add(shouldRun, FilterExpressionFactory); + return filterBuilder; + } + + /// + /// Creates a Contains filter over specified property and filter + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Targeted property type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder Contains( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null) + { + bool FilterShouldRun(TFilter filter) => filterFunc(filter) != null; + + Expression> FilterExpressionFactory(TFilter filter) + { + var filterValue = filterFunc(filter); + var memberAccessExpression = BasicExpressions.GetMemberExpression(dataExpression.Body); + var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); + var constantExpression = BasicExpressions.GetWrappedConstantExpression(filterValue); + var callExpression = Call(Shared.DbFunctionsExtensionsType, Shared.ContainsMethod, + Type.EmptyTypes, Shared.EfPropertyExpression, + memberAccessExpression, + constantExpression); + + return Lambda>(callExpression, parameterExpression); + } + + shouldRun ??= FilterShouldRun; + filterBuilder.Add(shouldRun, FilterExpressionFactory); + return filterBuilder; + } + + /// + /// Creates DateDiffDay filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffDay( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter(Shared.DateDiffDayMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + /// + /// Creates DateDiffMonth filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffMonth( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter(Shared.DateDiffMonthMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + /// + /// Creates DateDiffWeek filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffWeek( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter(Shared.DateDiffWeekMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + /// + /// Creates DateDiffYear filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffYear( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter(Shared.DateDiffYearMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + /// + /// Creates DateDiffHour filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffHour( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter(Shared.DateDiffHourMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + /// + /// Creates DateDiffMinute filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffMinute( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter(Shared.DateDiffMinuteMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + /// + /// Creates DateDiffSecond filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffSecond( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter(Shared.DateDiffSecondMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + /// + /// Creates DateDiffMillisecond filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffMillisecond( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter(Shared.DateDiffMillisecondMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + /// + /// Creates DateDiffMicrosecond filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffMicrosecond( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter(Shared.DateDiffMicrosecondMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + /// + /// Creates DateDiffNanosecond filter over entity property and filter value + /// + /// Instance of + /// Expression targeting entity property + /// Filtering delegate targeting property with details if filter should apply + /// Operation defines which operations is applied over property and filter value + /// Optional diff constant to use in comparison + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder IsDateDiffNanosecond( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + => filterBuilder.IsDateDiffFilter(Shared.DateDiffNanosecondMethod, + dataExpression, + filterFunc, + operation, + diffConstant, + shouldRun); + + private static IAsyncFilterBuilder IsDateDiffFilter( + this IAsyncFilterBuilder filterBuilder, + string methodName, + Expression> dataExpression, + Func filterFunc, + DateDiffOperation operation, + int diffConstant = 0, + Func? shouldRun = null) + { + bool FilterShouldRun(TFilter filter) => filterFunc(filter) != null; + + Expression> FilterExpressionFactory(TFilter filter) + { + var filterValue = filterFunc(filter).GetValueOrDefault(); + var memberAccessExpression = BasicExpressions.GetMemberExpression(dataExpression.Body); + var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); + var constantExpression = BasicExpressions.GetWrappedConstantExpression(filterValue); + var callExpression = Call(Shared.DbFunctionsExtensionsType, + methodName, + Type.EmptyTypes, Shared.EfPropertyExpression, + memberAccessExpression, + constantExpression); + + var operationExpressionFactory = GetOperationFactory(operation); + var operationExpression = operationExpressionFactory(callExpression, Constant(diffConstant)); + return Lambda>(operationExpression, parameterExpression); + } + + shouldRun ??= FilterShouldRun; + filterBuilder.Add(shouldRun, FilterExpressionFactory); + return filterBuilder; + } + + private static Func GetOperationFactory(DateDiffOperation operation) + => operation switch + { + DateDiffOperation.GreaterThan => GreaterThan, + DateDiffOperation.LessThan => LessThan, + DateDiffOperation.Equal => Equal, + DateDiffOperation.NotEqual => (callExpr, constant) => Not(Equal(callExpr, constant)), + DateDiffOperation.NotGreaterThan => (callExpr, constant) => Not(GreaterThan(callExpr, constant)), + DateDiffOperation.NotLessThan => (callExpr, constant) => Not(LessThan(callExpr, constant)), + _ => throw new ArgumentOutOfRangeException(nameof(operation), operation, null) + }; +} \ No newline at end of file diff --git a/src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/FilterBuilderExtensions.cs b/src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/FilterBuilderExtensions.cs index 3991158..208ddd0 100644 --- a/src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/FilterBuilderExtensions.cs +++ b/src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/FilterBuilderExtensions.cs @@ -11,22 +11,6 @@ namespace Ooze.Typed.EntityFrameworkCore.SqlServer.Extensions; ///
public static class FilterBuilderExtensions { - private static readonly Type DbFunctionsExtensionsType = typeof(SqlServerDbFunctionsExtensions); - private const string IsDateMethod = nameof(SqlServerDbFunctionsExtensions.IsDate); - private const string IsNumericMethod = nameof(SqlServerDbFunctionsExtensions.IsNumeric); - private const string ContainsMethod = nameof(SqlServerDbFunctionsExtensions.Contains); - private const string DateDiffDayMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffDay); - private const string DateDiffMonthMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffMonth); - private const string DateDiffWeekMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffWeek); - private const string DateDiffYearMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffYear); - private const string DateDiffHourMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffHour); - private const string DateDiffMinuteMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffMinute); - private const string DateDiffSecondMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffSecond); - private const string DateDiffMillisecondMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffMillisecond); - private const string DateDiffMicrosecondMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffMicrosecond); - private const string DateDiffNanosecondMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffNanosecond); - private static readonly MemberExpression EfPropertyExpression = Property(null, typeof(EF), nameof(EF.Functions)); - /// /// Creates a IsDate filter over a string property if filter requests it /// @@ -50,11 +34,8 @@ Expression> FilterExpressionFactory(TFilter filter) var filterValue = filterFunc(filter).GetValueOrDefault(); var memberAccessExpression = BasicExpressions.GetMemberExpression(dataExpression.Body); var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); - var callExpression = Call( - DbFunctionsExtensionsType, - IsDateMethod, - Type.EmptyTypes, - EfPropertyExpression, + var callExpression = Call(Shared.DbFunctionsExtensionsType, Shared.IsDateMethod, + Type.EmptyTypes, Shared.EfPropertyExpression, memberAccessExpression); Expression notExpression = filterValue == true ? callExpression @@ -91,11 +72,8 @@ Expression> FilterExpressionFactory(TFilter filter) var filterValue = filterFunc(filter).GetValueOrDefault(); var memberAccessExpression = BasicExpressions.GetMemberExpression(dataExpression.Body); var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); - var callExpression = Call( - DbFunctionsExtensionsType, - IsNumericMethod, - Type.EmptyTypes, - EfPropertyExpression, + var callExpression = Call(Shared.DbFunctionsExtensionsType, Shared.IsNumericMethod, + Type.EmptyTypes, Shared.EfPropertyExpression, memberAccessExpression); Expression notExpression = filterValue == true ? callExpression @@ -134,11 +112,8 @@ Expression> FilterExpressionFactory(TFilter filter) var memberAccessExpression = BasicExpressions.GetMemberExpression(dataExpression.Body); var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); var constantExpression = BasicExpressions.GetWrappedConstantExpression(filterValue); - var callExpression = Call( - DbFunctionsExtensionsType, - ContainsMethod, - Type.EmptyTypes, - EfPropertyExpression, + var callExpression = Call(Shared.DbFunctionsExtensionsType, Shared.ContainsMethod, + Type.EmptyTypes, Shared.EfPropertyExpression, memberAccessExpression, constantExpression); @@ -169,8 +144,7 @@ public static IFilterBuilder IsDateDiffDay( DateDiffOperation operation, int diffConstant = 0, Func? shouldRun = null) - => filterBuilder.IsDateDiffFilter( - DateDiffDayMethod, + => filterBuilder.IsDateDiffFilter(Shared.DateDiffDayMethod, dataExpression, filterFunc, operation, @@ -196,8 +170,7 @@ public static IFilterBuilder IsDateDiffMonth DateDiffOperation operation, int diffConstant = 0, Func? shouldRun = null) - => filterBuilder.IsDateDiffFilter( - DateDiffMonthMethod, + => filterBuilder.IsDateDiffFilter(Shared.DateDiffMonthMethod, dataExpression, filterFunc, operation, @@ -223,8 +196,7 @@ public static IFilterBuilder IsDateDiffWeek( DateDiffOperation operation, int diffConstant = 0, Func? shouldRun = null) - => filterBuilder.IsDateDiffFilter( - DateDiffWeekMethod, + => filterBuilder.IsDateDiffFilter(Shared.DateDiffWeekMethod, dataExpression, filterFunc, operation, @@ -250,8 +222,7 @@ public static IFilterBuilder IsDateDiffYear( DateDiffOperation operation, int diffConstant = 0, Func? shouldRun = null) - => filterBuilder.IsDateDiffFilter( - DateDiffYearMethod, + => filterBuilder.IsDateDiffFilter(Shared.DateDiffYearMethod, dataExpression, filterFunc, operation, @@ -277,8 +248,7 @@ public static IFilterBuilder IsDateDiffHour( DateDiffOperation operation, int diffConstant = 0, Func? shouldRun = null) - => filterBuilder.IsDateDiffFilter( - DateDiffHourMethod, + => filterBuilder.IsDateDiffFilter(Shared.DateDiffHourMethod, dataExpression, filterFunc, operation, @@ -304,8 +274,7 @@ public static IFilterBuilder IsDateDiffMinute? shouldRun = null) - => filterBuilder.IsDateDiffFilter( - DateDiffMinuteMethod, + => filterBuilder.IsDateDiffFilter(Shared.DateDiffMinuteMethod, dataExpression, filterFunc, operation, @@ -331,8 +300,7 @@ public static IFilterBuilder IsDateDiffSecond? shouldRun = null) - => filterBuilder.IsDateDiffFilter( - DateDiffSecondMethod, + => filterBuilder.IsDateDiffFilter(Shared.DateDiffSecondMethod, dataExpression, filterFunc, operation, @@ -358,8 +326,7 @@ public static IFilterBuilder IsDateDiffMillisecond? shouldRun = null) - => filterBuilder.IsDateDiffFilter( - DateDiffMillisecondMethod, + => filterBuilder.IsDateDiffFilter(Shared.DateDiffMillisecondMethod, dataExpression, filterFunc, operation, @@ -385,8 +352,7 @@ public static IFilterBuilder IsDateDiffMicrosecond? shouldRun = null) - => filterBuilder.IsDateDiffFilter( - DateDiffMicrosecondMethod, + => filterBuilder.IsDateDiffFilter(Shared.DateDiffMicrosecondMethod, dataExpression, filterFunc, operation, @@ -412,8 +378,7 @@ public static IFilterBuilder IsDateDiffNanosecond? shouldRun = null) - => filterBuilder.IsDateDiffFilter( - DateDiffNanosecondMethod, + => filterBuilder.IsDateDiffFilter(Shared.DateDiffNanosecondMethod, dataExpression, filterFunc, operation, @@ -437,11 +402,9 @@ Expression> FilterExpressionFactory(TFilter filter) var memberAccessExpression = BasicExpressions.GetMemberExpression(dataExpression.Body); var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); var constantExpression = BasicExpressions.GetWrappedConstantExpression(filterValue); - var callExpression = Call( - DbFunctionsExtensionsType, + var callExpression = Call(Shared.DbFunctionsExtensionsType, methodName, - Type.EmptyTypes, - EfPropertyExpression, + Type.EmptyTypes, Shared.EfPropertyExpression, memberAccessExpression, constantExpression); diff --git a/src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/Shared.cs b/src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/Shared.cs new file mode 100644 index 0000000..46b7036 --- /dev/null +++ b/src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/Shared.cs @@ -0,0 +1,23 @@ +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; + +namespace Ooze.Typed.EntityFrameworkCore.SqlServer.Extensions; + +internal static class Shared +{ + public const string IsDateMethod = nameof(SqlServerDbFunctionsExtensions.IsDate); + public const string IsNumericMethod = nameof(SqlServerDbFunctionsExtensions.IsNumeric); + public const string ContainsMethod = nameof(SqlServerDbFunctionsExtensions.Contains); + public const string DateDiffDayMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffDay); + public const string DateDiffMonthMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffMonth); + public const string DateDiffWeekMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffWeek); + public const string DateDiffYearMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffYear); + public const string DateDiffHourMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffHour); + public const string DateDiffMinuteMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffMinute); + public const string DateDiffSecondMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffSecond); + public const string DateDiffMillisecondMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffMillisecond); + public const string DateDiffMicrosecondMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffMicrosecond); + public const string DateDiffNanosecondMethod = nameof(SqlServerDbFunctionsExtensions.DateDiffNanosecond); + public static readonly Type DbFunctionsExtensionsType = typeof(SqlServerDbFunctionsExtensions); + public static readonly MemberExpression EfPropertyExpression = Expression.Property(null, typeof(EF), nameof(EF.Functions)); +} \ No newline at end of file diff --git a/src/Ooze.Typed.EntityFrameworkCore.Sqlite/Extensions/AsyncFilterBuilderExtensions.cs b/src/Ooze.Typed.EntityFrameworkCore.Sqlite/Extensions/AsyncFilterBuilderExtensions.cs new file mode 100644 index 0000000..8e3177c --- /dev/null +++ b/src/Ooze.Typed.EntityFrameworkCore.Sqlite/Extensions/AsyncFilterBuilderExtensions.cs @@ -0,0 +1,49 @@ +using System.Linq.Expressions; +using Ooze.Typed.Expressions; +using Ooze.Typed.Filters.Async; +using static System.Linq.Expressions.Expression; + +namespace Ooze.Typed.EntityFrameworkCore.Sqlite.Extensions; + +/// +/// Sqlite extensions for AsyncFilterBuilder +/// +public static class AsyncFilterBuilderExtensions +{ + /// + /// Applies a Glob filter over specified entity property and passed filter glob expression + /// + /// Instance of + /// Expression targeting entity property for glob filtering + /// Filter delegate targeting property with glob expression + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder Glob( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null) + { + bool FilterShouldRun(TFilter filter) => string.IsNullOrEmpty(filterFunc(filter)) == false; + + Expression> FilterExpressionFactory(TFilter filter) + { + var filterValue = filterFunc(filter); + var memberAccessExpression = BasicExpressions.GetMemberExpression(dataExpression.Body); + var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); + var constantExpression = BasicExpressions.GetWrappedConstantExpression(filterValue); + var callExpression = Call(Shared.DbFunctionsExtensionsType, Shared.GlobMethod, + Type.EmptyTypes, Shared.EfPropertyExpression, + memberAccessExpression!, + constantExpression); + + return Lambda>(callExpression, parameterExpression); + } + + shouldRun ??= FilterShouldRun; + filterBuilder.Add(shouldRun, FilterExpressionFactory); + return filterBuilder; + } +} \ No newline at end of file diff --git a/src/Ooze.Typed.EntityFrameworkCore.Sqlite/Extensions/FilterBuilderExtensions.cs b/src/Ooze.Typed.EntityFrameworkCore.Sqlite/Extensions/FilterBuilderExtensions.cs index d25695b..d74375a 100644 --- a/src/Ooze.Typed.EntityFrameworkCore.Sqlite/Extensions/FilterBuilderExtensions.cs +++ b/src/Ooze.Typed.EntityFrameworkCore.Sqlite/Extensions/FilterBuilderExtensions.cs @@ -1,5 +1,4 @@ using System.Linq.Expressions; -using Microsoft.EntityFrameworkCore; using Ooze.Typed.Expressions; using Ooze.Typed.Filters; using static System.Linq.Expressions.Expression; @@ -11,10 +10,6 @@ namespace Ooze.Typed.EntityFrameworkCore.Sqlite.Extensions; ///
public static class FilterBuilderExtensions { - private static readonly Type DbFunctionsExtensionsType = typeof(SqliteDbFunctionsExtensions); - private const string GlobMethod = nameof(SqliteDbFunctionsExtensions.Glob); - private static readonly MemberExpression EfPropertyExpression = Property(null, typeof(EF), nameof(EF.Functions)); - /// /// Applies a Glob filter over specified entity property and passed filter glob expression /// @@ -39,11 +34,8 @@ Expression> FilterExpressionFactory(TFilter filter) var memberAccessExpression = BasicExpressions.GetMemberExpression(dataExpression.Body); var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); var constantExpression = BasicExpressions.GetWrappedConstantExpression(filterValue); - var callExpression = Call( - DbFunctionsExtensionsType, - GlobMethod, - Type.EmptyTypes, - EfPropertyExpression, + var callExpression = Call(Shared.DbFunctionsExtensionsType, Shared.GlobMethod, + Type.EmptyTypes, Shared.EfPropertyExpression, memberAccessExpression!, constantExpression); diff --git a/src/Ooze.Typed.EntityFrameworkCore.Sqlite/Extensions/Shared.cs b/src/Ooze.Typed.EntityFrameworkCore.Sqlite/Extensions/Shared.cs new file mode 100644 index 0000000..8e7bbbd --- /dev/null +++ b/src/Ooze.Typed.EntityFrameworkCore.Sqlite/Extensions/Shared.cs @@ -0,0 +1,11 @@ +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; + +namespace Ooze.Typed.EntityFrameworkCore.Sqlite.Extensions; + +internal static class Shared +{ + public const string GlobMethod = nameof(SqliteDbFunctionsExtensions.Glob); + public static readonly Type DbFunctionsExtensionsType = typeof(SqliteDbFunctionsExtensions); + public static readonly MemberExpression EfPropertyExpression = Expression.Property(null, typeof(EF), nameof(EF.Functions)); +} \ No newline at end of file diff --git a/src/Ooze.Typed.EntityFrameworkCore/Extensions/AsyncFilterBuilderExtensions.cs b/src/Ooze.Typed.EntityFrameworkCore/Extensions/AsyncFilterBuilderExtensions.cs new file mode 100644 index 0000000..df2640b --- /dev/null +++ b/src/Ooze.Typed.EntityFrameworkCore/Extensions/AsyncFilterBuilderExtensions.cs @@ -0,0 +1,48 @@ +using System.Linq.Expressions; +using Ooze.Typed.Expressions; +using Ooze.Typed.Filters.Async; +using static System.Linq.Expressions.Expression; + +namespace Ooze.Typed.EntityFrameworkCore.Extensions; + +/// +/// Entity framework extensions for AsyncFilterBuilder +/// +public static class AsyncFilterBuilderExtensions +{ + /// + /// Applies a like filter over specified entity property and passed filter like expression + /// + /// Instance of + /// Expression targeting entity property for like filtering + /// Filter delegate targeting property with like expression + /// Delegate returning bool value which denotes if filter should be applied + /// Entity type + /// Filter type + /// Instance of builder for fluent building of multiple filter definitions + public static IAsyncFilterBuilder Like( + this IAsyncFilterBuilder filterBuilder, + Expression> dataExpression, + Func filterFunc, + Func? shouldRun = null) + { + bool FilterShouldRun(TFilter filter) => string.IsNullOrEmpty(filterFunc(filter)) == false; + Expression> FilterExpressionFactory(TFilter filter) + { + var filterValue = filterFunc(filter); + var memberAccessExpression = BasicExpressions.GetMemberExpression(dataExpression.Body); + var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); + var constantExpression = BasicExpressions.GetWrappedConstantExpression(filterValue); + var callExpression = Call(Shared.DbFunctionsExtensionsType, Shared.LikeMethod, + Type.EmptyTypes, Shared.EfPropertyExpression, + memberAccessExpression!, + constantExpression); + + return Lambda>(callExpression, parameterExpression); + } + + shouldRun ??= FilterShouldRun; + filterBuilder.Add(shouldRun, FilterExpressionFactory); + return filterBuilder; + } +} \ No newline at end of file diff --git a/src/Ooze.Typed.EntityFrameworkCore/Extensions/FilterBuilderExtensions.cs b/src/Ooze.Typed.EntityFrameworkCore/Extensions/FilterBuilderExtensions.cs index 68a23c1..d8a9500 100644 --- a/src/Ooze.Typed.EntityFrameworkCore/Extensions/FilterBuilderExtensions.cs +++ b/src/Ooze.Typed.EntityFrameworkCore/Extensions/FilterBuilderExtensions.cs @@ -1,5 +1,4 @@ using System.Linq.Expressions; -using Microsoft.EntityFrameworkCore; using Ooze.Typed.Expressions; using Ooze.Typed.Filters; using static System.Linq.Expressions.Expression; @@ -11,10 +10,6 @@ namespace Ooze.Typed.EntityFrameworkCore.Extensions; ///
public static class FilterBuilderExtensions { - private static readonly Type DbFunctionsExtensionsType = typeof(DbFunctionsExtensions); - private const string LikeMethod = nameof(DbFunctionsExtensions.Like); - private static readonly MemberExpression EfPropertyExpression = Property(null, typeof(EF), nameof(EF.Functions)); - /// /// Applies a like filter over specified entity property and passed filter like expression /// @@ -38,11 +33,8 @@ Expression> FilterExpressionFactory(TFilter filter) var memberAccessExpression = BasicExpressions.GetMemberExpression(dataExpression.Body); var parameterExpression = BasicExpressions.ExtractParameterExpression(memberAccessExpression); var constantExpression = BasicExpressions.GetWrappedConstantExpression(filterValue); - var callExpression = Call( - DbFunctionsExtensionsType, - LikeMethod, - Type.EmptyTypes, - EfPropertyExpression, + var callExpression = Call(Shared.DbFunctionsExtensionsType, Shared.LikeMethod, + Type.EmptyTypes, Shared.EfPropertyExpression, memberAccessExpression!, constantExpression); diff --git a/src/Ooze.Typed.EntityFrameworkCore/Extensions/Shared.cs b/src/Ooze.Typed.EntityFrameworkCore/Extensions/Shared.cs new file mode 100644 index 0000000..228351d --- /dev/null +++ b/src/Ooze.Typed.EntityFrameworkCore/Extensions/Shared.cs @@ -0,0 +1,11 @@ +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; + +namespace Ooze.Typed.EntityFrameworkCore.Extensions; + +internal static class Shared +{ + public const string LikeMethod = nameof(DbFunctionsExtensions.Like); + public static readonly Type DbFunctionsExtensionsType = typeof(DbFunctionsExtensions); + public static readonly MemberExpression EfPropertyExpression = Expression.Property(null, typeof(EF), nameof(EF.Functions)); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostEqualFiltersProvider.cs b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostEqualFiltersProvider.cs index 96a587e..e1520e3 100644 --- a/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostEqualFiltersProvider.cs +++ b/tests/Ooze.Typed.Tests/Integration/Setup/Async/AsyncPostEqualFiltersProvider.cs @@ -10,5 +10,14 @@ public ValueTask>> GetFilte .Equal(post => post.Name, filter => filter.Name) .Equal(post => post.Enabled, filter => filter.Enabled) .Equal(post => post.Date, filter => filter.Date) + .AddAsync(async filters => + { + await Task.CompletedTask; + return filters.Date != null; + }, async filters => + { + await Task.CompletedTask; + return post => post.Date == filters.Date; + }) .Build()); } \ No newline at end of file From 1704eb4bec5fa1ff7975bbe32295b5d8a8bb7dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20Pavlovi=C4=87?= Date: Thu, 4 Apr 2024 22:50:45 +0200 Subject: [PATCH 7/9] Add async tests --- .../Extensions/Shared.cs | 1 + .../MySqlAsyncIntegrationTests.cs | 164 ++++++ tests/Ooze.Typed.Tests.MySql/MySqlFixture.cs | 13 +- .../MySqlIntegrationTests.cs | 17 +- .../Async/PostAsyncFiltersProvider.cs | 19 + .../Async/PostAsyncSortersProvider.cs | 11 + .../OozeConfiguration/PostFiltersProvider.cs | 3 +- .../NpgsqlAsyncIntegrationTests.cs | 307 +++++++++++ .../Ooze.Typed.Tests.Npgsql/NpgsqlFixture.cs | 13 +- .../NpgsqlIntegrationTests.cs | 58 +- .../Async/PostAsyncFiltersProvider.cs | 25 + .../Async/PostAsyncSortersProvider.cs | 11 + .../OozeConfiguration/PostFiltersProvider.cs | 5 +- .../Async/PostAsyncFiltersProvider.cs | 36 ++ .../Async/PostAsyncSortersProvider.cs | 11 + .../SqlServerAsyncIntegrationTests.cs | 500 ++++++++++++++++++ .../SqlServerFixture.cs | 13 +- .../SqlServerIntegrationTests.cs | 76 +-- 18 files changed, 1195 insertions(+), 88 deletions(-) create mode 100644 tests/Ooze.Typed.Tests.MySql/MySqlAsyncIntegrationTests.cs create mode 100644 tests/Ooze.Typed.Tests.MySql/OozeConfiguration/Async/PostAsyncFiltersProvider.cs create mode 100644 tests/Ooze.Typed.Tests.MySql/OozeConfiguration/Async/PostAsyncSortersProvider.cs create mode 100644 tests/Ooze.Typed.Tests.Npgsql/NpgsqlAsyncIntegrationTests.cs create mode 100644 tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/Async/PostAsyncFiltersProvider.cs create mode 100644 tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/Async/PostAsyncSortersProvider.cs create mode 100644 tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/Async/PostAsyncFiltersProvider.cs create mode 100644 tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/Async/PostAsyncSortersProvider.cs create mode 100644 tests/Ooze.Typed.Tests.SqlServer/SqlServerAsyncIntegrationTests.cs diff --git a/src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/Shared.cs b/src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/Shared.cs index 46b7036..498202a 100644 --- a/src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/Shared.cs +++ b/src/Ooze.Typed.EntityFrameworkCore.SqlServer/Extensions/Shared.cs @@ -1,5 +1,6 @@ using System.Linq.Expressions; using Microsoft.EntityFrameworkCore; +// ReSharper disable InconsistentNaming namespace Ooze.Typed.EntityFrameworkCore.SqlServer.Extensions; diff --git a/tests/Ooze.Typed.Tests.MySql/MySqlAsyncIntegrationTests.cs b/tests/Ooze.Typed.Tests.MySql/MySqlAsyncIntegrationTests.cs new file mode 100644 index 0000000..af7b2cf --- /dev/null +++ b/tests/Ooze.Typed.Tests.MySql/MySqlAsyncIntegrationTests.cs @@ -0,0 +1,164 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Ooze.Typed.Sorters; +using Ooze.Typed.Tests.MySql.OozeConfiguration.Async; + +namespace Ooze.Typed.Tests.MySql; + +public class MySqlAsyncIntegrationTests(MySqlFixture fixture) : IClassFixture +{ + [Fact] + public async Task DateDiffDay_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffDayFilter: new DateTime(2022, 5, 20))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("TIMESTAMPDIFF(DAY,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == false); + } + + [Fact] + public async Task DateDiffMonth_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffMonthFilter: new DateTime(2022, 5, 20))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("TIMESTAMPDIFF(MONTH,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == false); + } + + [Fact] + public async Task DateDiffYear_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffYearFilter: new DateTime(2022, 5, 20))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("TIMESTAMPDIFF(YEAR,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == true); + } + + [Fact] + public async Task DateDiffHour_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffHourFilter: new DateTime(2022, 5, 20))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("TIMESTAMPDIFF(HOUR,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == false); + } + + [Fact] + public async Task DateDiffMinute_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffMinuteFilter: new DateTime(2022, 5, 20))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("TIMESTAMPDIFF(MINUTE,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == false); + } + + [Fact] + public async Task DateDiffSecond_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffSecondFilter: new DateTime(2022, 5, 20))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("TIMESTAMPDIFF(SECOND,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == false); + } + + [Fact] + public async Task DateDiffMicrosecond_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffMicrosecondFilter: new DateTime(2022, 2, 2, 20, 20, 22))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("TIMESTAMPDIFF(MICROSECOND,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + } + + [Fact] + public async Task Id_Sorter_Should_Update_Query_And_Return_Correctly_Ordered_Data() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + var defaultIds = await query.Select(x => x.Id) + .ToListAsync(); + + query = await resolver.SortAsync(query, + new[] { new PostSorters(Id: SortDirection.Descending) }); + var sortedIds = await query.Select(x => x.Id) + .ToListAsync(); + + Assert.False(defaultIds.SequenceEqual(sortedIds)); + Assert.False(defaultIds.Except(sortedIds).Any()); + Assert.Equal(100, defaultIds.Intersect(sortedIds).Count()); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("ORDER BY", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + } +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests.MySql/MySqlFixture.cs b/tests/Ooze.Typed.Tests.MySql/MySqlFixture.cs index e0a2d02..cd2c538 100644 --- a/tests/Ooze.Typed.Tests.MySql/MySqlFixture.cs +++ b/tests/Ooze.Typed.Tests.MySql/MySqlFixture.cs @@ -14,21 +14,22 @@ public class MySqlFixture : IAsyncLifetime .WithCleanUp(true) .Build(); - private static IServiceCollection CreateServiceCollection() + private static IServiceCollection CreateServiceCollection(bool enableAsyncSupport = false) { var services = new ServiceCollection().AddLogging(); - services.AddOozeTyped() - .Add() - .Add(); + var oozeBuilder = services.AddOozeTyped(); + if (enableAsyncSupport == true) + oozeBuilder.EnableAsyncResolvers(); + oozeBuilder.Add(); return services; } - public readonly IServiceProvider ServiceProvider = new DefaultServiceProviderFactory( + public IServiceProvider CreateServiceProvider(bool enableAsyncSupport = false) => new DefaultServiceProviderFactory( new ServiceProviderOptions { ValidateScopes = false - }).CreateServiceProvider(CreateServiceCollection()); + }).CreateServiceProvider(CreateServiceCollection(enableAsyncSupport)); public MySqlContext CreateContext() { diff --git a/tests/Ooze.Typed.Tests.MySql/MySqlIntegrationTests.cs b/tests/Ooze.Typed.Tests.MySql/MySqlIntegrationTests.cs index da82502..fdaba9a 100644 --- a/tests/Ooze.Typed.Tests.MySql/MySqlIntegrationTests.cs +++ b/tests/Ooze.Typed.Tests.MySql/MySqlIntegrationTests.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Ooze.Typed.Sorters; +using Ooze.Typed.Tests.MySql.OozeConfiguration; namespace Ooze.Typed.Tests.MySql; @@ -11,7 +12,7 @@ public async Task DateDiffDay_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffDayFilter: new DateTime(2022, 5, 20))); @@ -29,7 +30,7 @@ public async Task DateDiffMonth_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffMonthFilter: new DateTime(2022, 5, 20))); @@ -47,7 +48,7 @@ public async Task DateDiffYear_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffYearFilter: new DateTime(2022, 5, 20))); @@ -65,7 +66,7 @@ public async Task DateDiffHour_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffHourFilter: new DateTime(2022, 5, 20))); @@ -83,7 +84,7 @@ public async Task DateDiffMinute_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffMinuteFilter: new DateTime(2022, 5, 20))); @@ -101,7 +102,7 @@ public async Task DateDiffSecond_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffSecondFilter: new DateTime(2022, 5, 20))); @@ -119,7 +120,7 @@ public async Task DateDiffMicrosecond_Should_Update_Query_And_Return_Correct_Que { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffMicrosecondFilter: new DateTime(2022, 2, 2, 20, 20, 22))); @@ -134,7 +135,7 @@ public async Task Id_Sorter_Should_Update_Query_And_Return_Correctly_Ordered_Dat { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); var defaultIds = await query.Select(x => x.Id) .ToListAsync(); diff --git a/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/Async/PostAsyncFiltersProvider.cs b/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/Async/PostAsyncFiltersProvider.cs new file mode 100644 index 0000000..259b5db --- /dev/null +++ b/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/Async/PostAsyncFiltersProvider.cs @@ -0,0 +1,19 @@ +using Ooze.Typed.EntityFrameworkCore.MySql.Extensions; +using Ooze.Typed.Filters.Async; + +namespace Ooze.Typed.Tests.MySql.OozeConfiguration.Async; + +public class PostAsyncFiltersProvider : IAsyncFilterProvider +{ + public ValueTask>> GetFiltersAsync() + => ValueTask.FromResult(AsyncFilters.CreateFor() + .IsDateDiffDay(post => post.Date, filter => filter.DateDiffDayFilter, DateDiffOperation.Equal) + .IsDateDiffMonth(post => post.Date, filter => filter.DateDiffMonthFilter, DateDiffOperation.Equal) + .IsDateDiffYear(post => post.Date, filter => filter.DateDiffYearFilter, DateDiffOperation.Equal) + .IsDateDiffHour(post => post.Date, filter => filter.DateDiffHourFilter, DateDiffOperation.Equal) + .IsDateDiffMinute(post => post.Date, filter => filter.DateDiffMinuteFilter, DateDiffOperation.Equal) + .IsDateDiffSecond(post => post.Date, filter => filter.DateDiffSecondFilter, DateDiffOperation.Equal) + .IsDateDiffMicrosecond(post => post.Date, filter => filter.DateDiffMicrosecondFilter, + DateDiffOperation.Equal) + .Build()); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/Async/PostAsyncSortersProvider.cs b/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/Async/PostAsyncSortersProvider.cs new file mode 100644 index 0000000..52f7713 --- /dev/null +++ b/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/Async/PostAsyncSortersProvider.cs @@ -0,0 +1,11 @@ +using Ooze.Typed.Sorters.Async; + +namespace Ooze.Typed.Tests.MySql.OozeConfiguration.Async; + +public class PostAsyncSortersProvider : IAsyncSorterProvider +{ + public ValueTask>> GetSortersAsync() + => ValueTask.FromResult(AsyncSorters.CreateFor() + .SortBy(post => post.Id, sort => sort.Id) + .Build()); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/PostFiltersProvider.cs b/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/PostFiltersProvider.cs index b0a2c6c..1ab553e 100644 --- a/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/PostFiltersProvider.cs +++ b/tests/Ooze.Typed.Tests.MySql/OozeConfiguration/PostFiltersProvider.cs @@ -13,6 +13,7 @@ public IEnumerable> GetFilters() .IsDateDiffHour(post => post.Date, filter => filter.DateDiffHourFilter, DateDiffOperation.Equal) .IsDateDiffMinute(post => post.Date, filter => filter.DateDiffMinuteFilter, DateDiffOperation.Equal) .IsDateDiffSecond(post => post.Date, filter => filter.DateDiffSecondFilter, DateDiffOperation.Equal) - .IsDateDiffMicrosecond(post => post.Date, filter => filter.DateDiffMicrosecondFilter, DateDiffOperation.Equal) + .IsDateDiffMicrosecond(post => post.Date, filter => filter.DateDiffMicrosecondFilter, + DateDiffOperation.Equal) .Build(); } \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests.Npgsql/NpgsqlAsyncIntegrationTests.cs b/tests/Ooze.Typed.Tests.Npgsql/NpgsqlAsyncIntegrationTests.cs new file mode 100644 index 0000000..3f25c4a --- /dev/null +++ b/tests/Ooze.Typed.Tests.Npgsql/NpgsqlAsyncIntegrationTests.cs @@ -0,0 +1,307 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Ooze.Typed.Filters; +using Ooze.Typed.Sorters; +using Ooze.Typed.Tests.Npgsql.OozeConfiguration.Async; + +namespace Ooze.Typed.Tests.Npgsql; + +public class NpgsqlAsyncIntegrationTests(NpgsqlFixture fixture) : IClassFixture +{ + [Theory] + [InlineData(10)] + [InlineData(1)] + [InlineData(5)] + public async Task Equal_Should_Update_Query_And_Return_Correct_Query(int postId) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(PostId: postId)); + + var filteredItemsCount = await query.CountAsync(); + Assert.True(filteredItemsCount == 1); + } + + [Theory] + [InlineData(10)] + [InlineData(1)] + [InlineData(5)] + public async Task NotEqual_Should_Update_Query_And_Return_Correct_Query(int postId) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(NotEqualPostId: postId)); + + var containsPostId = await query.AnyAsync(post => post.Id == postId); + Assert.True(containsPostId == false); + } + + [Theory] + [InlineData(1)] + [InlineData(50)] + [InlineData(100)] + public async Task GreaterThan_Should_Update_Query_And_Return_Correct_Query(int postId) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(GreaterThanPostId: postId)); + + var filteredItemsCount = await query.CountAsync(); + var expectedCount = NpgsqlContext.TotalRecords - postId; + Assert.True(filteredItemsCount == expectedCount); + } + + [Theory] + [InlineData(1)] + [InlineData(50)] + [InlineData(100)] + public async Task LessThan_Should_Update_Query_And_Return_Correct_Query(int postId) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(LessThanPostId: postId)); + + var filteredItemsCount = await query.CountAsync(); + var expectedCount = postId - 1; + Assert.True(filteredItemsCount == expectedCount); + } + + [Fact] + public async Task In_Should_Update_Query_And_Return_Correct_Query() + { + var postIds = new long[] { 1, 10, 100 }; + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(IdIn: postIds)); + + var materializedIds = await query.Select(x => x.Id) + .ToListAsync(); + var containsAll = materializedIds.SequenceEqual(postIds); + + Assert.True(containsAll == true); + } + + [Fact] + public async Task NotIn_Should_Update_Query_And_Return_Correct_Query() + { + var postIds = new long[] { -1, 1000, -100 }; + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(IdIn: postIds)); + + var materializedIds = await query.Select(x => x.Id) + .ToListAsync(); + var containsAny = materializedIds.Intersect(postIds).Any(); + + Assert.True(containsAny == false); + } + + [Theory] + [InlineData(-200, 1000)] + [InlineData(1, 100)] + [InlineData(50, 50)] + [InlineData(30, 101)] + public async Task Range_Should_Update_Query_And_Return_Correct_Query( + long from, + long to) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(IdRange: new RangeFilter + { + From = from, + To = to + })); + + var materializedIds = await query.Select(x => x.Id) + .ToListAsync(); + var allIdsValid = materializedIds.All(x => x >= from && x <= to); + + Assert.True(allIdsValid == true); + } + + [Theory] + [InlineData(-200, 1000)] + [InlineData(1, 100)] + [InlineData(50, 50)] + [InlineData(30, 101)] + public async Task OutOfRange_Should_Update_Query_And_Return_Correct_Query( + long from, + long to) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(IdOutOfRange: new RangeFilter + { + From = from, + To = to + })); + + var materializedIds = await query.Select(x => x.Id) + .ToListAsync(); + var allIdsValid = materializedIds.All(x => x < from || x > to); + + Assert.True(allIdsValid == true); + } + + [Theory] + [InlineData("1_Sample")] + [InlineData("12_Sample")] + [InlineData("50_Sample")] + public async Task StartsWith_Should_Update_Query_And_Return_Correct_Query(string prefix) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(NameStartsWith: prefix)); + + var materialized = await query.ToListAsync(); + var allEntitiesValid = materialized.All(x => x.Name.StartsWith(prefix)); + + Assert.True(allEntitiesValid == true); + } + + [Theory] + [InlineData("dlkjsad")] + [InlineData("3213")] + [InlineData("$!#")] + public async Task DoesntStartWith_Should_Update_Query_And_Return_Correct_Query(string prefix) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(NameDoesntWith: prefix)); + + var materialized = await query.ToListAsync(); + var allEntitiesValid = materialized.All(x => x.Name.StartsWith(prefix) == false); + + Assert.True(allEntitiesValid == true); + } + + [Theory] + [InlineData("post_1")] + [InlineData("post_12")] + [InlineData("post_50")] + public async Task EndsWith_Should_Update_Query_And_Return_Correct_Query(string suffix) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(NameEndsWith: suffix)); + + var materialized = await query.ToListAsync(); + var allEntitiesValid = materialized.All(x => x.Name.EndsWith(suffix)); + + Assert.True(allEntitiesValid == true); + } + + [Theory] + [InlineData("dlkjsad")] + [InlineData("3213")] + [InlineData("$!#")] + public async Task DoesntEndWith_Should_Update_Query_And_Return_Correct_Query(string suffix) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(NameDoesntEndWith: suffix)); + + var materialized = await query.ToListAsync(); + var allEntitiesValid = materialized.All(x => x.Name.EndsWith(suffix) == false); + + Assert.True(allEntitiesValid == true); + } + + [Fact] + public async Task InsensitiveLike_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(NameLikeFilter: "%Sample%")); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("ILIKE", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == true); + } + + [Fact] + public async Task SoundexEqual_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(NameSoundexEqual: "%Sample%")); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("soundex", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == false); + } + + [Fact] + public async Task Id_Sorter_Should_Update_Query_And_Return_Correctly_Ordered_Data() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + var defaultIds = await query.Select(x => x.Id) + .ToListAsync(); + + query = await resolver.SortAsync(query, + new[] { new PostSorters(Id: SortDirection.Descending) }); + var sortedIds = await query.Select(x => x.Id) + .ToListAsync(); + + Assert.False(defaultIds.SequenceEqual(sortedIds)); + Assert.False(defaultIds.Except(sortedIds).Any()); + Assert.Equal(100, defaultIds.Intersect(sortedIds).Count()); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("ORDER BY", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + } +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests.Npgsql/NpgsqlFixture.cs b/tests/Ooze.Typed.Tests.Npgsql/NpgsqlFixture.cs index c51b175..fbf9655 100644 --- a/tests/Ooze.Typed.Tests.Npgsql/NpgsqlFixture.cs +++ b/tests/Ooze.Typed.Tests.Npgsql/NpgsqlFixture.cs @@ -14,21 +14,22 @@ public class NpgsqlFixture : IAsyncLifetime .WithCleanUp(true) .Build(); - private static IServiceCollection CreateServiceCollection() + private static IServiceCollection CreateServiceCollection(bool enableAsyncSupport = false) { var services = new ServiceCollection().AddLogging(); - services.AddOozeTyped() - .Add() - .Add(); + var oozeBuilder = services.AddOozeTyped(); + if (enableAsyncSupport == true) + oozeBuilder.EnableAsyncResolvers(); + oozeBuilder.Add(); return services; } - public readonly IServiceProvider ServiceProvider = new DefaultServiceProviderFactory( + public IServiceProvider CreateServiceProvider(bool enableAsyncSupport = false) => new DefaultServiceProviderFactory( new ServiceProviderOptions { ValidateScopes = false - }).CreateServiceProvider(CreateServiceCollection()); + }).CreateServiceProvider(CreateServiceCollection(enableAsyncSupport)); public NpgsqlContext CreateContext() { diff --git a/tests/Ooze.Typed.Tests.Npgsql/NpgsqlIntegrationTests.cs b/tests/Ooze.Typed.Tests.Npgsql/NpgsqlIntegrationTests.cs index 7a2cfe0..4bbc8d5 100644 --- a/tests/Ooze.Typed.Tests.Npgsql/NpgsqlIntegrationTests.cs +++ b/tests/Ooze.Typed.Tests.Npgsql/NpgsqlIntegrationTests.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using Ooze.Typed.Filters; using Ooze.Typed.Sorters; +using Ooze.Typed.Tests.Npgsql.OozeConfiguration; namespace Ooze.Typed.Tests.Npgsql; @@ -15,7 +16,8 @@ public async Task Equal_Should_Update_Query_And_Return_Correct_Query(int postId) { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider() + .GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(PostId: postId)); @@ -31,7 +33,8 @@ public async Task NotEqual_Should_Update_Query_And_Return_Correct_Query(int post { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider() + .GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NotEqualPostId: postId)); @@ -47,7 +50,8 @@ public async Task GreaterThan_Should_Update_Query_And_Return_Correct_Query(int p { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider() + .GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(GreaterThanPostId: postId)); @@ -64,7 +68,8 @@ public async Task LessThan_Should_Update_Query_And_Return_Correct_Query(int post { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider() + .GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(LessThanPostId: postId)); @@ -79,7 +84,8 @@ public async Task In_Should_Update_Query_And_Return_Correct_Query() var postIds = new long[] { 1, 10, 100 }; await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider() + .GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdIn: postIds)); @@ -96,7 +102,8 @@ public async Task NotIn_Should_Update_Query_And_Return_Correct_Query() var postIds = new long[] { -1, 1000, -100 }; await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider() + .GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdIn: postIds)); @@ -118,7 +125,8 @@ public async Task Range_Should_Update_Query_And_Return_Correct_Query( { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider() + .GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdRange: new RangeFilter { @@ -144,7 +152,8 @@ public async Task OutOfRange_Should_Update_Query_And_Return_Correct_Query( { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider() + .GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdOutOfRange: new RangeFilter { @@ -167,7 +176,8 @@ public async Task StartsWith_Should_Update_Query_And_Return_Correct_Query(string { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider() + .GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameStartsWith: prefix)); @@ -185,7 +195,8 @@ public async Task DoesntStartWith_Should_Update_Query_And_Return_Correct_Query(s { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider() + .GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameDoesntWith: prefix)); @@ -203,7 +214,8 @@ public async Task EndsWith_Should_Update_Query_And_Return_Correct_Query(string s { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider() + .GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameEndsWith: suffix)); @@ -221,7 +233,8 @@ public async Task DoesntEndWith_Should_Update_Query_And_Return_Correct_Query(str { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider() + .GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameDoesntEndWith: suffix)); @@ -236,7 +249,8 @@ public async Task InsensitiveLike_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider() + .GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameLikeFilter: "%Sample%")); @@ -245,7 +259,7 @@ public async Task InsensitiveLike_Should_Update_Query_And_Return_Correct_Query() Assert.True(sqlContainsCall); var hasFilteredItems = await query.AnyAsync(); - Assert.True(hasFilteredItems == false); + Assert.True(hasFilteredItems == true); } [Fact] @@ -253,7 +267,8 @@ public async Task SoundexEqual_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider() + .GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameSoundexEqual: "%Sample%")); @@ -264,26 +279,27 @@ public async Task SoundexEqual_Should_Update_Query_And_Return_Correct_Query() var hasFilteredItems = await query.AnyAsync(); Assert.True(hasFilteredItems == false); } - + [Fact] public async Task Id_Sorter_Should_Update_Query_And_Return_Correctly_Ordered_Data() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider() + .GetRequiredService(); IQueryable query = context.Set(); var defaultIds = await query.Select(x => x.Id) .ToListAsync(); - + query = resolver.Sort(query, new[] { new PostSorters(Id: SortDirection.Descending) }); var sortedIds = await query.Select(x => x.Id) - .ToListAsync(); - + .ToListAsync(); + Assert.True(defaultIds.SequenceEqual(sortedIds) == false); Assert.True(defaultIds.Except(sortedIds).Any() == false); Assert.True(defaultIds.Intersect(sortedIds).Count() == 100); - + var sql = query.ToQueryString(); var sqlContainsCall = sql.Contains("ORDER BY", StringComparison.InvariantCultureIgnoreCase); Assert.True(sqlContainsCall); diff --git a/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/Async/PostAsyncFiltersProvider.cs b/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/Async/PostAsyncFiltersProvider.cs new file mode 100644 index 0000000..88f8c54 --- /dev/null +++ b/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/Async/PostAsyncFiltersProvider.cs @@ -0,0 +1,25 @@ +using Ooze.Typed.EntityFrameworkCore.Npgsql.Extensions; +using Ooze.Typed.Filters.Async; + +namespace Ooze.Typed.Tests.Npgsql.OozeConfiguration.Async; + +public class PostAsyncFiltersProvider : IAsyncFilterProvider +{ + public ValueTask>> GetFiltersAsync() + => ValueTask.FromResult(AsyncFilters.CreateFor() + .Equal(post => post.Id, filter => filter.PostId) + .NotEqual(post => post.Id, filter => filter.NotEqualPostId) + .GreaterThan(post => post.Id, filter => filter.GreaterThanPostId) + .LessThan(post => post.Id, filter => filter.LessThanPostId) + .In(post => post.Id, filter => filter.IdIn) + .NotIn(post => post.Id, filter => filter.IdNotIn) + .Range(post => post.Id, filter => filter.IdRange) + .OutOfRange(post => post.Id, filter => filter.IdOutOfRange) + .StartsWith(post => post.Name, filter => filter.NameStartsWith) + .DoesntStartWith(post => post.Name, filter => filter.NameDoesntWith) + .EndsWith(post => post.Name, filter => filter.NameEndsWith) + .DoesntEndWith(post => post.Name, filter => filter.NameDoesntEndWith) + .InsensitiveLike(post => post.Name, filter => filter.NameLikeFilter) + .SoundexEqual(post => post.Name, filter => filter.NameSoundexEqual) + .Build()); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/Async/PostAsyncSortersProvider.cs b/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/Async/PostAsyncSortersProvider.cs new file mode 100644 index 0000000..7e218df --- /dev/null +++ b/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/Async/PostAsyncSortersProvider.cs @@ -0,0 +1,11 @@ +using Ooze.Typed.Sorters.Async; + +namespace Ooze.Typed.Tests.Npgsql.OozeConfiguration.Async; + +public class PostAsyncSortersProvider : IAsyncSorterProvider +{ + public ValueTask>> GetSortersAsync() + => ValueTask.FromResult(AsyncSorters.CreateFor() + .SortBy(post => post.Id, sort => sort.Id) + .Build()); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/PostFiltersProvider.cs b/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/PostFiltersProvider.cs index 1f9aaca..143ef69 100644 --- a/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/PostFiltersProvider.cs +++ b/tests/Ooze.Typed.Tests.Npgsql/OozeConfiguration/PostFiltersProvider.cs @@ -7,10 +7,7 @@ public class PostFiltersProvider : IFilterProvider { public IEnumerable> GetFilters() => Filters.Filters.CreateFor() - .Equal(post => post.Id, filter => filter.PostId, _ => - { - return true; - }) + .Equal(post => post.Id, filter => filter.PostId) .NotEqual(post => post.Id, filter => filter.NotEqualPostId) .GreaterThan(post => post.Id, filter => filter.GreaterThanPostId) .LessThan(post => post.Id, filter => filter.LessThanPostId) diff --git a/tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/Async/PostAsyncFiltersProvider.cs b/tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/Async/PostAsyncFiltersProvider.cs new file mode 100644 index 0000000..2b844e4 --- /dev/null +++ b/tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/Async/PostAsyncFiltersProvider.cs @@ -0,0 +1,36 @@ +using Ooze.Typed.EntityFrameworkCore.SqlServer.Extensions; +using Ooze.Typed.Filters.Async; + +namespace Ooze.Typed.Tests.SqlServer.OozeConfiguration.Async; + +public class PostAsyncFiltersProvider : IAsyncFilterProvider +{ + public ValueTask>> GetFiltersAsync() + => ValueTask.FromResult(AsyncFilters.CreateFor() + .Equal(post => post.Id, filter => filter.PostId) + .NotEqual(post => post.Id, filter => filter.NotEqualPostId) + .GreaterThan(post => post.Id, filter => filter.GreaterThanPostId) + .LessThan(post => post.Id, filter => filter.LessThanPostId) + .In(post => post.Id, filter => filter.IdIn) + .NotIn(post => post.Id, filter => filter.IdNotIn) + .Range(post => post.Id, filter => filter.IdRange) + .OutOfRange(post => post.Id, filter => filter.IdOutOfRange) + .StartsWith(post => post.Name, filter => filter.NameStartsWith) + .DoesntStartWith(post => post.Name, filter => filter.NameDoesntWith) + .EndsWith(post => post.Name, filter => filter.NameEndsWith) + .DoesntEndWith(post => post.Name, filter => filter.NameDoesntEndWith) + .IsDate(post => post.Name, filter => filter.IsNameDate) + .IsNumeric(post => post.Name, filter => filter.IsIdNumeric) + .IsDateDiffDay(post => post.Date, filter => filter.DateDiffDay, DateDiffOperation.NotEqual) + .IsDateDiffDay(post => post.Date, filter => filter.DateDiffDayEqual, DateDiffOperation.Equal) + .IsDateDiffMonth(post => post.Date, filter => filter.DateDiffMonth, DateDiffOperation.NotEqual) + .IsDateDiffYear(post => post.Date, filter => filter.DateDiffYear, DateDiffOperation.NotEqual) + .IsDateDiffWeek(post => post.Date, filter => filter.DateDiffWeek, DateDiffOperation.NotEqual) + .IsDateDiffHour(post => post.Date, filter => filter.DateDiffHour, DateDiffOperation.NotEqual) + .IsDateDiffMinute(post => post.Date, filter => filter.DateDiffMinute, DateDiffOperation.NotEqual) + .IsDateDiffSecond(post => post.Date, filter => filter.DateDiffSecond, DateDiffOperation.NotEqual) + .IsDateDiffMillisecond(post => post.Date, filter => filter.DateDiffMillisecond, DateDiffOperation.NotEqual) + .IsDateDiffMicrosecond(post => post.Date, filter => filter.DateDiffMicrosecond, DateDiffOperation.NotEqual) + .IsDateDiffNanosecond(post => post.Date, filter => filter.DateDiffNanosecond, DateDiffOperation.NotEqual) + .Build()); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/Async/PostAsyncSortersProvider.cs b/tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/Async/PostAsyncSortersProvider.cs new file mode 100644 index 0000000..f7a957a --- /dev/null +++ b/tests/Ooze.Typed.Tests.SqlServer/OozeConfiguration/Async/PostAsyncSortersProvider.cs @@ -0,0 +1,11 @@ +using Ooze.Typed.Sorters.Async; + +namespace Ooze.Typed.Tests.SqlServer.OozeConfiguration.Async; + +public class PostAsyncSortersProvider : IAsyncSorterProvider +{ + public ValueTask>> GetSortersAsync() + => ValueTask.FromResult(AsyncSorters.CreateFor() + .SortBy(post => post.Id, sort => sort.Id) + .Build()); +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests.SqlServer/SqlServerAsyncIntegrationTests.cs b/tests/Ooze.Typed.Tests.SqlServer/SqlServerAsyncIntegrationTests.cs new file mode 100644 index 0000000..f948362 --- /dev/null +++ b/tests/Ooze.Typed.Tests.SqlServer/SqlServerAsyncIntegrationTests.cs @@ -0,0 +1,500 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Ooze.Typed.Filters; +using Ooze.Typed.Sorters; +using Ooze.Typed.Tests.SqlServer.OozeConfiguration.Async; + +namespace Ooze.Typed.Tests.SqlServer; + +public class SqlServerAsyncIntegrationTests(SqlServerFixture fixture) : IClassFixture +{ + [Theory] + [InlineData(10)] + [InlineData(1)] + [InlineData(5)] + public async Task Equal_Should_Update_Query_And_Return_Correct_Query(int postId) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(PostId: postId)); + + var filteredItemsCount = await query.CountAsync(); + Assert.True(filteredItemsCount == 1); + } + + [Theory] + [InlineData(10)] + [InlineData(1)] + [InlineData(5)] + public async Task NotEqual_Should_Update_Query_And_Return_Correct_Query(int postId) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(NotEqualPostId: postId)); + + var containsPostId = await query.AnyAsync(post => post.Id == postId); + Assert.True(containsPostId == false); + } + + [Theory] + [InlineData(1)] + [InlineData(50)] + [InlineData(100)] + public async Task GreaterThan_Should_Update_Query_And_Return_Correct_Query(int postId) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(GreaterThanPostId: postId)); + + var filteredItemsCount = await query.CountAsync(); + var expectedCount = SqlServerContext.TotalRecords - postId; + Assert.True(filteredItemsCount == expectedCount); + } + + [Theory] + [InlineData(1)] + [InlineData(50)] + [InlineData(100)] + public async Task LessThan_Should_Update_Query_And_Return_Correct_Query(int postId) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(LessThanPostId: postId)); + + var filteredItemsCount = await query.CountAsync(); + var expectedCount = postId - 1; + Assert.True(filteredItemsCount == expectedCount); + } + + [Fact] + public async Task In_Should_Update_Query_And_Return_Correct_Query() + { + var postIds = new long[] { 1, 10, 100 }; + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(IdIn: postIds)); + + var materializedIds = await query.Select(x => x.Id) + .ToListAsync(); + var containsAll = materializedIds.SequenceEqual(postIds); + + Assert.True(containsAll == true); + } + + [Fact] + public async Task NotIn_Should_Update_Query_And_Return_Correct_Query() + { + var postIds = new long[] { -1, 1000, -100 }; + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(IdIn: postIds)); + + var materializedIds = await query.Select(x => x.Id) + .ToListAsync(); + var containsAny = materializedIds.Intersect(postIds).Any(); + + Assert.True(containsAny == false); + } + + [Theory] + [InlineData(-200, 1000)] + [InlineData(1, 100)] + [InlineData(50, 50)] + [InlineData(30, 101)] + public async Task Range_Should_Update_Query_And_Return_Correct_Query( + long from, + long to) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(IdRange: new RangeFilter + { + From = from, + To = to + })); + + var materializedIds = await query.Select(x => x.Id) + .ToListAsync(); + var allIdsValid = materializedIds.All(x => x >= from && x <= to); + + Assert.True(allIdsValid == true); + } + + [Theory] + [InlineData(-200, 1000)] + [InlineData(1, 100)] + [InlineData(50, 50)] + [InlineData(30, 101)] + public async Task OutOfRange_Should_Update_Query_And_Return_Correct_Query( + long from, + long to) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(IdOutOfRange: new RangeFilter + { + From = from, + To = to + })); + + var materializedIds = await query.Select(x => x.Id) + .ToListAsync(); + var allIdsValid = materializedIds.All(x => x < from || x > to); + + Assert.True(allIdsValid == true); + } + + [Theory] + [InlineData("1_Sample")] + [InlineData("12_Sample")] + [InlineData("50_Sample")] + public async Task StartsWith_Should_Update_Query_And_Return_Correct_Query(string prefix) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(NameStartsWith: prefix)); + + var materialized = await query.ToListAsync(); + var allEntitiesValid = materialized.All(x => x.Name.StartsWith(prefix)); + + Assert.True(allEntitiesValid == true); + } + + [Theory] + [InlineData("dlkjsad")] + [InlineData("3213")] + [InlineData("$!#")] + public async Task DoesntStartWith_Should_Update_Query_And_Return_Correct_Query(string prefix) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(NameDoesntWith: prefix)); + + var materialized = await query.ToListAsync(); + var allEntitiesValid = materialized.All(x => x.Name.StartsWith(prefix) == false); + + Assert.True(allEntitiesValid == true); + } + + [Theory] + [InlineData("post_1")] + [InlineData("post_12")] + [InlineData("post_50")] + public async Task EndsWith_Should_Update_Query_And_Return_Correct_Query(string suffix) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(NameEndsWith: suffix)); + + var materialized = await query.ToListAsync(); + var allEntitiesValid = materialized.All(x => x.Name.EndsWith(suffix)); + + Assert.True(allEntitiesValid == true); + } + + [Theory] + [InlineData("dlkjsad")] + [InlineData("3213")] + [InlineData("$!#")] + public async Task DoesntEndWith_Should_Update_Query_And_Return_Correct_Query(string suffix) + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(NameDoesntEndWith: suffix)); + + var materialized = await query.ToListAsync(); + var allEntitiesValid = materialized.All(x => x.Name.EndsWith(suffix) == false); + + Assert.True(allEntitiesValid == true); + } + + [Fact] + public async Task IsDate_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(IsNameDate: true)); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("ISDATE", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == false); + } + + [Fact] + public async Task IsNumeric_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, new PostFilters(IsIdNumeric: true)); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("ISNUMERIC", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == false); + } + + [Fact] + public async Task DateDiffDay_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffDay: new DateTime(2022, 5, 20))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("DATEDIFF(day,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == true); + } + + [Fact] + public async Task DateDiffMonth_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffMonth: new DateTime(2022, 5, 20))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("DATEDIFF(month,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == true); + } + + [Fact] + public async Task DateDiffYear_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffYear: new DateTime(2022, 5, 20))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("DATEDIFF(year,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == false); + } + + [Fact] + public async Task DateDiffWeek_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffWeek: new DateTime(2022, 5, 20))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("DATEDIFF(week,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == true); + } + + [Fact] + public async Task DateDiffHour_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffHour: new DateTime(2022, 5, 20))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("DATEDIFF(hour,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == true); + } + + [Fact] + public async Task DateDiffMinute_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffMinute: new DateTime(2022, 5, 20))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("DATEDIFF(minute,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == true); + } + + [Fact] + public async Task DateDiffSecond_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffSecond: new DateTime(2022, 5, 20))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("DATEDIFF(second,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == true); + } + + [Fact] + public async Task DateDiffMillisecond_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffMillisecond: new DateTime(2022, 2, 2, 20, 20, 22), + DateDiffDayEqual: new DateTime(2022, 2, 2))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("DATEDIFF(millisecond,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == false); + } + + [Fact] + public async Task DateDiffMicrosecond_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffMicrosecond: new DateTime(2022, 2, 2, 20, 20, 22), + DateDiffDayEqual: new DateTime(2022, 2, 2, 20, 20, 22))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("DATEDIFF(microsecond,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == false); + } + + [Fact] + public async Task DateDiffNanosecond_Should_Update_Query_And_Return_Correct_Query() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + query = await resolver.FilterAsync(query, + new PostFilters(DateDiffDayEqual: new DateTime(2022, 2, 2, 20, 20, 22), + DateDiffNanosecond: new DateTime(2022, 2, 2, 20, 20, 22))); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("DATEDIFF(nanosecond,", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + + var hasFilteredItems = await query.AnyAsync(); + Assert.True(hasFilteredItems == false); + } + + [Fact] + public async Task Id_Sorter_Should_Update_Query_And_Return_Correctly_Ordered_Data() + { + await using var context = fixture.CreateContext(); + + var resolver = fixture.CreateServiceProvider(true) + .GetRequiredService(); + IQueryable query = context.Set(); + var defaultIds = await query.Select(x => x.Id) + .ToListAsync(); + + query = await resolver.SortAsync(query, + new[] { new PostSorters(Id: SortDirection.Descending) }); + var sortedIds = await query.Select(x => x.Id) + .ToListAsync(); + + Assert.False(defaultIds.SequenceEqual(sortedIds)); + Assert.False(defaultIds.Except(sortedIds).Any()); + Assert.Equal(100, defaultIds.Intersect(sortedIds).Count()); + + var sql = query.ToQueryString(); + var sqlContainsCall = sql.Contains("ORDER BY", StringComparison.InvariantCultureIgnoreCase); + Assert.True(sqlContainsCall); + } +} \ No newline at end of file diff --git a/tests/Ooze.Typed.Tests.SqlServer/SqlServerFixture.cs b/tests/Ooze.Typed.Tests.SqlServer/SqlServerFixture.cs index efc8056..be72022 100644 --- a/tests/Ooze.Typed.Tests.SqlServer/SqlServerFixture.cs +++ b/tests/Ooze.Typed.Tests.SqlServer/SqlServerFixture.cs @@ -14,21 +14,22 @@ public class SqlServerFixture : IAsyncLifetime .WithCleanUp(true) .Build(); - private static IServiceCollection CreateServiceCollection() + private static IServiceCollection CreateServiceCollection(bool enableAsyncSupport = false) { var services = new ServiceCollection().AddLogging(); - services.AddOozeTyped() - .Add() - .Add(); + var oozeBuilder = services.AddOozeTyped(); + if (enableAsyncSupport == true) + oozeBuilder.EnableAsyncResolvers(); + oozeBuilder.Add(); return services; } - public readonly IServiceProvider ServiceProvider = new DefaultServiceProviderFactory( + public IServiceProvider CreateServiceProvider(bool enableAsyncSupport = false) => new DefaultServiceProviderFactory( new ServiceProviderOptions { ValidateScopes = false - }).CreateServiceProvider(CreateServiceCollection()); + }).CreateServiceProvider(CreateServiceCollection(enableAsyncSupport)); public SqlServerContext CreateContext() { diff --git a/tests/Ooze.Typed.Tests.SqlServer/SqlServerIntegrationTests.cs b/tests/Ooze.Typed.Tests.SqlServer/SqlServerIntegrationTests.cs index fdb9b38..f4a6372 100644 --- a/tests/Ooze.Typed.Tests.SqlServer/SqlServerIntegrationTests.cs +++ b/tests/Ooze.Typed.Tests.SqlServer/SqlServerIntegrationTests.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using Ooze.Typed.Filters; using Ooze.Typed.Sorters; +using Ooze.Typed.Tests.SqlServer.OozeConfiguration; namespace Ooze.Typed.Tests.SqlServer; @@ -15,7 +16,7 @@ public async Task Equal_Should_Update_Query_And_Return_Correct_Query(int postId) { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(PostId: postId)); @@ -31,7 +32,7 @@ public async Task NotEqual_Should_Update_Query_And_Return_Correct_Query(int post { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NotEqualPostId: postId)); @@ -47,7 +48,7 @@ public async Task GreaterThan_Should_Update_Query_And_Return_Correct_Query(int p { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(GreaterThanPostId: postId)); @@ -64,7 +65,7 @@ public async Task LessThan_Should_Update_Query_And_Return_Correct_Query(int post { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(LessThanPostId: postId)); @@ -79,7 +80,7 @@ public async Task In_Should_Update_Query_And_Return_Correct_Query() var postIds = new long[] { 1, 10, 100 }; await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdIn: postIds)); @@ -96,7 +97,7 @@ public async Task NotIn_Should_Update_Query_And_Return_Correct_Query() var postIds = new long[] { -1, 1000, -100 }; await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdIn: postIds)); @@ -118,7 +119,7 @@ public async Task Range_Should_Update_Query_And_Return_Correct_Query( { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdRange: new RangeFilter { @@ -144,7 +145,7 @@ public async Task OutOfRange_Should_Update_Query_And_Return_Correct_Query( { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IdOutOfRange: new RangeFilter { @@ -167,7 +168,7 @@ public async Task StartsWith_Should_Update_Query_And_Return_Correct_Query(string { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameStartsWith: prefix)); @@ -185,7 +186,7 @@ public async Task DoesntStartWith_Should_Update_Query_And_Return_Correct_Query(s { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameDoesntWith: prefix)); @@ -203,7 +204,7 @@ public async Task EndsWith_Should_Update_Query_And_Return_Correct_Query(string s { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameEndsWith: suffix)); @@ -221,7 +222,7 @@ public async Task DoesntEndWith_Should_Update_Query_And_Return_Correct_Query(str { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(NameDoesntEndWith: suffix)); @@ -236,7 +237,7 @@ public async Task IsDate_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IsNameDate: true)); @@ -253,7 +254,7 @@ public async Task IsNumeric_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(IsIdNumeric: true)); @@ -270,7 +271,7 @@ public async Task DateDiffDay_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffDay: new DateTime(2022, 5, 20))); @@ -288,7 +289,7 @@ public async Task DateDiffMonth_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffMonth: new DateTime(2022, 5, 20))); @@ -306,7 +307,7 @@ public async Task DateDiffYear_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffYear: new DateTime(2022, 5, 20))); @@ -324,7 +325,7 @@ public async Task DateDiffWeek_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffWeek: new DateTime(2022, 5, 20))); @@ -342,7 +343,7 @@ public async Task DateDiffHour_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffHour: new DateTime(2022, 5, 20))); @@ -360,7 +361,7 @@ public async Task DateDiffMinute_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffMinute: new DateTime(2022, 5, 20))); @@ -378,7 +379,7 @@ public async Task DateDiffSecond_Should_Update_Query_And_Return_Correct_Query() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, new PostFilters(DateDiffSecond: new DateTime(2022, 5, 20))); @@ -396,10 +397,11 @@ public async Task DateDiffMillisecond_Should_Update_Query_And_Return_Correct_Que { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, - new PostFilters(DateDiffMillisecond: new DateTime(2022, 2, 2, 20, 20, 22), DateDiffDayEqual: new DateTime(2022, 2, 2))); + new PostFilters(DateDiffMillisecond: new DateTime(2022, 2, 2, 20, 20, 22), + DateDiffDayEqual: new DateTime(2022, 2, 2))); var sql = query.ToQueryString(); var sqlContainsCall = sql.Contains("DATEDIFF(millisecond,", StringComparison.InvariantCultureIgnoreCase); @@ -414,10 +416,11 @@ public async Task DateDiffMicrosecond_Should_Update_Query_And_Return_Correct_Que { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, - new PostFilters(DateDiffMicrosecond: new DateTime(2022, 2, 2, 20, 20, 22), DateDiffDayEqual: new DateTime(2022, 2, 2, 20, 20, 22))); + new PostFilters(DateDiffMicrosecond: new DateTime(2022, 2, 2, 20, 20, 22), + DateDiffDayEqual: new DateTime(2022, 2, 2, 20, 20, 22))); var sql = query.ToQueryString(); var sqlContainsCall = sql.Contains("DATEDIFF(microsecond,", StringComparison.InvariantCultureIgnoreCase); @@ -432,10 +435,11 @@ public async Task DateDiffNanosecond_Should_Update_Query_And_Return_Correct_Quer { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); query = resolver.Filter(query, - new PostFilters(DateDiffDayEqual: new DateTime(2022, 2, 2, 20, 20, 22), DateDiffNanosecond: new DateTime(2022, 2, 2, 20, 20, 22))); + new PostFilters(DateDiffDayEqual: new DateTime(2022, 2, 2, 20, 20, 22), + DateDiffNanosecond: new DateTime(2022, 2, 2, 20, 20, 22))); var sql = query.ToQueryString(); var sqlContainsCall = sql.Contains("DATEDIFF(nanosecond,", StringComparison.InvariantCultureIgnoreCase); @@ -444,26 +448,26 @@ public async Task DateDiffNanosecond_Should_Update_Query_And_Return_Correct_Quer var hasFilteredItems = await query.AnyAsync(); Assert.True(hasFilteredItems == false); } - + [Fact] public async Task Id_Sorter_Should_Update_Query_And_Return_Correctly_Ordered_Data() { await using var context = fixture.CreateContext(); - var resolver = fixture.ServiceProvider.GetRequiredService(); + var resolver = fixture.CreateServiceProvider().GetRequiredService(); IQueryable query = context.Set(); var defaultIds = await query.Select(x => x.Id) .ToListAsync(); - + query = resolver.Sort(query, new[] { new PostSorters(Id: SortDirection.Descending) }); var sortedIds = await query.Select(x => x.Id) - .ToListAsync(); - - Assert.True(defaultIds.SequenceEqual(sortedIds) == false); - Assert.True(defaultIds.Except(sortedIds).Any() == false); - Assert.True(defaultIds.Intersect(sortedIds).Count() == 100); - + .ToListAsync(); + + Assert.False(defaultIds.SequenceEqual(sortedIds)); + Assert.False(defaultIds.Except(sortedIds).Any()); + Assert.Equal(100, defaultIds.Intersect(sortedIds).Count()); + var sql = query.ToQueryString(); var sqlContainsCall = sql.Contains("ORDER BY", StringComparison.InvariantCultureIgnoreCase); Assert.True(sqlContainsCall); From 87424e1a5462231ffbee5e7f271311b13271b2d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20Pavlovi=C4=87?= Date: Mon, 8 Apr 2024 20:37:58 +0200 Subject: [PATCH 8/9] Add configure await calls --- src/Ooze.Typed/AsyncOperationResolver.cs | 12 ++++++++---- src/Ooze.Typed/Filters/Async/AsyncFilterHandler.cs | 10 +++++++--- src/Ooze.Typed/Sorters/Async/AsyncSorterHandler.cs | 11 +++++++---- tests/Ooze.Typed.Tests.MySql/MySqlFixture.cs | 1 - 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/Ooze.Typed/AsyncOperationResolver.cs b/src/Ooze.Typed/AsyncOperationResolver.cs index f072125..d39bdce 100644 --- a/src/Ooze.Typed/AsyncOperationResolver.cs +++ b/src/Ooze.Typed/AsyncOperationResolver.cs @@ -26,7 +26,8 @@ public async ValueTask> FilterAsync( } var filterHandler = serviceProvider.GetRequiredService>(); - query = await filterHandler.ApplyAsync(query, filters); + query = await filterHandler.ApplyAsync(query, filters) + .ConfigureAwait(false); return query; } @@ -43,7 +44,8 @@ public async ValueTask> SortAsync( } var sorterHandler = serviceProvider.GetRequiredService>(); - query = await sorterHandler.ApplyAsync(query, sorters); + query = await sorterHandler.ApplyAsync(query, sorters) + .ConfigureAwait(false); return query; } @@ -58,8 +60,8 @@ public ValueTask> PageAsync( return ValueTask.FromResult(query); } - var sorterHandler = serviceProvider.GetRequiredService>(); - query = sorterHandler.Apply(query, pagingOptions); + var pagingHandler = serviceProvider.GetRequiredService>(); + query = pagingHandler.Apply(query, pagingOptions); return ValueTask.FromResult(query); } @@ -144,6 +146,7 @@ public async ValueTask> ApplyAsync() _resolverData = _resolverData with { Query = await sorterHandler.ApplyAsync(_resolverData.Query, sorters) + .ConfigureAwait(false) }; } @@ -154,6 +157,7 @@ public async ValueTask> ApplyAsync() _resolverData = _resolverData with { Query = await filterHandler.ApplyAsync(_resolverData.Query, filters) + .ConfigureAwait(false) }; } diff --git a/src/Ooze.Typed/Filters/Async/AsyncFilterHandler.cs b/src/Ooze.Typed/Filters/Async/AsyncFilterHandler.cs index 0f61cf7..ed0fd00 100644 --- a/src/Ooze.Typed/Filters/Async/AsyncFilterHandler.cs +++ b/src/Ooze.Typed/Filters/Async/AsyncFilterHandler.cs @@ -16,17 +16,21 @@ public async ValueTask> ApplyAsync( var filterDefinitions = new List>(); foreach (var provider in filterProviders) { - var definitions = await provider.GetFiltersAsync(); + var definitions = await provider.GetFiltersAsync() + .ConfigureAwait(false); filterDefinitions.AddRange(definitions); } foreach (var filterDefinition in filterDefinitions) { - var shouldRun = await filterDefinition.ShouldRun(filters); + var shouldRun = await filterDefinition.ShouldRun(filters) + .ConfigureAwait(false); if (shouldRun == false) continue; - var filterExpr = await filterDefinition.FilterExpressionFactory.Invoke(filters); + var filterExpr = await filterDefinition.FilterExpressionFactory + .Invoke(filters) + .ConfigureAwait(false); if (filterExpr is null) continue; diff --git a/src/Ooze.Typed/Sorters/Async/AsyncSorterHandler.cs b/src/Ooze.Typed/Sorters/Async/AsyncSorterHandler.cs index d138f32..ff44123 100644 --- a/src/Ooze.Typed/Sorters/Async/AsyncSorterHandler.cs +++ b/src/Ooze.Typed/Sorters/Async/AsyncSorterHandler.cs @@ -20,7 +20,8 @@ public async ValueTask> ApplyAsync( var sortDefinitions = new List>(); foreach (var provider in sortProviders) { - var definitions = await provider.GetSortersAsync(); + var definitions = await provider.GetSortersAsync() + .ConfigureAwait(false); sortDefinitions.AddRange(definitions); } @@ -30,7 +31,8 @@ public async ValueTask> ApplyAsync( { foreach (var sorterDefinition in sortDefinitions) { - var shouldRun = await sorterDefinition.ShouldRun(sorter); + var shouldRun = await sorterDefinition.ShouldRun(sorter) + .ConfigureAwait(false); if (shouldRun == true) return sorterDefinition; } @@ -38,12 +40,13 @@ public async ValueTask> ApplyAsync( return default; } - var sortDefinition = await GetSorterDefinition(); + var sortDefinition = await GetSorterDefinition().ConfigureAwait(false); if (sortDefinition is null) continue; var sorterType = BasicExpressions.GetMemberExpression(sortDefinition.DataExpression.Body).Type; - var direction = await sortDefinition.GetSortDirection(sorter); + var direction = await sortDefinition.GetSortDirection(sorter) + .ConfigureAwait(false); MethodInfo? method; if (query.Expression.Type == typeof(IOrderedQueryable)) diff --git a/tests/Ooze.Typed.Tests.MySql/MySqlFixture.cs b/tests/Ooze.Typed.Tests.MySql/MySqlFixture.cs index cd2c538..d39a724 100644 --- a/tests/Ooze.Typed.Tests.MySql/MySqlFixture.cs +++ b/tests/Ooze.Typed.Tests.MySql/MySqlFixture.cs @@ -1,7 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Ooze.Typed.Extensions; -using Ooze.Typed.Tests.MySql.OozeConfiguration; using Pomelo.EntityFrameworkCore.MySql.Infrastructure; using Testcontainers.MariaDb; From 99f2fe01fa6206bbe0cf0c474a3890244e3f9755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20Pavlovi=C4=87?= Date: Mon, 8 Apr 2024 20:38:25 +0200 Subject: [PATCH 9/9] Update README --- README.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/README.md b/README.md index 4e665d3..de80807 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,61 @@ public record class Input(MyEntityFilters Filters, MyEntitySorters Sorters, Pagi **NOTE:** Example before is bound to POST method, but you can use GET or anything else that suits you. For more elaborate example look [here](https://github.com/DenisPav/Ooze/tree/master/tests/Ooze.Typed.Web). Ooze only cares that you provide instances of your `filters`, `sorters` which will be then applied to `IQueryable` instances. +## Async support 🔃 +If needed you can opt in for the `async` version of the pipeline for the resolvers for different operations. In order to opt into async support you'll need to call `EnableAsyncResolvers()` call on the `IOozeServiceCollectionBuilder` which is exposed when calling `.AddOoze()` extension. Then you can just register providers as before via `.Add()` method. + +In order for `FilterProvider` or `SorterProvider` to be of `async` nature you need to use `IAsyncFilterProvider` or `IAsyncSorterProvider` interfaces. Accompanying `AsyncFilters` and `AsyncSorters` static classes are present to help you out with the building process as they are in non async version. + +In the end you'll need to use `IAsyncOperationResolver` istead of `IOperationResolver` and that should be it. Example of this can be seen below: +```csharp +//provider definition +public class MyEntityAsyncFiltersProvider : IAsyncFilterProvider +{ + public ValueTask>> GetFiltersAsync() + => ValueTask.FromResult(AsyncFilters.CreateFor() + .Equal(entity => entity.Id, filter => filter.PostId) + .NotEqual(entity => entity.Id, filter => filter.NotEqualPostId) + .AddAsync(async filters => + { + await Task.CompletedTask; + return filters.Date != null; + }, async filters => + { + await Task.CompletedTask; + return entity => entity.Date == filters.Date; + }) + .Build()); +} + + +//in Program.cs or where your service collection registration is: +builder.Services.AddOozeTyped() + .EnableAsyncResolvers() + .Add(); + +//in your endpoints/controller actions/middlewares call the appropriate resolver methods: +[HttpPost("/sql-server")] +public async Task PostSqlServer( + [FromServices] IAsyncOperationResolver asyncResolver, + Input model) + { + IQueryable query = _sqlServerDb.Set(); + + query = await asyncResolver.WithQuery(query) + .Filter(model.Filters) + .Sort(model.Sorters) + .ApplyAsync(); + + var results = await query.ToListAsync(); + return Ok(results); + } + +record class Input(MyFilters Filters, IEnumerable Sorters, PagingOptions Paging); +``` + +**NOTE:** +`AsyncFilters/Sorters` builders will currently internally wrap the operations into `Tasks` even if they initially do not look like ones. + ## Advanced 🧠