diff --git a/Tzkt.Api/Extensions/ModelBindingContextExtension.cs b/Tzkt.Api/Extensions/ModelBindingContextExtension.cs index 06bbbccac..1f2a805d9 100644 --- a/Tzkt.Api/Extensions/ModelBindingContextExtension.cs +++ b/Tzkt.Api/Extensions/ModelBindingContextExtension.cs @@ -453,6 +453,56 @@ public static bool TryGetContractKind(this ModelBindingContext bindingContext, s return true; } + public static bool TryGetContractKindList(this ModelBindingContext bindingContext, string name, ref bool hasValue, out List result) + { + result = null; + var valueObject = bindingContext.ValueProvider.GetValue(name); + + if (valueObject != ValueProviderResult.None) + { + bindingContext.ModelState.SetModelValue(name, valueObject); + if (!string.IsNullOrEmpty(valueObject.FirstValue)) + { + var rawValues = valueObject.FirstValue.Split(',', StringSplitOptions.RemoveEmptyEntries); + + if (rawValues.Length == 0) + { + bindingContext.ModelState.TryAddModelError(name, "List should contain at least one item."); + return false; + } + + hasValue = true; + result = new List(rawValues.Length); + + foreach (var rawValue in rawValues) + { + if (rawValue == ContractKinds.Asset) + { + hasValue = true; + result.Add(2); + } + else if (rawValue == ContractKinds.SmartContract) + { + hasValue = true; + result.Add(1); + } + else if (rawValue == ContractKinds.Delegator) + { + hasValue = true; + result.Add(0); + } + else + { + bindingContext.ModelState.TryAddModelError(name, "List contains invalid contract kind."); + return false; + } + } + } + } + + return true; + } + public static bool TryGetVoterStatus(this ModelBindingContext bindingContext, string name, ref bool hasValue, out int? result) { result = null; diff --git a/Tzkt.Api/Parameters/Binders/ContractKindBinder.cs b/Tzkt.Api/Parameters/Binders/ContractKindBinder.cs index 8d2d95431..fde954019 100644 --- a/Tzkt.Api/Parameters/Binders/ContractKindBinder.cs +++ b/Tzkt.Api/Parameters/Binders/ContractKindBinder.cs @@ -22,6 +22,12 @@ public Task BindModelAsync(ModelBindingContext bindingContext) if (!bindingContext.TryGetContractKind($"{model}.ne", ref hasValue, out var ne)) return Task.CompletedTask; + if (!bindingContext.TryGetContractKindList($"{model}.in", ref hasValue, out var @in)) + return Task.CompletedTask; + + if (!bindingContext.TryGetContractKindList($"{model}.ni", ref hasValue, out var ni)) + return Task.CompletedTask; + if (!hasValue) { bindingContext.Result = ModelBindingResult.Success(null); @@ -31,7 +37,9 @@ public Task BindModelAsync(ModelBindingContext bindingContext) bindingContext.Result = ModelBindingResult.Success(new ContractKindParameter { Eq = value ?? eq, - Ne = ne + Ne = ne, + In = @in, + Ni = ni }); return Task.CompletedTask; diff --git a/Tzkt.Api/Parameters/ContractKindParameter.cs b/Tzkt.Api/Parameters/ContractKindParameter.cs index f271a9613..299db486e 100644 --- a/Tzkt.Api/Parameters/ContractKindParameter.cs +++ b/Tzkt.Api/Parameters/ContractKindParameter.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc; using NJsonSchema.Annotations; namespace Tzkt.Api @@ -19,9 +20,27 @@ public class ContractKindParameter /// **Not equal** filter mode. \ /// Specify a contract kind to get items where the specified field is not equal to the specified value. /// - /// Example: `?type.ne=delegator_contract`. + /// Example: `?kind.ne=delegator_contract`. /// [JsonSchemaType(typeof(string))] public int? Ne { get; set; } + + /// + /// **In list** (any of) filter mode. \ + /// Specify a comma-separated list of contract kinds to get items where the specified field is equal to one of the specified values. + /// + /// Example: `?kind.in=smart_contract,asset`. + /// + [JsonSchemaType(typeof(List))] + public List In { get; set; } + + /// + /// **Not in list** (none of) filter mode. \ + /// Specify a comma-separated list of contract kinds to get items where the specified field is not equal to all the specified values. + /// + /// Example: `?kind.ni=smart_contract,asset`. + /// + [JsonSchemaType(typeof(List))] + public List Ni { get; set; } } } diff --git a/Tzkt.Api/Repositories/AccountRepository.cs b/Tzkt.Api/Repositories/AccountRepository.cs index 885942fe3..5c1918a53 100644 --- a/Tzkt.Api/Repositories/AccountRepository.cs +++ b/Tzkt.Api/Repositories/AccountRepository.cs @@ -3031,11 +3031,11 @@ IEnumerable GetTzips(int? value) if (value == null || value == 0) return null; var res = new List(1); - if (((int)value & (int)Data.Models.Tzip.FA2) > 0) + if (((int)value & (int)Data.Models.Tzip.FA2) == (int)Data.Models.Tzip.FA2) res.Add("fa2"); - else if (((int)value & (int)Data.Models.Tzip.FA12) > 0) + else if (((int)value & (int)Data.Models.Tzip.FA12) == (int)Data.Models.Tzip.FA12) res.Add("fa12"); - else if (((int)value & (int)Data.Models.Tzip.FA1) > 0) + else if (((int)value & (int)Data.Models.Tzip.FA1) == (int)Data.Models.Tzip.FA1) res.Add("fa1"); return res; diff --git a/Tzkt.Api/Repositories/OperationRepository.cs b/Tzkt.Api/Repositories/OperationRepository.cs index 747776dfb..ef5d8ea83 100644 --- a/Tzkt.Api/Repositories/OperationRepository.cs +++ b/Tzkt.Api/Repositories/OperationRepository.cs @@ -4409,13 +4409,13 @@ public async Task> GetTransactions( res = res.Where(x => x.Parameters != null); if (parameters.As != null) { - var pattern = $"^{parameters.As.Replace("%", ".*")}$"; - res = res.Where(x => System.Text.RegularExpressions.Regex.IsMatch(x.Parameters, pattern)); + var pattern = $"^{parameters.As.Replace("%", ".*").Replace("[", "\\[").Replace("]", "\\]").Replace("{", "\\{").Replace("}", "\\}")}$"; + res = res.Where(x => x.Parameters != null && System.Text.RegularExpressions.Regex.IsMatch(x.Parameters, pattern)); } if (parameters.Un != null) { - var pattern = $"^{parameters.Un.Replace("%", ".*")}$"; - res = res.Where(x => !System.Text.RegularExpressions.Regex.IsMatch(x.Parameters, pattern)); + var pattern = $"^{parameters.Un.Replace("%", ".*").Replace("[", "\\[").Replace("]", "\\]").Replace("{", "\\{").Replace("}", "\\}")}$"; + res = res.Where(x => x.Parameters != null && !System.Text.RegularExpressions.Regex.IsMatch(x.Parameters, pattern)); } if (sort?.Asc != null) diff --git a/Tzkt.Api/Utils/SqlBuilder.cs b/Tzkt.Api/Utils/SqlBuilder.cs index 5753d2209..022ca8927 100644 --- a/Tzkt.Api/Utils/SqlBuilder.cs +++ b/Tzkt.Api/Utils/SqlBuilder.cs @@ -107,6 +107,18 @@ public SqlBuilder Filter(string column, ContractKindParameter kind) if (kind.Ne != null) AppendFilter($@"""{column}"" != {kind.Ne}"); + if (kind.In != null) + { + AppendFilter($@"""{column}"" = ANY (@p{Counter})"); + Params.Add($"p{Counter++}", kind.In); + } + + if (kind.Ni != null && kind.Ni.Count > 0) + { + AppendFilter($@"NOT (""{column}"" = ANY (@p{Counter}))"); + Params.Add($"p{Counter++}", kind.Ni); + } + return this; }