(bugfix): paged request filter should be predicate

This commit is contained in:
Maksym Sadovnychyy 2024-12-30 21:24:16 +01:00
parent 896bcba334
commit 39d97500e9
3 changed files with 55 additions and 63 deletions

View File

@ -1,10 +1,12 @@
namespace MaksIT.Core.Tests.Webapi.Models;
using System.Linq;
using Xunit;
using MaksIT.Core.Webapi.Models; // Ensure namespace matches the actual namespace of PagedRequest
public class PagedRequestTests {
public class TestEntity {
public string? Name { get; set; }
public int Age { get; set; }
public required string Name { get; set; }
public required int Age { get; set; }
}
// Setup a mock IQueryable to test against
@ -17,7 +19,7 @@ public class PagedRequestTests {
}
[Fact]
public void ApplyFilters_ShouldHandleEqualsOperator() {
public void BuildFilterExpression_ShouldHandleEqualsOperator() {
// Arrange
var queryable = GetTestQueryable();
var request = new PagedRequest {
@ -25,147 +27,131 @@ public class PagedRequestTests {
};
// Act
var filtered = request.ApplyFilters(queryable);
var predicate = request.BuildFilterExpression<TestEntity>();
var filtered = queryable.Where(predicate).ToList();
// Assert
Assert.Contains(filtered, t => t.Name == "John");
Assert.Single(filtered);
}
// Add similar changes for other tests
[Fact]
public void ApplyFilters_ShouldHandleNotEqualsOperator() {
// Arrange
public void BuildFilterExpression_ShouldHandleNotEqualsOperator() {
var queryable = GetTestQueryable();
var request = new PagedRequest {
Filters = "Name != \"John\""
};
// Act
var filtered = request.ApplyFilters(queryable);
var predicate = request.BuildFilterExpression<TestEntity>();
var filtered = queryable.Where(predicate).ToList();
// Assert
Assert.DoesNotContain(filtered, t => t.Name == "John");
}
[Fact]
public void ApplyFilters_ShouldHandleGreaterThanOperator() {
// Arrange
public void BuildFilterExpression_ShouldHandleGreaterThanOperator() {
var queryable = GetTestQueryable();
var request = new PagedRequest {
Filters = "Age > 30"
};
// Act
var filtered = request.ApplyFilters(queryable);
var predicate = request.BuildFilterExpression<TestEntity>();
var filtered = queryable.Where(predicate).ToList();
// Assert
Assert.All(filtered, t => Assert.True(t.Age > 30));
}
[Fact]
public void ApplyFilters_ShouldHandleLessThanOperator() {
// Arrange
public void BuildFilterExpression_ShouldHandleLessThanOperator() {
var queryable = GetTestQueryable();
var request = new PagedRequest {
Filters = "Age < 30"
};
// Act
var filtered = request.ApplyFilters(queryable);
var predicate = request.BuildFilterExpression<TestEntity>();
var filtered = queryable.Where(predicate).ToList();
// Assert
Assert.All(filtered, t => Assert.True(t.Age < 30));
}
[Fact]
public void ApplyFilters_ShouldHandleAndOperator() {
// Arrange
public void BuildFilterExpression_ShouldHandleAndOperator() {
var queryable = GetTestQueryable();
var request = new PagedRequest {
Filters = "Name == \"John\" && Age > 30"
};
// Act
var filtered = request.ApplyFilters(queryable);
var predicate = request.BuildFilterExpression<TestEntity>();
var filtered = queryable.Where(predicate).ToList();
// Assert
Assert.Contains(filtered, t => t.Name == "John" && t.Age > 30);
Assert.Single(filtered);
}
[Fact]
public void ApplyFilters_ShouldHandleOrOperator() {
// Arrange
public void BuildFilterExpression_ShouldHandleOrOperator() {
var queryable = GetTestQueryable();
var request = new PagedRequest {
Filters = "Name == \"John\" || Age > 30"
};
// Act
var filtered = request.ApplyFilters(queryable);
var predicate = request.BuildFilterExpression<TestEntity>();
var filtered = queryable.Where(predicate).ToList();
// Assert
Assert.Contains(filtered, t => t.Name == "John" || t.Age > 30);
}
[Fact]
public void ApplyFilters_ShouldHandleNegation() {
// Arrange
public void BuildFilterExpression_ShouldHandleNegation() {
var queryable = GetTestQueryable();
var request = new PagedRequest {
Filters = "!(Name == \"John\")"
};
// Act
var filtered = request.ApplyFilters(queryable);
var predicate = request.BuildFilterExpression<TestEntity>();
var filtered = queryable.Where(predicate).ToList();
// Assert
Assert.DoesNotContain(filtered, t => t.Name == "John");
}
[Fact]
public void ApplyFilters_ShouldHandleContainsOperator() {
// Arrange
public void BuildFilterExpression_ShouldHandleContainsOperator() {
var queryable = GetTestQueryable();
var request = new PagedRequest {
Filters = "Name.Contains(\"oh\")"
};
// Act
var filtered = request.ApplyFilters(queryable);
var predicate = request.BuildFilterExpression<TestEntity>();
var filtered = queryable.Where(predicate).ToList();
// Assert
Assert.Contains(filtered, t => t.Name.Contains("oh"));
}
[Fact]
public void ApplyFilters_ShouldHandleStartsWithOperator() {
// Arrange
public void BuildFilterExpression_ShouldHandleStartsWithOperator() {
var queryable = GetTestQueryable();
var request = new PagedRequest {
Filters = "Name.StartsWith(\"Jo\")"
};
// Act
var filtered = request.ApplyFilters(queryable);
var predicate = request.BuildFilterExpression<TestEntity>();
var filtered = queryable.Where(predicate).ToList();
// Assert
Assert.Contains(filtered, t => t.Name.StartsWith("John"));
Assert.Single(filtered); // Assuming only "Johnny" starts with "John"
Assert.Contains(filtered, t => t.Name.StartsWith("Jo"));
Assert.Single(filtered); // Assuming only "John" starts with "Jo"
}
[Fact]
public void ApplyFilters_ShouldHandleEndsWithOperator() {
// Arrange
public void BuildFilterExpression_ShouldHandleEndsWithOperator() {
var queryable = GetTestQueryable();
var request = new PagedRequest {
Filters = "Name.EndsWith(\"hn\")"
};
// Act
var filtered = request.ApplyFilters(queryable);
var predicate = request.BuildFilterExpression<TestEntity>();
var filtered = queryable.Where(predicate).ToList();
// Assert
Assert.Contains(filtered, t => t.Name.EndsWith("hn"));
}
}

View File

@ -8,7 +8,7 @@
<!-- NuGet package metadata -->
<PackageId>MaksIT.Core</PackageId>
<Version>1.2.9</Version>
<Version>1.3.0</Version>
<Authors>Maksym Sadovnychyy</Authors>
<Company>MAKS-IT</Company>
<Product>MaksIT.Core</Product>

View File

@ -1,5 +1,5 @@
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using MaksIT.Core.Abstractions.Webapi;
public class PagedRequest : RequestModelBase {
@ -10,16 +10,22 @@ public class PagedRequest : RequestModelBase {
public string? SortBy { get; set; }
public bool IsAscending { get; set; } = true;
public IQueryable<T> ApplyFilters<T>(IQueryable<T> query) {
if (!string.IsNullOrWhiteSpace(Filters)) {
query = query.Where(Filters); // Filters interpreted directly
}
public Expression<Func<T, bool>> BuildFilterExpression<T>() {
if (string.IsNullOrWhiteSpace(Filters))
return x => true; // Returns an expression that doesn't filter anything.
if (!string.IsNullOrWhiteSpace(SortBy)) {
var direction = IsAscending ? "ascending" : "descending";
query = query.OrderBy($"{SortBy} {direction}");
}
// Parse the filter string into a dynamic lambda expression.
var predicate = DynamicExpressionParser.ParseLambda<T, bool>(
new ParsingConfig(), false, Filters);
return predicate;
}
return query.Skip((PageNumber - 1) * PageSize).Take(PageSize);
public Func<IQueryable<T>, IOrderedQueryable<T>> BuildSortExpression<T>() {
if (string.IsNullOrWhiteSpace(SortBy))
return q => (IOrderedQueryable<T>)q; // Cast to IOrderedQueryable
var direction = IsAscending ? "ascending" : "descending";
// Return a Func that takes an IQueryable and applies the sorting to it.
return q => (IOrderedQueryable<T>)q.OrderBy($"{SortBy} {direction}");
}
}