diff --git a/Sieve/Extensions/OrderByDynamic.cs b/Sieve/Extensions/OrderByDynamic.cs index 46a8c31..2483776 100644 --- a/Sieve/Extensions/OrderByDynamic.cs +++ b/Sieve/Extensions/OrderByDynamic.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Linq.Expressions; +using System.Reflection; namespace Sieve.Extensions { @@ -9,10 +10,11 @@ namespace Sieve.Extensions public static IQueryable OrderByDynamic( this IQueryable source, string fullPropertyName, + PropertyInfo propertyInfo, bool desc, bool useThenBy) { - var lambda = GenerateLambdaWithSafeMemberAccess(fullPropertyName); + var lambda = GenerateLambdaWithSafeMemberAccess(fullPropertyName, propertyInfo); var command = desc ? (useThenBy ? "ThenByDescending" : "OrderByDescending") @@ -28,7 +30,11 @@ namespace Sieve.Extensions return source.Provider.CreateQuery(resultExpression); } - private static Expression> GenerateLambdaWithSafeMemberAccess(string fullPropertyName) + private static Expression> GenerateLambdaWithSafeMemberAccess + ( + string fullPropertyName, + PropertyInfo propertyInfo + ) { var parameter = Expression.Parameter(typeof(TEntity), "e"); Expression propertyValue = parameter; @@ -36,7 +42,15 @@ namespace Sieve.Extensions foreach (var name in fullPropertyName.Split('.')) { - propertyValue = Expression.PropertyOrField(propertyValue, name); + try + { + propertyValue = Expression.PropertyOrField(propertyValue, name); + } + catch (ArgumentException) + { + // name is not a direct property of field of propertyValue expression. construct a memberAccess then. + propertyValue = Expression.MakeMemberAccess(propertyValue, propertyInfo); + } if (propertyValue.Type.IsNullable()) { diff --git a/Sieve/Services/SieveProcessor.cs b/Sieve/Services/SieveProcessor.cs index a7d2a9c..f04c2b2 100644 --- a/Sieve/Services/SieveProcessor.cs +++ b/Sieve/Services/SieveProcessor.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Linq.Expressions; @@ -340,7 +341,7 @@ namespace Sieve.Services if (property != null) { - result = result.OrderByDynamic(fullName, sortTerm.Descending, useThenBy); + result = result.OrderByDynamic(fullName, property, sortTerm.Descending, useThenBy); } else { @@ -461,16 +462,35 @@ namespace Sieve.Services } else { - var incompatibleCustomMethod = parent?.GetType() - .GetMethod(name, - _options.Value.CaseSensitive ? BindingFlags.Default : BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); + var incompatibleCustomMethods = parent? + .GetType() + .GetMethods + ( + _options.Value.CaseSensitive + ? BindingFlags.Default + : BindingFlags.IgnoreCase | BindingFlags.Public | + BindingFlags.Instance + ) + .Where(method => string.Equals(method.Name, name, + _options.Value.CaseSensitive + ? StringComparison.InvariantCulture + : StringComparison.InvariantCultureIgnoreCase)) + .ToList() + ?? + new List(); - if (incompatibleCustomMethod != null) + if (incompatibleCustomMethods.Any()) { - var expected = typeof(IQueryable); - var actual = incompatibleCustomMethod.ReturnType; - throw new SieveIncompatibleMethodException(name, expected, actual, - $"{name} failed. Expected a custom method for type {expected} but only found for type {actual}"); + var incompatibles = + from incompatibleCustomMethod in incompatibleCustomMethods + let expected = typeof(IQueryable) + let actual = incompatibleCustomMethod.ReturnType + select new SieveIncompatibleMethodException(name, expected, actual, + $"{name} failed. Expected a custom method for type {expected} but only found for type {actual}"); + + var aggregate = new AggregateException(incompatibles); + + throw new SieveIncompatibleMethodException(aggregate.Message, aggregate); } else { diff --git a/SieveUnitTests/Abstractions/Entity/IBaseEntity.cs b/SieveUnitTests/Abstractions/Entity/IBaseEntity.cs new file mode 100644 index 0000000..ce1edf9 --- /dev/null +++ b/SieveUnitTests/Abstractions/Entity/IBaseEntity.cs @@ -0,0 +1,10 @@ +using System; + +namespace SieveUnitTests.Abstractions.Entity +{ + public interface IBaseEntity + { + int Id { get; set; } + DateTimeOffset DateCreated { get; set; } + } +} diff --git a/SieveUnitTests/Abstractions/Entity/IComment.cs b/SieveUnitTests/Abstractions/Entity/IComment.cs new file mode 100644 index 0000000..ec1a01e --- /dev/null +++ b/SieveUnitTests/Abstractions/Entity/IComment.cs @@ -0,0 +1,7 @@ +namespace SieveUnitTests.Abstractions.Entity +{ + public interface IComment: IBaseEntity + { + string Text { get; set; } + } +} diff --git a/SieveUnitTests/Abstractions/Entity/IPost.cs b/SieveUnitTests/Abstractions/Entity/IPost.cs new file mode 100644 index 0000000..b05d365 --- /dev/null +++ b/SieveUnitTests/Abstractions/Entity/IPost.cs @@ -0,0 +1,24 @@ +using Sieve.Attributes; +using SieveUnitTests.Entities; + +namespace SieveUnitTests.Abstractions.Entity +{ + public interface IPost: IBaseEntity + { + [Sieve(CanFilter = true, CanSort = true)] + string Title { get; set; } + [Sieve(CanFilter = true, CanSort = true)] + int LikeCount { get; set; } + [Sieve(CanFilter = true, CanSort = true)] + int CommentCount { get; set; } + [Sieve(CanFilter = true, CanSort = true)] + int? CategoryId { get; set; } + [Sieve(CanFilter = true, CanSort = true)] + bool IsDraft { get; set; } + string ThisHasNoAttribute { get; set; } + string ThisHasNoAttributeButIsAccessible { get; set; } + int OnlySortableViaFluentApi { get; set; } + Comment TopComment { get; set; } + Comment FeaturedComment { get; set; } + } +} diff --git a/SieveUnitTests/Entities/BaseEntity.cs b/SieveUnitTests/Entities/BaseEntity.cs index 1bb6af3..6660033 100644 --- a/SieveUnitTests/Entities/BaseEntity.cs +++ b/SieveUnitTests/Entities/BaseEntity.cs @@ -1,9 +1,10 @@ using System; using Sieve.Attributes; +using SieveUnitTests.Abstractions.Entity; namespace SieveUnitTests.Entities { - public class BaseEntity + public class BaseEntity : IBaseEntity { public int Id { get; set; } diff --git a/SieveUnitTests/Entities/Comment.cs b/SieveUnitTests/Entities/Comment.cs index 4d3a299..b892796 100644 --- a/SieveUnitTests/Entities/Comment.cs +++ b/SieveUnitTests/Entities/Comment.cs @@ -1,8 +1,9 @@ using Sieve.Attributes; +using SieveUnitTests.Abstractions.Entity; namespace SieveUnitTests.Entities { - public class Comment : BaseEntity + public class Comment : BaseEntity, IComment { [Sieve(CanFilter = true)] public string Text { get; set; } diff --git a/SieveUnitTests/Entities/Post.cs b/SieveUnitTests/Entities/Post.cs index 9ae8e34..aa931e9 100644 --- a/SieveUnitTests/Entities/Post.cs +++ b/SieveUnitTests/Entities/Post.cs @@ -1,9 +1,10 @@ using System; using Sieve.Attributes; +using SieveUnitTests.Abstractions.Entity; namespace SieveUnitTests.Entities { - public class Post : BaseEntity + public class Post : BaseEntity, IPost { [Sieve(CanFilter = true, CanSort = true)] diff --git a/SieveUnitTests/General.cs b/SieveUnitTests/General.cs index 17df973..5379825 100644 --- a/SieveUnitTests/General.cs +++ b/SieveUnitTests/General.cs @@ -5,6 +5,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Sieve.Exceptions; using Sieve.Models; using Sieve.Services; +using SieveUnitTests.Abstractions.Entity; using SieveUnitTests.Entities; using SieveUnitTests.Services; @@ -564,5 +565,23 @@ namespace SieveUnitTests var filteredPosts = result.ToList(); Assert.AreEqual(filteredPosts[0].Id, 2); } + + [TestMethod] + public void BaseDefinedPropertyMappingSortingWorks_WithCustomName() + { + var model = new SieveModel() + { + Sorts = "-CreateDate" + }; + + var result = _processor.Apply(model, _posts); + Assert.AreEqual(4, result.Count()); + + var posts = result.ToList(); + Assert.AreEqual(posts[0].Id, 3); + Assert.AreEqual(posts[1].Id, 2); + Assert.AreEqual(posts[2].Id, 1); + Assert.AreEqual(posts[3].Id, 0); + } } } diff --git a/SieveUnitTests/GeneralWithInterfaces.cs b/SieveUnitTests/GeneralWithInterfaces.cs new file mode 100644 index 0000000..45f6c9f --- /dev/null +++ b/SieveUnitTests/GeneralWithInterfaces.cs @@ -0,0 +1,588 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Sieve.Exceptions; +using Sieve.Models; +using Sieve.Services; +using SieveUnitTests.Abstractions.Entity; +using SieveUnitTests.Entities; +using SieveUnitTests.Services; + +namespace SieveUnitTests +{ + [TestClass] + public class GeneralWithInterfaces + { + private readonly SieveProcessor _processor; + private readonly IQueryable _posts; + private readonly IQueryable _comments; + + public GeneralWithInterfaces() + { + _processor = new ApplicationSieveProcessor(new SieveOptionsAccessor(), + new SieveCustomSortMethods(), + new SieveCustomFilterMethods()); + + _posts = new List + { + new Post() { + Id = 0, + Title = "A", + LikeCount = 100, + IsDraft = true, + CategoryId = null, + TopComment = new Comment { Id = 0, Text = "A1" }, + FeaturedComment = new Comment { Id = 4, Text = "A2" } + }, + new Post() { + Id = 1, + Title = "B", + LikeCount = 50, + IsDraft = false, + CategoryId = 1, + TopComment = new Comment { Id = 3, Text = "B1" }, + FeaturedComment = new Comment { Id = 5, Text = "B2" } + }, + new Post() { + Id = 2, + Title = "C", + LikeCount = 0, + CategoryId = 1, + TopComment = new Comment { Id = 2, Text = "C1" }, + FeaturedComment = new Comment { Id = 6, Text = "C2" } + }, + new Post() { + Id = 3, + Title = "D", + LikeCount = 3, + IsDraft = true, + CategoryId = 2, + TopComment = new Comment { Id = 1, Text = "D1" }, + FeaturedComment = new Comment { Id = 7, Text = "D2" } + }, + }.AsQueryable(); + + _comments = new List + { + new Comment() { + Id = 0, + DateCreated = DateTimeOffset.UtcNow.AddDays(-20), + Text = "This is an old comment." + }, + new Comment() { + Id = 1, + DateCreated = DateTimeOffset.UtcNow.AddDays(-1), + Text = "This is a fairly new comment." + }, + new Comment() { + Id = 2, + DateCreated = DateTimeOffset.UtcNow, + Text = "This is a brand new comment. (Text in braces)" + }, + }.AsQueryable(); + } + + [TestMethod] + public void ContainsCanBeCaseInsensitive() + { + var model = new SieveModel() + { + Filters = "Title@=*a" + }; + + var result = _processor.Apply(model, _posts); + + Assert.AreEqual(result.First().Id, 0); + Assert.IsTrue(result.Count() == 1); + } + + [TestMethod] + public void NotEqualsCanBeCaseInsensitive() + { + var model = new SieveModel() + { + Filters = "Title!=*a" + }; + + var result = _processor.Apply(model, _posts); + + Assert.AreEqual(result.First().Id, 1); + Assert.IsTrue(result.Count() == 3); + } + + [TestMethod] + public void ContainsIsCaseSensitive() + { + var model = new SieveModel() + { + Filters = "Title@=a", + }; + + var result = _processor.Apply(model, _posts); + + Assert.IsTrue(result.Count() == 0); + } + + [TestMethod] + public void NotContainsWorks() + { + var model = new SieveModel() + { + Filters = "Title!@=D", + }; + + var result = _processor.Apply(model, _posts); + + Assert.IsTrue(result.Count() == 3); + } + + [TestMethod] + public void CanFilterBools() + { + var model = new SieveModel() + { + Filters = "IsDraft==false" + }; + + var result = _processor.Apply(model, _posts); + + Assert.IsTrue(result.Count() == 2); + } + + [TestMethod] + public void CanSortBools() + { + var model = new SieveModel() + { + Sorts = "-IsDraft" + }; + + var result = _processor.Apply(model, _posts); + + Assert.AreEqual(result.First().Id, 0); + } + + [TestMethod] + public void CanFilterNullableInts() + { + var model = new SieveModel() + { + Filters = "CategoryId==1" + }; + + var result = _processor.Apply(model, _posts); + + Assert.IsTrue(result.Count() == 2); + } + + [TestMethod] + public void EqualsDoesntFailWithNonStringTypes() + { + var model = new SieveModel() + { + Filters = "LikeCount==50", + }; + + Console.WriteLine(model.GetFiltersParsed()[0].Values); + Console.WriteLine(model.GetFiltersParsed()[0].Operator); + Console.WriteLine(model.GetFiltersParsed()[0].OperatorParsed); + + var result = _processor.Apply(model, _posts); + + Assert.AreEqual(result.First().Id, 1); + Assert.IsTrue(result.Count() == 1); + } + + [TestMethod] + public void CustomFiltersWork() + { + var model = new SieveModel() + { + Filters = "Isnew", + }; + + var result = _processor.Apply(model, _posts); + + Assert.IsFalse(result.Any(p => p.Id == 0)); + Assert.IsTrue(result.Count() == 3); + } + + [TestMethod] + public void CustomGenericFiltersWork() + { + var model = new SieveModel() + { + Filters = "Latest", + }; + + var result = _processor.Apply(model, _comments); + + Assert.IsFalse(result.Any(p => p.Id == 0)); + Assert.IsTrue(result.Count() == 2); + } + + [TestMethod] + public void CustomFiltersWithOperatorsWork() + { + var model = new SieveModel() + { + Filters = "HasInTitle==A", + }; + + var result = _processor.Apply(model, _posts); + + Assert.IsTrue(result.Any(p => p.Id == 0)); + Assert.IsTrue(result.Count() == 1); + } + + [TestMethod] + public void CustomFiltersMixedWithUsualWork1() + { + var model = new SieveModel() + { + Filters = "Isnew,CategoryId==2", + }; + + var result = _processor.Apply(model, _posts); + + Assert.IsTrue(result.Any(p => p.Id == 3)); + Assert.IsTrue(result.Count() == 1); + } + + [TestMethod] + public void CustomFiltersMixedWithUsualWork2() + { + var model = new SieveModel() + { + Filters = "CategoryId==2,Isnew", + }; + + var result = _processor.Apply(model, _posts); + + Assert.IsTrue(result.Any(p => p.Id == 3)); + Assert.IsTrue(result.Count() == 1); + } + + [TestMethod] + public void CustomFiltersOnDifferentSourcesCanShareName() + { + var postModel = new SieveModel() + { + Filters = "CategoryId==2,Isnew", + }; + + var postResult = _processor.Apply(postModel, _posts); + + Assert.IsTrue(postResult.Any(p => p.Id == 3)); + Assert.AreEqual(1, postResult.Count()); + + var commentModel = new SieveModel() + { + Filters = "Isnew", + }; + + var commentResult = _processor.Apply(commentModel, _comments); + + Assert.IsTrue(commentResult.Any(c => c.Id == 2)); + Assert.AreEqual(2, commentResult.Count()); + } + + [TestMethod] + public void CustomSortsWork() + { + var model = new SieveModel() + { + Sorts = "Popularity", + }; + + var result = _processor.Apply(model, _posts); + + Assert.IsFalse(result.First().Id == 0); + } + + [TestMethod] + public void CustomGenericSortsWork() + { + var model = new SieveModel() + { + Sorts = "Oldest", + }; + + var result = _processor.Apply(model, _posts); + + Assert.IsTrue(result.Last().Id == 0); + } + + [TestMethod] + public void MethodNotFoundExceptionWork() + { + var model = new SieveModel() + { + Filters = "does not exist", + }; + + Assert.ThrowsException(() => _processor.Apply(model, _posts)); + } + + [TestMethod] + public void IncompatibleMethodExceptionsWork() + { + var model = new SieveModel() + { + Filters = "TestComment", + }; + + Assert.ThrowsException(() => _processor.Apply(model, _posts)); + } + + [TestMethod] + public void OrNameFilteringWorks() + { + var model = new SieveModel() + { + Filters = "(Title|LikeCount)==3", + }; + + var result = _processor.Apply(model, _posts); + var entry = result.FirstOrDefault(); + var resultCount = result.Count(); + + Assert.IsNotNull(entry); + Assert.AreEqual(1, resultCount); + Assert.AreEqual(3, entry.Id); + } + + [DataTestMethod] + [DataRow("CategoryId==1,(CategoryId|LikeCount)==50")] + [DataRow("(CategoryId|LikeCount)==50,CategoryId==1")] + public void CombinedAndOrFilterIndependentOfOrder(string filter) + { + var model = new SieveModel() + { + Filters = filter, + }; + + var result = _processor.Apply(model, _posts); + var entry = result.FirstOrDefault(); + var resultCount = result.Count(); + + Assert.IsNotNull(entry); + Assert.AreEqual(1, resultCount); + } + + [TestMethod] + public void CombinedAndOrWithSpaceFilteringWorks() + { + var model = new SieveModel() + { + Filters = "Title==D, (Title|LikeCount)==3", + }; + + var result = _processor.Apply(model, _posts); + var entry = result.FirstOrDefault(); + var resultCount = result.Count(); + + Assert.IsNotNull(entry); + Assert.AreEqual(1, resultCount); + Assert.AreEqual(3, entry.Id); + } + + [TestMethod] + public void OrValueFilteringWorks() + { + var model = new SieveModel() + { + Filters = "Title==C|D", + }; + + var result = _processor.Apply(model, _posts); + Assert.AreEqual(2, result.Count()); + Assert.IsTrue(result.Any(p => p.Id == 2)); + Assert.IsTrue(result.Any(p => p.Id == 3)); + } + + [TestMethod] + public void OrValueFilteringWorks2() + { + var model = new SieveModel() + { + Filters = "Text@=(|)", + }; + + var result = _processor.Apply(model, _comments); + Assert.AreEqual(1, result.Count()); + Assert.AreEqual(2, result.FirstOrDefault().Id); + } + + [TestMethod] + public void NestedFilteringWorks() + { + var model = new SieveModel() + { + Filters = "TopComment.Text!@=A", + }; + + var result = _processor.Apply(model, _posts); + Assert.AreEqual(3, result.Count()); + var posts = result.ToList(); + Assert.IsTrue(posts[0].TopComment.Text.Contains("B")); + Assert.IsTrue(posts[1].TopComment.Text.Contains("C")); + Assert.IsTrue(posts[2].TopComment.Text.Contains("D")); + } + + [TestMethod] + public void NestedSortingWorks() + { + var model = new SieveModel() + { + Sorts = "TopComment.Id", + }; + + var result = _processor.Apply(model, _posts); + Assert.AreEqual(4, result.Count()); + var posts = result.ToList(); + Assert.AreEqual(posts[0].Id, 0); + Assert.AreEqual(posts[1].Id, 3); + Assert.AreEqual(posts[2].Id, 2); + Assert.AreEqual(posts[3].Id, 1); + } + + [TestMethod] + public void NestedFilteringWithIdenticTypesWorks() + { + var model = new SieveModel() + { + Filters = "(topc|featc)@=*2", + }; + + var result = _processor.Apply(model, _posts); + Assert.AreEqual(4, result.Count()); + + model = new SieveModel() + { + Filters = "(topc|featc)@=*B", + }; + + result = _processor.Apply(model, _posts); + Assert.AreEqual(1, result.Count()); + } + + [TestMethod] + public void FilteringNullsWorks() + { + var posts = new List + { + new Post() { + Id = 1, + Title = null, + LikeCount = 0, + IsDraft = false, + CategoryId = null, + TopComment = null, + FeaturedComment = null + }, + }.AsQueryable(); + + var model = new SieveModel() + { + Filters = "FeaturedComment.Text!@=Some value", + }; + + var result = _processor.Apply(model, posts); + Assert.AreEqual(0, result.Count()); + } + + [TestMethod] + public void SortingNullsWorks() + { + var posts = new List + { + new Post() { + Id = 1, + Title = null, + LikeCount = 0, + IsDraft = false, + CategoryId = null, + TopComment = new Comment { Id = 1 }, + FeaturedComment = null + }, + new Post() { + Id = 2, + Title = null, + LikeCount = 0, + IsDraft = false, + CategoryId = null, + TopComment = null, + FeaturedComment = null + }, + }.AsQueryable(); + + var model = new SieveModel() + { + Sorts = "TopComment.Id", + }; + + var result = _processor.Apply(model, posts); + Assert.AreEqual(2, result.Count()); + var sortedPosts = result.ToList(); + Assert.AreEqual(sortedPosts[0].Id, 2); + Assert.AreEqual(sortedPosts[1].Id, 1); + } + + [TestMethod] + public void FilteringOnNullWorks() + { + var posts = new List + { + new Post() { + Id = 1, + Title = null, + LikeCount = 0, + IsDraft = false, + CategoryId = null, + TopComment = null, + FeaturedComment = null + }, + new Post() { + Id = 2, + Title = null, + LikeCount = 0, + IsDraft = false, + CategoryId = null, + TopComment = null, + FeaturedComment = new Comment { Id = 1, Text = null } + }, + }.AsQueryable(); + + var model = new SieveModel() + { + Filters = "FeaturedComment.Text==null", + }; + + var result = _processor.Apply(model, posts); + Assert.AreEqual(1, result.Count()); + var filteredPosts = result.ToList(); + Assert.AreEqual(filteredPosts[0].Id, 2); + } + + [TestMethod] + public void BaseDefinedPropertyMappingSortingWorks_WithCustomName() + { + var model = new SieveModel() + { + Sorts = "-CreateDate" + }; + + var result = _processor.Apply(model, _posts); + Assert.AreEqual(4, result.Count()); + + var posts = result.ToList(); + Assert.AreEqual(posts[0].Id, 3); + Assert.AreEqual(posts[1].Id, 2); + Assert.AreEqual(posts[2].Id, 1); + Assert.AreEqual(posts[3].Id, 0); + } + } +} diff --git a/SieveUnitTests/Services/ApplicationSieveProcessor.cs b/SieveUnitTests/Services/ApplicationSieveProcessor.cs index 726c706..f0a8815 100644 --- a/SieveUnitTests/Services/ApplicationSieveProcessor.cs +++ b/SieveUnitTests/Services/ApplicationSieveProcessor.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Options; using Sieve.Models; using Sieve.Services; +using SieveUnitTests.Abstractions.Entity; using SieveUnitTests.Entities; namespace SieveUnitTests.Services @@ -39,6 +40,39 @@ namespace SieveUnitTests.Services .CanFilter() .HasName("featc"); + mapper + .Property(p => p.DateCreated) + .CanSort() + .HasName("CreateDate"); + + // interfaces + mapper.Property(p => p.ThisHasNoAttributeButIsAccessible) + .CanSort() + .CanFilter() + .HasName("shortname"); + + mapper.Property(p => p.TopComment.Text) + .CanFilter(); + + mapper.Property(p => p.TopComment.Id) + .CanSort(); + + mapper.Property(p => p.OnlySortableViaFluentApi) + .CanSort(); + + mapper.Property(p => p.TopComment.Text) + .CanFilter() + .HasName("topc"); + + mapper.Property(p => p.FeaturedComment.Text) + .CanFilter() + .HasName("featc"); + + mapper + .Property(p => p.DateCreated) + .CanSort() + .HasName("CreateDate"); + return mapper; } } diff --git a/SieveUnitTests/Services/SieveCustomFilterMethods.cs b/SieveUnitTests/Services/SieveCustomFilterMethods.cs index 5b6eecc..0f534e3 100644 --- a/SieveUnitTests/Services/SieveCustomFilterMethods.cs +++ b/SieveUnitTests/Services/SieveCustomFilterMethods.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using Sieve.Services; +using SieveUnitTests.Abstractions.Entity; using SieveUnitTests.Entities; namespace SieveUnitTests.Services @@ -38,5 +39,31 @@ namespace SieveUnitTests.Services var result = source.Where(c => c.DateCreated > DateTimeOffset.UtcNow.AddDays(-14)); return result; } + + public IQueryable IsNew(IQueryable source, string op, string[] values) + { + var result = source.Where(p => p.LikeCount < 100); + + return result; + } + + public IQueryable HasInTitle(IQueryable source, string op, string[] values) + { + var result = source.Where(p => p.Title.Contains(values[0])); + + return result; + } + + public IQueryable IsNew(IQueryable source, string op, string[] values) + { + var result = source.Where(c => c.DateCreated > DateTimeOffset.UtcNow.AddDays(-2)); + + return result; + } + + public IQueryable TestComment(IQueryable source, string op, string[] values) + { + return source; + } } } diff --git a/SieveUnitTests/Services/SieveCustomSortMethods.cs b/SieveUnitTests/Services/SieveCustomSortMethods.cs index 12bdb7f..d8a21d3 100644 --- a/SieveUnitTests/Services/SieveCustomSortMethods.cs +++ b/SieveUnitTests/Services/SieveCustomSortMethods.cs @@ -1,5 +1,6 @@ using System.Linq; using Sieve.Services; +using SieveUnitTests.Abstractions.Entity; using SieveUnitTests.Entities; namespace SieveUnitTests.Services @@ -17,7 +18,18 @@ namespace SieveUnitTests.Services return result; } - public IQueryable Oldest(IQueryable source, bool useThenBy, bool desc) where T : BaseEntity + public IQueryable Popularity(IQueryable source, bool useThenBy, bool desc) + { + var result = useThenBy ? + ((IOrderedQueryable)source).ThenBy(p => p.LikeCount) : + source.OrderBy(p => p.LikeCount) + .ThenBy(p => p.CommentCount) + .ThenBy(p => p.DateCreated); + + return result; + } + + public IQueryable Oldest(IQueryable source, bool useThenBy, bool desc) where T : IBaseEntity { var result = useThenBy ? ((IOrderedQueryable)source).ThenByDescending(p => p.DateCreated) : diff --git a/SieveUnitTests/Services/SieveOptionsAccessor.cs b/SieveUnitTests/Services/SieveOptionsAccessor.cs index 38e69e6..5badea9 100644 --- a/SieveUnitTests/Services/SieveOptionsAccessor.cs +++ b/SieveUnitTests/Services/SieveOptionsAccessor.cs @@ -1,7 +1,7 @@ using Microsoft.Extensions.Options; using Sieve.Models; -namespace SieveUnitTests +namespace SieveUnitTests.Services { public class SieveOptionsAccessor : IOptions {