mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Rework filters completly
This commit is contained in:
parent
e8351e960d
commit
e9aaa184cf
@ -61,11 +61,11 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <summary>
|
||||
/// Get the first resource that match the predicate.
|
||||
/// </summary>
|
||||
/// <param name="where">A predicate to filter the resource.</param>
|
||||
/// <param name="filter">A predicate to filter the resource.</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item could not be found.</exception>
|
||||
/// <returns>The resource found</returns>
|
||||
Task<T> Get(Expression<Func<T, bool>> where, Include<T>? include = default);
|
||||
Task<T> Get(Filter<T> filter, Include<T>? include = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get a resource from it's ID or null if it is not found.
|
||||
@ -86,11 +86,11 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <summary>
|
||||
/// Get the first resource that match the predicate or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="where">A predicate to filter the resource.</param>
|
||||
/// <param name="filter">A predicate to filter the resource.</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <param name="sortBy">A custom sort method to handle cases where multiples items match the filters.</param>
|
||||
/// <returns>The resource found</returns>
|
||||
Task<T?> GetOrDefault(Expression<Func<T, bool>> where,
|
||||
Task<T?> GetOrDefault(Filter<T>? filter,
|
||||
Include<T>? include = default,
|
||||
Sort<T>? sortBy = default);
|
||||
|
||||
@ -105,22 +105,22 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <summary>
|
||||
/// Get every resources that match all filters
|
||||
/// </summary>
|
||||
/// <param name="where">A filter predicate</param>
|
||||
/// <param name="filter">A filter predicate</param>
|
||||
/// <param name="sort">Sort information about the query (sort by, sort order)</param>
|
||||
/// <param name="limit">How pagination should be done (where to start and how many to return)</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <param name="limit">How pagination should be done (where to start and how many to return)</param>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
Task<ICollection<T>> GetAll(Expression<Func<T, bool>>? where = null,
|
||||
Task<ICollection<T>> GetAll(Filter<T>? filter = null,
|
||||
Sort<T>? sort = default,
|
||||
Pagination? limit = default,
|
||||
Include<T>? include = default);
|
||||
Include<T>? include = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of resources that match the filter's predicate.
|
||||
/// </summary>
|
||||
/// <param name="where">A filter predicate</param>
|
||||
/// <param name="filter">A filter predicate</param>
|
||||
/// <returns>How many resources matched that filter</returns>
|
||||
Task<int> GetCount(Expression<Func<T, bool>>? where = null);
|
||||
Task<int> GetCount(Filter<T>? filter = null);
|
||||
|
||||
/// <summary>
|
||||
/// Map a list of ids to a list of items (keep the order).
|
||||
@ -217,9 +217,9 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <summary>
|
||||
/// Delete all resources that match the predicate.
|
||||
/// </summary>
|
||||
/// <param name="where">A predicate to filter resources to delete. Every resource that match this will be deleted.</param>
|
||||
/// <param name="filter">A predicate to filter resources to delete. Every resource that match this will be deleted.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task DeleteAll(Expression<Func<T, bool>> where);
|
||||
Task DeleteAll(Filter<T> filter);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a resource has been edited.
|
||||
|
69
back/src/Kyoo.Abstractions/Models/Utils/Filter.cs
Normal file
69
back/src/Kyoo.Abstractions/Models/Utils/Filter.cs
Normal file
@ -0,0 +1,69 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
namespace Kyoo.Abstractions.Models.Utils;
|
||||
|
||||
public abstract record Filter
|
||||
{
|
||||
public static Filter<T>? And<T>(params Filter<T>?[] filters)
|
||||
{
|
||||
return filters
|
||||
.Where(x => x != null)
|
||||
.Aggregate((Filter<T>?)null, (acc, filter) =>
|
||||
{
|
||||
if (acc == null)
|
||||
return filter;
|
||||
return new Filter<T>.And(acc, filter!);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public abstract record Filter<T> : Filter
|
||||
{
|
||||
public record And(Filter<T> first, Filter<T> second) : Filter<T>;
|
||||
|
||||
public record Or(Filter<T> first, Filter<T> second) : Filter<T>;
|
||||
|
||||
public record Not(Filter<T> filter) : Filter<T>;
|
||||
|
||||
public record Eq(string property, object value) : Filter<T>;
|
||||
|
||||
public record Ne<T2>(string property, T2 value) : Filter<T>;
|
||||
|
||||
public record Gt<T2>(string property, T2 value) : Filter<T>;
|
||||
|
||||
public record Ge<T2>(string property, T2 value) : Filter<T>;
|
||||
|
||||
public record Lt<T2>(string property, T2 value) : Filter<T>;
|
||||
|
||||
public record Le<T2>(string property, T2 value) : Filter<T>;
|
||||
|
||||
public record Has<T2>(string property, T2 value) : Filter<T>;
|
||||
|
||||
public record In(string property, object[] value) : Filter<T>;
|
||||
|
||||
public record Lambda(Expression<Func<T, bool>> lambda) : Filter<T>;
|
||||
|
||||
public static Filter<T> From(string filter)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -99,13 +99,14 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
/// identifier.Matcher<Season>(x => x.ShowID, x => x.Show.Slug)
|
||||
/// </code>
|
||||
/// </example>
|
||||
public Expression<Func<T, bool>> Matcher<T>(Expression<Func<T, int>> idGetter,
|
||||
public Filter<T> Matcher<T>(Expression<Func<T, int>> idGetter,
|
||||
Expression<Func<T, string>> slugGetter)
|
||||
{
|
||||
ConstantExpression self = Expression.Constant(_id.HasValue ? _id.Value : _slug);
|
||||
BinaryExpression equal = Expression.Equal(_id.HasValue ? idGetter.Body : slugGetter.Body, self);
|
||||
ICollection<ParameterExpression> parameters = _id.HasValue ? idGetter.Parameters : slugGetter.Parameters;
|
||||
return Expression.Lambda<Func<T, bool>>(equal, parameters);
|
||||
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(equal, parameters);
|
||||
return new Filter<T>.Lambda(lambda);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -117,13 +118,14 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
/// <param name="slugGetter">An expression to retrieve a slug from the type <typeparamref name="T"/>.</param>
|
||||
/// <typeparam name="T">The type to match against this identifier.</typeparam>
|
||||
/// <returns>An expression to match the type <typeparamref name="T"/> to this identifier.</returns>
|
||||
public Expression<Func<T, bool>> Matcher<T>(Expression<Func<T, int?>> idGetter,
|
||||
public Filter<T> Matcher<T>(Expression<Func<T, int?>> idGetter,
|
||||
Expression<Func<T, string>> slugGetter)
|
||||
{
|
||||
ConstantExpression self = Expression.Constant(_id.HasValue ? _id.Value : _slug);
|
||||
BinaryExpression equal = Expression.Equal(_id.HasValue ? idGetter.Body : slugGetter.Body, self);
|
||||
ICollection<ParameterExpression> parameters = _id.HasValue ? idGetter.Parameters : slugGetter.Parameters;
|
||||
return Expression.Lambda<Func<T, bool>>(equal, parameters);
|
||||
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(equal, parameters);
|
||||
return new Filter<T>.Lambda(lambda);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -142,13 +144,21 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return an expression that return true if this <see cref="Identifier"/> match a given resource.
|
||||
/// Return a filter to get this <see cref="Identifier"/> match a given resource.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of resource to match against.</typeparam>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the given resource match this identifier, <c>false</c> otherwise.
|
||||
/// </returns>
|
||||
public Expression<Func<T, bool>> IsSame<T>()
|
||||
public Filter<T> IsSame<T>()
|
||||
where T : IResource
|
||||
{
|
||||
return _id.HasValue
|
||||
? new Filter<T>.Eq("Id", _id.Value)
|
||||
: new Filter<T>.Eq("Slug", _slug!);
|
||||
}
|
||||
|
||||
private Expression<Func<T, bool>> _IsSameExpression<T>()
|
||||
where T : IResource
|
||||
{
|
||||
return _id.HasValue
|
||||
@ -163,7 +173,7 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
/// <typeparam name="T">The type that contain the list to check.</typeparam>
|
||||
/// <typeparam name="T2">The type of resource to check this identifier against.</typeparam>
|
||||
/// <returns>An expression to check if this <see cref="Identifier"/> is contained.</returns>
|
||||
public Expression<Func<T, bool>> IsContainedIn<T, T2>(Expression<Func<T, IEnumerable<T2>>> listGetter)
|
||||
public Filter<T> IsContainedIn<T, T2>(Expression<Func<T, IEnumerable<T2>?>> listGetter)
|
||||
where T2 : IResource
|
||||
{
|
||||
MethodInfo method = typeof(Enumerable)
|
||||
@ -171,8 +181,9 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
.Where(x => x.Name == nameof(Enumerable.Any))
|
||||
.FirstOrDefault(x => x.GetParameters().Length == 2)!
|
||||
.MakeGenericMethod(typeof(T2));
|
||||
MethodCallExpression call = Expression.Call(null, method, listGetter.Body, IsSame<T2>());
|
||||
return Expression.Lambda<Func<T, bool>>(call, listGetter.Parameters);
|
||||
MethodCallExpression call = Expression.Call(null, method, listGetter.Body, _IsSameExpression<T2>());
|
||||
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, listGetter.Parameters);
|
||||
return new Filter<T>.Lambda(lambda);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -21,7 +21,7 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <summary>
|
||||
/// Information about the pagination. How many items should be displayed and where to start.
|
||||
/// </summary>
|
||||
public class Pagination
|
||||
public struct Pagination
|
||||
{
|
||||
/// <summary>
|
||||
/// The count of items to return.
|
||||
|
@ -1,125 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Kyoo.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Static class containing MethodOf calls.
|
||||
/// </summary>
|
||||
public static class MethodOfUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a MethodInfo from a direct method.
|
||||
/// </summary>
|
||||
/// <param name="action">The method (without any arguments or return value.</param>
|
||||
/// <returns>The <see cref="MethodInfo"/> of the given method</returns>
|
||||
public static MethodInfo MethodOf(Action action)
|
||||
{
|
||||
return action.Method;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a MethodInfo from a direct method.
|
||||
/// </summary>
|
||||
/// <param name="action">The method (without any arguments or return value.</param>
|
||||
/// <typeparam name="T">The first parameter of the action.</typeparam>
|
||||
/// <returns>The <see cref="MethodInfo"/> of the given method</returns>
|
||||
public static MethodInfo MethodOf<T>(Action<T> action)
|
||||
{
|
||||
return action.Method;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a MethodInfo from a direct method.
|
||||
/// </summary>
|
||||
/// <param name="action">The method (without any arguments or return value.</param>
|
||||
/// <typeparam name="T">The first parameter of the action.</typeparam>
|
||||
/// <typeparam name="T2">The second parameter of the action.</typeparam>
|
||||
/// <returns>The <see cref="MethodInfo"/> of the given method</returns>
|
||||
public static MethodInfo MethodOf<T, T2>(Action<T, T2> action)
|
||||
{
|
||||
return action.Method;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a MethodInfo from a direct method.
|
||||
/// </summary>
|
||||
/// <param name="action">The method (without any arguments or return value.</param>
|
||||
/// <typeparam name="T">The first parameter of the action.</typeparam>
|
||||
/// <typeparam name="T2">The second parameter of the action.</typeparam>
|
||||
/// <typeparam name="T3">The third parameter of the action.</typeparam>
|
||||
/// <returns>The <see cref="MethodInfo"/> of the given method</returns>
|
||||
public static MethodInfo MethodOf<T, T2, T3>(Action<T, T2, T3> action)
|
||||
{
|
||||
return action.Method;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a MethodInfo from a direct method.
|
||||
/// </summary>
|
||||
/// <param name="action">The method (without any arguments or return value.</param>
|
||||
/// <typeparam name="T">The return type of function.</typeparam>
|
||||
/// <returns>The <see cref="MethodInfo"/> of the given method</returns>
|
||||
public static MethodInfo MethodOf<T>(Func<T> action)
|
||||
{
|
||||
return action.Method;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a MethodInfo from a direct method.
|
||||
/// </summary>
|
||||
/// <param name="action">The method (without any arguments or return value.</param>
|
||||
/// <typeparam name="T">The first parameter of the function.</typeparam>
|
||||
/// <typeparam name="T2">The return type of function.</typeparam>
|
||||
/// <returns>The <see cref="MethodInfo"/> of the given method</returns>
|
||||
public static MethodInfo MethodOf<T, T2>(Func<T, T2> action)
|
||||
{
|
||||
return action.Method;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a MethodInfo from a direct method.
|
||||
/// </summary>
|
||||
/// <param name="action">The method (without any arguments or return value.</param>
|
||||
/// <typeparam name="T">The first parameter of the function.</typeparam>
|
||||
/// <typeparam name="T2">The second parameter of the function.</typeparam>
|
||||
/// <typeparam name="T3">The return type of function.</typeparam>
|
||||
/// <returns>The <see cref="MethodInfo"/> of the given method</returns>
|
||||
public static MethodInfo MethodOf<T, T2, T3>(Func<T, T2, T3> action)
|
||||
{
|
||||
return action.Method;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a MethodInfo from a direct method.
|
||||
/// </summary>
|
||||
/// <param name="action">The method (without any arguments or return value.</param>
|
||||
/// <typeparam name="T">The first parameter of the function.</typeparam>
|
||||
/// <typeparam name="T2">The second parameter of the function.</typeparam>
|
||||
/// <typeparam name="T3">The third parameter of the function.</typeparam>
|
||||
/// <typeparam name="T4">The return type of function.</typeparam>
|
||||
/// <returns>The <see cref="MethodInfo"/> of the given method</returns>
|
||||
public static MethodInfo MethodOf<T, T2, T3, T4>(Func<T, T2, T3, T4> action)
|
||||
{
|
||||
return action.Method;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Kyoo.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing helper method for tasks.
|
||||
/// </summary>
|
||||
public static class TaskUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Run a method after the execution of the task.
|
||||
/// </summary>
|
||||
/// <param name="task">The task to wait.</param>
|
||||
/// <param name="then">
|
||||
/// The method to run after the task finish. This will only be run if the task finished successfully.
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type of the item in the task.</typeparam>
|
||||
/// <returns>A continuation task wrapping the initial task and adding a continuation method.</returns>
|
||||
/// <exception cref="TaskCanceledException">The source task has been canceled.</exception>
|
||||
public static Task<T> Then<T>(this Task<T> task, Action<T> then)
|
||||
{
|
||||
return task.ContinueWith(x =>
|
||||
{
|
||||
if (x.IsFaulted)
|
||||
x.Exception!.InnerException!.ReThrow();
|
||||
if (x.IsCanceled)
|
||||
throw new TaskCanceledException();
|
||||
then(x.Result);
|
||||
return x.Result;
|
||||
}, TaskContinuationOptions.ExecuteSynchronously);
|
||||
}
|
||||
}
|
||||
}
|
@ -342,18 +342,5 @@ namespace Kyoo.Utils
|
||||
return string.Empty;
|
||||
return "?" + string.Join('&', query.Select(x => $"{x.Key}={x.Value}"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rethrow the exception without modifying the stack trace.
|
||||
/// This is similar to the <c>rethrow;</c> code but is useful when the exception is not in a catch block.
|
||||
/// </summary>
|
||||
/// <param name="ex">The exception to rethrow.</param>
|
||||
[System.Diagnostics.CodeAnalysis.DoesNotReturn]
|
||||
public static void ReThrow(this Exception ex)
|
||||
{
|
||||
if (ex == null)
|
||||
throw new ArgumentNullException(nameof(ex));
|
||||
ExceptionDispatchInfo.Capture(ex).Throw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ namespace Kyoo.Authentication.Views
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))]
|
||||
public async Task<ActionResult<JwtToken>> Login([FromBody] LoginRequest request)
|
||||
{
|
||||
User? user = await _users.GetOrDefault(x => x.Username == request.Username);
|
||||
User? user = await _users.GetOrDefault(new Filter<User>.Eq(nameof(Abstractions.Models.User.Username), request.Username));
|
||||
if (user == null || !BCryptNet.Verify(request.Password, user.Password))
|
||||
return Forbid(new RequestError("The user and password does not match."));
|
||||
|
||||
@ -126,7 +126,7 @@ namespace Kyoo.Authentication.Views
|
||||
User user = request.ToUser();
|
||||
user.Permissions = _permissions.NewUser;
|
||||
// If no users exists, the new one will be an admin. Give it every permissions.
|
||||
if (await _users.GetOrDefault(where: x => true) == null)
|
||||
if (await _users.GetOrDefault(1) == null)
|
||||
user.Permissions = PermissionOption.Admin;
|
||||
try
|
||||
{
|
||||
|
@ -53,7 +53,7 @@ namespace Kyoo.Core.Controllers
|
||||
public override async Task<ICollection<Collection>> Search(string query, Include<Collection>? include = default)
|
||||
{
|
||||
return await AddIncludes(_database.Collections, include)
|
||||
.Where(_database.Like<Collection>(x => x.Name + " " + x.Slug, $"%{query}%"))
|
||||
.Where(x => EF.Functions.ILike(x.Name + " " + x.Slug, $"%{query}%"))
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ namespace Kyoo.Core.Controllers
|
||||
public override async Task<ICollection<Episode>> Search(string query, Include<Episode>? include = default)
|
||||
{
|
||||
return await AddIncludes(_database.Episodes, include)
|
||||
.Where(_database.Like<Episode>(x => x.Name!, $"%{query}%"))
|
||||
.Where(x => EF.Functions.ILike(x.Name!, $"%{query}%"))
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
}
|
||||
@ -92,8 +92,8 @@ namespace Kyoo.Core.Controllers
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
await _database.SaveChangesAsync(() =>
|
||||
obj is { SeasonNumber: not null, EpisodeNumber: not null }
|
||||
? Get(x => x.ShowId == obj.ShowId && x.SeasonNumber == obj.SeasonNumber && x.EpisodeNumber == obj.EpisodeNumber)
|
||||
: Get(x => x.ShowId == obj.ShowId && x.AbsoluteNumber == obj.AbsoluteNumber));
|
||||
? _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == obj.ShowId && x.SeasonNumber == obj.SeasonNumber && x.EpisodeNumber == obj.EpisodeNumber)
|
||||
: _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == obj.ShowId && x.AbsoluteNumber == obj.AbsoluteNumber));
|
||||
await IRepository<Episode>.OnResourceCreated(obj);
|
||||
return obj;
|
||||
}
|
||||
|
@ -29,10 +29,8 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
using InterpolatedSql.Dapper;
|
||||
using InterpolatedSql.SqlBuilders;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Abstractions.Models.Exceptions;
|
||||
using Kyoo.Abstractions.Models.Utils;
|
||||
using Kyoo.Utils;
|
||||
@ -72,11 +70,10 @@ namespace Kyoo.Core.Controllers
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<ILibraryItem> Get(
|
||||
Expression<Func<ILibraryItem, bool>> where,
|
||||
public virtual async Task<ILibraryItem> Get(Filter<ILibraryItem> filter,
|
||||
Include<ILibraryItem>? include = default)
|
||||
{
|
||||
ILibraryItem? ret = await GetOrDefault(where, include: include);
|
||||
ILibraryItem? ret = await GetOrDefault(filter, include: include);
|
||||
if (ret == null)
|
||||
throw new ItemNotFoundException($"No {nameof(ILibraryItem)} found with the given predicate.");
|
||||
return ret;
|
||||
@ -92,7 +89,8 @@ namespace Kyoo.Core.Controllers
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<ILibraryItem?> GetOrDefault(Expression<Func<ILibraryItem, bool>> where, Include<ILibraryItem>? include = null, Sort<ILibraryItem>? sortBy = null)
|
||||
public Task<ILibraryItem?> GetOrDefault(Filter<ILibraryItem>? filter, Include<ILibraryItem>? include = default,
|
||||
Sort<ILibraryItem>? sortBy = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@ -108,9 +106,11 @@ namespace Kyoo.Core.Controllers
|
||||
return $"coalesce({string.Join(", ", keys)})";
|
||||
}
|
||||
|
||||
public static string ProcessSort<T>(Sort<T> sort, Dictionary<string, Type> config, bool recurse = false)
|
||||
public static string ProcessSort<T>(Sort<T>? sort, Dictionary<string, Type> config, bool recurse = false)
|
||||
where T : IQuery
|
||||
{
|
||||
sort ??= new Sort<T>.Default();
|
||||
|
||||
string ret = sort switch
|
||||
{
|
||||
Sort<T>.Default(var value) => ProcessSort(value, config, true),
|
||||
@ -188,12 +188,24 @@ namespace Kyoo.Core.Controllers
|
||||
return $"{prefix}*" + projStr;
|
||||
}
|
||||
|
||||
public async Task<ICollection<ILibraryItem>> GetAll(
|
||||
Expression<Func<ILibraryItem, bool>>? where = null,
|
||||
Sort<ILibraryItem>? sort = null,
|
||||
Pagination? limit = null,
|
||||
Include<ILibraryItem>? include = null)
|
||||
public static string ProcessFilter<T>(Filter<T> filter, Dictionary<string, Type> config)
|
||||
{
|
||||
return filter switch
|
||||
{
|
||||
Filter<T>.And(var first, var second) => $"({ProcessFilter(first, config)} and {ProcessFilter(second, config)})",
|
||||
Filter<T>.Or(var first, var second) => $"({ProcessFilter(first, config)} or {ProcessFilter(second, config)})",
|
||||
Filter<T>.Not(var inner) => $"(not {ProcessFilter(inner, config)})",
|
||||
Filter<T>.Eq(var property, var value) => $"({_Property(property, config)} = {value})",
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ICollection<ILibraryItem>> GetAll(Filter<ILibraryItem>? filter = null,
|
||||
Sort<ILibraryItem>? sort = default,
|
||||
Include<ILibraryItem>? include = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
include ??= new();
|
||||
|
||||
Dictionary<string, Type> config = new()
|
||||
{
|
||||
{ "s", typeof(Show) },
|
||||
@ -203,7 +215,7 @@ namespace Kyoo.Core.Controllers
|
||||
var (includeConfig, includeJoin, mapIncludes) = ProcessInclude(include, config);
|
||||
|
||||
// language=PostgreSQL
|
||||
IDapperSqlCommand query = _database.SqlBuilder($"""
|
||||
var query = _database.SqlBuilder($"""
|
||||
select
|
||||
{ExpendProjections<Show>("s", include):raw},
|
||||
m.*,
|
||||
@ -224,7 +236,10 @@ namespace Kyoo.Core.Controllers
|
||||
{includeJoin:raw}
|
||||
order by {ProcessSort(sort, config):raw}
|
||||
limit {limit.Limit}
|
||||
""").Build();
|
||||
""");
|
||||
|
||||
if (filter != null)
|
||||
query += $"where {ProcessFilter(filter, config):raw}";
|
||||
|
||||
Type[] types = config.Select(x => x.Value)
|
||||
.Concat(includeConfig.Select(x => x.Value))
|
||||
@ -242,7 +257,7 @@ namespace Kyoo.Core.Controllers
|
||||
return data.ToList();
|
||||
}
|
||||
|
||||
public Task<int> GetCount(Expression<Func<ILibraryItem, bool>>? where = null)
|
||||
public Task<int> GetCount(Filter<ILibraryItem>? filter = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@ -252,7 +267,7 @@ namespace Kyoo.Core.Controllers
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task DeleteAll(Expression<Func<ILibraryItem, bool>> where)
|
||||
public Task DeleteAll(Filter<ILibraryItem> filter)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
@ -113,6 +113,11 @@ namespace Kyoo.Core.Controllers
|
||||
return _Sort(query, sortBy, false).ThenBy(x => x.Id);
|
||||
}
|
||||
|
||||
protected static Expression<Func<T, bool>> ParseFilter(Filter<T>? filter)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static Func<Expression, Expression, BinaryExpression> _GetComparisonExpression(
|
||||
bool desc,
|
||||
bool next,
|
||||
@ -297,9 +302,9 @@ namespace Kyoo.Core.Controllers
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<T> Get(Expression<Func<T, bool>> where, Include<T>? include = default)
|
||||
public virtual async Task<T> Get(Filter<T> filter, Include<T>? include = default)
|
||||
{
|
||||
T? ret = await GetOrDefault(where, include: include);
|
||||
T? ret = await GetOrDefault(filter, include: include);
|
||||
if (ret == null)
|
||||
throw new ItemNotFoundException($"No {typeof(T).Name} found with the given predicate.");
|
||||
return ret;
|
||||
@ -326,7 +331,7 @@ namespace Kyoo.Core.Controllers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<T?> GetOrDefault(Expression<Func<T, bool>> where,
|
||||
public virtual Task<T?> GetOrDefault(Filter<T>? filter,
|
||||
Include<T>? include = default,
|
||||
Sort<T>? sortBy = default)
|
||||
{
|
||||
@ -334,7 +339,7 @@ namespace Kyoo.Core.Controllers
|
||||
AddIncludes(Database.Set<T>(), include),
|
||||
sortBy
|
||||
)
|
||||
.FirstOrDefaultAsync(where);
|
||||
.FirstOrDefaultAsync(ParseFilter(filter));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@ -353,12 +358,12 @@ namespace Kyoo.Core.Controllers
|
||||
public abstract Task<ICollection<T>> Search(string query, Include<T>? include = default);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual Task<ICollection<T>> GetAll(Expression<Func<T, bool>>? where = null,
|
||||
public virtual Task<ICollection<T>> GetAll(Filter<T>? filter = null,
|
||||
Sort<T>? sort = default,
|
||||
Pagination? limit = default,
|
||||
Include<T>? include = default)
|
||||
Include<T>? include = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
return ApplyFilters(Database.Set<T>(), where, sort, limit, include);
|
||||
return ApplyFilters(Database.Set<T>(), ParseFilter(filter), sort, limit, include);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -373,7 +378,7 @@ namespace Kyoo.Core.Controllers
|
||||
protected async Task<ICollection<T>> ApplyFilters(IQueryable<T> query,
|
||||
Expression<Func<T, bool>>? where = null,
|
||||
Sort<T>? sort = default,
|
||||
Pagination? limit = default,
|
||||
Pagination limit = default,
|
||||
Include<T>? include = default)
|
||||
{
|
||||
query = AddIncludes(query, include);
|
||||
@ -381,25 +386,25 @@ namespace Kyoo.Core.Controllers
|
||||
if (where != null)
|
||||
query = query.Where(where);
|
||||
|
||||
if (limit?.AfterID != null)
|
||||
if (limit.AfterID != null)
|
||||
{
|
||||
T reference = await Get(limit.AfterID.Value);
|
||||
query = query.Where(KeysetPaginate(sort, reference, !limit.Reverse));
|
||||
}
|
||||
if (limit?.Reverse == true)
|
||||
if (limit.Reverse)
|
||||
query = query.Reverse();
|
||||
if (limit?.Limit > 0)
|
||||
if (limit.Limit > 0)
|
||||
query = query.Take(limit.Limit);
|
||||
|
||||
return await query.ToListAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual Task<int> GetCount(Expression<Func<T, bool>>? where = null)
|
||||
public virtual Task<int> GetCount(Filter<T>? filter = null)
|
||||
{
|
||||
IQueryable<T> query = Database.Set<T>();
|
||||
if (where != null)
|
||||
query = query.Where(where);
|
||||
if (filter != null)
|
||||
query = query.Where(ParseFilter(filter));
|
||||
return query.CountAsync();
|
||||
}
|
||||
|
||||
@ -559,9 +564,9 @@ namespace Kyoo.Core.Controllers
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task DeleteAll(Expression<Func<T, bool>> where)
|
||||
public async Task DeleteAll(Filter<T> filter)
|
||||
{
|
||||
foreach (T resource in await GetAll(where))
|
||||
foreach (T resource in await GetAll(filter))
|
||||
await Delete(resource);
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ namespace Kyoo.Core.Controllers
|
||||
public override async Task<ICollection<Movie>> Search(string query, Include<Movie>? include = default)
|
||||
{
|
||||
return await AddIncludes(_database.Movies, include)
|
||||
.Where(_database.Like<Movie>(x => x.Name + " " + x.Slug, $"%{query}%"))
|
||||
.Where(x => EF.Functions.ILike(x.Name + " " + x.Slug, $"%{query}%"))
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ namespace Kyoo.Core.Controllers
|
||||
public override async Task<ICollection<People>> Search(string query, Include<People>? include = default)
|
||||
{
|
||||
return await AddIncludes(_database.People, include)
|
||||
.Where(_database.Like<People>(x => x.Name, $"%{query}%"))
|
||||
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ namespace Kyoo.Core.Controllers
|
||||
public override async Task<ICollection<Season>> Search(string query, Include<Season>? include = default)
|
||||
{
|
||||
return await AddIncludes(_database.Seasons, include)
|
||||
.Where(_database.Like<Season>(x => x.Name!, $"%{query}%"))
|
||||
.Where(x => EF.Functions.ILike(x.Name!, $"%{query}%"))
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
}
|
||||
@ -85,7 +85,9 @@ namespace Kyoo.Core.Controllers
|
||||
await base.Create(obj);
|
||||
obj.ShowSlug = _database.Shows.First(x => x.Id == obj.ShowId).Slug;
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
await _database.SaveChangesAsync(() => Get(x => x.ShowId == obj.ShowId && x.SeasonNumber == obj.SeasonNumber));
|
||||
await _database.SaveChangesAsync(() =>
|
||||
_database.Seasons.FirstOrDefaultAsync(x => x.ShowId == obj.ShowId && x.SeasonNumber == obj.SeasonNumber)
|
||||
);
|
||||
await IRepository<Season>.OnResourceCreated(obj);
|
||||
return obj;
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ namespace Kyoo.Core.Controllers
|
||||
public override async Task<ICollection<Show>> Search(string query, Include<Show>? include = default)
|
||||
{
|
||||
return await AddIncludes(_database.Shows, include)
|
||||
.Where(_database.Like<Show>(x => x.Name + " " + x.Slug, $"%{query}%"))
|
||||
.Where(x => EF.Functions.ILike(x.Name + " " + x.Slug, $"%{query}%"))
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
}
|
||||
@ -88,8 +88,6 @@ namespace Kyoo.Core.Controllers
|
||||
/// <inheritdoc />
|
||||
protected override async Task Validate(Show resource)
|
||||
{
|
||||
resource.Slug ??= Utility.ToSlug(resource.Name);
|
||||
|
||||
await base.Validate(resource);
|
||||
if (resource.Studio != null)
|
||||
{
|
||||
|
@ -53,7 +53,7 @@ namespace Kyoo.Core.Controllers
|
||||
public override async Task<ICollection<Studio>> Search(string query, Include<Studio>? include = default)
|
||||
{
|
||||
return await AddIncludes(_database.Studios, include)
|
||||
.Where(_database.Like<Studio>(x => x.Name, $"%{query}%"))
|
||||
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
}
|
||||
@ -68,13 +68,6 @@ namespace Kyoo.Core.Controllers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task Validate(Studio resource)
|
||||
{
|
||||
resource.Slug ??= Utility.ToSlug(resource.Name);
|
||||
await base.Validate(resource);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task Delete(Studio obj)
|
||||
{
|
||||
|
@ -52,7 +52,7 @@ namespace Kyoo.Core.Controllers
|
||||
public override async Task<ICollection<User>> Search(string query, Include<User>? include = default)
|
||||
{
|
||||
return await AddIncludes(_database.Users, include)
|
||||
.Where(_database.Like<User>(x => x.Username, $"%{query}%"))
|
||||
.Where(x => EF.Functions.ILike(x.Username, $"%{query}%"))
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models;
|
||||
@ -86,16 +85,16 @@ namespace Kyoo.Core.Api
|
||||
/// <remarks>
|
||||
/// Get the number of resources that match the filters.
|
||||
/// </remarks>
|
||||
/// <param name="where">A list of filters to respect.</param>
|
||||
/// <param name="filter">A list of filters to respect.</param>
|
||||
/// <returns>How many resources matched that filter.</returns>
|
||||
/// <response code="400">Invalid filters.</response>
|
||||
[HttpGet("count")]
|
||||
[PartialPermission(Kind.Read)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
||||
public async Task<ActionResult<int>> GetCount([FromQuery] Dictionary<string, string> where)
|
||||
public async Task<ActionResult<int>> GetCount([FromQuery] Filter<T> filter)
|
||||
{
|
||||
return await Repository.GetCount(ApiHelper.ParseWhere<T>(where));
|
||||
return await Repository.GetCount(filter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -105,7 +104,7 @@ namespace Kyoo.Core.Api
|
||||
/// Get all resources that match the given filter.
|
||||
/// </remarks>
|
||||
/// <param name="sortBy">Sort information about the query (sort by, sort order).</param>
|
||||
/// <param name="where">Filter the returned items.</param>
|
||||
/// <param name="filter">Filter the returned items.</param>
|
||||
/// <param name="pagination">How many items per page should be returned, where should the page start...</param>
|
||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||
/// <returns>A list of resources that match every filters.</returns>
|
||||
@ -116,15 +115,15 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
||||
public async Task<ActionResult<Page<T>>> GetAll(
|
||||
[FromQuery] Sort<T> sortBy,
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] Filter<T>? filter,
|
||||
[FromQuery] Pagination pagination,
|
||||
[FromQuery] Include<T>? fields)
|
||||
{
|
||||
ICollection<T> resources = await Repository.GetAll(
|
||||
ApiHelper.ParseWhere<T>(where),
|
||||
filter,
|
||||
sortBy,
|
||||
pagination,
|
||||
fields
|
||||
fields,
|
||||
pagination
|
||||
);
|
||||
|
||||
return Page(resources, pagination.Limit);
|
||||
@ -231,20 +230,19 @@ namespace Kyoo.Core.Api
|
||||
/// <remarks>
|
||||
/// Delete all items matching the given filters. If no filter is specified, delete all items.
|
||||
/// </remarks>
|
||||
/// <param name="where">The list of filters.</param>
|
||||
/// <param name="filter">The list of filters.</param>
|
||||
/// <returns>The item(s) has successfully been deleted.</returns>
|
||||
/// <response code="400">One or multiple filters are invalid.</response>
|
||||
[HttpDelete]
|
||||
[PartialPermission(Kind.Delete)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
||||
public async Task<IActionResult> Delete([FromQuery] Dictionary<string, string> where)
|
||||
public async Task<IActionResult> Delete([FromQuery] Filter<T> filter)
|
||||
{
|
||||
Expression<Func<T, bool>>? w = ApiHelper.ParseWhere<T>(where);
|
||||
if (w == null)
|
||||
if (filter == null)
|
||||
return BadRequest(new RequestError("Incule a filter to delete items, all items won't be deleted."));
|
||||
|
||||
await Repository.DeleteAll(w);
|
||||
await Repository.DeleteAll(filter);
|
||||
return NoContent();
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ namespace Kyoo.Core.Api
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the <see cref="Studio"/>.</param>
|
||||
/// <param name="sortBy">A key to sort shows by.</param>
|
||||
/// <param name="where">An optional list of filters.</param>
|
||||
/// <param name="filter">An optional list of filters.</param>
|
||||
/// <param name="pagination">The number of shows to return.</param>
|
||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||
/// <returns>A page of shows.</returns>
|
||||
@ -79,15 +79,15 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Page<Show>>> GetShows(Identifier identifier,
|
||||
[FromQuery] Sort<Show> sortBy,
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] Filter<Show>? filter,
|
||||
[FromQuery] Pagination pagination,
|
||||
[FromQuery] Include<Show> fields)
|
||||
{
|
||||
ICollection<Show> resources = await _libraryManager.Shows.GetAll(
|
||||
ApiHelper.ParseWhere(where, identifier.Matcher<Show>(x => x.StudioId, x => x.Studio!.Slug)),
|
||||
Filter.And(filter, identifier.Matcher<Show>(x => x.StudioId, x => x.Studio!.Slug)),
|
||||
sortBy,
|
||||
pagination,
|
||||
fields
|
||||
fields,
|
||||
pagination
|
||||
);
|
||||
|
||||
if (!resources.Any() && await _libraryManager.Studios.GetOrDefault(identifier.IsSame<Studio>()) == null)
|
||||
|
@ -126,7 +126,7 @@ namespace Kyoo.Core.Api
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the <see cref="Collection"/>.</param>
|
||||
/// <param name="sortBy">A key to sort items by.</param>
|
||||
/// <param name="where">An optional list of filters.</param>
|
||||
/// <param name="filter">An optional list of filters.</param>
|
||||
/// <param name="pagination">The number of items to return.</param>
|
||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||
/// <returns>A page of items.</returns>
|
||||
@ -140,21 +140,21 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Page<ILibraryItem>>> GetItems(Identifier identifier,
|
||||
[FromQuery] Sort<ILibraryItem> sortBy,
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] Filter<ILibraryItem>? filter,
|
||||
[FromQuery] Pagination pagination,
|
||||
[FromQuery] Include<ILibraryItem>? fields)
|
||||
{
|
||||
ICollection<ILibraryItem> resources = await _items.GetAllOfCollection(
|
||||
identifier.IsSame<Collection>(),
|
||||
ApiHelper.ParseWhere<ILibraryItem>(where),
|
||||
sortBy == new Sort<ILibraryItem>.Default() ? new Sort<ILibraryItem>.By(nameof(Movie.AirDate)) : sortBy,
|
||||
pagination,
|
||||
fields
|
||||
);
|
||||
// ICollection<ILibraryItem> resources = await _items.GetAllOfCollection(
|
||||
// identifier.IsSame<Collection>(),
|
||||
// filter,
|
||||
// sortBy == new Sort<ILibraryItem>.Default() ? new Sort<ILibraryItem>.By(nameof(Movie.AirDate)) : sortBy,
|
||||
// pagination,
|
||||
// fields
|
||||
// );
|
||||
|
||||
if (!resources.Any() && await _libraryManager.Collections.GetOrDefault(identifier.IsSame<Collection>()) == null)
|
||||
// if (!resources.Any() && await _libraryManager.Collections.GetOrDefault(identifier.IsSame<Collection>()) == null)
|
||||
return NotFound();
|
||||
return Page(resources, pagination.Limit);
|
||||
// return Page(resources, pagination.Limit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -165,9 +165,9 @@ namespace Kyoo.Core.Api
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the <see cref="Collection"/>.</param>
|
||||
/// <param name="sortBy">A key to sort shows by.</param>
|
||||
/// <param name="where">An optional list of filters.</param>
|
||||
/// <param name="filter">An optional list of filters.</param>
|
||||
/// <param name="pagination">The number of shows to return.</param>
|
||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||
/// <param name="fields">The additional fields to include in the result.</param>
|
||||
/// <returns>A page of shows.</returns>
|
||||
/// <response code="400">The filters or the sort parameters are invalid.</response>
|
||||
/// <response code="404">No collection with the given ID could be found.</response>
|
||||
@ -179,15 +179,15 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Page<Show>>> GetShows(Identifier identifier,
|
||||
[FromQuery] Sort<Show> sortBy,
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] Filter<Show>? filter,
|
||||
[FromQuery] Pagination pagination,
|
||||
[FromQuery] Include<Show>? fields)
|
||||
{
|
||||
ICollection<Show> resources = await _libraryManager.Shows.GetAll(
|
||||
ApiHelper.ParseWhere(where, identifier.IsContainedIn<Show, Collection>(x => x.Collections!)),
|
||||
Filter.And(filter, identifier.IsContainedIn<Show, Collection>(x => x.Collections)),
|
||||
sortBy == new Sort<Show>.Default() ? new Sort<Show>.By(x => x.AirDate) : sortBy,
|
||||
pagination,
|
||||
fields
|
||||
fields,
|
||||
pagination
|
||||
);
|
||||
|
||||
if (!resources.Any() && await _libraryManager.Collections.GetOrDefault(identifier.IsSame<Collection>()) == null)
|
||||
@ -203,7 +203,7 @@ namespace Kyoo.Core.Api
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the <see cref="Collection"/>.</param>
|
||||
/// <param name="sortBy">A key to sort movies by.</param>
|
||||
/// <param name="where">An optional list of filters.</param>
|
||||
/// <param name="filter">An optional list of filters.</param>
|
||||
/// <param name="pagination">The number of movies to return.</param>
|
||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||
/// <returns>A page of movies.</returns>
|
||||
@ -217,15 +217,15 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Page<Movie>>> GetMovies(Identifier identifier,
|
||||
[FromQuery] Sort<Movie> sortBy,
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] Filter<Movie>? filter,
|
||||
[FromQuery] Pagination pagination,
|
||||
[FromQuery] Include<Movie>? fields)
|
||||
{
|
||||
ICollection<Movie> resources = await _libraryManager.Movies.GetAll(
|
||||
ApiHelper.ParseWhere(where, identifier.IsContainedIn<Movie, Collection>(x => x.Collections!)),
|
||||
Filter.And(filter, identifier.IsContainedIn<Movie, Collection>(x => x.Collections)),
|
||||
sortBy == new Sort<Movie>.Default() ? new Sort<Movie>.By(x => x.AirDate) : sortBy,
|
||||
pagination,
|
||||
fields
|
||||
fields,
|
||||
pagination
|
||||
);
|
||||
|
||||
if (!resources.Any() && await _libraryManager.Collections.GetOrDefault(identifier.IsSame<Collection>()) == null)
|
||||
|
@ -120,7 +120,7 @@ namespace Kyoo.Core.Api
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the <see cref="Show"/>.</param>
|
||||
/// <param name="sortBy">A key to sort collections by.</param>
|
||||
/// <param name="where">An optional list of filters.</param>
|
||||
/// <param name="filter">An optional list of filters.</param>
|
||||
/// <param name="pagination">The number of collections to return.</param>
|
||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||
/// <returns>A page of collections.</returns>
|
||||
@ -134,15 +134,15 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Page<Collection>>> GetCollections(Identifier identifier,
|
||||
[FromQuery] Sort<Collection> sortBy,
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] Filter<Collection>? filter,
|
||||
[FromQuery] Pagination pagination,
|
||||
[FromQuery] Include<Collection> fields)
|
||||
{
|
||||
ICollection<Collection> resources = await _libraryManager.Collections.GetAll(
|
||||
ApiHelper.ParseWhere(where, identifier.IsContainedIn<Collection, Movie>(x => x.Movies!)),
|
||||
Filter.And(filter, identifier.IsContainedIn<Collection, Movie>(x => x.Movies)),
|
||||
sortBy,
|
||||
pagination,
|
||||
fields
|
||||
fields,
|
||||
pagination
|
||||
);
|
||||
|
||||
if (!resources.Any() && await _libraryManager.Movies.GetOrDefault(identifier.IsSame<Movie>()) == null)
|
||||
|
@ -16,13 +16,10 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Abstractions.Models.Permissions;
|
||||
using Kyoo.Core.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using static Kyoo.Abstractions.Models.Utils.Constants;
|
||||
|
||||
@ -37,25 +34,10 @@ namespace Kyoo.Core.Api
|
||||
[ResourceView]
|
||||
[PartialPermission("LibraryItem")]
|
||||
[ApiDefinition("News", Group = ResourcesGroup)]
|
||||
public class NewsApi : BaseApi
|
||||
public class NewsApi : CrudThumbsApi<News>
|
||||
{
|
||||
private readonly NewsRepository _news;
|
||||
|
||||
public NewsApi(NewsRepository news)
|
||||
{
|
||||
_news = news;
|
||||
}
|
||||
|
||||
public async Task<ActionResult<Page<News>>> GetAll(
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] Pagination pagination)
|
||||
{
|
||||
ICollection<News> resources = await _news.GetAll(
|
||||
ApiHelper.ParseWhere<News>(where),
|
||||
limit: pagination
|
||||
);
|
||||
|
||||
return Page(resources, pagination.Limit);
|
||||
}
|
||||
public NewsApi(IRepository<News> news, IThumbnailsManager thumbs)
|
||||
: base(news, thumbs)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ namespace Kyoo.Core.Api
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the <see cref="Season"/>.</param>
|
||||
/// <param name="sortBy">A key to sort episodes by.</param>
|
||||
/// <param name="where">An optional list of filters.</param>
|
||||
/// <param name="filter">An optional list of filters.</param>
|
||||
/// <param name="pagination">The number of episodes to return.</param>
|
||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||
/// <returns>A page of episodes.</returns>
|
||||
@ -81,15 +81,15 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Page<Episode>>> GetEpisode(Identifier identifier,
|
||||
[FromQuery] Sort<Episode> sortBy,
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] Filter<Episode>? filter,
|
||||
[FromQuery] Pagination pagination,
|
||||
[FromQuery] Include<Episode> fields)
|
||||
{
|
||||
ICollection<Episode> resources = await _libraryManager.Episodes.GetAll(
|
||||
ApiHelper.ParseWhere(where, identifier.Matcher<Episode>(x => x.SeasonId, x => x.Season!.Slug)),
|
||||
Filter.And(filter, identifier.Matcher<Episode>(x => x.SeasonId, x => x.Season!.Slug)),
|
||||
sortBy,
|
||||
pagination,
|
||||
fields
|
||||
fields,
|
||||
pagination
|
||||
);
|
||||
|
||||
if (!resources.Any() && await _libraryManager.Seasons.GetOrDefault(identifier.IsSame<Season>()) == null)
|
||||
|
@ -67,7 +67,7 @@ namespace Kyoo.Core.Api
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the <see cref="Show"/>.</param>
|
||||
/// <param name="sortBy">A key to sort seasons by.</param>
|
||||
/// <param name="where">An optional list of filters.</param>
|
||||
/// <param name="filter">An optional list of filters.</param>
|
||||
/// <param name="pagination">The number of seasons to return.</param>
|
||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||
/// <returns>A page of seasons.</returns>
|
||||
@ -81,15 +81,15 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Page<Season>>> GetSeasons(Identifier identifier,
|
||||
[FromQuery] Sort<Season> sortBy,
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] Filter<Season>? filter,
|
||||
[FromQuery] Pagination pagination,
|
||||
[FromQuery] Include<Season> fields)
|
||||
{
|
||||
ICollection<Season> resources = await _libraryManager.Seasons.GetAll(
|
||||
ApiHelper.ParseWhere(where, identifier.Matcher<Season>(x => x.ShowId, x => x.Show!.Slug)),
|
||||
Filter.And(filter, identifier.Matcher<Season>(x => x.ShowId, x => x.Show!.Slug)),
|
||||
sortBy,
|
||||
pagination,
|
||||
fields
|
||||
fields,
|
||||
pagination
|
||||
);
|
||||
|
||||
if (!resources.Any() && await _libraryManager.Shows.GetOrDefault(identifier.IsSame<Show>()) == null)
|
||||
@ -105,7 +105,7 @@ namespace Kyoo.Core.Api
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the <see cref="Show"/>.</param>
|
||||
/// <param name="sortBy">A key to sort episodes by.</param>
|
||||
/// <param name="where">An optional list of filters.</param>
|
||||
/// <param name="filter">An optional list of filters.</param>
|
||||
/// <param name="pagination">The number of episodes to return.</param>
|
||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||
/// <returns>A page of episodes.</returns>
|
||||
@ -119,15 +119,15 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Page<Episode>>> GetEpisodes(Identifier identifier,
|
||||
[FromQuery] Sort<Episode> sortBy,
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] Filter<Episode>? filter,
|
||||
[FromQuery] Pagination pagination,
|
||||
[FromQuery] Include<Episode> fields)
|
||||
{
|
||||
ICollection<Episode> resources = await _libraryManager.Episodes.GetAll(
|
||||
ApiHelper.ParseWhere(where, identifier.Matcher<Episode>(x => x.ShowId, x => x.Show!.Slug)),
|
||||
Filter.And(filter, identifier.Matcher<Episode>(x => x.ShowId, x => x.Show!.Slug)),
|
||||
sortBy,
|
||||
pagination,
|
||||
fields
|
||||
fields,
|
||||
pagination
|
||||
);
|
||||
|
||||
if (!resources.Any() && await _libraryManager.Shows.GetOrDefault(identifier.IsSame<Show>()) == null)
|
||||
@ -197,7 +197,7 @@ namespace Kyoo.Core.Api
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the <see cref="Show"/>.</param>
|
||||
/// <param name="sortBy">A key to sort collections by.</param>
|
||||
/// <param name="where">An optional list of filters.</param>
|
||||
/// <param name="filter">An optional list of filters.</param>
|
||||
/// <param name="pagination">The number of collections to return.</param>
|
||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||
/// <returns>A page of collections.</returns>
|
||||
@ -211,15 +211,15 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Page<Collection>>> GetCollections(Identifier identifier,
|
||||
[FromQuery] Sort<Collection> sortBy,
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] Filter<Collection>? filter,
|
||||
[FromQuery] Pagination pagination,
|
||||
[FromQuery] Include<Collection> fields)
|
||||
{
|
||||
ICollection<Collection> resources = await _libraryManager.Collections.GetAll(
|
||||
ApiHelper.ParseWhere(where, identifier.IsContainedIn<Collection, Show>(x => x.Shows!)),
|
||||
Filter.And(filter, identifier.IsContainedIn<Collection, Show>(x => x.Shows!)),
|
||||
sortBy,
|
||||
pagination,
|
||||
fields
|
||||
fields,
|
||||
pagination
|
||||
);
|
||||
|
||||
if (!resources.Any() && await _libraryManager.Shows.GetOrDefault(identifier.IsSame<Show>()) == null)
|
||||
|
@ -541,14 +541,5 @@ namespace Kyoo.Postgresql
|
||||
entry.State = EntityState.Detached;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a case insensitive like operation.
|
||||
/// </summary>
|
||||
/// <param name="query">An accessor to get the item that will be checked.</param>
|
||||
/// <param name="format">The second operator of the like format.</param>
|
||||
/// <typeparam name="T">The type of the item to query</typeparam>
|
||||
/// <returns>An expression representing the like query. It can directly be passed to a where call.</returns>
|
||||
public abstract Expression<Func<T, bool>> Like<T>(Expression<Func<T, string>> query, string format);
|
||||
}
|
||||
}
|
||||
|
@ -139,14 +139,5 @@ namespace Kyoo.Postgresql
|
||||
{
|
||||
return ex.InnerException is PostgresException { SqlState: PostgresErrorCodes.UniqueViolation };
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Expression<Func<T, bool>> Like<T>(Expression<Func<T, string>> query, string format)
|
||||
{
|
||||
MethodInfo iLike = MethodOfUtils.MethodOf<string, string, bool>(EF.Functions.ILike);
|
||||
MethodCallExpression call = Expression.Call(iLike, Expression.Constant(EF.Functions), query.Body, Expression.Constant(format));
|
||||
|
||||
return Expression.Lambda<Func<T, bool>>(call, query.Parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,21 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
|
@ -89,7 +89,7 @@ namespace Kyoo.Tests.Database
|
||||
}
|
||||
|
||||
public IRepository<T> GetRepository<T>()
|
||||
where T : class, IResource
|
||||
where T : class, IResource, IQuery
|
||||
{
|
||||
return _repositories.First(x => x.RepositoryType == typeof(T)) as IRepository<T>;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ using Xunit;
|
||||
namespace Kyoo.Tests.Database
|
||||
{
|
||||
public abstract class RepositoryTests<T> : IDisposable, IAsyncDisposable
|
||||
where T : class, IResource
|
||||
where T : class, IResource, IQuery
|
||||
{
|
||||
protected readonly RepositoryActivator Repositories;
|
||||
private readonly IRepository<T> _repository;
|
||||
|
Loading…
x
Reference in New Issue
Block a user