Smart Filter Encoding Fix (#2387)
This commit is contained in:
parent
b6d4938e22
commit
9894a2623c
133 changed files with 677 additions and 471 deletions
|
@ -1,24 +0,0 @@
|
|||
using API.DTOs.Filtering.v2;
|
||||
using API.Entities;
|
||||
|
||||
namespace API.Helpers.Builders;
|
||||
|
||||
public class SmartFilterBuilder : IEntityBuilder<AppUserSmartFilter>
|
||||
{
|
||||
private AppUserSmartFilter _smartFilter;
|
||||
public AppUserSmartFilter Build() => _smartFilter;
|
||||
|
||||
public SmartFilterBuilder(FilterV2Dto filter)
|
||||
{
|
||||
_smartFilter = new AppUserSmartFilter()
|
||||
{
|
||||
Name = filter.Name,
|
||||
Filter = SmartFilterHelper.Encode(filter)
|
||||
};
|
||||
}
|
||||
|
||||
// public SmartFilterBuilder WithName(string name)
|
||||
// {
|
||||
//
|
||||
// }
|
||||
}
|
|
@ -4,6 +4,7 @@ using API.Entities.Interfaces;
|
|||
using API.Services;
|
||||
|
||||
namespace API.Helpers;
|
||||
#nullable enable
|
||||
|
||||
public interface ICacheHelper
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using Hangfire;
|
||||
|
||||
namespace API.Helpers.Converters;
|
||||
#nullable enable
|
||||
|
||||
public static class CronConverter
|
||||
{
|
||||
|
|
|
@ -7,6 +7,7 @@ using API.Entities.Enums;
|
|||
using API.Extensions;
|
||||
|
||||
namespace API.Helpers.Converters;
|
||||
#nullable enable
|
||||
|
||||
public static class FilterFieldValueConverter
|
||||
{
|
||||
|
|
|
@ -6,6 +6,7 @@ using API.Entities.Enums;
|
|||
using AutoMapper;
|
||||
|
||||
namespace API.Helpers.Converters;
|
||||
#nullable enable
|
||||
|
||||
public class ServerSettingConverter : ITypeConverter<IEnumerable<ServerSetting>, ServerSettingDto>
|
||||
{
|
||||
|
|
|
@ -3,6 +3,7 @@ using API.DTOs.Scrobbling;
|
|||
using API.Entities.Enums;
|
||||
|
||||
namespace API.Helpers;
|
||||
#nullable enable
|
||||
|
||||
public static class LibraryTypeHelper
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
namespace API.Helpers;
|
||||
#nullable enable
|
||||
|
||||
public static class NumberHelper
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using API.Entities;
|
||||
|
||||
namespace API.Helpers;
|
||||
#nullable enable
|
||||
|
||||
public static class OrderableHelper
|
||||
{
|
||||
|
|
|
@ -5,10 +5,11 @@ using System.Threading.Tasks;
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace API.Helpers;
|
||||
#nullable enable
|
||||
|
||||
public class PagedList<T> : List<T>
|
||||
{
|
||||
public PagedList(IEnumerable<T> items, int count, int pageNumber, int pageSize)
|
||||
private PagedList(IEnumerable<T> items, int count, int pageNumber, int pageSize)
|
||||
{
|
||||
CurrentPage = pageNumber;
|
||||
TotalPages = (int) Math.Ceiling(count / (double) pageSize);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
namespace API.Helpers;
|
||||
#nullable enable
|
||||
|
||||
public class PaginationHeader
|
||||
{
|
||||
|
|
|
@ -6,6 +6,7 @@ using API.Services.Tasks.Scanner;
|
|||
using API.Services.Tasks.Scanner.Parser;
|
||||
|
||||
namespace API.Helpers;
|
||||
#nullable enable
|
||||
|
||||
public static class ParserInfoHelpers
|
||||
{
|
||||
|
|
|
@ -8,6 +8,7 @@ using API.Extensions;
|
|||
using API.Helpers.Builders;
|
||||
|
||||
namespace API.Helpers;
|
||||
#nullable enable
|
||||
|
||||
public static class PersonHelper
|
||||
{
|
||||
|
@ -29,6 +30,7 @@ public static class PersonHelper
|
|||
foreach (var name in names)
|
||||
{
|
||||
var normalizedName = name.ToNormalized();
|
||||
// BUG: Doesn't this create a duplicate entry because allPeopleTypeRoles is a different instance?
|
||||
var person = allPeopleTypeRole.Find(p =>
|
||||
p.NormalizedName != null && p.NormalizedName.Equals(normalizedName));
|
||||
if (person == null)
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace API.Helpers;
|
||||
|
||||
public static class SqlHelper
|
||||
{
|
||||
public static List<T> RawSqlQuery<T>(DbContext context, string query, Func<DbDataReader, T> map)
|
||||
{
|
||||
using var command = context.Database.GetDbConnection().CreateCommand();
|
||||
command.CommandText = query;
|
||||
command.CommandType = CommandType.Text;
|
||||
|
||||
context.Database.OpenConnection();
|
||||
|
||||
using var result = command.ExecuteReader();
|
||||
var entities = new List<T>();
|
||||
|
||||
while (result.Read())
|
||||
{
|
||||
entities.Add(map(result));
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ using API.Extensions;
|
|||
using API.Services.Tasks.Scanner;
|
||||
|
||||
namespace API.Helpers;
|
||||
#nullable enable
|
||||
|
||||
public static class SeriesHelper
|
||||
{
|
||||
|
|
|
@ -5,14 +5,24 @@ using System.Web;
|
|||
using API.DTOs.Filtering;
|
||||
using API.DTOs.Filtering.v2;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace API.Helpers;
|
||||
|
||||
public static class SmartFilterHelper
|
||||
{
|
||||
private const string SortOptionsKey = "sortOptions=";
|
||||
private const string NameKey = "name=";
|
||||
private const string SortFieldKey = "sortField=";
|
||||
private const string IsAscendingKey = "isAscending=";
|
||||
private const string StatementsKey = "stmts=";
|
||||
private const string LimitToKey = "limitTo=";
|
||||
private const string CombinationKey = "combination=";
|
||||
private const string StatementComparisonKey = "comparison=";
|
||||
private const string StatementFieldKey = "field=";
|
||||
private const string StatementValueKey = "value=";
|
||||
public const string StatementSeparator = "\ufffd";
|
||||
public const string InnerStatementSeparator = "¦";
|
||||
|
||||
public static FilterV2Dto Decode(string? encodedFilter)
|
||||
{
|
||||
|
@ -21,7 +31,7 @@ public static class SmartFilterHelper
|
|||
return new FilterV2Dto(); // Create a default filter if the input is empty
|
||||
}
|
||||
|
||||
string[] parts = encodedFilter.Split('&');
|
||||
var parts = encodedFilter.Split('&');
|
||||
var filter = new FilterV2Dto();
|
||||
|
||||
foreach (var part in parts)
|
||||
|
@ -42,7 +52,7 @@ public static class SmartFilterHelper
|
|||
{
|
||||
filter.Statements = DecodeFilterStatementDtos(part.Substring(StatementsKey.Length));
|
||||
}
|
||||
else if (part.StartsWith("name="))
|
||||
else if (part.StartsWith(NameKey))
|
||||
{
|
||||
filter.Name = HttpUtility.UrlDecode(part.Substring(5));
|
||||
}
|
||||
|
@ -51,7 +61,7 @@ public static class SmartFilterHelper
|
|||
return filter;
|
||||
}
|
||||
|
||||
public static string Encode(FilterV2Dto filter)
|
||||
public static string Encode(FilterV2Dto? filter)
|
||||
{
|
||||
if (filter == null)
|
||||
return string.Empty;
|
||||
|
@ -59,50 +69,50 @@ public static class SmartFilterHelper
|
|||
var encodedStatements = EncodeFilterStatementDtos(filter.Statements);
|
||||
var encodedSortOptions = filter.SortOptions != null
|
||||
? $"{SortOptionsKey}{EncodeSortOptions(filter.SortOptions)}"
|
||||
: "";
|
||||
: string.Empty;
|
||||
var encodedLimitTo = $"{LimitToKey}{filter.LimitTo}";
|
||||
|
||||
return $"{EncodeName(filter.Name)}{encodedStatements}&{encodedSortOptions}&{encodedLimitTo}&{CombinationKey}{(int) filter.Combination}";
|
||||
}
|
||||
|
||||
private static string EncodeName(string name)
|
||||
private static string EncodeName(string? name)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(name) ? string.Empty : $"name={HttpUtility.UrlEncode(name)}&";
|
||||
return string.IsNullOrWhiteSpace(name) ? string.Empty : $"{NameKey}{Uri.EscapeDataString(name)}&";
|
||||
}
|
||||
|
||||
private static string EncodeSortOptions(SortOptions sortOptions)
|
||||
{
|
||||
return Uri.EscapeDataString($"sortField={(int) sortOptions.SortField},isAscending={sortOptions.IsAscending}");
|
||||
return Uri.EscapeDataString($"{SortFieldKey}{(int) sortOptions.SortField}{InnerStatementSeparator}{IsAscendingKey}{sortOptions.IsAscending}");
|
||||
}
|
||||
|
||||
private static string EncodeFilterStatementDtos(ICollection<FilterStatementDto> statements)
|
||||
private static string EncodeFilterStatementDtos(ICollection<FilterStatementDto>? statements)
|
||||
{
|
||||
if (statements == null || statements.Count == 0)
|
||||
return string.Empty;
|
||||
|
||||
var encodedStatements = StatementsKey + Uri.EscapeDataString(string.Join(",", statements.Select(EncodeFilterStatementDto)));
|
||||
var encodedStatements = StatementsKey + Uri.EscapeDataString(string.Join(StatementSeparator, statements.Select(EncodeFilterStatementDto)));
|
||||
return encodedStatements;
|
||||
}
|
||||
|
||||
private static string EncodeFilterStatementDto(FilterStatementDto statement)
|
||||
{
|
||||
var encodedComparison = $"comparison={(int) statement.Comparison}";
|
||||
var encodedField = $"field={(int) statement.Field}";
|
||||
var encodedValue = $"value={Uri.EscapeDataString(statement.Value)}";
|
||||
|
||||
return Uri.EscapeDataString($"{encodedComparison},{encodedField},{encodedValue}");
|
||||
var encodedComparison = $"{StatementComparisonKey}{(int) statement.Comparison}";
|
||||
var encodedField = $"{StatementFieldKey}{(int) statement.Field}";
|
||||
var encodedValue = $"{StatementValueKey}{Uri.EscapeDataString(statement.Value)}";
|
||||
|
||||
return Uri.EscapeDataString($"{encodedComparison}{InnerStatementSeparator}{encodedField}{InnerStatementSeparator}{encodedValue}");
|
||||
}
|
||||
|
||||
private static List<FilterStatementDto> DecodeFilterStatementDtos(string encodedStatements)
|
||||
{
|
||||
encodedStatements = HttpUtility.UrlDecode(encodedStatements);
|
||||
string[] statementStrings = encodedStatements.Split(',');
|
||||
var statementStrings = Uri.UnescapeDataString(encodedStatements).Split(StatementSeparator);
|
||||
|
||||
var statements = new List<FilterStatementDto>();
|
||||
|
||||
foreach (var statementString in statementStrings)
|
||||
{
|
||||
var parts = statementString.Split('&');
|
||||
var parts = Uri.UnescapeDataString(statementString).Split(InnerStatementSeparator);
|
||||
if (parts.Length < 3)
|
||||
continue;
|
||||
|
||||
|
@ -110,7 +120,7 @@ public static class SmartFilterHelper
|
|||
{
|
||||
Comparison = Enum.Parse<FilterComparison>(parts[0].Split("=")[1]),
|
||||
Field = Enum.Parse<FilterField>(parts[1].Split("=")[1]),
|
||||
Value = HttpUtility.UrlDecode(parts[2].Split("=")[1])
|
||||
Value = Uri.UnescapeDataString(parts[2].Split("=")[1])
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -119,22 +129,22 @@ public static class SmartFilterHelper
|
|||
|
||||
private static SortOptions DecodeSortOptions(string encodedSortOptions)
|
||||
{
|
||||
string[] parts = encodedSortOptions.Split(',');
|
||||
var sortFieldPart = parts.FirstOrDefault(part => part.StartsWith("sortField="));
|
||||
var isAscendingPart = parts.FirstOrDefault(part => part.StartsWith("isAscending="));
|
||||
var parts = Uri.UnescapeDataString(encodedSortOptions).Split(InnerStatementSeparator);
|
||||
var sortFieldPart = parts.FirstOrDefault(part => part.StartsWith(SortFieldKey));
|
||||
var isAscendingPart = parts.FirstOrDefault(part => part.StartsWith(IsAscendingKey));
|
||||
|
||||
var isAscending = isAscendingPart?.Substring(11).Equals("true", StringComparison.OrdinalIgnoreCase) ?? false;
|
||||
if (sortFieldPart != null)
|
||||
if (sortFieldPart == null)
|
||||
{
|
||||
var sortField = Enum.Parse<SortField>(sortFieldPart.Split("=")[1]);
|
||||
|
||||
return new SortOptions
|
||||
{
|
||||
SortField = sortField,
|
||||
IsAscending = isAscending
|
||||
};
|
||||
return new SortOptions();
|
||||
}
|
||||
|
||||
return null;
|
||||
var sortField = Enum.Parse<SortField>(sortFieldPart.Split("=")[1]);
|
||||
|
||||
return new SortOptions
|
||||
{
|
||||
SortField = sortField,
|
||||
IsAscending = isAscending
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ using API.Extensions;
|
|||
using API.Helpers.Builders;
|
||||
|
||||
namespace API.Helpers;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public static class TagHelper
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
namespace API.Helpers;
|
||||
#nullable enable
|
||||
|
||||
public class UserParams
|
||||
{
|
||||
|
@ -15,7 +16,7 @@ public class UserParams
|
|||
init => _pageSize = (value == 0) ? MaxPageSize : value;
|
||||
}
|
||||
|
||||
public static readonly UserParams Default = new UserParams()
|
||||
public static readonly UserParams Default = new()
|
||||
{
|
||||
PageSize = 20,
|
||||
PageNumber = 1
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue