From 2e05f770eeab0777200942679e1d120c248bdc2c Mon Sep 17 00:00:00 2001 From: Jason Woods Date: Fri, 8 Mar 2024 11:34:03 +1100 Subject: [PATCH] #121 - Added support for multiple filters with same name name in query string or body --- .../Models/ListOptions/ListArgsBinder.cs | 230 ++++++++++-------- 1 file changed, 125 insertions(+), 105 deletions(-) diff --git a/OrderCloud.Catalyst/Models/ListOptions/ListArgsBinder.cs b/OrderCloud.Catalyst/Models/ListOptions/ListArgsBinder.cs index c15414e..1af952d 100644 --- a/OrderCloud.Catalyst/Models/ListOptions/ListArgsBinder.cs +++ b/OrderCloud.Catalyst/Models/ListOptions/ListArgsBinder.cs @@ -8,117 +8,137 @@ namespace OrderCloud.Catalyst { - public class ListArgsModelBinder : IModelBinder - { - public Task BindModelAsync(ModelBindingContext bindingContext) - { - Require.That(bindingContext != null, new ArgumentNullException(nameof(bindingContext))); + public class ListArgsModelBinder : IModelBinder + { + public Task BindModelAsync(ModelBindingContext bindingContext) + { + Require.That(bindingContext != null, new ArgumentNullException(nameof(bindingContext))); - if (bindingContext.ModelType.WithoutGenericArgs() != typeof(ListArgs<>) && bindingContext.ModelType.WithoutGenericArgs() != typeof(SearchArgs<>)) - return Task.CompletedTask; + if (bindingContext.ModelType.WithoutGenericArgs() != typeof(ListArgs<>) && bindingContext.ModelType.WithoutGenericArgs() != typeof(SearchArgs<>)) + return Task.CompletedTask; - var listArgs = (IListArgs)Activator.CreateInstance(bindingContext.ModelType); - LoadFromQueryString(bindingContext.HttpContext.Request.Query, listArgs); - listArgs.ValidateAndNormalize(); - bindingContext.Model = listArgs; - bindingContext.Result = ModelBindingResult.Success(listArgs); - return Task.CompletedTask; - } + var listArgs = (IListArgs)Activator.CreateInstance(bindingContext.ModelType); + LoadFromQueryString(bindingContext.HttpContext.Request.Query, listArgs); + listArgs.ValidateAndNormalize(); + bindingContext.Model = listArgs; + bindingContext.Result = ModelBindingResult.Success(listArgs); + return Task.CompletedTask; + } - public virtual void LoadFromQueryString(IQueryCollection query, IListArgs listArgs) - { - listArgs.Filters = new List(); - foreach (var param in query) - { - int i; - var (key, value) = (param.Key, param.Value); - switch (key.ToLower()) - { - case "sortby": - listArgs.SortBy = value.ToString().Split(',').Distinct().ToArray(); - break; - case "page": - if (int.TryParse(value, out i) && i >= 1) - listArgs.Page = i; - else - throw new UserErrorException("page must be an integer greater than or equal to 1."); - break; - case "pagesize": - if (int.TryParse(value, out i) && i >= 1 && i <= 100) - listArgs.PageSize = i; - else - throw new UserErrorException($"pageSize must be an integer between 1 and 100."); - break; - case "search": - listArgs.Search = value.ToString(); - break; - case "searchon": - listArgs.SearchOn = value.ToString(); - break; - case "searchtype": - var prop = listArgs.GetType().GetProperty(nameof(SearchType)); - if (prop != null) - { - if (!Enum.TryParse(value, true, out SearchType searchType)) - { - var options = string.Join(", ", Enum.GetNames(typeof(SearchType))); - throw new UserErrorException($"searchType must be one of: {options}"); - } - prop.SetValue(listArgs, searchType); - } - else - { - // model has no SearchType. it's a ListArgs and not a SearchArgs - listArgs.Filters.Add(new ListFilter(key, value)); - } - break; - default: - listArgs.Filters.Add(new ListFilter(key, value)); - break; - } - } - } - } + public virtual void LoadFromQueryString(IQueryCollection query, IListArgs listArgs) + { + listArgs.Filters = new List(); + foreach (var param in query) + { + int i; + var (key, value) = (param.Key, param.Value); + switch (key.ToLower()) + { + case "sortby": + listArgs.SortBy = value.ToString().Split(',').Distinct().ToArray(); + break; + case "page": + if (int.TryParse(value, out i) && i >= 1) + listArgs.Page = i; + else + throw new UserErrorException("page must be an integer greater than or equal to 1."); + break; + case "pagesize": + if (int.TryParse(value, out i) && i >= 1 && i <= 100) + listArgs.PageSize = i; + else + throw new UserErrorException($"pageSize must be an integer between 1 and 100."); + break; + case "search": + listArgs.Search = value.ToString(); + break; + case "searchon": + listArgs.SearchOn = value.ToString(); + break; + case "searchtype": + var prop = listArgs.GetType().GetProperty(nameof(SearchType)); + if (prop != null) + { + if (!Enum.TryParse(value, true, out SearchType searchType)) + { + var options = string.Join(", ", Enum.GetNames(typeof(SearchType))); + throw new UserErrorException($"searchType must be one of: {options}"); + } + prop.SetValue(listArgs, searchType); + } + else + { + // model has no SearchType. it's a ListArgs and not a SearchArgs + if (value.Count > 1) + { + foreach (var val in value) + { + listArgs.Filters.Add(new ListFilter(key, val)); + } + } + else + { + listArgs.Filters.Add(new ListFilter(key, value)); + } + } + break; + default: + if (value.Count > 1) + { + foreach (var val in value) + { + listArgs.Filters.Add(new ListFilter(key, val)); + } + } + else + { + listArgs.Filters.Add(new ListFilter(key, value)); + } + break; + } + } + } + } - public class ListArgsPageOnlyModelBinder : IModelBinder - { - public Task BindModelAsync(ModelBindingContext bindingContext) - { - Require.That(bindingContext != null, new ArgumentNullException(nameof(bindingContext))); + public class ListArgsPageOnlyModelBinder : IModelBinder + { + public Task BindModelAsync(ModelBindingContext bindingContext) + { + Require.That(bindingContext != null, new ArgumentNullException(nameof(bindingContext))); - if (bindingContext.ModelType.WithoutGenericArgs() != typeof(ListArgsPageOnly)) - return Task.CompletedTask; + if (bindingContext.ModelType.WithoutGenericArgs() != typeof(ListArgsPageOnly)) + return Task.CompletedTask; - var listArgs = (ListArgsPageOnly)Activator.CreateInstance(bindingContext.ModelType); - LoadFromQueryString(bindingContext.HttpContext.Request.Query, listArgs); - bindingContext.Model = listArgs; - bindingContext.Result = ModelBindingResult.Success(listArgs); - return Task.CompletedTask; - } + var listArgs = (ListArgsPageOnly)Activator.CreateInstance(bindingContext.ModelType); + LoadFromQueryString(bindingContext.HttpContext.Request.Query, listArgs); + bindingContext.Model = listArgs; + bindingContext.Result = ModelBindingResult.Success(listArgs); + return Task.CompletedTask; + } - public virtual void LoadFromQueryString(IQueryCollection query, ListArgsPageOnly listArgs) - { - foreach (var param in query) - { - int i; - var (key, value) = (param.Key, param.Value); - switch (key.ToLower()) - { + public virtual void LoadFromQueryString(IQueryCollection query, ListArgsPageOnly listArgs) + { + foreach (var param in query) + { + int i; + var (key, value) = (param.Key, param.Value); + switch (key.ToLower()) + { - case "page": - if (int.TryParse(value, out i) && i >= 1) - listArgs.Page = i; - else - throw new UserErrorException("page must be an integer greater than or equal to 1."); - break; - case "pagesize": - if (int.TryParse(value, out i) && i >= 1 && i <= 100) - listArgs.PageSize = i; - else - throw new UserErrorException($"pageSize must be an integer between 1 and 100."); - break; - } - } - } - } + case "page": + if (int.TryParse(value, out i) && i >= 1) + listArgs.Page = i; + else + throw new UserErrorException("page must be an integer greater than or equal to 1."); + break; + case "pagesize": + if (int.TryParse(value, out i) && i >= 1 && i <= 100) + listArgs.PageSize = i; + else + throw new UserErrorException($"pageSize must be an integer between 1 and 100."); + break; + } + } + } + } }