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>
|
/// <summary>
|
||||||
/// Get the first resource that match the predicate.
|
/// Get the first resource that match the predicate.
|
||||||
/// </summary>
|
/// </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="include">The related fields to include.</param>
|
||||||
/// <exception cref="ItemNotFoundException">If the item could not be found.</exception>
|
/// <exception cref="ItemNotFoundException">If the item could not be found.</exception>
|
||||||
/// <returns>The resource found</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Get a resource from it's ID or null if it is not found.
|
/// Get a resource from it's ID or null if it is not found.
|
||||||
@ -86,11 +86,11 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the first resource that match the predicate or null if it is not found.
|
/// Get the first resource that match the predicate or null if it is not found.
|
||||||
/// </summary>
|
/// </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="include">The related fields to include.</param>
|
||||||
/// <param name="sortBy">A custom sort method to handle cases where multiples items match the filters.</param>
|
/// <param name="sortBy">A custom sort method to handle cases where multiples items match the filters.</param>
|
||||||
/// <returns>The resource found</returns>
|
/// <returns>The resource found</returns>
|
||||||
Task<T?> GetOrDefault(Expression<Func<T, bool>> where,
|
Task<T?> GetOrDefault(Filter<T>? filter,
|
||||||
Include<T>? include = default,
|
Include<T>? include = default,
|
||||||
Sort<T>? sortBy = default);
|
Sort<T>? sortBy = default);
|
||||||
|
|
||||||
@ -105,22 +105,22 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get every resources that match all filters
|
/// Get every resources that match all filters
|
||||||
/// </summary>
|
/// </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="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="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>
|
/// <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,
|
Sort<T>? sort = default,
|
||||||
Pagination? limit = default,
|
Include<T>? include = default,
|
||||||
Include<T>? include = default);
|
Pagination limit = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the number of resources that match the filter's predicate.
|
/// Get the number of resources that match the filter's predicate.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="where">A filter predicate</param>
|
/// <param name="filter">A filter predicate</param>
|
||||||
/// <returns>How many resources matched that filter</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Map a list of ids to a list of items (keep the order).
|
/// Map a list of ids to a list of items (keep the order).
|
||||||
@ -217,9 +217,9 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delete all resources that match the predicate.
|
/// Delete all resources that match the predicate.
|
||||||
/// </summary>
|
/// </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>
|
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||||
Task DeleteAll(Expression<Func<T, bool>> where);
|
Task DeleteAll(Filter<T> filter);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when a resource has been edited.
|
/// 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)
|
/// identifier.Matcher<Season>(x => x.ShowID, x => x.Show.Slug)
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </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)
|
Expression<Func<T, string>> slugGetter)
|
||||||
{
|
{
|
||||||
ConstantExpression self = Expression.Constant(_id.HasValue ? _id.Value : _slug);
|
ConstantExpression self = Expression.Constant(_id.HasValue ? _id.Value : _slug);
|
||||||
BinaryExpression equal = Expression.Equal(_id.HasValue ? idGetter.Body : slugGetter.Body, self);
|
BinaryExpression equal = Expression.Equal(_id.HasValue ? idGetter.Body : slugGetter.Body, self);
|
||||||
ICollection<ParameterExpression> parameters = _id.HasValue ? idGetter.Parameters : slugGetter.Parameters;
|
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>
|
/// <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>
|
/// <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>
|
/// <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>
|
/// <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)
|
Expression<Func<T, string>> slugGetter)
|
||||||
{
|
{
|
||||||
ConstantExpression self = Expression.Constant(_id.HasValue ? _id.Value : _slug);
|
ConstantExpression self = Expression.Constant(_id.HasValue ? _id.Value : _slug);
|
||||||
BinaryExpression equal = Expression.Equal(_id.HasValue ? idGetter.Body : slugGetter.Body, self);
|
BinaryExpression equal = Expression.Equal(_id.HasValue ? idGetter.Body : slugGetter.Body, self);
|
||||||
ICollection<ParameterExpression> parameters = _id.HasValue ? idGetter.Parameters : slugGetter.Parameters;
|
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>
|
/// <summary>
|
||||||
@ -142,13 +144,21 @@ namespace Kyoo.Abstractions.Models.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of resource to match against.</typeparam>
|
/// <typeparam name="T">The type of resource to match against.</typeparam>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if the given resource match this identifier, <c>false</c> otherwise.
|
/// <c>true</c> if the given resource match this identifier, <c>false</c> otherwise.
|
||||||
/// </returns>
|
/// </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
|
where T : IResource
|
||||||
{
|
{
|
||||||
return _id.HasValue
|
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="T">The type that contain the list to check.</typeparam>
|
||||||
/// <typeparam name="T2">The type of resource to check this identifier against.</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>
|
/// <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
|
where T2 : IResource
|
||||||
{
|
{
|
||||||
MethodInfo method = typeof(Enumerable)
|
MethodInfo method = typeof(Enumerable)
|
||||||
@ -171,8 +181,9 @@ namespace Kyoo.Abstractions.Models.Utils
|
|||||||
.Where(x => x.Name == nameof(Enumerable.Any))
|
.Where(x => x.Name == nameof(Enumerable.Any))
|
||||||
.FirstOrDefault(x => x.GetParameters().Length == 2)!
|
.FirstOrDefault(x => x.GetParameters().Length == 2)!
|
||||||
.MakeGenericMethod(typeof(T2));
|
.MakeGenericMethod(typeof(T2));
|
||||||
MethodCallExpression call = Expression.Call(null, method, listGetter.Body, IsSame<T2>());
|
MethodCallExpression call = Expression.Call(null, method, listGetter.Body, _IsSameExpression<T2>());
|
||||||
return Expression.Lambda<Func<T, bool>>(call, listGetter.Parameters);
|
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, listGetter.Parameters);
|
||||||
|
return new Filter<T>.Lambda(lambda);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -21,7 +21,7 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Information about the pagination. How many items should be displayed and where to start.
|
/// Information about the pagination. How many items should be displayed and where to start.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Pagination
|
public struct Pagination
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The count of items to return.
|
/// 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.Empty;
|
||||||
return "?" + string.Join('&', query.Select(x => $"{x.Key}={x.Value}"));
|
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))]
|
[ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))]
|
||||||
public async Task<ActionResult<JwtToken>> Login([FromBody] LoginRequest request)
|
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))
|
if (user == null || !BCryptNet.Verify(request.Password, user.Password))
|
||||||
return Forbid(new RequestError("The user and password does not match."));
|
return Forbid(new RequestError("The user and password does not match."));
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ namespace Kyoo.Authentication.Views
|
|||||||
User user = request.ToUser();
|
User user = request.ToUser();
|
||||||
user.Permissions = _permissions.NewUser;
|
user.Permissions = _permissions.NewUser;
|
||||||
// If no users exists, the new one will be an admin. Give it every permissions.
|
// 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;
|
user.Permissions = PermissionOption.Admin;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -53,7 +53,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
public override async Task<ICollection<Collection>> Search(string query, Include<Collection>? include = default)
|
public override async Task<ICollection<Collection>> Search(string query, Include<Collection>? include = default)
|
||||||
{
|
{
|
||||||
return await AddIncludes(_database.Collections, include)
|
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)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
public override async Task<ICollection<Episode>> Search(string query, Include<Episode>? include = default)
|
public override async Task<ICollection<Episode>> Search(string query, Include<Episode>? include = default)
|
||||||
{
|
{
|
||||||
return await AddIncludes(_database.Episodes, include)
|
return await AddIncludes(_database.Episodes, include)
|
||||||
.Where(_database.Like<Episode>(x => x.Name!, $"%{query}%"))
|
.Where(x => EF.Functions.ILike(x.Name!, $"%{query}%"))
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
@ -92,8 +92,8 @@ namespace Kyoo.Core.Controllers
|
|||||||
_database.Entry(obj).State = EntityState.Added;
|
_database.Entry(obj).State = EntityState.Added;
|
||||||
await _database.SaveChangesAsync(() =>
|
await _database.SaveChangesAsync(() =>
|
||||||
obj is { SeasonNumber: not null, EpisodeNumber: not null }
|
obj is { SeasonNumber: not null, EpisodeNumber: not null }
|
||||||
? Get(x => x.ShowId == obj.ShowId && x.SeasonNumber == obj.SeasonNumber && x.EpisodeNumber == obj.EpisodeNumber)
|
? _database.Episodes.FirstOrDefaultAsync(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.AbsoluteNumber == obj.AbsoluteNumber));
|
||||||
await IRepository<Episode>.OnResourceCreated(obj);
|
await IRepository<Episode>.OnResourceCreated(obj);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
@ -29,10 +29,8 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using InterpolatedSql.Dapper;
|
using InterpolatedSql.Dapper;
|
||||||
using InterpolatedSql.SqlBuilders;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
|
||||||
using Kyoo.Abstractions.Models.Exceptions;
|
using Kyoo.Abstractions.Models.Exceptions;
|
||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Kyoo.Utils;
|
using Kyoo.Utils;
|
||||||
@ -72,11 +70,10 @@ namespace Kyoo.Core.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual async Task<ILibraryItem> Get(
|
public virtual async Task<ILibraryItem> Get(Filter<ILibraryItem> filter,
|
||||||
Expression<Func<ILibraryItem, bool>> where,
|
|
||||||
Include<ILibraryItem>? include = default)
|
Include<ILibraryItem>? include = default)
|
||||||
{
|
{
|
||||||
ILibraryItem? ret = await GetOrDefault(where, include: include);
|
ILibraryItem? ret = await GetOrDefault(filter, include: include);
|
||||||
if (ret == null)
|
if (ret == null)
|
||||||
throw new ItemNotFoundException($"No {nameof(ILibraryItem)} found with the given predicate.");
|
throw new ItemNotFoundException($"No {nameof(ILibraryItem)} found with the given predicate.");
|
||||||
return ret;
|
return ret;
|
||||||
@ -92,7 +89,8 @@ namespace Kyoo.Core.Controllers
|
|||||||
throw new NotImplementedException();
|
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();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
@ -108,9 +106,11 @@ namespace Kyoo.Core.Controllers
|
|||||||
return $"coalesce({string.Join(", ", keys)})";
|
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
|
where T : IQuery
|
||||||
{
|
{
|
||||||
|
sort ??= new Sort<T>.Default();
|
||||||
|
|
||||||
string ret = sort switch
|
string ret = sort switch
|
||||||
{
|
{
|
||||||
Sort<T>.Default(var value) => ProcessSort(value, config, true),
|
Sort<T>.Default(var value) => ProcessSort(value, config, true),
|
||||||
@ -188,12 +188,24 @@ namespace Kyoo.Core.Controllers
|
|||||||
return $"{prefix}*" + projStr;
|
return $"{prefix}*" + projStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ICollection<ILibraryItem>> GetAll(
|
public static string ProcessFilter<T>(Filter<T> filter, Dictionary<string, Type> config)
|
||||||
Expression<Func<ILibraryItem, bool>>? where = null,
|
|
||||||
Sort<ILibraryItem>? sort = null,
|
|
||||||
Pagination? limit = null,
|
|
||||||
Include<ILibraryItem>? include = null)
|
|
||||||
{
|
{
|
||||||
|
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()
|
Dictionary<string, Type> config = new()
|
||||||
{
|
{
|
||||||
{ "s", typeof(Show) },
|
{ "s", typeof(Show) },
|
||||||
@ -203,7 +215,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
var (includeConfig, includeJoin, mapIncludes) = ProcessInclude(include, config);
|
var (includeConfig, includeJoin, mapIncludes) = ProcessInclude(include, config);
|
||||||
|
|
||||||
// language=PostgreSQL
|
// language=PostgreSQL
|
||||||
IDapperSqlCommand query = _database.SqlBuilder($"""
|
var query = _database.SqlBuilder($"""
|
||||||
select
|
select
|
||||||
{ExpendProjections<Show>("s", include):raw},
|
{ExpendProjections<Show>("s", include):raw},
|
||||||
m.*,
|
m.*,
|
||||||
@ -224,7 +236,10 @@ namespace Kyoo.Core.Controllers
|
|||||||
{includeJoin:raw}
|
{includeJoin:raw}
|
||||||
order by {ProcessSort(sort, config):raw}
|
order by {ProcessSort(sort, config):raw}
|
||||||
limit {limit.Limit}
|
limit {limit.Limit}
|
||||||
""").Build();
|
""");
|
||||||
|
|
||||||
|
if (filter != null)
|
||||||
|
query += $"where {ProcessFilter(filter, config):raw}";
|
||||||
|
|
||||||
Type[] types = config.Select(x => x.Value)
|
Type[] types = config.Select(x => x.Value)
|
||||||
.Concat(includeConfig.Select(x => x.Value))
|
.Concat(includeConfig.Select(x => x.Value))
|
||||||
@ -242,7 +257,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
return data.ToList();
|
return data.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> GetCount(Expression<Func<ILibraryItem, bool>>? where = null)
|
public Task<int> GetCount(Filter<ILibraryItem>? filter = null)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
@ -252,7 +267,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task DeleteAll(Expression<Func<ILibraryItem, bool>> where)
|
public Task DeleteAll(Filter<ILibraryItem> filter)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,11 @@ namespace Kyoo.Core.Controllers
|
|||||||
return _Sort(query, sortBy, false).ThenBy(x => x.Id);
|
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(
|
private static Func<Expression, Expression, BinaryExpression> _GetComparisonExpression(
|
||||||
bool desc,
|
bool desc,
|
||||||
bool next,
|
bool next,
|
||||||
@ -297,9 +302,9 @@ namespace Kyoo.Core.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <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)
|
if (ret == null)
|
||||||
throw new ItemNotFoundException($"No {typeof(T).Name} found with the given predicate.");
|
throw new ItemNotFoundException($"No {typeof(T).Name} found with the given predicate.");
|
||||||
return ret;
|
return ret;
|
||||||
@ -326,7 +331,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual Task<T?> GetOrDefault(Expression<Func<T, bool>> where,
|
public virtual Task<T?> GetOrDefault(Filter<T>? filter,
|
||||||
Include<T>? include = default,
|
Include<T>? include = default,
|
||||||
Sort<T>? sortBy = default)
|
Sort<T>? sortBy = default)
|
||||||
{
|
{
|
||||||
@ -334,7 +339,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
AddIncludes(Database.Set<T>(), include),
|
AddIncludes(Database.Set<T>(), include),
|
||||||
sortBy
|
sortBy
|
||||||
)
|
)
|
||||||
.FirstOrDefaultAsync(where);
|
.FirstOrDefaultAsync(ParseFilter(filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@ -353,12 +358,12 @@ namespace Kyoo.Core.Controllers
|
|||||||
public abstract Task<ICollection<T>> Search(string query, Include<T>? include = default);
|
public abstract Task<ICollection<T>> Search(string query, Include<T>? include = default);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <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,
|
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>
|
/// <summary>
|
||||||
@ -373,7 +378,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
protected async Task<ICollection<T>> ApplyFilters(IQueryable<T> query,
|
protected async Task<ICollection<T>> ApplyFilters(IQueryable<T> query,
|
||||||
Expression<Func<T, bool>>? where = null,
|
Expression<Func<T, bool>>? where = null,
|
||||||
Sort<T>? sort = default,
|
Sort<T>? sort = default,
|
||||||
Pagination? limit = default,
|
Pagination limit = default,
|
||||||
Include<T>? include = default)
|
Include<T>? include = default)
|
||||||
{
|
{
|
||||||
query = AddIncludes(query, include);
|
query = AddIncludes(query, include);
|
||||||
@ -381,25 +386,25 @@ namespace Kyoo.Core.Controllers
|
|||||||
if (where != null)
|
if (where != null)
|
||||||
query = query.Where(where);
|
query = query.Where(where);
|
||||||
|
|
||||||
if (limit?.AfterID != null)
|
if (limit.AfterID != null)
|
||||||
{
|
{
|
||||||
T reference = await Get(limit.AfterID.Value);
|
T reference = await Get(limit.AfterID.Value);
|
||||||
query = query.Where(KeysetPaginate(sort, reference, !limit.Reverse));
|
query = query.Where(KeysetPaginate(sort, reference, !limit.Reverse));
|
||||||
}
|
}
|
||||||
if (limit?.Reverse == true)
|
if (limit.Reverse)
|
||||||
query = query.Reverse();
|
query = query.Reverse();
|
||||||
if (limit?.Limit > 0)
|
if (limit.Limit > 0)
|
||||||
query = query.Take(limit.Limit);
|
query = query.Take(limit.Limit);
|
||||||
|
|
||||||
return await query.ToListAsync();
|
return await query.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <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>();
|
IQueryable<T> query = Database.Set<T>();
|
||||||
if (where != null)
|
if (filter != null)
|
||||||
query = query.Where(where);
|
query = query.Where(ParseFilter(filter));
|
||||||
return query.CountAsync();
|
return query.CountAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -559,9 +564,9 @@ namespace Kyoo.Core.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <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);
|
await Delete(resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
public override async Task<ICollection<Movie>> Search(string query, Include<Movie>? include = default)
|
public override async Task<ICollection<Movie>> Search(string query, Include<Movie>? include = default)
|
||||||
{
|
{
|
||||||
return await AddIncludes(_database.Movies, include)
|
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)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
public override async Task<ICollection<People>> Search(string query, Include<People>? include = default)
|
public override async Task<ICollection<People>> Search(string query, Include<People>? include = default)
|
||||||
{
|
{
|
||||||
return await AddIncludes(_database.People, include)
|
return await AddIncludes(_database.People, include)
|
||||||
.Where(_database.Like<People>(x => x.Name, $"%{query}%"))
|
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
public override async Task<ICollection<Season>> Search(string query, Include<Season>? include = default)
|
public override async Task<ICollection<Season>> Search(string query, Include<Season>? include = default)
|
||||||
{
|
{
|
||||||
return await AddIncludes(_database.Seasons, include)
|
return await AddIncludes(_database.Seasons, include)
|
||||||
.Where(_database.Like<Season>(x => x.Name!, $"%{query}%"))
|
.Where(x => EF.Functions.ILike(x.Name!, $"%{query}%"))
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
@ -85,7 +85,9 @@ namespace Kyoo.Core.Controllers
|
|||||||
await base.Create(obj);
|
await base.Create(obj);
|
||||||
obj.ShowSlug = _database.Shows.First(x => x.Id == obj.ShowId).Slug;
|
obj.ShowSlug = _database.Shows.First(x => x.Id == obj.ShowId).Slug;
|
||||||
_database.Entry(obj).State = EntityState.Added;
|
_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);
|
await IRepository<Season>.OnResourceCreated(obj);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
public override async Task<ICollection<Show>> Search(string query, Include<Show>? include = default)
|
public override async Task<ICollection<Show>> Search(string query, Include<Show>? include = default)
|
||||||
{
|
{
|
||||||
return await AddIncludes(_database.Shows, include)
|
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)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
@ -88,8 +88,6 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override async Task Validate(Show resource)
|
protected override async Task Validate(Show resource)
|
||||||
{
|
{
|
||||||
resource.Slug ??= Utility.ToSlug(resource.Name);
|
|
||||||
|
|
||||||
await base.Validate(resource);
|
await base.Validate(resource);
|
||||||
if (resource.Studio != null)
|
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)
|
public override async Task<ICollection<Studio>> Search(string query, Include<Studio>? include = default)
|
||||||
{
|
{
|
||||||
return await AddIncludes(_database.Studios, include)
|
return await AddIncludes(_database.Studios, include)
|
||||||
.Where(_database.Like<Studio>(x => x.Name, $"%{query}%"))
|
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
@ -68,13 +68,6 @@ namespace Kyoo.Core.Controllers
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override async Task Validate(Studio resource)
|
|
||||||
{
|
|
||||||
resource.Slug ??= Utility.ToSlug(resource.Name);
|
|
||||||
await base.Validate(resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task Delete(Studio obj)
|
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)
|
public override async Task<ICollection<User>> Search(string query, Include<User>? include = default)
|
||||||
{
|
{
|
||||||
return await AddIncludes(_database.Users, include)
|
return await AddIncludes(_database.Users, include)
|
||||||
.Where(_database.Like<User>(x => x.Username, $"%{query}%"))
|
.Where(x => EF.Functions.ILike(x.Username, $"%{query}%"))
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
@ -86,16 +85,16 @@ namespace Kyoo.Core.Api
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Get the number of resources that match the filters.
|
/// Get the number of resources that match the filters.
|
||||||
/// </remarks>
|
/// </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>
|
/// <returns>How many resources matched that filter.</returns>
|
||||||
/// <response code="400">Invalid filters.</response>
|
/// <response code="400">Invalid filters.</response>
|
||||||
[HttpGet("count")]
|
[HttpGet("count")]
|
||||||
[PartialPermission(Kind.Read)]
|
[PartialPermission(Kind.Read)]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
[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>
|
/// <summary>
|
||||||
@ -105,7 +104,7 @@ namespace Kyoo.Core.Api
|
|||||||
/// Get all resources that match the given filter.
|
/// Get all resources that match the given filter.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="sortBy">Sort information about the query (sort by, sort order).</param>
|
/// <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="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>
|
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||||
/// <returns>A list of resources that match every filters.</returns>
|
/// <returns>A list of resources that match every filters.</returns>
|
||||||
@ -116,15 +115,15 @@ namespace Kyoo.Core.Api
|
|||||||
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
||||||
public async Task<ActionResult<Page<T>>> GetAll(
|
public async Task<ActionResult<Page<T>>> GetAll(
|
||||||
[FromQuery] Sort<T> sortBy,
|
[FromQuery] Sort<T> sortBy,
|
||||||
[FromQuery] Dictionary<string, string> where,
|
[FromQuery] Filter<T>? filter,
|
||||||
[FromQuery] Pagination pagination,
|
[FromQuery] Pagination pagination,
|
||||||
[FromQuery] Include<T>? fields)
|
[FromQuery] Include<T>? fields)
|
||||||
{
|
{
|
||||||
ICollection<T> resources = await Repository.GetAll(
|
ICollection<T> resources = await Repository.GetAll(
|
||||||
ApiHelper.ParseWhere<T>(where),
|
filter,
|
||||||
sortBy,
|
sortBy,
|
||||||
pagination,
|
fields,
|
||||||
fields
|
pagination
|
||||||
);
|
);
|
||||||
|
|
||||||
return Page(resources, pagination.Limit);
|
return Page(resources, pagination.Limit);
|
||||||
@ -231,20 +230,19 @@ namespace Kyoo.Core.Api
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Delete all items matching the given filters. If no filter is specified, delete all items.
|
/// Delete all items matching the given filters. If no filter is specified, delete all items.
|
||||||
/// </remarks>
|
/// </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>
|
/// <returns>The item(s) has successfully been deleted.</returns>
|
||||||
/// <response code="400">One or multiple filters are invalid.</response>
|
/// <response code="400">One or multiple filters are invalid.</response>
|
||||||
[HttpDelete]
|
[HttpDelete]
|
||||||
[PartialPermission(Kind.Delete)]
|
[PartialPermission(Kind.Delete)]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
[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 (filter == null)
|
||||||
if (w == null)
|
|
||||||
return BadRequest(new RequestError("Incule a filter to delete items, all items won't be deleted."));
|
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();
|
return NoContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ namespace Kyoo.Core.Api
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="identifier">The ID or slug of the <see cref="Studio"/>.</param>
|
/// <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="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="pagination">The number of shows to return.</param>
|
||||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||||
/// <returns>A page of shows.</returns>
|
/// <returns>A page of shows.</returns>
|
||||||
@ -79,15 +79,15 @@ namespace Kyoo.Core.Api
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<Page<Show>>> GetShows(Identifier identifier,
|
public async Task<ActionResult<Page<Show>>> GetShows(Identifier identifier,
|
||||||
[FromQuery] Sort<Show> sortBy,
|
[FromQuery] Sort<Show> sortBy,
|
||||||
[FromQuery] Dictionary<string, string> where,
|
[FromQuery] Filter<Show>? filter,
|
||||||
[FromQuery] Pagination pagination,
|
[FromQuery] Pagination pagination,
|
||||||
[FromQuery] Include<Show> fields)
|
[FromQuery] Include<Show> fields)
|
||||||
{
|
{
|
||||||
ICollection<Show> resources = await _libraryManager.Shows.GetAll(
|
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,
|
sortBy,
|
||||||
pagination,
|
fields,
|
||||||
fields
|
pagination
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!resources.Any() && await _libraryManager.Studios.GetOrDefault(identifier.IsSame<Studio>()) == null)
|
if (!resources.Any() && await _libraryManager.Studios.GetOrDefault(identifier.IsSame<Studio>()) == null)
|
||||||
|
@ -126,7 +126,7 @@ namespace Kyoo.Core.Api
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="identifier">The ID or slug of the <see cref="Collection"/>.</param>
|
/// <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="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="pagination">The number of items to return.</param>
|
||||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||||
/// <returns>A page of items.</returns>
|
/// <returns>A page of items.</returns>
|
||||||
@ -140,21 +140,21 @@ namespace Kyoo.Core.Api
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<Page<ILibraryItem>>> GetItems(Identifier identifier,
|
public async Task<ActionResult<Page<ILibraryItem>>> GetItems(Identifier identifier,
|
||||||
[FromQuery] Sort<ILibraryItem> sortBy,
|
[FromQuery] Sort<ILibraryItem> sortBy,
|
||||||
[FromQuery] Dictionary<string, string> where,
|
[FromQuery] Filter<ILibraryItem>? filter,
|
||||||
[FromQuery] Pagination pagination,
|
[FromQuery] Pagination pagination,
|
||||||
[FromQuery] Include<ILibraryItem>? fields)
|
[FromQuery] Include<ILibraryItem>? fields)
|
||||||
{
|
{
|
||||||
ICollection<ILibraryItem> resources = await _items.GetAllOfCollection(
|
// ICollection<ILibraryItem> resources = await _items.GetAllOfCollection(
|
||||||
identifier.IsSame<Collection>(),
|
// identifier.IsSame<Collection>(),
|
||||||
ApiHelper.ParseWhere<ILibraryItem>(where),
|
// filter,
|
||||||
sortBy == new Sort<ILibraryItem>.Default() ? new Sort<ILibraryItem>.By(nameof(Movie.AirDate)) : sortBy,
|
// sortBy == new Sort<ILibraryItem>.Default() ? new Sort<ILibraryItem>.By(nameof(Movie.AirDate)) : sortBy,
|
||||||
pagination,
|
// pagination,
|
||||||
fields
|
// 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 NotFound();
|
||||||
return Page(resources, pagination.Limit);
|
// return Page(resources, pagination.Limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -165,9 +165,9 @@ namespace Kyoo.Core.Api
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="identifier">The ID or slug of the <see cref="Collection"/>.</param>
|
/// <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="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="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>
|
/// <returns>A page of shows.</returns>
|
||||||
/// <response code="400">The filters or the sort parameters are invalid.</response>
|
/// <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>
|
/// <response code="404">No collection with the given ID could be found.</response>
|
||||||
@ -179,15 +179,15 @@ namespace Kyoo.Core.Api
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<Page<Show>>> GetShows(Identifier identifier,
|
public async Task<ActionResult<Page<Show>>> GetShows(Identifier identifier,
|
||||||
[FromQuery] Sort<Show> sortBy,
|
[FromQuery] Sort<Show> sortBy,
|
||||||
[FromQuery] Dictionary<string, string> where,
|
[FromQuery] Filter<Show>? filter,
|
||||||
[FromQuery] Pagination pagination,
|
[FromQuery] Pagination pagination,
|
||||||
[FromQuery] Include<Show>? fields)
|
[FromQuery] Include<Show>? fields)
|
||||||
{
|
{
|
||||||
ICollection<Show> resources = await _libraryManager.Shows.GetAll(
|
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,
|
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)
|
if (!resources.Any() && await _libraryManager.Collections.GetOrDefault(identifier.IsSame<Collection>()) == null)
|
||||||
@ -203,7 +203,7 @@ namespace Kyoo.Core.Api
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="identifier">The ID or slug of the <see cref="Collection"/>.</param>
|
/// <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="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="pagination">The number of movies to return.</param>
|
||||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||||
/// <returns>A page of movies.</returns>
|
/// <returns>A page of movies.</returns>
|
||||||
@ -217,15 +217,15 @@ namespace Kyoo.Core.Api
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<Page<Movie>>> GetMovies(Identifier identifier,
|
public async Task<ActionResult<Page<Movie>>> GetMovies(Identifier identifier,
|
||||||
[FromQuery] Sort<Movie> sortBy,
|
[FromQuery] Sort<Movie> sortBy,
|
||||||
[FromQuery] Dictionary<string, string> where,
|
[FromQuery] Filter<Movie>? filter,
|
||||||
[FromQuery] Pagination pagination,
|
[FromQuery] Pagination pagination,
|
||||||
[FromQuery] Include<Movie>? fields)
|
[FromQuery] Include<Movie>? fields)
|
||||||
{
|
{
|
||||||
ICollection<Movie> resources = await _libraryManager.Movies.GetAll(
|
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,
|
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)
|
if (!resources.Any() && await _libraryManager.Collections.GetOrDefault(identifier.IsSame<Collection>()) == null)
|
||||||
|
@ -120,7 +120,7 @@ namespace Kyoo.Core.Api
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="identifier">The ID or slug of the <see cref="Show"/>.</param>
|
/// <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="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="pagination">The number of collections to return.</param>
|
||||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||||
/// <returns>A page of collections.</returns>
|
/// <returns>A page of collections.</returns>
|
||||||
@ -134,15 +134,15 @@ namespace Kyoo.Core.Api
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<Page<Collection>>> GetCollections(Identifier identifier,
|
public async Task<ActionResult<Page<Collection>>> GetCollections(Identifier identifier,
|
||||||
[FromQuery] Sort<Collection> sortBy,
|
[FromQuery] Sort<Collection> sortBy,
|
||||||
[FromQuery] Dictionary<string, string> where,
|
[FromQuery] Filter<Collection>? filter,
|
||||||
[FromQuery] Pagination pagination,
|
[FromQuery] Pagination pagination,
|
||||||
[FromQuery] Include<Collection> fields)
|
[FromQuery] Include<Collection> fields)
|
||||||
{
|
{
|
||||||
ICollection<Collection> resources = await _libraryManager.Collections.GetAll(
|
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,
|
sortBy,
|
||||||
pagination,
|
fields,
|
||||||
fields
|
pagination
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!resources.Any() && await _libraryManager.Movies.GetOrDefault(identifier.IsSame<Movie>()) == null)
|
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
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// 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.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Core.Controllers;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using static Kyoo.Abstractions.Models.Utils.Constants;
|
using static Kyoo.Abstractions.Models.Utils.Constants;
|
||||||
|
|
||||||
@ -37,25 +34,10 @@ namespace Kyoo.Core.Api
|
|||||||
[ResourceView]
|
[ResourceView]
|
||||||
[PartialPermission("LibraryItem")]
|
[PartialPermission("LibraryItem")]
|
||||||
[ApiDefinition("News", Group = ResourcesGroup)]
|
[ApiDefinition("News", Group = ResourcesGroup)]
|
||||||
public class NewsApi : BaseApi
|
public class NewsApi : CrudThumbsApi<News>
|
||||||
{
|
{
|
||||||
private readonly NewsRepository _news;
|
public NewsApi(IRepository<News> news, IThumbnailsManager thumbs)
|
||||||
|
: base(news, thumbs)
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ namespace Kyoo.Core.Api
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="identifier">The ID or slug of the <see cref="Season"/>.</param>
|
/// <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="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="pagination">The number of episodes to return.</param>
|
||||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||||
/// <returns>A page of episodes.</returns>
|
/// <returns>A page of episodes.</returns>
|
||||||
@ -81,15 +81,15 @@ namespace Kyoo.Core.Api
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<Page<Episode>>> GetEpisode(Identifier identifier,
|
public async Task<ActionResult<Page<Episode>>> GetEpisode(Identifier identifier,
|
||||||
[FromQuery] Sort<Episode> sortBy,
|
[FromQuery] Sort<Episode> sortBy,
|
||||||
[FromQuery] Dictionary<string, string> where,
|
[FromQuery] Filter<Episode>? filter,
|
||||||
[FromQuery] Pagination pagination,
|
[FromQuery] Pagination pagination,
|
||||||
[FromQuery] Include<Episode> fields)
|
[FromQuery] Include<Episode> fields)
|
||||||
{
|
{
|
||||||
ICollection<Episode> resources = await _libraryManager.Episodes.GetAll(
|
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,
|
sortBy,
|
||||||
pagination,
|
fields,
|
||||||
fields
|
pagination
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!resources.Any() && await _libraryManager.Seasons.GetOrDefault(identifier.IsSame<Season>()) == null)
|
if (!resources.Any() && await _libraryManager.Seasons.GetOrDefault(identifier.IsSame<Season>()) == null)
|
||||||
|
@ -67,7 +67,7 @@ namespace Kyoo.Core.Api
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="identifier">The ID or slug of the <see cref="Show"/>.</param>
|
/// <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="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="pagination">The number of seasons to return.</param>
|
||||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||||
/// <returns>A page of seasons.</returns>
|
/// <returns>A page of seasons.</returns>
|
||||||
@ -81,15 +81,15 @@ namespace Kyoo.Core.Api
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<Page<Season>>> GetSeasons(Identifier identifier,
|
public async Task<ActionResult<Page<Season>>> GetSeasons(Identifier identifier,
|
||||||
[FromQuery] Sort<Season> sortBy,
|
[FromQuery] Sort<Season> sortBy,
|
||||||
[FromQuery] Dictionary<string, string> where,
|
[FromQuery] Filter<Season>? filter,
|
||||||
[FromQuery] Pagination pagination,
|
[FromQuery] Pagination pagination,
|
||||||
[FromQuery] Include<Season> fields)
|
[FromQuery] Include<Season> fields)
|
||||||
{
|
{
|
||||||
ICollection<Season> resources = await _libraryManager.Seasons.GetAll(
|
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,
|
sortBy,
|
||||||
pagination,
|
fields,
|
||||||
fields
|
pagination
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!resources.Any() && await _libraryManager.Shows.GetOrDefault(identifier.IsSame<Show>()) == null)
|
if (!resources.Any() && await _libraryManager.Shows.GetOrDefault(identifier.IsSame<Show>()) == null)
|
||||||
@ -105,7 +105,7 @@ namespace Kyoo.Core.Api
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="identifier">The ID or slug of the <see cref="Show"/>.</param>
|
/// <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="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="pagination">The number of episodes to return.</param>
|
||||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||||
/// <returns>A page of episodes.</returns>
|
/// <returns>A page of episodes.</returns>
|
||||||
@ -119,15 +119,15 @@ namespace Kyoo.Core.Api
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<Page<Episode>>> GetEpisodes(Identifier identifier,
|
public async Task<ActionResult<Page<Episode>>> GetEpisodes(Identifier identifier,
|
||||||
[FromQuery] Sort<Episode> sortBy,
|
[FromQuery] Sort<Episode> sortBy,
|
||||||
[FromQuery] Dictionary<string, string> where,
|
[FromQuery] Filter<Episode>? filter,
|
||||||
[FromQuery] Pagination pagination,
|
[FromQuery] Pagination pagination,
|
||||||
[FromQuery] Include<Episode> fields)
|
[FromQuery] Include<Episode> fields)
|
||||||
{
|
{
|
||||||
ICollection<Episode> resources = await _libraryManager.Episodes.GetAll(
|
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,
|
sortBy,
|
||||||
pagination,
|
fields,
|
||||||
fields
|
pagination
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!resources.Any() && await _libraryManager.Shows.GetOrDefault(identifier.IsSame<Show>()) == null)
|
if (!resources.Any() && await _libraryManager.Shows.GetOrDefault(identifier.IsSame<Show>()) == null)
|
||||||
@ -197,7 +197,7 @@ namespace Kyoo.Core.Api
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="identifier">The ID or slug of the <see cref="Show"/>.</param>
|
/// <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="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="pagination">The number of collections to return.</param>
|
||||||
/// <param name="fields">The aditional fields to include in the result.</param>
|
/// <param name="fields">The aditional fields to include in the result.</param>
|
||||||
/// <returns>A page of collections.</returns>
|
/// <returns>A page of collections.</returns>
|
||||||
@ -211,15 +211,15 @@ namespace Kyoo.Core.Api
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<Page<Collection>>> GetCollections(Identifier identifier,
|
public async Task<ActionResult<Page<Collection>>> GetCollections(Identifier identifier,
|
||||||
[FromQuery] Sort<Collection> sortBy,
|
[FromQuery] Sort<Collection> sortBy,
|
||||||
[FromQuery] Dictionary<string, string> where,
|
[FromQuery] Filter<Collection>? filter,
|
||||||
[FromQuery] Pagination pagination,
|
[FromQuery] Pagination pagination,
|
||||||
[FromQuery] Include<Collection> fields)
|
[FromQuery] Include<Collection> fields)
|
||||||
{
|
{
|
||||||
ICollection<Collection> resources = await _libraryManager.Collections.GetAll(
|
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,
|
sortBy,
|
||||||
pagination,
|
fields,
|
||||||
fields
|
pagination
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!resources.Any() && await _libraryManager.Shows.GetOrDefault(identifier.IsSame<Show>()) == null)
|
if (!resources.Any() && await _libraryManager.Shows.GetOrDefault(identifier.IsSame<Show>()) == null)
|
||||||
|
@ -541,14 +541,5 @@ namespace Kyoo.Postgresql
|
|||||||
entry.State = EntityState.Detached;
|
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 };
|
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.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -89,7 +89,7 @@ namespace Kyoo.Tests.Database
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IRepository<T> GetRepository<T>()
|
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>;
|
return _repositories.First(x => x.RepositoryType == typeof(T)) as IRepository<T>;
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ using Xunit;
|
|||||||
namespace Kyoo.Tests.Database
|
namespace Kyoo.Tests.Database
|
||||||
{
|
{
|
||||||
public abstract class RepositoryTests<T> : IDisposable, IAsyncDisposable
|
public abstract class RepositoryTests<T> : IDisposable, IAsyncDisposable
|
||||||
where T : class, IResource
|
where T : class, IResource, IQuery
|
||||||
{
|
{
|
||||||
protected readonly RepositoryActivator Repositories;
|
protected readonly RepositoryActivator Repositories;
|
||||||
private readonly IRepository<T> _repository;
|
private readonly IRepository<T> _repository;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user