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>? w = ApiHelper.ParseWhere(where); - if (w == null) + if (filter == null) return BadRequest(new RequestError("Incule a filter to delete items, all items won't be deleted.")); - await Repository.DeleteAll(w); + await Repository.DeleteAll(filter); return NoContent(); } } diff --git a/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs b/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs index 7439af0f..362d24ba 100644 --- a/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs +++ b/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs @@ -65,7 +65,7 @@ namespace Kyoo.Core.Api /// /// The ID or slug of the . /// A key to sort shows by. - /// An optional list of filters. + /// An optional list of filters. /// The number of shows to return. /// The aditional fields to include in the result. /// A page of shows. @@ -79,15 +79,15 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetShows(Identifier identifier, [FromQuery] Sort sortBy, - [FromQuery] Dictionary where, + [FromQuery] Filter? filter, [FromQuery] Pagination pagination, [FromQuery] Include fields) { ICollection resources = await _libraryManager.Shows.GetAll( - ApiHelper.ParseWhere(where, identifier.Matcher(x => x.StudioId, x => x.Studio!.Slug)), + Filter.And(filter, identifier.Matcher(x => x.StudioId, x => x.Studio!.Slug)), sortBy, - pagination, - fields + fields, + pagination ); if (!resources.Any() && await _libraryManager.Studios.GetOrDefault(identifier.IsSame()) == null) diff --git a/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs b/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs index c4289488..e8d3d285 100644 --- a/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs @@ -126,7 +126,7 @@ namespace Kyoo.Core.Api /// /// The ID or slug of the . /// A key to sort items by. - /// An optional list of filters. + /// An optional list of filters. /// The number of items to return. /// The aditional fields to include in the result. /// A page of items. @@ -140,21 +140,21 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetItems(Identifier identifier, [FromQuery] Sort sortBy, - [FromQuery] Dictionary where, + [FromQuery] Filter? filter, [FromQuery] Pagination pagination, [FromQuery] Include? fields) { - ICollection resources = await _items.GetAllOfCollection( - identifier.IsSame(), - ApiHelper.ParseWhere(where), - sortBy == new Sort.Default() ? new Sort.By(nameof(Movie.AirDate)) : sortBy, - pagination, - fields - ); + // ICollection resources = await _items.GetAllOfCollection( + // identifier.IsSame(), + // filter, + // sortBy == new Sort.Default() ? new Sort.By(nameof(Movie.AirDate)) : sortBy, + // pagination, + // fields + // ); - if (!resources.Any() && await _libraryManager.Collections.GetOrDefault(identifier.IsSame()) == null) + // if (!resources.Any() && await _libraryManager.Collections.GetOrDefault(identifier.IsSame()) == null) return NotFound(); - return Page(resources, pagination.Limit); + // return Page(resources, pagination.Limit); } /// @@ -165,9 +165,9 @@ namespace Kyoo.Core.Api /// /// The ID or slug of the . /// A key to sort shows by. - /// An optional list of filters. + /// An optional list of filters. /// The number of shows to return. - /// The aditional fields to include in the result. + /// The additional fields to include in the result. /// A page of shows. /// The filters or the sort parameters are invalid. /// No collection with the given ID could be found. @@ -179,15 +179,15 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetShows(Identifier identifier, [FromQuery] Sort sortBy, - [FromQuery] Dictionary where, + [FromQuery] Filter? filter, [FromQuery] Pagination pagination, [FromQuery] Include? fields) { ICollection resources = await _libraryManager.Shows.GetAll( - ApiHelper.ParseWhere(where, identifier.IsContainedIn(x => x.Collections!)), + Filter.And(filter, identifier.IsContainedIn(x => x.Collections)), sortBy == new Sort.Default() ? new Sort.By(x => x.AirDate) : sortBy, - pagination, - fields + fields, + pagination ); if (!resources.Any() && await _libraryManager.Collections.GetOrDefault(identifier.IsSame()) == null) @@ -203,7 +203,7 @@ namespace Kyoo.Core.Api /// /// The ID or slug of the . /// A key to sort movies by. - /// An optional list of filters. + /// An optional list of filters. /// The number of movies to return. /// The aditional fields to include in the result. /// A page of movies. @@ -217,15 +217,15 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetMovies(Identifier identifier, [FromQuery] Sort sortBy, - [FromQuery] Dictionary where, + [FromQuery] Filter? filter, [FromQuery] Pagination pagination, [FromQuery] Include? fields) { ICollection resources = await _libraryManager.Movies.GetAll( - ApiHelper.ParseWhere(where, identifier.IsContainedIn(x => x.Collections!)), + Filter.And(filter, identifier.IsContainedIn(x => x.Collections)), sortBy == new Sort.Default() ? new Sort.By(x => x.AirDate) : sortBy, - pagination, - fields + fields, + pagination ); if (!resources.Any() && await _libraryManager.Collections.GetOrDefault(identifier.IsSame()) == null) diff --git a/back/src/Kyoo.Core/Views/Resources/MovieApi.cs b/back/src/Kyoo.Core/Views/Resources/MovieApi.cs index da0ae654..6cfec030 100644 --- a/back/src/Kyoo.Core/Views/Resources/MovieApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/MovieApi.cs @@ -120,7 +120,7 @@ namespace Kyoo.Core.Api /// /// The ID or slug of the . /// A key to sort collections by. - /// An optional list of filters. + /// An optional list of filters. /// The number of collections to return. /// The aditional fields to include in the result. /// A page of collections. @@ -134,15 +134,15 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetCollections(Identifier identifier, [FromQuery] Sort sortBy, - [FromQuery] Dictionary where, + [FromQuery] Filter? filter, [FromQuery] Pagination pagination, [FromQuery] Include fields) { ICollection resources = await _libraryManager.Collections.GetAll( - ApiHelper.ParseWhere(where, identifier.IsContainedIn(x => x.Movies!)), + Filter.And(filter, identifier.IsContainedIn(x => x.Movies)), sortBy, - pagination, - fields + fields, + pagination ); if (!resources.Any() && await _libraryManager.Movies.GetOrDefault(identifier.IsSame()) == null) diff --git a/back/src/Kyoo.Core/Views/Resources/NewsApi.cs b/back/src/Kyoo.Core/Views/Resources/NewsApi.cs index c75b7a8f..9127b7b5 100644 --- a/back/src/Kyoo.Core/Views/Resources/NewsApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/NewsApi.cs @@ -16,13 +16,10 @@ // You should have received a copy of the GNU General Public License // along with Kyoo. If not, see . -using System.Collections.Generic; -using System.Threading.Tasks; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models.Attributes; using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Core.Controllers; using Microsoft.AspNetCore.Mvc; using static Kyoo.Abstractions.Models.Utils.Constants; @@ -37,25 +34,10 @@ namespace Kyoo.Core.Api [ResourceView] [PartialPermission("LibraryItem")] [ApiDefinition("News", Group = ResourcesGroup)] - public class NewsApi : BaseApi + public class NewsApi : CrudThumbsApi { - private readonly NewsRepository _news; - - public NewsApi(NewsRepository news) - { - _news = news; - } - - public async Task>> GetAll( - [FromQuery] Dictionary where, - [FromQuery] Pagination pagination) - { - ICollection resources = await _news.GetAll( - ApiHelper.ParseWhere(where), - limit: pagination - ); - - return Page(resources, pagination.Limit); - } + public NewsApi(IRepository news, IThumbnailsManager thumbs) + : base(news, thumbs) + { } } } diff --git a/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs b/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs index a878acc1..df276170 100644 --- a/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs @@ -67,7 +67,7 @@ namespace Kyoo.Core.Api /// /// The ID or slug of the . /// A key to sort episodes by. - /// An optional list of filters. + /// An optional list of filters. /// The number of episodes to return. /// The aditional fields to include in the result. /// A page of episodes. @@ -81,15 +81,15 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetEpisode(Identifier identifier, [FromQuery] Sort sortBy, - [FromQuery] Dictionary where, + [FromQuery] Filter? filter, [FromQuery] Pagination pagination, [FromQuery] Include fields) { ICollection resources = await _libraryManager.Episodes.GetAll( - ApiHelper.ParseWhere(where, identifier.Matcher(x => x.SeasonId, x => x.Season!.Slug)), + Filter.And(filter, identifier.Matcher(x => x.SeasonId, x => x.Season!.Slug)), sortBy, - pagination, - fields + fields, + pagination ); if (!resources.Any() && await _libraryManager.Seasons.GetOrDefault(identifier.IsSame()) == null) diff --git a/back/src/Kyoo.Core/Views/Resources/ShowApi.cs b/back/src/Kyoo.Core/Views/Resources/ShowApi.cs index 861bce7f..077b99dd 100644 --- a/back/src/Kyoo.Core/Views/Resources/ShowApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/ShowApi.cs @@ -67,7 +67,7 @@ namespace Kyoo.Core.Api /// /// The ID or slug of the . /// A key to sort seasons by. - /// An optional list of filters. + /// An optional list of filters. /// The number of seasons to return. /// The aditional fields to include in the result. /// A page of seasons. @@ -81,15 +81,15 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetSeasons(Identifier identifier, [FromQuery] Sort sortBy, - [FromQuery] Dictionary where, + [FromQuery] Filter? filter, [FromQuery] Pagination pagination, [FromQuery] Include fields) { ICollection resources = await _libraryManager.Seasons.GetAll( - ApiHelper.ParseWhere(where, identifier.Matcher(x => x.ShowId, x => x.Show!.Slug)), + Filter.And(filter, identifier.Matcher(x => x.ShowId, x => x.Show!.Slug)), sortBy, - pagination, - fields + fields, + pagination ); if (!resources.Any() && await _libraryManager.Shows.GetOrDefault(identifier.IsSame()) == null) @@ -105,7 +105,7 @@ namespace Kyoo.Core.Api /// /// The ID or slug of the . /// A key to sort episodes by. - /// An optional list of filters. + /// An optional list of filters. /// The number of episodes to return. /// The aditional fields to include in the result. /// A page of episodes. @@ -119,15 +119,15 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetEpisodes(Identifier identifier, [FromQuery] Sort sortBy, - [FromQuery] Dictionary where, + [FromQuery] Filter? filter, [FromQuery] Pagination pagination, [FromQuery] Include fields) { ICollection resources = await _libraryManager.Episodes.GetAll( - ApiHelper.ParseWhere(where, identifier.Matcher(x => x.ShowId, x => x.Show!.Slug)), + Filter.And(filter, identifier.Matcher(x => x.ShowId, x => x.Show!.Slug)), sortBy, - pagination, - fields + fields, + pagination ); if (!resources.Any() && await _libraryManager.Shows.GetOrDefault(identifier.IsSame()) == null) @@ -197,7 +197,7 @@ namespace Kyoo.Core.Api /// /// The ID or slug of the . /// A key to sort collections by. - /// An optional list of filters. + /// An optional list of filters. /// The number of collections to return. /// The aditional fields to include in the result. /// A page of collections. @@ -211,15 +211,15 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetCollections(Identifier identifier, [FromQuery] Sort sortBy, - [FromQuery] Dictionary where, + [FromQuery] Filter? filter, [FromQuery] Pagination pagination, [FromQuery] Include fields) { ICollection resources = await _libraryManager.Collections.GetAll( - ApiHelper.ParseWhere(where, identifier.IsContainedIn(x => x.Shows!)), + Filter.And(filter, identifier.IsContainedIn(x => x.Shows!)), sortBy, - pagination, - fields + fields, + pagination ); if (!resources.Any() && await _libraryManager.Shows.GetOrDefault(identifier.IsSame()) == null) diff --git a/back/src/Kyoo.Postgresql/DatabaseContext.cs b/back/src/Kyoo.Postgresql/DatabaseContext.cs index b6345ac0..547d4b3c 100644 --- a/back/src/Kyoo.Postgresql/DatabaseContext.cs +++ b/back/src/Kyoo.Postgresql/DatabaseContext.cs @@ -541,14 +541,5 @@ namespace Kyoo.Postgresql entry.State = EntityState.Detached; } } - - /// - /// Perform a case insensitive like operation. - /// - /// An accessor to get the item that will be checked. - /// The second operator of the like format. - /// The type of the item to query - /// An expression representing the like query. It can directly be passed to a where call. - public abstract Expression> Like(Expression> query, string format); } } diff --git a/back/src/Kyoo.Postgresql/PostgresContext.cs b/back/src/Kyoo.Postgresql/PostgresContext.cs index 0f6b25bb..4a04b8c5 100644 --- a/back/src/Kyoo.Postgresql/PostgresContext.cs +++ b/back/src/Kyoo.Postgresql/PostgresContext.cs @@ -139,14 +139,5 @@ namespace Kyoo.Postgresql { return ex.InnerException is PostgresException { SqlState: PostgresErrorCodes.UniqueViolation }; } - - /// - public override Expression> Like(Expression> query, string format) - { - MethodInfo iLike = MethodOfUtils.MethodOf(EF.Functions.ILike); - MethodCallExpression call = Expression.Call(iLike, Expression.Constant(EF.Functions), query.Body, Expression.Constant(format)); - - return Expression.Lambda>(call, query.Parameters); - } } } diff --git a/back/src/Kyoo.Postgresql/Utils/ListTypeHandler.cs b/back/src/Kyoo.Postgresql/Utils/ListTypeHandler.cs index 8f20f9ba..77b88845 100644 --- a/back/src/Kyoo.Postgresql/Utils/ListTypeHandler.cs +++ b/back/src/Kyoo.Postgresql/Utils/ListTypeHandler.cs @@ -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 . + using System.Collections.Generic; using System.Data; using System.Linq; diff --git a/back/tests/Kyoo.Tests/Database/RepositoryActivator.cs b/back/tests/Kyoo.Tests/Database/RepositoryActivator.cs index 08b914fe..292a22f5 100644 --- a/back/tests/Kyoo.Tests/Database/RepositoryActivator.cs +++ b/back/tests/Kyoo.Tests/Database/RepositoryActivator.cs @@ -89,7 +89,7 @@ namespace Kyoo.Tests.Database } public IRepository GetRepository() - where T : class, IResource + where T : class, IResource, IQuery { return _repositories.First(x => x.RepositoryType == typeof(T)) as IRepository; } diff --git a/back/tests/Kyoo.Tests/Database/RepositoryTests.cs b/back/tests/Kyoo.Tests/Database/RepositoryTests.cs index ae4b10d3..78592000 100644 --- a/back/tests/Kyoo.Tests/Database/RepositoryTests.cs +++ b/back/tests/Kyoo.Tests/Database/RepositoryTests.cs @@ -29,7 +29,7 @@ using Xunit; namespace Kyoo.Tests.Database { public abstract class RepositoryTests : IDisposable, IAsyncDisposable - where T : class, IResource + where T : class, IResource, IQuery { protected readonly RepositoryActivator Repositories; private readonly IRepository _repository;