Creating the genre API (using the unimplemented contain api)

This commit is contained in:
Zoe Roux 2020-08-06 17:35:41 +02:00
parent 7e098b4005
commit 83a463851d
2 changed files with 118 additions and 15 deletions

View File

@ -20,13 +20,14 @@ namespace Kyoo.CommonApi
return operand(left, right);
}
public static Expression<Func<T, bool>> ParseWhere<T>(Dictionary<string, string> where)
public static Expression<Func<T, bool>> ParseWhere<T>(Dictionary<string, string> where,
Expression<Func<T, bool>> defaultWhere = null)
{
if (where == null || where.Count == 0)
return null;
ParameterExpression param = Expression.Parameter(typeof(T));
Expression expression = null;
Expression expression = defaultWhere?.Body;
foreach ((string key, string desired) in where)
{
@ -42,21 +43,26 @@ namespace Kyoo.CommonApi
if (property == null)
throw new ArgumentException($"No filterable parameter with the name {key}.");
MemberExpression propertyExpr = Expression.Property(param, property);
Type propertyType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
object val = string.IsNullOrEmpty(value) || value.Equals("null", StringComparison.OrdinalIgnoreCase)
? null
: Convert.ChangeType(value, propertyType);
ConstantExpression valueExpr = Expression.Constant(val, property.PropertyType);
ConstantExpression valueExpr = null;
if (operand != "ctn")
{
Type propertyType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
object val = string.IsNullOrEmpty(value) || value.Equals("null", StringComparison.OrdinalIgnoreCase)
? null
: Convert.ChangeType(value, propertyType);
valueExpr = Expression.Constant(val, property.PropertyType);
}
Expression condition = operand switch
{
"eq" => Expression.Equal(propertyExpr, valueExpr),
"not" => Expression.NotEqual(propertyExpr, valueExpr),
"eq" => Expression.Equal(propertyExpr, valueExpr!),
"not" => Expression.NotEqual(propertyExpr, valueExpr!),
"lt" => StringCompatibleExpression(Expression.LessThan, propertyExpr, valueExpr),
"lte" => StringCompatibleExpression(Expression.LessThanOrEqual, propertyExpr, valueExpr),
"gt" => StringCompatibleExpression(Expression.GreaterThan, propertyExpr, valueExpr),
"gte" => StringCompatibleExpression(Expression.GreaterThanOrEqual, propertyExpr, valueExpr),
"ctn" => ContainsResourceExpression(propertyExpr, value),
_ => throw new ArgumentException($"Invalid operand: {operand}")
};
@ -68,5 +74,37 @@ namespace Kyoo.CommonApi
return Expression.Lambda<Func<T, bool>>(expression!, param);
}
private static Expression ContainsResourceExpression(MemberExpression xProperty, string value)
{
// x => x.PROPERTY.Any(y => y.Slug == value)
Expression ret = null;
ParameterExpression y = Expression.Parameter(xProperty.Type.GenericTypeArguments.First(), "y");
foreach (string val in value.Split(','))
{
MemberExpression yProperty;
ConstantExpression yValue;
if (int.TryParse(val, out int id))
{
yProperty = Expression.Property(y, "ID");
yValue = Expression.Constant(id);
}
else
{
yProperty = Expression.Property(y, "Slug");
yValue = Expression.Constant(val);
}
LambdaExpression lambda = Expression.Lambda(Expression.Equal(yProperty, yValue), y);
Expression iteration = Expression.Call(typeof(Enumerable), "Any", xProperty.Type.GenericTypeArguments,
xProperty, lambda);
if (ret == null)
ret = iteration;
else
ret = Expression.AndAlso(ret, iteration);
}
return ret;
}
}
}

View File

@ -1,27 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.CommonApi;
using Kyoo.Controllers;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
namespace Kyoo.Api
{
[Route("api/genres")]
[Route("api/genre")]
[Route("api/genres")]
[ApiController]
public class GenresAPI : ControllerBase
public class GenresAPI : CrudApi<Genre>
{
private readonly ILibraryManager _libraryManager;
public GenresAPI(ILibraryManager libraryManager)
public GenresAPI(ILibraryManager libraryManager, IConfiguration config)
: base(libraryManager.GenreRepository, config)
{
_libraryManager = libraryManager;
}
public async Task<ActionResult<IEnumerable<Genre>>> Index()
[HttpGet("{id:int}/show")]
[HttpGet("{id:int}/shows")]
[Authorize(Policy = "Read")]
public async Task<ActionResult<Page<Show>>> GetShows(int id,
[FromQuery] string sortBy,
[FromQuery] int afterID,
[FromQuery] Dictionary<string, string> where,
[FromQuery] int limit = 20)
{
return (await _libraryManager.GetGenres()).ToList();
where.Remove("sortBy");
where.Remove("limit");
where.Remove("afterID");
try
{
ICollection<Show> ressources = await _libraryManager.GetShows(
ApiHelper.ParseWhere<Show>(where, x => x.Genres.Any(y => y.ID == id)),
new Sort<Show>(sortBy),
new Pagination(limit, afterID));
return Page(ressources, limit);
}
catch (ItemNotFound)
{
return NotFound();
}
catch (ArgumentException ex)
{
return BadRequest(new {Error = ex.Message});
}
}
[HttpGet("{slug}/show")]
[HttpGet("{slug}/shows")]
[Authorize(Policy = "Read")]
public async Task<ActionResult<Page<Show>>> GetShows(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,
[FromQuery] Dictionary<string, string> where,
[FromQuery] int limit = 20)
{
where.Remove("sortBy");
where.Remove("limit");
where.Remove("afterID");
try
{
ICollection<Show> ressources = await _libraryManager.GetShows(
ApiHelper.ParseWhere<Show>(where, x => x.Genres.Any(y => y.Slug == slug)),
new Sort<Show>(sortBy),
new Pagination(limit, afterID));
return Page(ressources, limit);
}
catch (ItemNotFound)
{
return NotFound();
}
catch (ArgumentException ex)
{
return BadRequest(new {Error = ex.Message});
}
}
}
}