diff --git a/back/src/Kyoo.Abstractions/Controllers/ILibraryManager.cs b/back/src/Kyoo.Abstractions/Controllers/ILibraryManager.cs
index 646f44ca..91e0064f 100644
--- a/back/src/Kyoo.Abstractions/Controllers/ILibraryManager.cs
+++ b/back/src/Kyoo.Abstractions/Controllers/ILibraryManager.cs
@@ -389,11 +389,23 @@ namespace Kyoo.Abstractions.Controllers
/// Edit a resource
///
/// The resource to edit, it's ID can't change.
- /// Should old properties of the resource be discarded or should null values considered as not changed?
/// The type of resources
/// If the item is not found
/// The resource edited and completed by database's information (related items and so on)
- Task Edit(T item, bool resetOld)
+ Task Edit(T item)
+ where T : class, IResource;
+
+ ///
+ /// Edit only specific properties of a resource
+ ///
+ /// The id of the resource to edit
+ ///
+ /// A method that will be called when you need to update every properties that you want to
+ /// persist. It can return false to abort the process via an ArgumentException
+ ///
+ /// If the item is not found
+ /// The resource edited and completed by database's information (related items and so on)
+ Task Patch(int id, Func> patch)
where T : class, IResource;
///
diff --git a/back/src/Kyoo.Abstractions/Controllers/IRepository.cs b/back/src/Kyoo.Abstractions/Controllers/IRepository.cs
index ead00dbf..fb00b768 100644
--- a/back/src/Kyoo.Abstractions/Controllers/IRepository.cs
+++ b/back/src/Kyoo.Abstractions/Controllers/IRepository.cs
@@ -129,13 +129,24 @@ namespace Kyoo.Abstractions.Controllers
event ResourceEventHandler OnCreated;
///
- /// Edit a resource
+ /// Edit a resource and replace every property
///
/// The resource to edit, it's ID can't change.
- /// Should old properties of the resource be discarded or should null values considered as not changed?
/// If the item is not found
/// The resource edited and completed by database's information (related items and so on)
- Task Edit(T edited, bool resetOld);
+ Task Edit(T edited);
+
+ ///
+ /// Edit only specific properties of a resource
+ ///
+ /// The id of the resource to edit
+ ///
+ /// A method that will be called when you need to update every properties that you want to
+ /// persist. It can return false to abort the process via an ArgumentException
+ ///
+ /// If the item is not found
+ /// The resource edited and completed by database's information (related items and so on)
+ Task Patch(int id, Func> patch);
///
/// Called when a resource has been edited.
diff --git a/back/tests/Kyoo.Tests/Database/SpecificTests/GenreTests.cs b/back/src/Kyoo.Abstractions/Models/PartialResource.cs
similarity index 51%
rename from back/tests/Kyoo.Tests/Database/SpecificTests/GenreTests.cs
rename to back/src/Kyoo.Abstractions/Models/PartialResource.cs
index b4456756..7e9b0089 100644
--- a/back/tests/Kyoo.Tests/Database/SpecificTests/GenreTests.cs
+++ b/back/src/Kyoo.Abstractions/Models/PartialResource.cs
@@ -16,33 +16,11 @@
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see .
-using System.Diagnostics.CodeAnalysis;
-using Kyoo.Abstractions.Controllers;
-using Kyoo.Abstractions.Models;
-using Xunit;
-using Xunit.Abstractions;
+namespace Kyoo.Models;
-namespace Kyoo.Tests.Database
+public class PartialResource
{
- namespace PostgreSQL
- {
- [Collection(nameof(Postgresql))]
- public class GenreTests : AGenreTests
- {
- public GenreTests(PostgresFixture postgres, ITestOutputHelper output)
- : base(new RepositoryActivator(output, postgres)) { }
- }
- }
+ public int? Id { get; set; }
- public abstract class AGenreTests : RepositoryTests
- {
- [SuppressMessage("ReSharper", "NotAccessedField.Local")]
- private readonly IGenreRepository _repository;
-
- protected AGenreTests(RepositoryActivator repositories)
- : base(repositories)
- {
- _repository = Repositories.LibraryManager.GenreRepository;
- }
- }
+ public string? Slug { get; set; }
}
diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs b/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs
index eda05306..786f2572 100644
--- a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs
+++ b/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs
@@ -43,7 +43,7 @@ namespace Kyoo.Abstractions.Models
{
if (ShowSlug != null || Show?.Slug != null)
return GetSlug(ShowSlug ?? Show.Slug, SeasonNumber, EpisodeNumber, AbsoluteNumber);
- return GetSlug(ShowID.ToString(), SeasonNumber, EpisodeNumber, AbsoluteNumber);
+ return GetSlug(ShowId.ToString(), SeasonNumber, EpisodeNumber, AbsoluteNumber);
}
[UsedImplicitly]
@@ -81,17 +81,17 @@ namespace Kyoo.Abstractions.Models
///
/// The ID of the Show containing this episode.
///
- [SerializeIgnore] public int ShowID { get; set; }
+ [SerializeIgnore] public int ShowId { get; set; }
///
/// The show that contains this episode. This must be explicitly loaded via a call to .
///
- [LoadableRelation(nameof(ShowID))] public Show? Show { get; set; }
+ [LoadableRelation(nameof(ShowId))] public Show? Show { get; set; }
///
/// The ID of the Season containing this episode.
///
- [SerializeIgnore] public int? SeasonID { get; set; }
+ [SerializeIgnore] public int? SeasonId { get; set; }
///
/// The season that contains this episode.
@@ -101,7 +101,7 @@ namespace Kyoo.Abstractions.Models
/// This can be null if the season is unknown and the episode is only identified
/// by it's .
///
- [LoadableRelation(nameof(SeasonID))] public Season? Season { get; set; }
+ [LoadableRelation(nameof(SeasonId))] public Season? Season { get; set; }
///
/// The season in witch this episode is in.
diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Season.cs b/back/src/Kyoo.Abstractions/Models/Resources/Season.cs
index b1eacec4..6cd21c3c 100644
--- a/back/src/Kyoo.Abstractions/Models/Resources/Season.cs
+++ b/back/src/Kyoo.Abstractions/Models/Resources/Season.cs
@@ -42,7 +42,7 @@ namespace Kyoo.Abstractions.Models
get
{
if (ShowSlug == null && Show == null)
- return $"{ShowID}-s{SeasonNumber}";
+ return $"{ShowId}-s{SeasonNumber}";
return $"{ShowSlug ?? Show?.Slug}-s{SeasonNumber}";
}
@@ -67,13 +67,13 @@ namespace Kyoo.Abstractions.Models
///
/// The ID of the Show containing this season.
///
- [SerializeIgnore] public int ShowID { get; set; }
+ [SerializeIgnore] public int ShowId { get; set; }
///
/// The show that contains this season.
/// This must be explicitly loaded via a call to .
///
- [LoadableRelation(nameof(ShowID))] public Show? Show { get; set; }
+ [LoadableRelation(nameof(ShowId))] public Show? Show { get; set; }
///
/// The number of this season. This can be set to 0 to indicate specials.
diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Show.cs b/back/src/Kyoo.Abstractions/Models/Resources/Show.cs
index 38a4a42e..aeba5c68 100644
--- a/back/src/Kyoo.Abstractions/Models/Resources/Show.cs
+++ b/back/src/Kyoo.Abstractions/Models/Resources/Show.cs
@@ -51,7 +51,7 @@ namespace Kyoo.Abstractions.Models
///
/// The list of alternative titles of this show.
///
- public string[] Aliases { get; set; } = Array.Empty();
+ public List Aliases { get; set; } = new();
///
/// The summary of this show.
@@ -61,12 +61,12 @@ namespace Kyoo.Abstractions.Models
///
/// A list of tags that match this movie.
///
- public string[] Tags { get; set; } = Array.Empty();
+ public List Tags { get; set; } = new();
///
/// The list of genres (themes) this show has.
///
- public Genre[] Genres { get; set; } = Array.Empty();
+ public List Genres { get; set; } = new();
///
/// Is this show airing, not aired yet or finished?
@@ -106,13 +106,13 @@ namespace Kyoo.Abstractions.Models
///
/// The ID of the Studio that made this show.
///
- [SerializeIgnore] public int? StudioID { get; set; }
+ [SerializeIgnore] public int? StudioId { get; set; }
///
/// The Studio that made this show.
/// This must be explicitly loaded via a call to .
///
- [LoadableRelation(nameof(StudioID))][EditableRelation] public Studio? Studio { get; set; }
+ [LoadableRelation(nameof(StudioId))][EditableRelation] public Studio? Studio { get; set; }
///
/// The list of people that made this show.
diff --git a/back/src/Kyoo.Abstractions/Utility/EnumerableExtensions.cs b/back/src/Kyoo.Abstractions/Utility/EnumerableExtensions.cs
index fd1e4f4d..5d3d9038 100644
--- a/back/src/Kyoo.Abstractions/Utility/EnumerableExtensions.cs
+++ b/back/src/Kyoo.Abstractions/Utility/EnumerableExtensions.cs
@@ -17,9 +17,7 @@
// along with Kyoo. If not, see .
using System;
-using System.Collections;
using System.Collections.Generic;
-using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Kyoo.Utils
@@ -29,125 +27,6 @@ namespace Kyoo.Utils
///
public static class EnumerableExtensions
{
- ///
- /// A Select where the index of the item can be used.
- ///
- /// The IEnumerable to map. If self is null, an empty list is returned
- /// The function that will map each items
- /// The type of items in
- /// The type of items in the returned list
- /// The list mapped.
- /// The list or the mapper can't be null
- [LinqTunnel]
- public static IEnumerable Map(this IEnumerable self,
- Func mapper)
- {
- if (self == null)
- throw new ArgumentNullException(nameof(self));
- if (mapper == null)
- throw new ArgumentNullException(nameof(mapper));
-
- static IEnumerable Generator(IEnumerable self, Func mapper)
- {
- using IEnumerator enumerator = self.GetEnumerator();
- int index = 0;
-
- while (enumerator.MoveNext())
- {
- yield return mapper(enumerator.Current, index);
- index++;
- }
- }
- return Generator(self, mapper);
- }
-
- ///
- /// A map where the mapping function is asynchronous.
- /// Note: might interest you.
- ///
- /// The IEnumerable to map.
- /// The asynchronous function that will map each items.
- /// The type of items in .
- /// The type of items in the returned list.
- /// The list mapped as an AsyncEnumerable.
- /// The list or the mapper can't be null.
- [LinqTunnel]
- public static IAsyncEnumerable MapAsync(this IEnumerable self,
- Func> mapper)
- {
- if (self == null)
- throw new ArgumentNullException(nameof(self));
- if (mapper == null)
- throw new ArgumentNullException(nameof(mapper));
-
- static async IAsyncEnumerable Generator(IEnumerable self, Func> mapper)
- {
- using IEnumerator enumerator = self.GetEnumerator();
- int index = 0;
-
- while (enumerator.MoveNext())
- {
- yield return await mapper(enumerator.Current, index);
- index++;
- }
- }
-
- return Generator(self, mapper);
- }
-
- ///
- /// An asynchronous version of Select.
- ///
- /// The IEnumerable to map
- /// The asynchronous function that will map each items
- /// The type of items in
- /// The type of items in the returned list
- /// The list mapped as an AsyncEnumerable
- /// The list or the mapper can't be null
- [LinqTunnel]
- public static IAsyncEnumerable SelectAsync(this IEnumerable self,
- Func> mapper)
- {
- if (self == null)
- throw new ArgumentNullException(nameof(self));
- if (mapper == null)
- throw new ArgumentNullException(nameof(mapper));
-
- static async IAsyncEnumerable Generator(IEnumerable self, Func> mapper)
- {
- using IEnumerator enumerator = self.GetEnumerator();
-
- while (enumerator.MoveNext())
- yield return await mapper(enumerator.Current);
- }
-
- return Generator(self, mapper);
- }
-
- ///
- /// Convert an AsyncEnumerable to a List by waiting for every item.
- ///
- /// The async list
- /// The type of items in the async list and in the returned list.
- /// A task that will return a simple list
- /// The list can't be null
- [LinqTunnel]
- public static Task> ToListAsync(this IAsyncEnumerable self)
- {
- if (self == null)
- throw new ArgumentNullException(nameof(self));
-
- static async Task> ToList(IAsyncEnumerable self)
- {
- List ret = new();
- await foreach (T i in self)
- ret.Add(i);
- return ret;
- }
-
- return ToList(self);
- }
-
///
/// If the enumerable is empty, execute an action.
///
@@ -197,104 +76,5 @@ namespace Kyoo.Utils
foreach (T i in self)
action(i);
}
-
- ///
- /// A foreach used as a function with a little specificity: the list can be null.
- ///
- /// The list to enumerate. If this is null, the function result in a no-op
- /// The action to execute for each arguments
- public static void ForEach(this IEnumerable? self, Action
public static class Merger
{
- ///
- /// Merge two lists, can keep duplicates or remove them.
- ///
- /// The first enumerable to merge
- /// The second enumerable to merge, if items from this list are equals to one from the first, they are not kept
- /// Equality function to compare items. If this is null, duplicated elements are kept
- /// The type of items in the lists to merge.
- /// The two list merged as an array
- [ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
- public static T[] MergeLists(IEnumerable? first,
- IEnumerable? second,
- Func? isEqual = null)
- {
- if (first == null)
- return second?.ToArray();
- if (second == null)
- return first.ToArray();
- if (isEqual == null)
- return first.Concat(second).ToArray();
- List list = first.ToList();
- return list.Concat(second.Where(x => !list.Any(y => isEqual(x, y)))).ToArray();
- }
-
- ///
- /// Merge two dictionary, if the same key is found on both dictionary, the values of the first one is kept.
- ///
- /// The first dictionary to merge
- /// The second dictionary to merge
- /// The type of the keys in dictionaries
- /// The type of values in the dictionaries
- /// The first dictionary with the missing elements of .
- ///
- [ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
- public static IDictionary MergeDictionaries(IDictionary? first,
- IDictionary? second)
- {
- return MergeDictionaries(first, second, out bool _);
- }
-
- ///
- /// Merge two dictionary, if the same key is found on both dictionary, the values of the first one is kept.
- ///
- /// The first dictionary to merge
- /// The second dictionary to merge
- ///
- /// true if a new items has been added to the dictionary, false otherwise.
- ///
- /// The type of the keys in dictionaries
- /// The type of values in the dictionaries
- /// The first dictionary with the missing elements of .
- [ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
- public static IDictionary MergeDictionaries(IDictionary? first,
- IDictionary? second,
- out bool hasChanged)
- {
- if (first == null)
- {
- hasChanged = true;
- return second;
- }
-
- hasChanged = false;
- if (second == null)
- return first;
- foreach ((T key, T2 value) in second)
- {
- bool success = first.TryAdd(key, value);
- hasChanged |= success;
-
- if (success || first[key]?.Equals(default) == false || value?.Equals(default) != false)
- continue;
- first[key] = value;
- hasChanged = true;
- }
-
- return first;
- }
-
///
/// Merge two dictionary, if the same key is found on both dictionary, the values of the second one is kept.
///
- ///
- /// The only difference in this function compared to
- ///
- /// is the way is calculated and the order of the arguments.
- ///
- /// MergeDictionaries(first, second);
- ///
- /// will do the same thing as
- ///
- /// CompleteDictionaries(second, first, out bool _);
- ///
- ///
/// The first dictionary to merge
/// The second dictionary to merge
///
@@ -138,7 +45,7 @@ namespace Kyoo.Utils
/// set to those of .
///
[ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
- public static IDictionary CompleteDictionaries(IDictionary? first,
+ public static IDictionary? CompleteDictionaries(IDictionary? first,
IDictionary? second,
out bool hasChanged)
{
@@ -160,14 +67,8 @@ namespace Kyoo.Utils
///
/// Set every non-default values of seconds to the corresponding property of second.
/// Dictionaries are handled like anonymous objects with a property per key/pair value
- /// (see
- ///
- /// for more details).
/// At the end, the OnMerge method of first will be called if first is a
///
- ///
- /// This does the opposite of .
- ///
///
/// {id: 0, slug: "test"}, {id: 4, slug: "foo"} -> {id: 4, slug: "foo"}
///
@@ -182,19 +83,16 @@ namespace Kyoo.Utils
///
/// Fields of T will be completed
///
- /// If first is null
- public static T Complete([NotNull] T first,
+ public static T Complete(T first,
T? second,
[InstantHandle] Func? where = null)
{
- if (first == null)
- throw new ArgumentNullException(nameof(first));
if (second == null)
return first;
Type type = typeof(T);
IEnumerable properties = type.GetProperties()
- .Where(x => x.CanRead && x.CanWrite
+ .Where(x => x is { CanRead: true, CanWrite: true }
&& Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null);
if (where != null)
@@ -202,17 +100,16 @@ namespace Kyoo.Utils
foreach (PropertyInfo property in properties)
{
- object value = property.GetValue(second);
- object defaultValue = property.GetCustomAttribute()?.Value
- ?? property.PropertyType.GetClrDefault();
+ object? value = property.GetValue(second);
- if (value?.Equals(defaultValue) != false || value.Equals(property.GetValue(first)))
+ if (value?.Equals(property.GetValue(first)) == true)
continue;
+
if (Utility.IsOfGenericType(property.PropertyType, typeof(IDictionary<,>)))
{
Type[] dictionaryTypes = Utility.GetGenericDefinition(property.PropertyType, typeof(IDictionary<,>))
.GenericTypeArguments;
- object[] parameters =
+ object?[] parameters =
{
property.GetValue(first),
value,
@@ -222,8 +119,8 @@ namespace Kyoo.Utils
typeof(Merger),
nameof(CompleteDictionaries),
dictionaryTypes,
- parameters);
- if ((bool)parameters[2])
+ parameters)!;
+ if ((bool)parameters[2]!)
property.SetValue(first, newDictionary);
}
else
@@ -234,109 +131,5 @@ namespace Kyoo.Utils
merge.OnMerge(second);
return first;
}
-
- ///
- /// This will set missing values of to the corresponding values of .
- /// Enumerable will be merged (concatenated) and Dictionaries too.
- /// At the end, the OnMerge method of first will be called if first is a .
- ///
- ///
- /// {id: 0, slug: "test"}, {id: 4, slug: "foo"} -> {id: 4, slug: "test"}
- ///
- ///
- /// The object to complete
- ///
- ///
- /// Missing fields of first will be completed by fields of this item. If second is null, the function no-op.
- ///
- ///
- /// Filter fields that will be merged
- ///
- /// Fields of T will be merged
- ///
- [ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
- public static T Merge(T? first,
- T? second,
- [InstantHandle] Func? where = null)
- {
- if (first == null)
- return second;
- if (second == null)
- return first;
-
- Type type = typeof(T);
- IEnumerable properties = type.GetProperties()
- .Where(x => x.CanRead && x.CanWrite
- && Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null);
-
- if (where != null)
- properties = properties.Where(where);
-
- foreach (PropertyInfo property in properties)
- {
- object oldValue = property.GetValue(first);
- object newValue = property.GetValue(second);
- object defaultValue = property.PropertyType.GetClrDefault();
-
- if (oldValue?.Equals(defaultValue) != false)
- property.SetValue(first, newValue);
- else if (Utility.IsOfGenericType(property.PropertyType, typeof(IDictionary<,>)))
- {
- Type[] dictionaryTypes = Utility.GetGenericDefinition(property.PropertyType, typeof(IDictionary<,>))
- .GenericTypeArguments;
- object[] parameters =
- {
- oldValue,
- newValue,
- false
- };
- object newDictionary = Utility.RunGenericMethod(
- typeof(Merger),
- nameof(MergeDictionaries),
- dictionaryTypes,
- parameters);
- if ((bool)parameters[2])
- property.SetValue(first, newDictionary);
- }
- else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType)
- && property.PropertyType != typeof(string))
- {
- Type enumerableType = Utility.GetGenericDefinition(property.PropertyType, typeof(IEnumerable<>))
- .GenericTypeArguments
- .First();
- Func equalityComparer = enumerableType.IsAssignableTo(typeof(IResource))
- ? (x, y) => x.Slug == y.Slug
- : null;
- property.SetValue(first, Utility.RunGenericMethod(
- typeof(Merger),
- nameof(MergeLists),
- enumerableType,
- oldValue, newValue, equalityComparer));
- }
- }
-
- if (first is IOnMerge merge)
- merge.OnMerge(second);
- return first;
- }
-
- ///
- /// Set every fields of to the default value.
- ///
- /// The object to nullify
- /// Fields of T will be nullified
- ///
- public static T Nullify(T obj)
- {
- Type type = typeof(T);
- foreach (PropertyInfo property in type.GetProperties())
- {
- if (!property.CanWrite || property.GetCustomAttribute() != null)
- continue;
- property.SetValue(obj, property.PropertyType.GetClrDefault());
- }
-
- return obj;
- }
}
}
diff --git a/back/src/Kyoo.Abstractions/Utility/TaskUtils.cs b/back/src/Kyoo.Abstractions/Utility/TaskUtils.cs
index 42c01027..973ec3ea 100644
--- a/back/src/Kyoo.Abstractions/Utility/TaskUtils.cs
+++ b/back/src/Kyoo.Abstractions/Utility/TaskUtils.cs
@@ -18,7 +18,6 @@
using System;
using System.Threading.Tasks;
-using JetBrains.Annotations;
namespace Kyoo.Utils
{
@@ -49,37 +48,5 @@ namespace Kyoo.Utils
return x.Result;
}, TaskContinuationOptions.ExecuteSynchronously);
}
-
- ///
- /// Map the result of a task to another result.
- ///
- /// The task to map.
- /// The mapper method, it take the task's result as a parameter and should return the new result.
- /// The type of returns of the given task
- /// The resulting task after the mapping method
- /// A task wrapping the initial task and mapping the initial result.
- /// The source task has been canceled.
- public static Task Map(this Task task, Func map)
- {
- return task.ContinueWith(x =>
- {
- if (x.IsFaulted)
- x.Exception!.InnerException!.ReThrow();
- if (x.IsCanceled)
- throw new TaskCanceledException();
- return map(x.Result);
- }, TaskContinuationOptions.ExecuteSynchronously);
- }
-
- ///
- /// A method to return the a default value from a task if the initial task is null.
- ///
- /// The initial task
- /// The type that the task will return
- /// A non-null task.
- public static Task DefaultIfNull(Task? value)
- {
- return value ?? Task.FromResult(default);
- }
}
}
diff --git a/back/src/Kyoo.Abstractions/Utility/Utility.cs b/back/src/Kyoo.Abstractions/Utility/Utility.cs
index 57e261df..ca9c513e 100644
--- a/back/src/Kyoo.Abstractions/Utility/Utility.cs
+++ b/back/src/Kyoo.Abstractions/Utility/Utility.cs
@@ -41,8 +41,6 @@ namespace Kyoo.Utils
/// True if the expression is a member, false otherwise
public static bool IsPropertyExpression(LambdaExpression ex)
{
- if (ex == null)
- return false;
return ex.Body is MemberExpression
|| (ex.Body.NodeType == ExpressionType.Convert && ((UnaryExpression)ex.Body).Operand is MemberExpression);
}
@@ -57,7 +55,7 @@ namespace Kyoo.Utils
{
if (!IsPropertyExpression(ex))
throw new ArgumentException($"{ex} is not a property expression.");
- MemberExpression member = ex.Body.NodeType == ExpressionType.Convert
+ MemberExpression? member = ex.Body.NodeType == ExpressionType.Convert
? ((UnaryExpression)ex.Body).Operand as MemberExpression
: ex.Body as MemberExpression;
return member!.Member.Name;
@@ -92,18 +90,6 @@ namespace Kyoo.Utils
return str;
}
- ///
- /// Get the default value of a type.
- ///
- /// The type to get the default value
- /// The default value of the given type.
- public static object GetClrDefault(this Type type)
- {
- return type.IsValueType
- ? Activator.CreateInstance(type)
- : null;
- }
-
///
/// Return every in the inheritance tree of the parameter (interfaces are not returned)
///
@@ -194,13 +180,6 @@ namespace Kyoo.Utils
Type[] generics,
object[] args)
{
- if (type == null)
- throw new ArgumentNullException(nameof(type));
- if (generics == null)
- throw new ArgumentNullException(nameof(generics));
- if (args == null)
- throw new ArgumentNullException(nameof(args));
-
MethodInfo[] methods = type.GetMethods(flag | BindingFlags.Public)
.Where(x => x.Name == name)
.Where(x => x.GetGenericArguments().Length == generics.Length)
@@ -295,12 +274,11 @@ namespace Kyoo.Utils
/// The return of the method you wanted to run.
///
///
- [PublicAPI]
- public static T RunGenericMethod(
+ public static T? RunGenericMethod(
Type owner,
string methodName,
Type[] types,
- params object[] args)
+ params object?[] args)
{
if (owner == null)
throw new ArgumentNullException(nameof(owner));
@@ -311,7 +289,7 @@ namespace Kyoo.Utils
if (types.Length < 1)
throw new ArgumentException($"The {nameof(types)} array is empty. At least one type is needed.");
MethodInfo method = GetMethod(owner, BindingFlags.Static, methodName, types, args);
- return (T)method.MakeGenericMethod(types).Invoke(null, args);
+ return (T?)method.MakeGenericMethod(types).Invoke(null, args);
}
///
diff --git a/back/src/Kyoo.Authentication/Views/AuthApi.cs b/back/src/Kyoo.Authentication/Views/AuthApi.cs
index a3b8135c..2040ac13 100644
--- a/back/src/Kyoo.Authentication/Views/AuthApi.cs
+++ b/back/src/Kyoo.Authentication/Views/AuthApi.cs
@@ -27,6 +27,7 @@ using Kyoo.Abstractions.Models.Permissions;
using Kyoo.Abstractions.Models.Utils;
using Kyoo.Authentication.Models;
using Kyoo.Authentication.Models.DTO;
+using Kyoo.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
@@ -229,7 +230,7 @@ namespace Kyoo.Authentication.Views
try
{
user.Id = userID;
- return await _users.Edit(user, true);
+ return await _users.Edit(user);
}
catch (ItemNotFoundException)
{
@@ -252,14 +253,15 @@ namespace Kyoo.Authentication.Views
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(RequestError))]
[ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))]
- public async Task> PatchMe(User user)
+ public async Task> PatchMe(PartialResource user)
{
if (!int.TryParse(User.FindFirstValue(Claims.Id), out int userID))
return Unauthorized(new RequestError("User not authenticated or token invalid."));
try
{
- user.Id = userID;
- return await _users.Edit(user, false);
+ if (user.Id.HasValue && user.Id != userID)
+ throw new ArgumentException("Can't edit your user id.");
+ return await _users.Patch(userID, TryUpdateModelAsync);
}
catch (ItemNotFoundException)
{
diff --git a/back/src/Kyoo.Core/Controllers/LibraryManager.cs b/back/src/Kyoo.Core/Controllers/LibraryManager.cs
index b134e76b..0cab6155 100644
--- a/back/src/Kyoo.Core/Controllers/LibraryManager.cs
+++ b/back/src/Kyoo.Core/Controllers/LibraryManager.cs
@@ -284,12 +284,12 @@ namespace Kyoo.Core.Controllers
(Show s, nameof(Show.Seasons)) => _SetRelation(s,
SeasonRepository.GetAll(x => x.Show.Id == obj.Id),
(x, y) => x.Seasons = y,
- (x, y) => { x.Show = y; x.ShowID = y.Id; }),
+ (x, y) => { x.Show = y; x.ShowId = y.Id; }),
(Show s, nameof(Show.Episodes)) => _SetRelation(s,
EpisodeRepository.GetAll(x => x.Show.Id == obj.Id),
(x, y) => x.Episodes = y,
- (x, y) => { x.Show = y; x.ShowID = y.Id; }),
+ (x, y) => { x.Show = y; x.ShowId = y.Id; }),
(Show s, nameof(Show.Collections)) => CollectionRepository
.GetAll(x => x.Shows.Any(y => y.Id == obj.Id))
@@ -300,21 +300,21 @@ namespace Kyoo.Core.Controllers
.Then(x =>
{
s.Studio = x;
- s.StudioID = x?.Id ?? 0;
+ s.StudioId = x?.Id ?? 0;
}),
(Season s, nameof(Season.Episodes)) => _SetRelation(s,
EpisodeRepository.GetAll(x => x.Season.Id == obj.Id),
(x, y) => x.Episodes = y,
- (x, y) => { x.Season = y; x.SeasonID = y.Id; }),
+ (x, y) => { x.Season = y; x.SeasonId = y.Id; }),
(Season s, nameof(Season.Show)) => ShowRepository
.GetOrDefault(x => x.Seasons.Any(y => y.Id == obj.Id))
.Then(x =>
{
s.Show = x;
- s.ShowID = x?.Id ?? 0;
+ s.ShowId = x?.Id ?? 0;
}),
@@ -323,7 +323,7 @@ namespace Kyoo.Core.Controllers
.Then(x =>
{
e.Show = x;
- e.ShowID = x?.Id ?? 0;
+ e.ShowId = x?.Id ?? 0;
}),
(Episode e, nameof(Episode.Season)) => SeasonRepository
@@ -331,18 +331,18 @@ namespace Kyoo.Core.Controllers
.Then(x =>
{
e.Season = x;
- e.SeasonID = x?.Id ?? 0;
+ e.SeasonId = x?.Id ?? 0;
}),
(Episode e, nameof(Episode.PreviousEpisode)) => EpisodeRepository
.GetAll(
- where: x => x.ShowID == e.ShowID,
+ where: x => x.ShowId == e.ShowId,
limit: new Pagination(1, e.Id, true)
).Then(x => e.PreviousEpisode = x.FirstOrDefault()),
(Episode e, nameof(Episode.NextEpisode)) => EpisodeRepository
.GetAll(
- where: x => x.ShowID == e.ShowID,
+ where: x => x.ShowId == e.ShowId,
limit: new Pagination(1, e.Id)
).Then(x => e.NextEpisode = x.FirstOrDefault()),
@@ -438,10 +438,17 @@ namespace Kyoo.Core.Controllers
}
///
- public Task Edit(T item, bool resetOld)
+ public Task Edit(T item)
where T : class, IResource
{
- return GetRepository().Edit(item, resetOld);
+ return GetRepository().Edit(item);
+ }
+
+ ///
+ public Task Patch(int id, Func> patch)
+ where T : class, IResource
+ {
+ return GetRepository().Patch(id, patch);
}
///
diff --git a/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs
index fe4e7412..f1a49d41 100644
--- a/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs
+++ b/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs
@@ -64,7 +64,7 @@ namespace Kyoo.Core.Controllers
// Edit episode slugs when the show's slug changes.
shows.OnEdited += (show) =>
{
- List episodes = _database.Episodes.AsTracking().Where(x => x.ShowID == show.Id).ToList();
+ List episodes = _database.Episodes.AsTracking().Where(x => x.ShowId == show.Id).ToList();
foreach (Episode ep in episodes)
{
ep.ShowSlug = show.Slug;
@@ -77,7 +77,7 @@ namespace Kyoo.Core.Controllers
///
public Task GetOrDefault(int showID, int seasonNumber, int episodeNumber)
{
- return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
+ return _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == showID
&& x.SeasonNumber == seasonNumber
&& x.EpisodeNumber == episodeNumber);
}
@@ -111,7 +111,7 @@ namespace Kyoo.Core.Controllers
///
public Task GetAbsolute(int showID, int absoluteNumber)
{
- return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
+ return _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == showID
&& x.AbsoluteNumber == absoluteNumber);
}
@@ -142,12 +142,12 @@ namespace Kyoo.Core.Controllers
public override async Task Create(Episode obj)
{
await base.Create(obj);
- obj.ShowSlug = obj.Show?.Slug ?? _database.Shows.First(x => x.Id == obj.ShowID).Slug;
+ obj.ShowSlug = obj.Show?.Slug ?? _database.Shows.First(x => x.Id == obj.ShowId).Slug;
_database.Entry(obj).State = EntityState.Added;
await _database.SaveChangesAsync(() =>
obj.SeasonNumber != null && obj.EpisodeNumber != null
- ? Get(obj.ShowID, obj.SeasonNumber.Value, obj.EpisodeNumber.Value)
- : GetAbsolute(obj.ShowID, obj.AbsoluteNumber.Value));
+ ? Get(obj.ShowId, obj.SeasonNumber.Value, obj.EpisodeNumber.Value)
+ : GetAbsolute(obj.ShowId, obj.AbsoluteNumber.Value));
OnResourceCreated(obj);
return obj;
}
@@ -156,14 +156,14 @@ namespace Kyoo.Core.Controllers
protected override async Task Validate(Episode resource)
{
await base.Validate(resource);
- if (resource.ShowID <= 0)
+ if (resource.ShowId <= 0)
{
if (resource.Show == null)
{
throw new ArgumentException($"Can't store an episode not related " +
- $"to any show (showID: {resource.ShowID}).");
+ $"to any show (showID: {resource.ShowId}).");
}
- resource.ShowID = resource.Show.Id;
+ resource.ShowId = resource.Show.Id;
}
}
@@ -173,12 +173,12 @@ namespace Kyoo.Core.Controllers
if (obj == null)
throw new ArgumentNullException(nameof(obj));
- int epCount = await _database.Episodes.Where(x => x.ShowID == obj.ShowID).Take(2).CountAsync();
+ int epCount = await _database.Episodes.Where(x => x.ShowId == obj.ShowId).Take(2).CountAsync();
_database.Entry(obj).State = EntityState.Deleted;
await _database.SaveChangesAsync();
await base.Delete(obj);
if (epCount == 1)
- await _shows.Delete(obj.ShowID);
+ await _shows.Delete(obj.ShowId);
}
}
}
diff --git a/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs
index 33dd9b8b..0a6350f8 100644
--- a/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs
+++ b/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs
@@ -100,7 +100,11 @@ namespace Kyoo.Core.Controllers
=> throw new InvalidOperationException();
///
- public override Task Edit(ILibraryItem obj, bool resetOld)
+ public override Task Edit(ILibraryItem obj)
+ => throw new InvalidOperationException();
+
+ ///
+ public override Task Patch(int id, Func> patch)
=> throw new InvalidOperationException();
///
diff --git a/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs
index 8e421c7c..0ced12d0 100644
--- a/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs
+++ b/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs
@@ -144,7 +144,7 @@ namespace Kyoo.Core.Controllers
/// The reference item (the AfterID query)
/// True if the following page should be returned, false for the previous.
/// An expression ready to be added to a Where close of a sorted query to handle the AfterID
- protected Expression> KeysetPaginatate(
+ protected Expression> KeysetPaginate(
Sort sort,
T reference,
bool next = true)
@@ -155,22 +155,22 @@ namespace Kyoo.Core.Controllers
ParameterExpression x = Expression.Parameter(typeof(T), "x");
ConstantExpression referenceC = Expression.Constant(reference, typeof(T));
- IEnumerable.By> _GetSortsBy(Sort sort)
+ IEnumerable.By> GetSortsBy(Sort sort)
{
return sort switch
{
- Sort.Default => _GetSortsBy(DefaultSort),
+ Sort.Default => GetSortsBy(DefaultSort),
Sort.By @sortBy => new[] { sortBy },
- Sort.Conglomerate(var list) => list.SelectMany(_GetSortsBy),
+ Sort.Conglomerate(var list) => list.SelectMany(GetSortsBy),
_ => Array.Empty.By>(),
};
}
- // Don't forget that every sorts must end with a ID sort (to differenciate equalities).
+ // Don't forget that every sorts must end with a ID sort (to differentiate equalities).
Sort.By id = new(x => x.Id);
- IEnumerable.By> sorts = _GetSortsBy(sort).Append(id);
+ IEnumerable.By> sorts = GetSortsBy(sort).Append(id);
- BinaryExpression filter = null;
+ BinaryExpression? filter = null;
List.By> previousSteps = new();
// TODO: Add an outer query >= for perf
// PERF: See https://use-the-index-luke.com/sql/partial-results/fetch-next-page#sb-equivalent-logic
@@ -180,9 +180,9 @@ namespace Kyoo.Core.Controllers
PropertyInfo property = typeof(T).GetProperty(key);
// Comparing a value with null always return false so we short opt < > comparisons with null.
- if (property.GetValue(reference) == null)
+ if (property!.GetValue(reference) == null)
{
- previousSteps.Add(new(key, desc));
+ previousSteps.Add(new Sort.By(key, desc));
continue;
}
@@ -206,7 +206,7 @@ namespace Kyoo.Core.Controllers
// Comparing a value with null always return false for nulls so we must add nulls to the results manually.
// Postgres sorts them after values so we will do the same
- // We only add this condition if the collumn type is nullable
+ // We only add this condition if the column type is nullable
if (Nullable.GetUnderlyingType(property.PropertyType) != null)
{
BinaryExpression equalNull = Expression.Equal(xkey, Expression.Constant(null));
@@ -223,7 +223,7 @@ namespace Kyoo.Core.Controllers
previousSteps.Add(new(key, desc));
}
- return Expression.Lambda>(filter, x);
+ return Expression.Lambda>(filter!, x);
}
///
@@ -316,7 +316,7 @@ namespace Kyoo.Core.Controllers
if (limit?.AfterID != null)
{
T reference = await Get(limit.AfterID.Value);
- query = query.Where(KeysetPaginatate(sort, reference, !limit.Reverse));
+ query = query.Where(KeysetPaginate(sort, reference, !limit.Reverse));
}
if (limit?.Reverse == true)
query = query.Reverse();
@@ -343,12 +343,9 @@ namespace Kyoo.Core.Controllers
await Validate(obj);
if (obj is IThumbnails thumbs)
{
- if (thumbs.Poster != null)
- Database.Entry(thumbs).Reference(x => x.Poster).TargetEntry.State = EntityState.Added;
- if (thumbs.Thumbnail != null)
- Database.Entry(thumbs).Reference(x => x.Thumbnail).TargetEntry.State = EntityState.Added;
- if (thumbs.Logo != null)
- Database.Entry(thumbs).Reference(x => x.Logo).TargetEntry.State = EntityState.Added;
+ Database.Entry(thumbs).Reference(x => x.Poster).IsModified = thumbs.Poster != null;
+ Database.Entry(thumbs).Reference(x => x.Thumbnail).IsModified = thumbs.Thumbnail != null;
+ Database.Entry(thumbs).Reference(x => x.Logo).IsModified = thumbs.Logo != null;
}
return obj;
}
@@ -376,9 +373,6 @@ namespace Kyoo.Core.Controllers
{
try
{
- if (obj == null)
- throw new ArgumentNullException(nameof(obj));
-
T old = await GetOrDefault(obj.Slug);
if (old != null)
return old;
@@ -392,21 +386,16 @@ namespace Kyoo.Core.Controllers
}
///
- public virtual async Task Edit(T edited, bool resetOld)
+ public virtual async Task Edit(T edited)
{
- if (edited == null)
- throw new ArgumentNullException(nameof(edited));
-
bool lazyLoading = Database.ChangeTracker.LazyLoadingEnabled;
Database.ChangeTracker.LazyLoadingEnabled = false;
try
{
T old = await GetWithTracking(edited.Id);
- if (resetOld)
- old = Merger.Nullify(old);
Merger.Complete(old, edited, x => x.GetCustomAttribute() == null);
- await EditRelations(old, edited, resetOld);
+ await EditRelations(old, edited, true);
await Database.SaveChangesAsync();
OnEdited?.Invoke(old);
return old;
@@ -418,6 +407,28 @@ namespace Kyoo.Core.Controllers
}
}
+ ///
+ public virtual async Task Patch(int id, Func> patch)
+ {
+ bool lazyLoading = Database.ChangeTracker.LazyLoadingEnabled;
+ Database.ChangeTracker.LazyLoadingEnabled = false;
+ try
+ {
+ T resource = await GetWithTracking(id);
+
+ if (!await patch(resource))
+ throw new ArgumentException("Could not patch resource");
+ await Database.SaveChangesAsync();
+ OnEdited?.Invoke(resource);
+ return resource;
+ }
+ finally
+ {
+ Database.ChangeTracker.LazyLoadingEnabled = lazyLoading;
+ Database.ChangeTracker.Clear();
+ }
+ }
+
///
/// An overridable method to edit relation of a resource.
///
@@ -434,12 +445,11 @@ namespace Kyoo.Core.Controllers
/// A representing the asynchronous operation.
protected virtual Task EditRelations(T resource, T changed, bool resetOld)
{
- if (resource is IThumbnails thumbs)
+ if (resource is IThumbnails thumbs && changed is IThumbnails chng)
{
- // FIXME: I think this throws if poster is set to null.
- Database.Entry(thumbs).Reference(x => x.Poster).TargetEntry.State = EntityState.Modified;
- Database.Entry(thumbs).Reference(x => x.Thumbnail).TargetEntry.State = EntityState.Modified;
- Database.Entry(thumbs).Reference(x => x.Logo).TargetEntry.State = EntityState.Modified;
+ Database.Entry(thumbs).Reference(x => x.Poster).IsModified = thumbs.Poster != chng.Poster;
+ Database.Entry(thumbs).Reference(x => x.Thumbnail).IsModified = thumbs.Thumbnail != chng.Thumbnail;
+ Database.Entry(thumbs).Reference(x => x.Logo).IsModified = thumbs.Logo != chng.Logo;
}
return Validate(resource);
}
diff --git a/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs
index 37a2e820..0265fc4a 100644
--- a/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs
+++ b/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs
@@ -55,7 +55,7 @@ namespace Kyoo.Core.Controllers
// Edit seasons slugs when the show's slug changes.
shows.OnEdited += (show) =>
{
- List seasons = _database.Seasons.AsTracking().Where(x => x.ShowID == show.Id).ToList();
+ List seasons = _database.Seasons.AsTracking().Where(x => x.ShowId == show.Id).ToList();
foreach (Season season in seasons)
{
season.ShowSlug = show.Slug;
@@ -86,7 +86,7 @@ namespace Kyoo.Core.Controllers
///
public Task GetOrDefault(int showID, int seasonNumber)
{
- return _database.Seasons.FirstOrDefaultAsync(x => x.ShowID == showID
+ return _database.Seasons.FirstOrDefaultAsync(x => x.ShowId == showID
&& x.SeasonNumber == seasonNumber);
}
@@ -112,9 +112,9 @@ namespace Kyoo.Core.Controllers
public override async Task Create(Season 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;
- await _database.SaveChangesAsync(() => Get(obj.ShowID, obj.SeasonNumber));
+ await _database.SaveChangesAsync(() => Get(obj.ShowId, obj.SeasonNumber));
OnResourceCreated(obj);
return obj;
}
@@ -123,14 +123,14 @@ namespace Kyoo.Core.Controllers
protected override async Task Validate(Season resource)
{
await base.Validate(resource);
- if (resource.ShowID <= 0)
+ if (resource.ShowId <= 0)
{
if (resource.Show == null)
{
throw new ArgumentException($"Can't store a season not related to any show " +
- $"(showID: {resource.ShowID}).");
+ $"(showID: {resource.ShowId}).");
}
- resource.ShowID = resource.Show.Id;
+ resource.ShowId = resource.Show.Id;
}
}
diff --git a/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs
index 2bfb8fda..810300a3 100644
--- a/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs
+++ b/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs
@@ -97,7 +97,7 @@ namespace Kyoo.Core.Controllers
if (resource.Studio != null)
{
resource.Studio = await _studios.CreateIfNotExists(resource.Studio);
- resource.StudioID = resource.Studio.Id;
+ resource.StudioId = resource.Studio.Id;
}
if (resource.People != null)
diff --git a/back/src/Kyoo.Core/Views/Helper/CrudApi.cs b/back/src/Kyoo.Core/Views/Helper/CrudApi.cs
index 2a786bd8..a593ac9a 100644
--- a/back/src/Kyoo.Core/Views/Helper/CrudApi.cs
+++ b/back/src/Kyoo.Core/Views/Helper/CrudApi.cs
@@ -16,12 +16,14 @@
// You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see .
+using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Permissions;
using Kyoo.Abstractions.Models.Utils;
+using Kyoo.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -162,11 +164,11 @@ namespace Kyoo.Core.Api
public async Task> Edit([FromBody] T resource)
{
if (resource.Id > 0)
- return await Repository.Edit(resource, true);
+ return await Repository.Edit(resource);
T old = await Repository.Get(resource.Slug);
resource.Id = old.Id;
- return await Repository.Edit(resource, true);
+ return await Repository.Edit(resource);
}
///
@@ -185,14 +187,15 @@ namespace Kyoo.Core.Api
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task> Patch([FromBody] T resource)
+ public async Task> Patch([FromBody] PartialResource resource)
{
- if (resource.Id > 0)
- return await Repository.Edit(resource, false);
+ if (resource.Id.HasValue)
+ return await Repository.Patch(resource.Id.Value, TryUpdateModelAsync);
+ if (resource.Slug == null)
+ throw new ArgumentException("Either the Id or the slug of the resource has to be defined to edit it.");
T old = await Repository.Get(resource.Slug);
- resource.Id = old.Id;
- return await Repository.Edit(resource, false);
+ return await Repository.Patch(old.Id, TryUpdateModelAsync);
}
///
diff --git a/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs b/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs
index 873c398f..90c72539 100644
--- a/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs
+++ b/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs
@@ -83,7 +83,7 @@ namespace Kyoo.Core.Api
[FromQuery] Pagination pagination)
{
ICollection resources = await _libraryManager.GetAll(
- ApiHelper.ParseWhere(where, identifier.Matcher(x => x.StudioID, x => x.Studio.Slug)),
+ ApiHelper.ParseWhere(where, identifier.Matcher(x => x.StudioId, x => x.Studio.Slug)),
Sort.From(sortBy),
pagination
);
diff --git a/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs b/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs
index d336e22c..f26bbe30 100644
--- a/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs
+++ b/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs
@@ -84,7 +84,7 @@ namespace Kyoo.Core.Api
[FromQuery] Pagination pagination)
{
ICollection resources = await _libraryManager.GetAll(
- ApiHelper.ParseWhere(where, identifier.Matcher(x => x.SeasonID, x => x.Season.Slug)),
+ ApiHelper.ParseWhere(where, identifier.Matcher(x => x.SeasonId, x => x.Season.Slug)),
Sort.From(sortBy),
pagination
);
diff --git a/back/src/Kyoo.Core/Views/Resources/ShowApi.cs b/back/src/Kyoo.Core/Views/Resources/ShowApi.cs
index 68d44eb1..2084291e 100644
--- a/back/src/Kyoo.Core/Views/Resources/ShowApi.cs
+++ b/back/src/Kyoo.Core/Views/Resources/ShowApi.cs
@@ -86,7 +86,7 @@ namespace Kyoo.Core.Api
[FromQuery] Pagination pagination)
{
ICollection resources = await _libraryManager.GetAll(
- ApiHelper.ParseWhere(where, identifier.Matcher(x => x.ShowID, x => x.Show.Slug)),
+ ApiHelper.ParseWhere(where, identifier.Matcher(x => x.ShowId, x => x.Show.Slug)),
Sort.From(sortBy),
pagination
);
@@ -121,7 +121,7 @@ namespace Kyoo.Core.Api
[FromQuery] Pagination pagination)
{
ICollection resources = await _libraryManager.GetAll(
- ApiHelper.ParseWhere(where, identifier.Matcher(x => x.ShowID, x => x.Show.Slug)),
+ ApiHelper.ParseWhere(where, identifier.Matcher(x => x.ShowId, x => x.Show.Slug)),
Sort.From(sortBy),
pagination
);
diff --git a/back/src/Kyoo.Postgresql/DatabaseContext.cs b/back/src/Kyoo.Postgresql/DatabaseContext.cs
index 3d794f2c..aa9e5ca5 100644
--- a/back/src/Kyoo.Postgresql/DatabaseContext.cs
+++ b/back/src/Kyoo.Postgresql/DatabaseContext.cs
@@ -306,13 +306,13 @@ namespace Kyoo.Postgresql
.HasIndex(x => x.Slug)
.IsUnique();
modelBuilder.Entity()
- .HasIndex(x => new { x.ShowID, x.SeasonNumber })
+ .HasIndex(x => new { ShowID = x.ShowId, x.SeasonNumber })
.IsUnique();
modelBuilder.Entity()
.HasIndex(x => x.Slug)
.IsUnique();
modelBuilder.Entity()
- .HasIndex(x => new { x.ShowID, x.SeasonNumber, x.EpisodeNumber, x.AbsoluteNumber })
+ .HasIndex(x => new { ShowID = x.ShowId, x.SeasonNumber, x.EpisodeNumber, x.AbsoluteNumber })
.IsUnique();
modelBuilder.Entity()
.HasIndex(x => x.Slug)
diff --git a/back/tests/Kyoo.Tests/Database/RepositoryTests.cs b/back/tests/Kyoo.Tests/Database/RepositoryTests.cs
index f6aa7de4..383d8fbe 100644
--- a/back/tests/Kyoo.Tests/Database/RepositoryTests.cs
+++ b/back/tests/Kyoo.Tests/Database/RepositoryTests.cs
@@ -140,16 +140,10 @@ namespace Kyoo.Tests.Database
KAssert.DeepEqual(expected, await _repository.CreateIfNotExists(TestSample.Get()));
}
- [Fact]
- public async Task EditNullTest()
- {
- await Assert.ThrowsAsync(() => _repository.Edit(null!, false));
- }
-
[Fact]
public async Task EditNonExistingTest()
{
- await Assert.ThrowsAsync(() => _repository.Edit(new T { Id = 56 }, false));
+ await Assert.ThrowsAsync(() => _repository.Edit(new T { Id = 56 }));
}
[Fact]
@@ -170,12 +164,6 @@ namespace Kyoo.Tests.Database
await Assert.ThrowsAsync(() => _repository.Get(x => x.Slug == "non-existing"));
}
- [Fact]
- public async Task GetExpressionNullTest()
- {
- await Assert.ThrowsAsync(() => _repository.Get((Expression>)null!));
- }
-
[Fact]
public async Task GetOrDefaultTest()
{
diff --git a/back/tests/Kyoo.Tests/Database/SpecificTests/CollectionsTests.cs b/back/tests/Kyoo.Tests/Database/SpecificTests/CollectionsTests.cs
index ef38efb1..2094afdb 100644
--- a/back/tests/Kyoo.Tests/Database/SpecificTests/CollectionsTests.cs
+++ b/back/tests/Kyoo.Tests/Database/SpecificTests/CollectionsTests.cs
@@ -66,29 +66,19 @@ namespace Kyoo.Tests.Database
Assert.Equal("2!", ret.Slug);
}
- [Fact]
- public async Task CreateWithoutNameTest()
- {
- Collection collection = TestSample.GetNew();
- collection.Name = null;
- await Assert.ThrowsAsync(() => _repository.Create(collection));
- }
-
[Fact]
public async Task CreateWithExternalIdTest()
{
Collection collection = TestSample.GetNew();
- collection.ExternalId = new[]
+ collection.ExternalId = new Dictionary
{
- new MetadataId
+ ["1"] = new()
{
- Provider = TestSample.Get(),
Link = "link",
DataId = "id"
},
- new MetadataId
+ ["2"] = new()
{
- Provider = TestSample.GetNew(),
Link = "new-provider-link",
DataId = "new-id"
}
@@ -96,7 +86,6 @@ namespace Kyoo.Tests.Database
await _repository.Create(collection);
Collection retrieved = await _repository.Get(2);
- await Repositories.LibraryManager.Load(retrieved, x => x.ExternalId);
Assert.Equal(2, retrieved.ExternalId.Count);
KAssert.DeepEqual(collection.ExternalId.First(), retrieved.ExternalId.First());
KAssert.DeepEqual(collection.ExternalId.Last(), retrieved.ExternalId.Last());
@@ -107,11 +96,8 @@ namespace Kyoo.Tests.Database
{
Collection value = await _repository.Get(TestSample.Get().Slug);
value.Name = "New Title";
- value.Images = new Dictionary
- {
- [Images.Poster] = "new-poster"
- };
- await _repository.Edit(value, false);
+ value.Poster = new Image("new-poster");
+ await _repository.Edit(value);
await using DatabaseContext database = Repositories.Context.New();
Collection retrieved = await database.Collections.FirstAsync();
@@ -123,21 +109,19 @@ namespace Kyoo.Tests.Database
public async Task EditMetadataTest()
{
Collection value = await _repository.Get(TestSample.Get().Slug);
- value.ExternalId = new[]
+ value.ExternalId = new Dictionary
{
- new MetadataId
+ ["test"] = new()
{
- Provider = TestSample.Get(),
Link = "link",
DataId = "id"
},
};
- await _repository.Edit(value, false);
+ await _repository.Edit(value);
await using DatabaseContext database = Repositories.Context.New();
Collection retrieved = await database.Collections
.Include(x => x.ExternalId)
- .ThenInclude(x => x.Provider)
.FirstAsync();
KAssert.DeepEqual(value, retrieved);
@@ -147,40 +131,36 @@ namespace Kyoo.Tests.Database
public async Task AddMetadataTest()
{
Collection value = await _repository.Get(TestSample.Get().Slug);
- value.ExternalId = new List
+ value.ExternalId = new Dictionary
{
- new()
+ ["toto"] = new()
{
- Provider = TestSample.Get(),
Link = "link",
DataId = "id"
},
};
- await _repository.Edit(value, false);
+ await _repository.Edit(value);
{
await using DatabaseContext database = Repositories.Context.New();
Collection retrieved = await database.Collections
.Include(x => x.ExternalId)
- .ThenInclude(x => x.Provider)
.FirstAsync();
KAssert.DeepEqual(value, retrieved);
}
- value.ExternalId.Add(new MetadataId
+ value.ExternalId.Add("test", new MetadataId
{
- Provider = TestSample.GetNew(),
Link = "link",
DataId = "id"
});
- await _repository.Edit(value, false);
+ await _repository.Edit(value);
{
await using DatabaseContext database = Repositories.Context.New();
Collection retrieved = await database.Collections
.Include(x => x.ExternalId)
- .ThenInclude(x => x.Provider)
.FirstAsync();
KAssert.DeepEqual(value, retrieved);
diff --git a/back/tests/Kyoo.Tests/Database/SpecificTests/EpisodeTests.cs b/back/tests/Kyoo.Tests/Database/SpecificTests/EpisodeTests.cs
index 8ee85408..dfb91af4 100644
--- a/back/tests/Kyoo.Tests/Database/SpecificTests/EpisodeTests.cs
+++ b/back/tests/Kyoo.Tests/Database/SpecificTests/EpisodeTests.cs
@@ -57,10 +57,10 @@ namespace Kyoo.Tests.Database
Assert.Equal($"{TestSample.Get().Slug}-s1e1", episode.Slug);
Show show = new()
{
- Id = episode.ShowID,
+ Id = episode.ShowId,
Slug = "new-slug"
};
- await Repositories.LibraryManager.ShowRepository.Edit(show, false);
+ await Repositories.LibraryManager.ShowRepository.Edit(show);
episode = await _repository.Get(1);
Assert.Equal("new-slug-s1e1", episode.Slug);
}
@@ -74,8 +74,8 @@ namespace Kyoo.Tests.Database
{
Id = 1,
SeasonNumber = 2,
- ShowID = 1
- }, false);
+ ShowId = 1
+ });
Assert.Equal($"{TestSample.Get().Slug}-s2e1", episode.Slug);
episode = await _repository.Get(1);
Assert.Equal($"{TestSample.Get().Slug}-s2e1", episode.Slug);
@@ -90,8 +90,8 @@ namespace Kyoo.Tests.Database
{
Id = 1,
EpisodeNumber = 2,
- ShowID = 1
- }, false);
+ ShowId = 1
+ });
Assert.Equal($"{TestSample.Get().Slug}-s1e2", episode.Slug);
episode = await _repository.Get(1);
Assert.Equal($"{TestSample.Get().Slug}-s1e2", episode.Slug);
@@ -102,7 +102,7 @@ namespace Kyoo.Tests.Database
{
Episode episode = await _repository.Create(new Episode
{
- ShowID = TestSample.Get().Id,
+ ShowId = TestSample.Get().Id,
SeasonNumber = 2,
EpisodeNumber = 4
});
@@ -129,10 +129,10 @@ namespace Kyoo.Tests.Database
Episode episode = await _repository.Create(TestSample.GetAbsoluteEpisode());
Show show = new()
{
- Id = episode.ShowID,
+ Id = episode.ShowId,
Slug = "new-slug"
};
- await Repositories.LibraryManager.ShowRepository.Edit(show, false);
+ await Repositories.LibraryManager.ShowRepository.Edit(show);
episode = await _repository.Get(2);
Assert.Equal($"new-slug-3", episode.Slug);
}
@@ -145,8 +145,8 @@ namespace Kyoo.Tests.Database
{
Id = 2,
AbsoluteNumber = 56,
- ShowID = 1
- }, false);
+ ShowId = 1
+ });
Assert.Equal($"{TestSample.Get().Slug}-56", episode.Slug);
episode = await _repository.Get(2);
Assert.Equal($"{TestSample.Get().Slug}-56", episode.Slug);
@@ -161,8 +161,8 @@ namespace Kyoo.Tests.Database
Id = 2,
SeasonNumber = 1,
EpisodeNumber = 2,
- ShowID = 1
- }, false);
+ ShowId = 1
+ });
Assert.Equal($"{TestSample.Get().Slug}-s1e2", episode.Slug);
episode = await _repository.Get(2);
Assert.Equal($"{TestSample.Get().Slug}-s1e2", episode.Slug);
@@ -174,49 +174,25 @@ namespace Kyoo.Tests.Database
Episode episode = await _repository.Get(1);
episode.SeasonNumber = null;
episode.AbsoluteNumber = 12;
- episode = await _repository.Edit(episode, true);
+ episode = await _repository.Edit(episode);
Assert.Equal($"{TestSample.Get().Slug}-12", episode.Slug);
episode = await _repository.Get(1);
Assert.Equal($"{TestSample.Get().Slug}-12", episode.Slug);
}
- [Fact]
- public async Task MovieEpisodeTest()
- {
- Episode episode = await _repository.Create(TestSample.GetMovieEpisode());
- Assert.Equal(TestSample.Get().Slug, episode.Slug);
- episode = await _repository.Get(3);
- Assert.Equal(TestSample.Get().Slug, episode.Slug);
- }
-
- [Fact]
- public async Task MovieEpisodeEditTest()
- {
- await _repository.Create(TestSample.GetMovieEpisode());
- await Repositories.LibraryManager.Edit(new Show
- {
- Id = 1,
- Slug = "john-wick"
- }, false);
- Episode episode = await _repository.Get(3);
- Assert.Equal("john-wick", episode.Slug);
- }
-
[Fact]
public async Task CreateWithExternalIdTest()
{
Episode value = TestSample.GetNew();
- value.ExternalId = new[]
+ value.ExternalId = new Dictionary
{
- new MetadataId
+ ["2"] = new()
{
- Provider = TestSample.Get(),
Link = "link",
DataId = "id"
},
- new MetadataId
+ ["3"] = new()
{
- Provider = TestSample.GetNew(),
Link = "new-provider-link",
DataId = "new-id"
}
@@ -224,7 +200,6 @@ namespace Kyoo.Tests.Database
await _repository.Create(value);
Episode retrieved = await _repository.Get(2);
- await Repositories.LibraryManager.Load(retrieved, x => x.ExternalId);
Assert.Equal(2, retrieved.ExternalId.Count);
KAssert.DeepEqual(value.ExternalId.First(), retrieved.ExternalId.First());
KAssert.DeepEqual(value.ExternalId.Last(), retrieved.ExternalId.Last());
@@ -235,11 +210,8 @@ namespace Kyoo.Tests.Database
{
Episode value = await _repository.Get(TestSample.Get().Slug);
value.Name = "New Title";
- value.Images = new Dictionary
- {
- [Images.Poster] = "new-poster"
- };
- await _repository.Edit(value, false);
+ value.Poster = new Image("poster");
+ await _repository.Edit(value);
await using DatabaseContext database = Repositories.Context.New();
Episode retrieved = await database.Episodes.FirstAsync();
@@ -251,22 +223,18 @@ namespace Kyoo.Tests.Database
public async Task EditMetadataTest()
{
Episode value = await _repository.Get(TestSample.Get().Slug);
- value.ExternalId = new[]
+ value.ExternalId = new Dictionary
{
- new MetadataId
+ ["1"] = new()
{
- Provider = TestSample.Get(),
Link = "link",
DataId = "id"
},
};
- await _repository.Edit(value, false);
+ await _repository.Edit(value);
await using DatabaseContext database = Repositories.Context.New();
- Episode retrieved = await database.Episodes
- .Include(x => x.ExternalId)
- .ThenInclude(x => x.Provider)
- .FirstAsync();
+ Episode retrieved = await database.Episodes.FirstAsync();
KAssert.DeepEqual(value, retrieved);
}
@@ -275,41 +243,33 @@ namespace Kyoo.Tests.Database
public async Task AddMetadataTest()
{
Episode value = await _repository.Get(TestSample.Get().Slug);
- value.ExternalId = new List
+ value.ExternalId = new Dictionary
{
- new()
+ ["toto"] = new()
{
- Provider = TestSample.Get(),
Link = "link",
DataId = "id"
},
};
- await _repository.Edit(value, false);
+ await _repository.Edit(value);
{
await using DatabaseContext database = Repositories.Context.New();
- Episode retrieved = await database.Episodes
- .Include(x => x.ExternalId)
- .ThenInclude(x => x.Provider)
- .FirstAsync();
+ Episode retrieved = await database.Episodes.FirstAsync();
KAssert.DeepEqual(value, retrieved);
}
- value.ExternalId.Add(new MetadataId
+ value.ExternalId.Add("test", new MetadataId
{
- Provider = TestSample.GetNew(),
Link = "link",
DataId = "id"
});
- await _repository.Edit(value, false);
+ await _repository.Edit(value);
{
await using DatabaseContext database = Repositories.Context.New();
- Episode retrieved = await database.Episodes
- .Include(x => x.ExternalId)
- .ThenInclude(x => x.Provider)
- .FirstAsync();
+ Episode retrieved = await database.Episodes.FirstAsync();
KAssert.DeepEqual(value, retrieved);
}
@@ -326,7 +286,7 @@ namespace Kyoo.Tests.Database
Episode value = new()
{
Name = "This is a test super title",
- ShowID = 1,
+ ShowId = 1,
AbsoluteNumber = 2
};
await _repository.Create(value);
@@ -343,8 +303,8 @@ namespace Kyoo.Tests.Database
Episode expected = TestSample.Get();
expected.Id = 0;
- expected.ShowID = (await Repositories.LibraryManager.ShowRepository.Create(TestSample.Get())).Id;
- expected.SeasonID = (await Repositories.LibraryManager.SeasonRepository.Create(TestSample.Get())).Id;
+ expected.ShowId = (await Repositories.LibraryManager.ShowRepository.Create(TestSample.Get())).Id;
+ expected.SeasonId = (await Repositories.LibraryManager.SeasonRepository.Create(TestSample.Get())).Id;
await _repository.Create(expected);
KAssert.DeepEqual(expected, await _repository.Get(expected.Slug));
}
@@ -355,8 +315,8 @@ namespace Kyoo.Tests.Database
Episode expected = TestSample.Get();
KAssert.DeepEqual(expected, await _repository.CreateIfNotExists(TestSample.Get()));
await _repository.Delete(TestSample.Get());
- expected.ShowID = (await Repositories.LibraryManager.ShowRepository.Create(TestSample.Get())).Id;
- expected.SeasonID = (await Repositories.LibraryManager.SeasonRepository.Create(TestSample.Get())).Id;
+ expected.ShowId = (await Repositories.LibraryManager.ShowRepository.Create(TestSample.Get())).Id;
+ expected.SeasonId = (await Repositories.LibraryManager.SeasonRepository.Create(TestSample.Get())).Id;
KAssert.DeepEqual(expected, await _repository.CreateIfNotExists(expected));
}
}
diff --git a/back/tests/Kyoo.Tests/Database/SpecificTests/LibraryItemTest.cs b/back/tests/Kyoo.Tests/Database/SpecificTests/LibraryItemTest.cs
deleted file mode 100644
index db572ea1..00000000
--- a/back/tests/Kyoo.Tests/Database/SpecificTests/LibraryItemTest.cs
+++ /dev/null
@@ -1,98 +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;
-using Kyoo.Abstractions.Controllers;
-using Kyoo.Abstractions.Models;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace Kyoo.Tests.Database
-{
- namespace PostgreSQL
- {
- [Collection(nameof(Postgresql))]
- public class LibraryItemTest : ALibraryItemTest
- {
- public LibraryItemTest(PostgresFixture postgres, ITestOutputHelper output)
- : base(new RepositoryActivator(output, postgres)) { }
- }
- }
-
- public abstract class ALibraryItemTest
- {
- private readonly ILibraryItemRepository _repository;
- private readonly RepositoryActivator _repositories;
-
- protected ALibraryItemTest(RepositoryActivator repositories)
- {
- _repositories = repositories;
- _repository = repositories.LibraryManager.LibraryItemRepository;
- }
-
- [Fact]
- public async Task CountTest()
- {
- Assert.Equal(2, await _repository.GetCount());
- }
-
- [Fact]
- public async Task GetShowTests()
- {
- LibraryItem expected = new(TestSample.Get());
- LibraryItem actual = await _repository.Get(1);
- KAssert.DeepEqual(expected, actual);
- }
-
- [Fact]
- public async Task GetCollectionTests()
- {
- LibraryItem expected = new(TestSample.Get());
- LibraryItem actual = await _repository.Get(-1);
- KAssert.DeepEqual(expected, actual);
- }
-
- [Fact]
- public async Task GetShowSlugTests()
- {
- LibraryItem expected = new(TestSample.Get());
- LibraryItem actual = await _repository.Get(TestSample.Get().Slug);
- KAssert.DeepEqual(expected, actual);
- }
-
- [Fact]
- public async Task GetCollectionSlugTests()
- {
- LibraryItem expected = new(TestSample.Get());
- LibraryItem actual = await _repository.Get(TestSample.Get().Slug);
- KAssert.DeepEqual(expected, actual);
- }
-
- [Fact]
- public async Task GetDuplicatedSlugTests()
- {
- await _repositories.LibraryManager.Create(new Collection
- {
- Slug = TestSample.Get().Slug,
- Name = "name"
- });
- await Assert.ThrowsAsync(() => _repository.Get(TestSample.Get().Slug));
- }
- }
-}
diff --git a/back/tests/Kyoo.Tests/Database/SpecificTests/LibraryTests.cs b/back/tests/Kyoo.Tests/Database/SpecificTests/LibraryTests.cs
deleted file mode 100644
index 57a7deea..00000000
--- a/back/tests/Kyoo.Tests/Database/SpecificTests/LibraryTests.cs
+++ /dev/null
@@ -1,169 +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.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Kyoo.Abstractions.Controllers;
-using Kyoo.Abstractions.Models;
-using Kyoo.Postgresql;
-using Kyoo.Utils;
-using Microsoft.EntityFrameworkCore;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace Kyoo.Tests.Database
-{
- namespace PostgreSQL
- {
- [Collection(nameof(Postgresql))]
- public class LibraryTests : ALibraryTests
- {
- public LibraryTests(PostgresFixture postgres, ITestOutputHelper output)
- : base(new RepositoryActivator(output, postgres)) { }
- }
- }
-
- public abstract class ALibraryTests : RepositoryTests
- {
- private readonly ILibraryRepository _repository;
-
- protected ALibraryTests(RepositoryActivator repositories)
- : base(repositories)
- {
- _repository = Repositories.LibraryManager.LibraryRepository;
- }
-
- [Fact]
- public async Task CreateWithoutPathTest()
- {
- Library library = TestSample.GetNew();
- library.Paths = null;
- await Assert.ThrowsAsync(() => _repository.Create(library));
- }
-
- [Fact]
- public async Task CreateWithEmptySlugTest()
- {
- Library library = TestSample.GetNew();
- library.Slug = string.Empty;
- await Assert.ThrowsAsync(() => _repository.Create(library));
- }
-
- [Fact]
- public async Task CreateWithNumberSlugTest()
- {
- Library library = TestSample.GetNew();
- library.Slug = "2";
- Library ret = await _repository.Create(library);
- Assert.Equal("2!", ret.Slug);
- }
-
- [Fact]
- public async Task CreateWithoutNameTest()
- {
- Library library = TestSample.GetNew();
- library.Name = null;
- await Assert.ThrowsAsync(() => _repository.Create(library));
- }
-
- [Fact]
- public async Task CreateWithProvider()
- {
- Library library = TestSample.GetNew();
- library.Providers = new[] { TestSample.Get() };
- await _repository.Create(library);
- Library retrieved = await _repository.Get(2);
- await Repositories.LibraryManager.Load(retrieved, x => x.Providers);
- Assert.Single(retrieved.Providers);
- Assert.Equal(TestSample.Get().Slug, retrieved.Providers.First().Slug);
- }
-
- [Fact]
- public async Task EditTest()
- {
- Library value = await _repository.Get(TestSample.Get().Slug);
- value.Paths = new[] { "/super", "/test" };
- value.Name = "New Title";
- Library edited = await _repository.Edit(value, false);
-
- await using DatabaseContext database = Repositories.Context.New();
- Library show = await database.Libraries.FirstAsync();
-
- KAssert.DeepEqual(show, edited);
- }
-
- [Fact]
- public async Task EditProvidersTest()
- {
- Library value = await _repository.Get(TestSample.Get().Slug);
- value.Providers = new[]
- {
- TestSample.GetNew()
- };
- Library edited = await _repository.Edit(value, false);
-
- await using DatabaseContext database = Repositories.Context.New();
- Library show = await database.Libraries
- .Include(x => x.Providers)
- .FirstAsync();
-
- show.Providers.ForEach(x => x.Libraries = null);
- edited.Providers.ForEach(x => x.Libraries = null);
- KAssert.DeepEqual(show, edited);
- }
-
- [Fact]
- public async Task AddProvidersTest()
- {
- Library value = await _repository.Get(TestSample.Get().Slug);
- await Repositories.LibraryManager.Load(value, x => x.Providers);
- value.Providers.Add(TestSample.GetNew());
- Library edited = await _repository.Edit(value, false);
-
- await using DatabaseContext database = Repositories.Context.New();
- Library show = await database.Libraries
- .Include(x => x.Providers)
- .FirstAsync();
-
- show.Providers.ForEach(x => x.Libraries = null);
- edited.Providers.ForEach(x => x.Libraries = null);
- KAssert.DeepEqual(show, edited);
- }
-
- [Theory]
- [InlineData("test")]
- [InlineData("super")]
- [InlineData("title")]
- [InlineData("TiTlE")]
- [InlineData("SuPeR")]
- public async Task SearchTest(string query)
- {
- Library value = new()
- {
- Slug = "super-test",
- Name = "This is a test title",
- Paths = new[] { "path" }
- };
- await _repository.Create(value);
- ICollection ret = await _repository.Search(query);
- KAssert.DeepEqual(value, ret.First());
- }
- }
-}
diff --git a/back/tests/Kyoo.Tests/Database/SpecificTests/PeopleTests.cs b/back/tests/Kyoo.Tests/Database/SpecificTests/PeopleTests.cs
index e6b58242..8bf3b9da 100644
--- a/back/tests/Kyoo.Tests/Database/SpecificTests/PeopleTests.cs
+++ b/back/tests/Kyoo.Tests/Database/SpecificTests/PeopleTests.cs
@@ -52,17 +52,15 @@ namespace Kyoo.Tests.Database
public async Task CreateWithExternalIdTest()
{
People value = TestSample.GetNew();
- value.ExternalId = new[]
+ value.ExternalId = new Dictionary
{
- new MetadataId
+ ["2"] = new()
{
- Provider = TestSample.Get(),
Link = "link",
DataId = "id"
},
- new MetadataId
+ ["1"] = new()
{
- Provider = TestSample.GetNew(),
Link = "new-provider-link",
DataId = "new-id"
}
@@ -70,7 +68,6 @@ namespace Kyoo.Tests.Database
await _repository.Create(value);
People retrieved = await _repository.Get(2);
- await Repositories.LibraryManager.Load(retrieved, x => x.ExternalId);
Assert.Equal(2, retrieved.ExternalId.Count);
KAssert.DeepEqual(value.ExternalId.First(), retrieved.ExternalId.First());
KAssert.DeepEqual(value.ExternalId.Last(), retrieved.ExternalId.Last());
@@ -81,11 +78,8 @@ namespace Kyoo.Tests.Database
{
People value = await _repository.Get(TestSample.Get().Slug);
value.Name = "New Name";
- value.Images = new Dictionary
- {
- [Images.Poster] = "new-poster"
- };
- await _repository.Edit(value, false);
+ value.Poster = new Image("poster");
+ await _repository.Edit(value);
await using DatabaseContext database = Repositories.Context.New();
People retrieved = await database.People.FirstAsync();
@@ -97,22 +91,18 @@ namespace Kyoo.Tests.Database
public async Task EditMetadataTest()
{
People value = await _repository.Get(TestSample.Get().Slug);
- value.ExternalId = new[]
+ value.ExternalId = new Dictionary
{
- new MetadataId
+ ["toto"] = new()
{
- Provider = TestSample.Get(),
Link = "link",
DataId = "id"
},
};
- await _repository.Edit(value, false);
+ await _repository.Edit(value);
await using DatabaseContext database = Repositories.Context.New();
- People retrieved = await database.People
- .Include(x => x.ExternalId)
- .ThenInclude(x => x.Provider)
- .FirstAsync();
+ People retrieved = await database.People.FirstAsync();
KAssert.DeepEqual(value, retrieved);
}
@@ -121,41 +111,32 @@ namespace Kyoo.Tests.Database
public async Task AddMetadataTest()
{
People value = await _repository.Get(TestSample.Get().Slug);
- value.ExternalId = new List
+ value.ExternalId = new Dictionary
{
- new()
+ ["1"] = new()
{
- Provider = TestSample.Get(),
Link = "link",
DataId = "id"
},
};
- await _repository.Edit(value, false);
+ await _repository.Edit(value);
{
await using DatabaseContext database = Repositories.Context.New();
- People retrieved = await database.People
- .Include(x => x.ExternalId)
- .ThenInclude(x => x.Provider)
- .FirstAsync();
-
+ People retrieved = await database.People.FirstAsync();
KAssert.DeepEqual(value, retrieved);
}
- value.ExternalId.Add(new MetadataId
+ value.ExternalId.Add("toto", new MetadataId
{
- Provider = TestSample.GetNew(),
Link = "link",
DataId = "id"
});
- await _repository.Edit(value, false);
+ await _repository.Edit(value);
{
await using DatabaseContext database = Repositories.Context.New();
- People retrieved = await database.People
- .Include(x => x.ExternalId)
- .ThenInclude(x => x.Provider)
- .FirstAsync();
+ People retrieved = await database.People.FirstAsync();
KAssert.DeepEqual(value, retrieved);
}
diff --git a/back/tests/Kyoo.Tests/Database/SpecificTests/ProviderTests.cs b/back/tests/Kyoo.Tests/Database/SpecificTests/ProviderTests.cs
deleted file mode 100644
index 49340367..00000000
--- a/back/tests/Kyoo.Tests/Database/SpecificTests/ProviderTests.cs
+++ /dev/null
@@ -1,48 +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.Diagnostics.CodeAnalysis;
-using Kyoo.Abstractions.Controllers;
-using Kyoo.Abstractions.Models;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace Kyoo.Tests.Database
-{
- namespace PostgreSQL
- {
- [Collection(nameof(Postgresql))]
- public class ProviderTests : AProviderTests
- {
- public ProviderTests(PostgresFixture postgres, ITestOutputHelper output)
- : base(new RepositoryActivator(output, postgres)) { }
- }
- }
-
- public abstract class AProviderTests : RepositoryTests
- {
- [SuppressMessage("ReSharper", "NotAccessedField.Local")]
- private readonly IProviderRepository _repository;
-
- protected AProviderTests(RepositoryActivator repositories)
- : base(repositories)
- {
- _repository = Repositories.LibraryManager.ProviderRepository;
- }
- }
-}
diff --git a/back/tests/Kyoo.Tests/Database/SpecificTests/SeasonTests.cs b/back/tests/Kyoo.Tests/Database/SpecificTests/SeasonTests.cs
index 886cbade..85a5c3d0 100644
--- a/back/tests/Kyoo.Tests/Database/SpecificTests/SeasonTests.cs
+++ b/back/tests/Kyoo.Tests/Database/SpecificTests/SeasonTests.cs
@@ -55,10 +55,10 @@ namespace Kyoo.Tests.Database
Assert.Equal("anohana-s1", season.Slug);
Show show = new()
{
- Id = season.ShowID,
+ Id = season.ShowId,
Slug = "new-slug"
};
- await Repositories.LibraryManager.ShowRepository.Edit(show, false);
+ await Repositories.LibraryManager.ShowRepository.Edit(show);
season = await _repository.Get(1);
Assert.Equal("new-slug-s1", season.Slug);
}
@@ -72,8 +72,8 @@ namespace Kyoo.Tests.Database
{
Id = 1,
SeasonNumber = 2,
- ShowID = 1
- }, false);
+ ShowId = 1
+ });
season = await _repository.Get(1);
Assert.Equal("anohana-s2", season.Slug);
}
@@ -83,7 +83,7 @@ namespace Kyoo.Tests.Database
{
Season season = await _repository.Create(new Season
{
- ShowID = TestSample.Get().Id,
+ ShowId = TestSample.Get().Id,
SeasonNumber = 2
});
Assert.Equal($"{TestSample.Get().Slug}-s2", season.Slug);
@@ -93,17 +93,15 @@ namespace Kyoo.Tests.Database
public async Task CreateWithExternalIdTest()
{
Season season = TestSample.GetNew();
- season.ExternalId = new[]
+ season.ExternalId = new Dictionary
{
- new MetadataId
+ ["2"] = new()
{
- Provider = TestSample.Get