diff --git a/back/src/Kyoo.Abstractions/Controllers/IRepository.cs b/back/src/Kyoo.Abstractions/Controllers/IRepository.cs
index c4303b99..e3fea697 100644
--- a/back/src/Kyoo.Abstractions/Controllers/IRepository.cs
+++ b/back/src/Kyoo.Abstractions/Controllers/IRepository.cs
@@ -61,11 +61,11 @@ namespace Kyoo.Abstractions.Controllers
///
/// Get the first resource that match the predicate.
///
- /// A predicate to filter the resource.
+ /// A predicate to filter the resource.
/// The related fields to include.
/// If the item could not be found.
/// The resource found
- Task Get(Expression> where, Include? include = default);
+ Task Get(Filter filter, Include? include = default);
///
/// Get a resource from it's ID or null if it is not found.
@@ -86,11 +86,11 @@ namespace Kyoo.Abstractions.Controllers
///
/// Get the first resource that match the predicate or null if it is not found.
///
- /// A predicate to filter the resource.
+ /// A predicate to filter the resource.
/// The related fields to include.
/// A custom sort method to handle cases where multiples items match the filters.
/// The resource found
- Task GetOrDefault(Expression> where,
+ Task GetOrDefault(Filter? filter,
Include? include = default,
Sort? sortBy = default);
@@ -105,22 +105,22 @@ namespace Kyoo.Abstractions.Controllers
///
/// Get every resources that match all filters
///
- /// A filter predicate
+ /// A filter predicate
/// Sort information about the query (sort by, sort order)
- /// How pagination should be done (where to start and how many to return)
/// The related fields to include.
+ /// How pagination should be done (where to start and how many to return)
/// A list of resources that match every filters
- Task> GetAll(Expression>? where = null,
+ Task> GetAll(Filter? filter = null,
Sort? sort = default,
- Pagination? limit = default,
- Include? include = default);
+ Include? include = default,
+ Pagination limit = default);
///
/// Get the number of resources that match the filter's predicate.
///
- /// A filter predicate
+ /// A filter predicate
/// How many resources matched that filter
- Task GetCount(Expression>? where = null);
+ Task GetCount(Filter? filter = null);
///
/// Map a list of ids to a list of items (keep the order).
@@ -217,9 +217,9 @@ namespace Kyoo.Abstractions.Controllers
///
/// Delete all resources that match the predicate.
///
- /// A predicate to filter resources to delete. Every resource that match this will be deleted.
+ /// A predicate to filter resources to delete. Every resource that match this will be deleted.
/// A representing the asynchronous operation.
- Task DeleteAll(Expression> where);
+ Task DeleteAll(Filter filter);
///
/// Called when a resource has been edited.
diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Filter.cs b/back/src/Kyoo.Abstractions/Models/Utils/Filter.cs
new file mode 100644
index 00000000..dee6cbed
--- /dev/null
+++ b/back/src/Kyoo.Abstractions/Models/Utils/Filter.cs
@@ -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 .
+
+using System;
+using System.Linq;
+using System.Linq.Expressions;
+namespace Kyoo.Abstractions.Models.Utils;
+
+public abstract record Filter
+{
+ public static Filter? And(params Filter?[] filters)
+ {
+ return filters
+ .Where(x => x != null)
+ .Aggregate((Filter?)null, (acc, filter) =>
+ {
+ if (acc == null)
+ return filter;
+ return new Filter.And(acc, filter!);
+ });
+ }
+}
+
+public abstract record Filter : Filter
+{
+ public record And(Filter first, Filter second) : Filter;
+
+ public record Or(Filter first, Filter second) : Filter;
+
+ public record Not(Filter filter) : Filter;
+
+ public record Eq(string property, object value) : Filter;
+
+ public record Ne(string property, T2 value) : Filter;
+
+ public record Gt(string property, T2 value) : Filter;
+
+ public record Ge(string property, T2 value) : Filter;
+
+ public record Lt(string property, T2 value) : Filter;
+
+ public record Le(string property, T2 value) : Filter;
+
+ public record Has(string property, T2 value) : Filter;
+
+ public record In(string property, object[] value) : Filter;
+
+ public record Lambda(Expression> lambda) : Filter;
+
+ public static Filter From(string filter)
+ {
+
+ }
+}
diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Identifier.cs b/back/src/Kyoo.Abstractions/Models/Utils/Identifier.cs
index c0a1cbdc..90ce62a5 100644
--- a/back/src/Kyoo.Abstractions/Models/Utils/Identifier.cs
+++ b/back/src/Kyoo.Abstractions/Models/Utils/Identifier.cs
@@ -99,13 +99,14 @@ namespace Kyoo.Abstractions.Models.Utils
/// identifier.Matcher<Season>(x => x.ShowID, x => x.Show.Slug)
///
///
- public Expression> Matcher(Expression> idGetter,
+ public Filter Matcher(Expression> idGetter,
Expression> slugGetter)
{
ConstantExpression self = Expression.Constant(_id.HasValue ? _id.Value : _slug);
BinaryExpression equal = Expression.Equal(_id.HasValue ? idGetter.Body : slugGetter.Body, self);
ICollection parameters = _id.HasValue ? idGetter.Parameters : slugGetter.Parameters;
- return Expression.Lambda>(equal, parameters);
+ Expression> lambda = Expression.Lambda>(equal, parameters);
+ return new Filter.Lambda(lambda);
}
///
@@ -117,13 +118,14 @@ namespace Kyoo.Abstractions.Models.Utils
/// An expression to retrieve a slug from the type .
/// The type to match against this identifier.
/// An expression to match the type to this identifier.
- public Expression> Matcher(Expression> idGetter,
+ public Filter Matcher(Expression> idGetter,
Expression> slugGetter)
{
ConstantExpression self = Expression.Constant(_id.HasValue ? _id.Value : _slug);
BinaryExpression equal = Expression.Equal(_id.HasValue ? idGetter.Body : slugGetter.Body, self);
ICollection parameters = _id.HasValue ? idGetter.Parameters : slugGetter.Parameters;
- return Expression.Lambda>(equal, parameters);
+ Expression> lambda = Expression.Lambda>(equal, parameters);
+ return new Filter.Lambda(lambda);
}
///
@@ -142,13 +144,21 @@ namespace Kyoo.Abstractions.Models.Utils
}
///
- /// Return an expression that return true if this match a given resource.
+ /// Return a filter to get this match a given resource.
///
/// The type of resource to match against.
///
/// true if the given resource match this identifier, false otherwise.
///
- public Expression> IsSame()
+ public Filter IsSame()
+ where T : IResource
+ {
+ return _id.HasValue
+ ? new Filter.Eq("Id", _id.Value)
+ : new Filter.Eq("Slug", _slug!);
+ }
+
+ private Expression> _IsSameExpression()
where T : IResource
{
return _id.HasValue
@@ -163,7 +173,7 @@ namespace Kyoo.Abstractions.Models.Utils
/// The type that contain the list to check.
/// The type of resource to check this identifier against.
/// An expression to check if this is contained.
- public Expression> IsContainedIn(Expression>> listGetter)
+ public Filter IsContainedIn(Expression?>> 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());
- return Expression.Lambda>(call, listGetter.Parameters);
+ MethodCallExpression call = Expression.Call(null, method, listGetter.Body, _IsSameExpression());
+ Expression> lambda = Expression.Lambda>(call, listGetter.Parameters);
+ return new Filter.Lambda(lambda);
}
///
diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Pagination.cs b/back/src/Kyoo.Abstractions/Models/Utils/Pagination.cs
index a76444ad..e7c1478e 100644
--- a/back/src/Kyoo.Abstractions/Models/Utils/Pagination.cs
+++ b/back/src/Kyoo.Abstractions/Models/Utils/Pagination.cs
@@ -21,7 +21,7 @@ namespace Kyoo.Abstractions.Controllers
///
/// Information about the pagination. How many items should be displayed and where to start.
///
- public class Pagination
+ public struct Pagination
{
///
/// The count of items to return.
diff --git a/back/src/Kyoo.Abstractions/Utility/MethodOfUtils.cs b/back/src/Kyoo.Abstractions/Utility/MethodOfUtils.cs
deleted file mode 100644
index 5a0629de..00000000
--- a/back/src/Kyoo.Abstractions/Utility/MethodOfUtils.cs
+++ /dev/null
@@ -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 .
-
-using System;
-using System.Reflection;
-
-namespace Kyoo.Utils
-{
- ///
- /// Static class containing MethodOf calls.
- ///
- public static class MethodOfUtils
- {
- ///
- /// Get a MethodInfo from a direct method.
- ///
- /// The method (without any arguments or return value.
- /// The of the given method
- public static MethodInfo MethodOf(Action action)
- {
- return action.Method;
- }
-
- ///
- /// Get a MethodInfo from a direct method.
- ///
- /// The method (without any arguments or return value.
- /// The first parameter of the action.
- /// The of the given method
- public static MethodInfo MethodOf(Action action)
- {
- return action.Method;
- }
-
- ///
- /// Get a MethodInfo from a direct method.
- ///
- /// The method (without any arguments or return value.
- /// The first parameter of the action.
- /// The second parameter of the action.
- /// The of the given method
- public static MethodInfo MethodOf(Action action)
- {
- return action.Method;
- }
-
- ///
- /// Get a MethodInfo from a direct method.
- ///
- /// The method (without any arguments or return value.
- /// The first parameter of the action.
- /// The second parameter of the action.
- /// The third parameter of the action.
- /// The of the given method
- public static MethodInfo MethodOf(Action action)
- {
- return action.Method;
- }
-
- ///
- /// Get a MethodInfo from a direct method.
- ///
- /// The method (without any arguments or return value.
- /// The return type of function.
- /// The of the given method
- public static MethodInfo MethodOf(Func action)
- {
- return action.Method;
- }
-
- ///
- /// Get a MethodInfo from a direct method.
- ///
- /// The method (without any arguments or return value.
- /// The first parameter of the function.
- /// The return type of function.
- /// The of the given method
- public static MethodInfo MethodOf(Func action)
- {
- return action.Method;
- }
-
- ///
- /// Get a MethodInfo from a direct method.
- ///
- /// The method (without any arguments or return value.
- /// The first parameter of the function.
- /// The second parameter of the function.
- /// The return type of function.
- /// The of the given method
- public static MethodInfo MethodOf(Func action)
- {
- return action.Method;
- }
-
- ///
- /// Get a MethodInfo from a direct method.
- ///
- /// The method (without any arguments or return value.
- /// The first parameter of the function.
- /// The second parameter of the function.
- /// The third parameter of the function.
- /// The return type of function.
- /// The of the given method
- public static MethodInfo MethodOf(Func action)
- {
- return action.Method;
- }
- }
-}
diff --git a/back/src/Kyoo.Abstractions/Utility/TaskUtils.cs b/back/src/Kyoo.Abstractions/Utility/TaskUtils.cs
deleted file mode 100644
index 973ec3ea..00000000
--- a/back/src/Kyoo.Abstractions/Utility/TaskUtils.cs
+++ /dev/null
@@ -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 .
-
-using System;
-using System.Threading.Tasks;
-
-namespace Kyoo.Utils
-{
- ///
- /// A class containing helper method for tasks.
- ///
- public static class TaskUtils
- {
- ///
- /// Run a method after the execution of the task.
- ///
- /// The task to wait.
- ///
- /// The method to run after the task finish. This will only be run if the task finished successfully.
- ///
- /// The type of the item in the task.
- /// A continuation task wrapping the initial task and adding a continuation method.
- /// The source task has been canceled.
- public static Task Then(this Task task, Action 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);
- }
- }
-}
diff --git a/back/src/Kyoo.Abstractions/Utility/Utility.cs b/back/src/Kyoo.Abstractions/Utility/Utility.cs
index 9f3a95bf..8ba02d98 100644
--- a/back/src/Kyoo.Abstractions/Utility/Utility.cs
+++ b/back/src/Kyoo.Abstractions/Utility/Utility.cs
@@ -342,18 +342,5 @@ namespace Kyoo.Utils
return string.Empty;
return "?" + string.Join('&', query.Select(x => $"{x.Key}={x.Value}"));
}
-
- ///
- /// Rethrow the exception without modifying the stack trace.
- /// This is similar to the rethrow; code but is useful when the exception is not in a catch block.
- ///
- /// The exception to rethrow.
- [System.Diagnostics.CodeAnalysis.DoesNotReturn]
- public static void ReThrow(this Exception ex)
- {
- if (ex == null)
- throw new ArgumentNullException(nameof(ex));
- ExceptionDispatchInfo.Capture(ex).Throw();
- }
}
}
diff --git a/back/src/Kyoo.Authentication/Views/AuthApi.cs b/back/src/Kyoo.Authentication/Views/AuthApi.cs
index 5effe83d..4422feca 100644
--- a/back/src/Kyoo.Authentication/Views/AuthApi.cs
+++ b/back/src/Kyoo.Authentication/Views/AuthApi.cs
@@ -96,7 +96,7 @@ namespace Kyoo.Authentication.Views
[ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))]
public async Task> Login([FromBody] LoginRequest request)
{
- User? user = await _users.GetOrDefault(x => x.Username == request.Username);
+ User? user = await _users.GetOrDefault(new Filter.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
{
diff --git a/back/src/Kyoo.Core/Controllers/Repositories/CollectionRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/CollectionRepository.cs
index f4fb0a03..d8ccb290 100644
--- a/back/src/Kyoo.Core/Controllers/Repositories/CollectionRepository.cs
+++ b/back/src/Kyoo.Core/Controllers/Repositories/CollectionRepository.cs
@@ -53,7 +53,7 @@ namespace Kyoo.Core.Controllers
public override async Task> Search(string query, Include? include = default)
{
return await AddIncludes(_database.Collections, include)
- .Where(_database.Like(x => x.Name + " " + x.Slug, $"%{query}%"))
+ .Where(x => EF.Functions.ILike(x.Name + " " + x.Slug, $"%{query}%"))
.Take(20)
.ToListAsync();
}
diff --git a/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs
index f9eb0d27..17519419 100644
--- a/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs
+++ b/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs
@@ -79,7 +79,7 @@ namespace Kyoo.Core.Controllers
public override async Task> Search(string query, Include? include = default)
{
return await AddIncludes(_database.Episodes, include)
- .Where(_database.Like(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.OnResourceCreated(obj);
return obj;
}
diff --git a/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs
index e7d07ce2..3b3a8a8f 100644
--- a/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs
+++ b/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs
@@ -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
}
///
- public virtual async Task Get(
- Expression> where,
+ public virtual async Task Get(Filter filter,
Include? 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 GetOrDefault(Expression> where, Include? include = null, Sort? sortBy = null)
+ public Task GetOrDefault(Filter? filter, Include? include = default,
+ Sort? sortBy = default)
{
throw new NotImplementedException();
}
@@ -108,9 +106,11 @@ namespace Kyoo.Core.Controllers
return $"coalesce({string.Join(", ", keys)})";
}
- public static string ProcessSort(Sort sort, Dictionary config, bool recurse = false)
+ public static string ProcessSort(Sort? sort, Dictionary config, bool recurse = false)
where T : IQuery
{
+ sort ??= new Sort.Default();
+
string ret = sort switch
{
Sort.Default(var value) => ProcessSort(value, config, true),
@@ -188,12 +188,24 @@ namespace Kyoo.Core.Controllers
return $"{prefix}*" + projStr;
}
- public async Task> GetAll(
- Expression>? where = null,
- Sort? sort = null,
- Pagination? limit = null,
- Include? include = null)
+ public static string ProcessFilter(Filter filter, Dictionary config)
{
+ return filter switch
+ {
+ Filter.And(var first, var second) => $"({ProcessFilter(first, config)} and {ProcessFilter(second, config)})",
+ Filter.Or(var first, var second) => $"({ProcessFilter(first, config)} or {ProcessFilter(second, config)})",
+ Filter.Not(var inner) => $"(not {ProcessFilter(inner, config)})",
+ Filter.Eq(var property, var value) => $"({_Property(property, config)} = {value})",
+ };
+ }
+
+ public async Task> GetAll(Filter? filter = null,
+ Sort? sort = default,
+ Include? include = default,
+ Pagination limit = default)
+ {
+ include ??= new();
+
Dictionary 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("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 GetCount(Expression>? where = null)
+ public Task GetCount(Filter? filter = null)
{
throw new NotImplementedException();
}
@@ -252,7 +267,7 @@ namespace Kyoo.Core.Controllers
throw new NotImplementedException();
}
- public Task DeleteAll(Expression> where)
+ public Task DeleteAll(Filter filter)
{
throw new NotImplementedException();
}
diff --git a/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs
index 6fead483..6ee5adac 100644
--- a/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs
+++ b/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs
@@ -113,6 +113,11 @@ namespace Kyoo.Core.Controllers
return _Sort(query, sortBy, false).ThenBy(x => x.Id);
}
+ protected static Expression> ParseFilter(Filter? filter)
+ {
+ throw new NotImplementedException();
+ }
+
private static Func _GetComparisonExpression(
bool desc,
bool next,
@@ -297,9 +302,9 @@ namespace Kyoo.Core.Controllers
}
///
- public virtual async Task Get(Expression> where, Include? include = default)
+ public virtual async Task Get(Filter filter, Include? 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
}
///
- public virtual Task GetOrDefault(Expression> where,
+ public virtual Task GetOrDefault(Filter? filter,
Include? include = default,
Sort? sortBy = default)
{
@@ -334,7 +339,7 @@ namespace Kyoo.Core.Controllers
AddIncludes(Database.Set(), include),
sortBy
)
- .FirstOrDefaultAsync(where);
+ .FirstOrDefaultAsync(ParseFilter(filter));
}
///
@@ -353,12 +358,12 @@ namespace Kyoo.Core.Controllers
public abstract Task> Search(string query, Include? include = default);
///
- public virtual Task> GetAll(Expression>? where = null,
+ public virtual Task> GetAll(Filter? filter = null,
Sort? sort = default,
- Pagination? limit = default,
- Include? include = default)
+ Include? include = default,
+ Pagination limit = default)
{
- return ApplyFilters(Database.Set(), where, sort, limit, include);
+ return ApplyFilters(Database.Set(), ParseFilter(filter), sort, limit, include);
}
///
@@ -373,7 +378,7 @@ namespace Kyoo.Core.Controllers
protected async Task> ApplyFilters(IQueryable query,
Expression>? where = null,
Sort? sort = default,
- Pagination? limit = default,
+ Pagination limit = default,
Include? 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();
}
///
- public virtual Task GetCount(Expression>? where = null)
+ public virtual Task GetCount(Filter? filter = null)
{
IQueryable query = Database.Set();
- 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
}
///
- public async Task DeleteAll(Expression> where)
+ public async Task DeleteAll(Filter filter)
{
- foreach (T resource in await GetAll(where))
+ foreach (T resource in await GetAll(filter))
await Delete(resource);
}
}
diff --git a/back/src/Kyoo.Core/Controllers/Repositories/MovieRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/MovieRepository.cs
index ae055f4e..e7c82827 100644
--- a/back/src/Kyoo.Core/Controllers/Repositories/MovieRepository.cs
+++ b/back/src/Kyoo.Core/Controllers/Repositories/MovieRepository.cs
@@ -69,7 +69,7 @@ namespace Kyoo.Core.Controllers
public override async Task> Search(string query, Include? include = default)
{
return await AddIncludes(_database.Movies, include)
- .Where(_database.Like(x => x.Name + " " + x.Slug, $"%{query}%"))
+ .Where(x => EF.Functions.ILike(x.Name + " " + x.Slug, $"%{query}%"))
.Take(20)
.ToListAsync();
}
diff --git a/back/src/Kyoo.Core/Controllers/Repositories/PeopleRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/PeopleRepository.cs
index 0c63c83b..27fc99b6 100644
--- a/back/src/Kyoo.Core/Controllers/Repositories/PeopleRepository.cs
+++ b/back/src/Kyoo.Core/Controllers/Repositories/PeopleRepository.cs
@@ -63,7 +63,7 @@ namespace Kyoo.Core.Controllers
public override async Task> Search(string query, Include? include = default)
{
return await AddIncludes(_database.People, include)
- .Where(_database.Like(x => x.Name, $"%{query}%"))
+ .Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
.Take(20)
.ToListAsync();
}
diff --git a/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs
index 95067a93..b57162f8 100644
--- a/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs
+++ b/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs
@@ -74,7 +74,7 @@ namespace Kyoo.Core.Controllers
public override async Task> Search(string query, Include? include = default)
{
return await AddIncludes(_database.Seasons, include)
- .Where(_database.Like(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.OnResourceCreated(obj);
return obj;
}
diff --git a/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs
index 139fea05..bf37e761 100644
--- a/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs
+++ b/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs
@@ -70,7 +70,7 @@ namespace Kyoo.Core.Controllers
public override async Task> Search(string query, Include? include = default)
{
return await AddIncludes(_database.Shows, include)
- .Where(_database.Like(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
///
protected override async Task Validate(Show resource)
{
- resource.Slug ??= Utility.ToSlug(resource.Name);
-
await base.Validate(resource);
if (resource.Studio != null)
{
diff --git a/back/src/Kyoo.Core/Controllers/Repositories/StudioRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/StudioRepository.cs
index 6dda27d2..1448b7cc 100644
--- a/back/src/Kyoo.Core/Controllers/Repositories/StudioRepository.cs
+++ b/back/src/Kyoo.Core/Controllers/Repositories/StudioRepository.cs
@@ -53,7 +53,7 @@ namespace Kyoo.Core.Controllers
public override async Task> Search(string query, Include? include = default)
{
return await AddIncludes(_database.Studios, include)
- .Where(_database.Like(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;
}
- ///
- protected override async Task Validate(Studio resource)
- {
- resource.Slug ??= Utility.ToSlug(resource.Name);
- await base.Validate(resource);
- }
-
///
public override async Task Delete(Studio obj)
{
diff --git a/back/src/Kyoo.Core/Controllers/Repositories/UserRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/UserRepository.cs
index 5e87a31c..bfb51ea7 100644
--- a/back/src/Kyoo.Core/Controllers/Repositories/UserRepository.cs
+++ b/back/src/Kyoo.Core/Controllers/Repositories/UserRepository.cs
@@ -52,7 +52,7 @@ namespace Kyoo.Core.Controllers
public override async Task> Search(string query, Include? include = default)
{
return await AddIncludes(_database.Users, include)
- .Where(_database.Like(x => x.Username, $"%{query}%"))
+ .Where(x => EF.Functions.ILike(x.Username, $"%{query}%"))
.Take(20)
.ToListAsync();
}
diff --git a/back/src/Kyoo.Core/Views/Helper/CrudApi.cs b/back/src/Kyoo.Core/Views/Helper/CrudApi.cs
index a58862bd..a0d67230 100644
--- a/back/src/Kyoo.Core/Views/Helper/CrudApi.cs
+++ b/back/src/Kyoo.Core/Views/Helper/CrudApi.cs
@@ -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
///
/// Get the number of resources that match the filters.
///
- /// A list of filters to respect.
+ /// A list of filters to respect.
/// How many resources matched that filter.
/// Invalid filters.
[HttpGet("count")]
[PartialPermission(Kind.Read)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
- public async Task> GetCount([FromQuery] Dictionary where)
+ public async Task> GetCount([FromQuery] Filter filter)
{
- return await Repository.GetCount(ApiHelper.ParseWhere(where));
+ return await Repository.GetCount(filter);
}
///
@@ -105,7 +104,7 @@ namespace Kyoo.Core.Api
/// Get all resources that match the given filter.
///
/// Sort information about the query (sort by, sort order).
- /// Filter the returned items.
+ /// Filter the returned items.
/// How many items per page should be returned, where should the page start...
/// The aditional fields to include in the result.
/// A list of resources that match every filters.
@@ -116,15 +115,15 @@ namespace Kyoo.Core.Api
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
public async Task>> GetAll(
[FromQuery] Sort sortBy,
- [FromQuery] Dictionary where,
+ [FromQuery] Filter? filter,
[FromQuery] Pagination pagination,
[FromQuery] Include? fields)
{
ICollection resources = await Repository.GetAll(
- ApiHelper.ParseWhere(where),
+ filter,
sortBy,
- pagination,
- fields
+ fields,
+ pagination
);
return Page(resources, pagination.Limit);
@@ -231,20 +230,19 @@ namespace Kyoo.Core.Api
///
/// Delete all items matching the given filters. If no filter is specified, delete all items.
///
- /// The list of filters.
+ /// The list of filters.
/// The item(s) has successfully been deleted.
/// One or multiple filters are invalid.
[HttpDelete]
[PartialPermission(Kind.Delete)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
- public async Task Delete([FromQuery] Dictionary where)
+ public async Task Delete([FromQuery] Filter filter)
{
- Expression