DATA-2: optional list params + normalize frontend's '==' Gridify operator
- GridifyParams wrapper: nullable page/pageSize/filter/orderBy so list endpoints work with no query params (Gridify's non-nullable int Page made [AsParameters] treat them as required -> 400). Defaults page=1,pageSize=20. - Normalize incoming filter '==' -> '=' : the frontend gridify.ts emits '==' for equals (its convention) but Gridify's equals is '='. Safe (values are escaped; != >= <= =* contain no '=='). Fixes status==Active (frontend default that 500'd). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,22 +6,49 @@ namespace GerbilManagerWebAPI.Common
|
||||
/// <summary>Standard paged list envelope returned by every list endpoint.</summary>
|
||||
public record PagedResult<T>(IReadOnlyList<T> Items, int TotalCount, int Page, int PageSize);
|
||||
|
||||
/// <summary>
|
||||
/// Query-string parameters for list endpoints. All optional (nullable) so the
|
||||
/// frontend can call a list with no params; Gridify's own GridifyQuery has
|
||||
/// non-nullable int Page/PageSize which [AsParameters] would make REQUIRED.
|
||||
/// Bound via [AsParameters]: ?filter=…&orderBy=…&page=1&pageSize=20.
|
||||
/// </summary>
|
||||
public class GridifyParams
|
||||
{
|
||||
public string? Filter { get; set; }
|
||||
public string? OrderBy { get; set; }
|
||||
public int? Page { get; set; }
|
||||
public int? PageSize { get; set; }
|
||||
|
||||
public GridifyQuery ToQuery() => new()
|
||||
{
|
||||
Filter = NormalizeFilter(Filter),
|
||||
OrderBy = OrderBy,
|
||||
Page = Page is > 0 ? Page.Value : 1,
|
||||
PageSize = PageSize is > 0 ? PageSize.Value : 20,
|
||||
};
|
||||
|
||||
// The frontend's gridify.ts emits "==" for equals (its documented convention),
|
||||
// but Gridify's equals operator is a single "=". Translate "==" -> "=". This is
|
||||
// safe because the frontend backslash-escapes any "=" inside values, and the
|
||||
// other operators it uses ( != >= <= =* ) contain no literal "==".
|
||||
private static string? NormalizeFilter(string? filter) =>
|
||||
string.IsNullOrEmpty(filter) ? filter : filter.Replace("==", "=");
|
||||
}
|
||||
|
||||
public static class QueryableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Apply a Gridify query (filter/order/page) to an EF query and project each
|
||||
/// row to a DTO, returning the standard paged envelope. Filter/orderBy names
|
||||
/// are the ENTITY property names (case-insensitive), e.g. "status==Active",
|
||||
/// "orderBy=dateOfBirth", "litterId==...".
|
||||
/// "orderBy=dateOfBirth", "litterId==…".
|
||||
/// </summary>
|
||||
public static async Task<PagedResult<TDto>> ToPagedResultAsync<TEntity, TDto>(
|
||||
this IQueryable<TEntity> source,
|
||||
GridifyQuery query,
|
||||
GridifyParams parameters,
|
||||
Func<TEntity, TDto> map)
|
||||
{
|
||||
query.Page = query.Page <= 0 ? 1 : query.Page;
|
||||
query.PageSize = query.PageSize <= 0 ? 20 : query.PageSize;
|
||||
|
||||
var query = parameters.ToQuery();
|
||||
Paging<TEntity> paging = await source.GridifyAsync(query);
|
||||
var items = paging.Data.Select(map).ToList();
|
||||
return new PagedResult<TDto>(items, paging.Count, query.Page, query.PageSize);
|
||||
|
||||
Reference in New Issue
Block a user