Making where & pagination works

This commit is contained in:
Zoe Roux 2020-07-05 18:29:16 +02:00
parent 4e9096ae76
commit 6bdd363b7b
6 changed files with 99 additions and 24 deletions

View File

@ -25,11 +25,14 @@ namespace Kyoo.Controllers
{
public Expression<Func<T, object>> Key { get; }
public bool Descendant { get; }
public Sort(Expression<Func<T, object>> key, bool descendant = false)
{
Key = key;
Descendant = descendant;
if (!(Key.Body is MemberExpression))
throw new ArgumentException("The given sort key is not valid.");
}
public Sort(string sortBy)

View File

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Kyoo.Models
{
public class Page<T>
{
public string This { get; set; }
public string First { get; set; }
public string Next { get; set; }
public int Count => Items.Count;
public ICollection<T> Items { get; set; }
public Page() { }
public Page(ICollection<T> items)
{
Items = items;
}
public Page(ICollection<T> items, string @this, string next, string first)
{
Items = items;
This = @this;
Next = next;
First = first;
}
public Page(ICollection<T> items,
Func<T, string> getID,
string url,
Dictionary<string, string> query,
int limit)
{
Items = items;
This = url + query.ToQueryString();
if (items.Count == limit)
{
query["afterID"] = getID(items.Last());
Next = url + query.ToQueryString();
}
query.Remove("afterID");
First = url + query.ToQueryString();
}
}
}

View File

@ -276,7 +276,7 @@ namespace Kyoo
object val = string.IsNullOrEmpty(value) || value.Equals("null", StringComparison.OrdinalIgnoreCase)
? null
: Convert.ChangeType(value, propertyType);
ConstantExpression valueExpr = Expression.Constant(val);
ConstantExpression valueExpr = Expression.Constant(val, property.PropertyType);
Expression condition = operand switch
{
@ -297,7 +297,14 @@ namespace Kyoo
expression = condition;
}
return Expression.Lambda<Func<T, bool>>(expression!);
return Expression.Lambda<Func<T, bool>>(expression!, param);
}
public static string ToQueryString(this Dictionary<string, string> query)
{
if (!query.Any())
return string.Empty;
return "?" + string.Join('&', query.Select(x => $"{x.Key}={x.Value}"));
}
}
}

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
@ -39,20 +40,13 @@ namespace Kyoo.Controllers
public Task<Episode> Get(string slug)
{
int sIndex = slug.IndexOf("-s", StringComparison.Ordinal);
int eIndex = slug.IndexOf("-e", StringComparison.Ordinal);
if (sIndex == -1 && eIndex == -1)
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == slug);
Match match = Regex.Match(slug, @"(<show>.*)-s(<season>\d*)-e(<episode>\d*)");
if (sIndex == -1 || eIndex == -1 || eIndex < sIndex)
throw new InvalidOperationException("Invalid episode slug. Format: {showSlug}-s{seasonNumber}-e{episodeNumber}");
string showSlug = slug.Substring(0, sIndex);
if (!int.TryParse(slug.Substring(sIndex + 2), out int seasonNumber))
throw new InvalidOperationException("Invalid episode slug. Format: {showSlug}-s{seasonNumber}-e{episodeNumber}");
if (!int.TryParse(slug.Substring(eIndex + 2), out int episodeNumber))
throw new InvalidOperationException("Invalid episode slug. Format: {showSlug}-s{seasonNumber}-e{episodeNumber}");
return Get(showSlug, seasonNumber, episodeNumber);
if (!match.Success)
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == slug);
return Get(match.Groups["show"].Value,
int.Parse(match.Groups["season"].Value),
int.Parse(match.Groups["episode"].Value));
}
public Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber)

View File

@ -92,7 +92,7 @@ namespace Kyoo.Controllers
(ParameterExpression)((MemberExpression)sortKey.Body).Expression
));
}
query = query.Take(page.Count <= 0 ? 20 : page.Count);
query = query.Take(page.Count);
return await query.ToListAsync();
}

View File

@ -1,4 +1,5 @@
using Kyoo.Models;
using System;
using Kyoo.Models;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
@ -6,6 +7,7 @@ using System.Threading.Tasks;
using Kyoo.Controllers;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
namespace Kyoo.Api
{
@ -19,23 +21,26 @@ namespace Kyoo.Api
private readonly DatabaseContext _database;
private readonly IThumbnailsManager _thumbnailsManager;
private readonly ITaskManager _taskManager;
private readonly string _baseURL;
public ShowsAPI(ILibraryManager libraryManager,
IProviderManager providerManager,
DatabaseContext database,
IThumbnailsManager thumbnailsManager,
ITaskManager taskManager)
ITaskManager taskManager,
IConfiguration configuration)
{
_libraryManager = libraryManager;
_providerManager = providerManager;
_database = database;
_thumbnailsManager = thumbnailsManager;
_taskManager = taskManager;
_baseURL = configuration.GetValue<string>("public_url").TrimEnd('/');
}
[HttpGet]
[Authorize(Policy="Read")]
public async Task<IEnumerable<Show>> GetShows([FromQuery] string sortBy,
public async Task<ActionResult<Page<Show>>> GetShows([FromQuery] string sortBy,
[FromQuery] int limit,
[FromQuery] int afterID,
[FromQuery] Dictionary<string, string> where)
@ -43,10 +48,26 @@ namespace Kyoo.Api
where.Remove("sortBy");
where.Remove("limit");
where.Remove("afterID");
return await _libraryManager.GetShows(Utility.ParseWhere<Show>(where),
new Sort<Show>(sortBy),
new Pagination(limit, afterID));
if (limit <= 0)
limit = 20;
ICollection<Show> shows;
try
{
shows = await _libraryManager.GetShows(Utility.ParseWhere<Show>(where),
new Sort<Show>(sortBy),
new Pagination(limit, afterID));
}
catch (ArgumentException ex)
{
return BadRequest(new { Error = ex.Message });
}
return new Page<Show>(shows,
x => $"{x.ID}",
_baseURL + Request.Path,
Request.Query.ToDictionary(x => x.Key, x => x.Value.ToString(), StringComparer.InvariantCultureIgnoreCase),
limit);
}
[HttpGet("{slug}")]