mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Creating the provider composite and testing the merger
This commit is contained in:
parent
2d45d6422d
commit
0265c27010
@ -53,4 +53,18 @@ namespace Kyoo.Controllers
|
||||
|
||||
Task<ICollection<PeopleRole>> GetPeople(Show show);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A special <see cref="IMetadataProvider"/> that merge results.
|
||||
/// This interface exists to specify witch provider to use but it can be used like any other metadata provider.
|
||||
/// </summary>
|
||||
public interface IProviderComposite : IMetadataProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Select witch providers to use.
|
||||
/// The <see cref="IMetadataProvider"/> associated with the given <see cref="Provider"/> will be used.
|
||||
/// </summary>
|
||||
/// <param name="providers">The list of providers to use</param>
|
||||
void UseProviders(IEnumerable<Provider> providers);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo
|
||||
@ -111,7 +112,8 @@ namespace Kyoo
|
||||
/// <param name="second">Missing fields of first will be completed by fields of this item. If second is null, the function no-op.</param>
|
||||
/// <typeparam name="T">Fields of T will be merged</typeparam>
|
||||
/// <returns><see cref="first"/></returns>
|
||||
public static T Merge<T>(T first, T second)
|
||||
[ContractAnnotation("=> null; first:notnull => notnull; second:notnull => notnull")]
|
||||
public static T Merge<T>([CanBeNull] T first, [CanBeNull] T second)
|
||||
{
|
||||
if (first == null)
|
||||
return second;
|
||||
@ -127,9 +129,7 @@ namespace Kyoo
|
||||
{
|
||||
object oldValue = property.GetValue(first);
|
||||
object newValue = property.GetValue(second);
|
||||
object defaultValue = property.PropertyType.IsValueType
|
||||
? Activator.CreateInstance(property.PropertyType)
|
||||
: null;
|
||||
object defaultValue = property.PropertyType.GetClrDefault();
|
||||
|
||||
if (oldValue?.Equals(defaultValue) != false)
|
||||
property.SetValue(first, newValue);
|
||||
@ -139,11 +139,14 @@ namespace Kyoo
|
||||
Type enumerableType = Utility.GetGenericDefinition(property.PropertyType, typeof(IEnumerable<>))
|
||||
.GenericTypeArguments
|
||||
.First();
|
||||
Func<IResource, IResource, bool> equalityComparer = enumerableType.IsAssignableTo(typeof(IResource))
|
||||
? (x, y) => x.Slug == y.Slug
|
||||
: null;
|
||||
property.SetValue(first, Utility.RunGenericMethod<object>(
|
||||
typeof(Utility),
|
||||
typeof(Merger),
|
||||
nameof(MergeLists),
|
||||
enumerableType,
|
||||
oldValue, newValue, null));
|
||||
enumerableType,
|
||||
oldValue, newValue, equalityComparer));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,15 +182,49 @@ namespace Kyoo
|
||||
return types.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
|
||||
}
|
||||
|
||||
public static MethodInfo GetMethod(Type type, BindingFlags flag, string name, Type[] generics, object[] args)
|
||||
/// <summary>
|
||||
/// Retrieve a method from an <see cref="Type"/> with the given name and respect the
|
||||
/// amount of parameters and generic parameters. This works for polymorphic methods.
|
||||
/// </summary>
|
||||
/// <param name="type">
|
||||
/// The type owning the method. For non static methods, this is the <c>this</c>.
|
||||
/// </param>
|
||||
/// <param name="flag">
|
||||
/// The binding flags of the method. This allow you to specify public/private and so on.
|
||||
/// </param>
|
||||
/// <param name="name">
|
||||
/// The name of the method.
|
||||
/// </param>
|
||||
/// <param name="generics">
|
||||
/// The list of generic parameters.
|
||||
/// </param>
|
||||
/// <param name="args">
|
||||
/// The list of parameters.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentException">No method match the given constraints.</exception>
|
||||
/// <returns>The method handle of the matching method.</returns>
|
||||
[PublicAPI]
|
||||
[NotNull]
|
||||
public static MethodInfo GetMethod([NotNull] Type type,
|
||||
BindingFlags flag,
|
||||
string name,
|
||||
[NotNull] Type[] generics,
|
||||
[NotNull] 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)
|
||||
.Where(x => x.GetParameters().Length == args.Length)
|
||||
.IfEmpty(() => throw new NullReferenceException($"A method named {name} with " +
|
||||
$"{args.Length} arguments and {generics.Length} generic " +
|
||||
$"types could not be found on {type.Name}."))
|
||||
.IfEmpty(() => throw new ArgumentException($"A method named {name} with " +
|
||||
$"{args.Length} arguments and {generics.Length} generic " +
|
||||
$"types could not be found on {type.Name}."))
|
||||
// TODO this won't work but I don't know why.
|
||||
// .Where(x =>
|
||||
// {
|
||||
@ -211,9 +245,34 @@ namespace Kyoo
|
||||
|
||||
if (methods.Length == 1)
|
||||
return methods[0];
|
||||
throw new NullReferenceException($"Multiple methods named {name} match the generics and parameters constraints.");
|
||||
throw new ArgumentException($"Multiple methods named {name} match the generics and parameters constraints.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run a generic static method for a runtime <see cref="Type"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// To run <see cref="Merger.MergeLists{T}"/> for a List where you don't know the type at compile type,
|
||||
/// you could do:
|
||||
/// <code>
|
||||
/// Utility.RunGenericMethod<object>(
|
||||
/// typeof(Utility),
|
||||
/// nameof(MergeLists),
|
||||
/// enumerableType,
|
||||
/// oldValue, newValue, equalityComparer)
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="owner">The type that owns the method. For non static methods, the type of <c>this</c>.</param>
|
||||
/// <param name="methodName">The name of the method. You should use the <c>nameof</c> keyword.</param>
|
||||
/// <param name="type">The generic type to run the method with.</param>
|
||||
/// <param name="args">The list of arguments of the method</param>
|
||||
/// <typeparam name="T">
|
||||
/// The return type of the method. You can put <see cref="object"/> for an unknown one.
|
||||
/// </typeparam>
|
||||
/// <exception cref="ArgumentException">No method match the given constraints.</exception>
|
||||
/// <returns>The return of the method you wanted to run.</returns>
|
||||
/// <seealso cref="RunGenericMethod{T}(object,string,System.Type,object[])"/>
|
||||
/// <seealso cref="RunGenericMethod{T}(System.Type,string,System.Type[],object[])"/>
|
||||
public static T RunGenericMethod<T>(
|
||||
[NotNull] Type owner,
|
||||
[NotNull] string methodName,
|
||||
@ -223,6 +282,34 @@ namespace Kyoo
|
||||
return RunGenericMethod<T>(owner, methodName, new[] {type}, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run a generic static method for a multiple runtime <see cref="Type"/>.
|
||||
/// If your generic method only needs one type, see
|
||||
/// <see cref="RunGenericMethod{T}(System.Type,string,System.Type,object[])"/>
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// To run <see cref="Merger.MergeLists{T}"/> for a List where you don't know the type at compile type,
|
||||
/// you could do:
|
||||
/// <code>
|
||||
/// Utility.RunGenericMethod<object>(
|
||||
/// typeof(Utility),
|
||||
/// nameof(MergeLists),
|
||||
/// enumerableType,
|
||||
/// oldValue, newValue, equalityComparer)
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="owner">The type that owns the method. For non static methods, the type of <c>this</c>.</param>
|
||||
/// <param name="methodName">The name of the method. You should use the <c>nameof</c> keyword.</param>
|
||||
/// <param name="types">The list of generic types to run the method with.</param>
|
||||
/// <param name="args">The list of arguments of the method</param>
|
||||
/// <typeparam name="T">
|
||||
/// The return type of the method. You can put <see cref="object"/> for an unknown one.
|
||||
/// </typeparam>
|
||||
/// <exception cref="ArgumentException">No method match the given constraints.</exception>
|
||||
/// <returns>The return of the method you wanted to run.</returns>
|
||||
/// <seealso cref="RunGenericMethod{T}(object,string,System.Type[],object[])"/>
|
||||
/// <seealso cref="RunGenericMethod{T}(System.Type,string,System.Type,object[])"/>
|
||||
[PublicAPI]
|
||||
public static T RunGenericMethod<T>(
|
||||
[NotNull] Type owner,
|
||||
[NotNull] string methodName,
|
||||
@ -238,9 +325,34 @@ namespace Kyoo
|
||||
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?.ToArray());
|
||||
return (T)method.MakeGenericMethod(types).Invoke(null, args.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run a generic method for a runtime <see cref="Type"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// To run <see cref="Merger.MergeLists{T}"/> for a List where you don't know the type at compile type,
|
||||
/// you could do:
|
||||
/// <code>
|
||||
/// Utility.RunGenericMethod<object>(
|
||||
/// typeof(Utility),
|
||||
/// nameof(MergeLists),
|
||||
/// enumerableType,
|
||||
/// oldValue, newValue, equalityComparer)
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="instance">The <c>this</c> of the method to run.</param>
|
||||
/// <param name="methodName">The name of the method. You should use the <c>nameof</c> keyword.</param>
|
||||
/// <param name="type">The generic type to run the method with.</param>
|
||||
/// <param name="args">The list of arguments of the method</param>
|
||||
/// <typeparam name="T">
|
||||
/// The return type of the method. You can put <see cref="object"/> for an unknown one.
|
||||
/// </typeparam>
|
||||
/// <exception cref="ArgumentException">No method match the given constraints.</exception>
|
||||
/// <returns>The return of the method you wanted to run.</returns>
|
||||
/// <seealso cref="RunGenericMethod{T}(object,string,System.Type,object[])"/>
|
||||
/// <seealso cref="RunGenericMethod{T}(System.Type,string,System.Type[],object[])"/>
|
||||
public static T RunGenericMethod<T>(
|
||||
[NotNull] object instance,
|
||||
[NotNull] string methodName,
|
||||
@ -250,6 +362,33 @@ namespace Kyoo
|
||||
return RunGenericMethod<T>(instance, methodName, new[] {type}, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run a generic method for a multiple runtime <see cref="Type"/>.
|
||||
/// If your generic method only needs one type, see
|
||||
/// <see cref="RunGenericMethod{T}(object,string,System.Type,object[])"/>
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// To run <see cref="Merger.MergeLists{T}"/> for a List where you don't know the type at compile type,
|
||||
/// you could do:
|
||||
/// <code>
|
||||
/// Utility.RunGenericMethod<object>(
|
||||
/// typeof(Utility),
|
||||
/// nameof(MergeLists),
|
||||
/// enumerableType,
|
||||
/// oldValue, newValue, equalityComparer)
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="instance">The <c>this</c> of the method to run.</param>
|
||||
/// <param name="methodName">The name of the method. You should use the <c>nameof</c> keyword.</param>
|
||||
/// <param name="types">The list of generic types to run the method with.</param>
|
||||
/// <param name="args">The list of arguments of the method</param>
|
||||
/// <typeparam name="T">
|
||||
/// The return type of the method. You can put <see cref="object"/> for an unknown one.
|
||||
/// </typeparam>
|
||||
/// <exception cref="ArgumentException">No method match the given constraints.</exception>
|
||||
/// <returns>The return of the method you wanted to run.</returns>
|
||||
/// <seealso cref="RunGenericMethod{T}(object,string,System.Type[],object[])"/>
|
||||
/// <seealso cref="RunGenericMethod{T}(System.Type,string,System.Type,object[])"/>
|
||||
public static T RunGenericMethod<T>(
|
||||
[NotNull] object instance,
|
||||
[NotNull] string methodName,
|
||||
@ -263,7 +402,7 @@ namespace Kyoo
|
||||
if (types == null || types.Length == 0)
|
||||
throw new ArgumentNullException(nameof(types));
|
||||
MethodInfo method = GetMethod(instance.GetType(), BindingFlags.Instance, methodName, types, args);
|
||||
return (T)method.MakeGenericMethod(types).Invoke(instance, args?.ToArray());
|
||||
return (T)method.MakeGenericMethod(types).Invoke(instance, args.ToArray());
|
||||
}
|
||||
|
||||
public static string ToQueryString(this Dictionary<string, string> query)
|
||||
|
@ -1,4 +1,8 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Attributes;
|
||||
using Xunit;
|
||||
|
||||
namespace Kyoo.Tests
|
||||
@ -17,5 +21,192 @@ namespace Kyoo.Tests
|
||||
Assert.Null(genre.Name);
|
||||
Assert.Null(genre.Slug);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeTest()
|
||||
{
|
||||
Genre genre = new()
|
||||
{
|
||||
ID = 5
|
||||
};
|
||||
Genre genre2 = new()
|
||||
{
|
||||
Name = "test"
|
||||
};
|
||||
Genre ret = Merger.Merge(genre, genre2);
|
||||
Assert.True(ReferenceEquals(genre, ret));
|
||||
Assert.Equal(5, ret.ID);
|
||||
Assert.Equal("test", genre.Name);
|
||||
Assert.Null(genre.Slug);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[SuppressMessage("ReSharper", "ExpressionIsAlwaysNull")]
|
||||
public void MergeNullTests()
|
||||
{
|
||||
Genre genre = new()
|
||||
{
|
||||
ID = 5
|
||||
};
|
||||
Assert.True(ReferenceEquals(genre, Merger.Merge(genre, null)));
|
||||
Assert.True(ReferenceEquals(genre, Merger.Merge(null, genre)));
|
||||
Assert.Null(Merger.Merge<Genre>(null, null));
|
||||
}
|
||||
|
||||
private class TestIOnMerge : IOnMerge
|
||||
{
|
||||
public void OnMerge(object other)
|
||||
{
|
||||
Exception exception = new();
|
||||
exception.Data[0] = other;
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnMergeTest()
|
||||
{
|
||||
TestIOnMerge test = new();
|
||||
TestIOnMerge test2 = new();
|
||||
Assert.Throws<Exception>(() => Merger.Merge(test, test2));
|
||||
try
|
||||
{
|
||||
Merger.Merge(test, test2);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Assert.True(ReferenceEquals(test2, ex.Data[0]));
|
||||
}
|
||||
}
|
||||
|
||||
private class Test
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
public int[] Numbers { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GlobalMergeListTest()
|
||||
{
|
||||
Test test = new()
|
||||
{
|
||||
ID = 5,
|
||||
Numbers = new [] { 1 }
|
||||
};
|
||||
Test test2 = new()
|
||||
{
|
||||
Numbers = new [] { 3 }
|
||||
};
|
||||
Test ret = Merger.Merge(test, test2);
|
||||
Assert.True(ReferenceEquals(test, ret));
|
||||
Assert.Equal(5, ret.ID);
|
||||
|
||||
Assert.Equal(2, ret.Numbers.Length);
|
||||
Assert.Equal(1, ret.Numbers[0]);
|
||||
Assert.Equal(3, ret.Numbers[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GlobalMergeListDuplicatesTest()
|
||||
{
|
||||
Test test = new()
|
||||
{
|
||||
ID = 5,
|
||||
Numbers = new [] { 1 }
|
||||
};
|
||||
Test test2 = new()
|
||||
{
|
||||
Numbers = new []
|
||||
{
|
||||
1,
|
||||
3,
|
||||
3
|
||||
}
|
||||
};
|
||||
Test ret = Merger.Merge(test, test2);
|
||||
Assert.True(ReferenceEquals(test, ret));
|
||||
Assert.Equal(5, ret.ID);
|
||||
|
||||
Assert.Equal(4, ret.Numbers.Length);
|
||||
Assert.Equal(1, ret.Numbers[0]);
|
||||
Assert.Equal(1, ret.Numbers[1]);
|
||||
Assert.Equal(3, ret.Numbers[2]);
|
||||
Assert.Equal(3, ret.Numbers[3]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GlobalMergeListDuplicatesResourcesTest()
|
||||
{
|
||||
Show test = new()
|
||||
{
|
||||
ID = 5,
|
||||
Genres = new [] { new Genre("test") }
|
||||
};
|
||||
Show test2 = new()
|
||||
{
|
||||
Genres = new []
|
||||
{
|
||||
new Genre("test"),
|
||||
new Genre("test2")
|
||||
}
|
||||
};
|
||||
Show ret = Merger.Merge(test, test2);
|
||||
Assert.True(ReferenceEquals(test, ret));
|
||||
Assert.Equal(5, ret.ID);
|
||||
|
||||
Assert.Equal(2, ret.Genres.Count);
|
||||
Assert.Equal("test", ret.Genres.ToArray()[0].Slug);
|
||||
Assert.Equal("test2", ret.Genres.ToArray()[1].Slug);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeListTest()
|
||||
{
|
||||
int[] first = { 1 };
|
||||
int[] second = {
|
||||
3,
|
||||
3
|
||||
};
|
||||
int[] ret = Merger.MergeLists(first, second);
|
||||
|
||||
Assert.Equal(3, ret.Length);
|
||||
Assert.Equal(1, ret[0]);
|
||||
Assert.Equal(3, ret[1]);
|
||||
Assert.Equal(3, ret[2]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeListDuplicateTest()
|
||||
{
|
||||
int[] first = { 1 };
|
||||
int[] second = {
|
||||
1,
|
||||
3,
|
||||
3
|
||||
};
|
||||
int[] ret = Merger.MergeLists(first, second);
|
||||
|
||||
Assert.Equal(4, ret.Length);
|
||||
Assert.Equal(1, ret[0]);
|
||||
Assert.Equal(1, ret[1]);
|
||||
Assert.Equal(3, ret[2]);
|
||||
Assert.Equal(3, ret[3]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeListDuplicateCustomEqualityTest()
|
||||
{
|
||||
int[] first = { 1 };
|
||||
int[] second = {
|
||||
3,
|
||||
2
|
||||
};
|
||||
int[] ret = Merger.MergeLists(first, second, (x, y) => x % 2 == y % 2);
|
||||
|
||||
Assert.Equal(2, ret.Length);
|
||||
Assert.Equal(1, ret[0]);
|
||||
Assert.Equal(2, ret[1]);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Kyoo.Models;
|
||||
using Xunit;
|
||||
|
||||
@ -31,5 +32,47 @@ namespace Kyoo.Tests
|
||||
Assert.Equal("ID", Utility.GetPropertyName(memberCast));
|
||||
Assert.Throws<ArgumentException>(() => Utility.GetPropertyName(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMethodTest()
|
||||
{
|
||||
MethodInfo method = Utility.GetMethod(typeof(UtilityTests),
|
||||
BindingFlags.Instance | BindingFlags.Public,
|
||||
nameof(GetMethodTest),
|
||||
Array.Empty<Type>(),
|
||||
Array.Empty<object>());
|
||||
Assert.Equal(MethodBase.GetCurrentMethod(), method);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMethodInvalidGenericsTest()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => Utility.GetMethod(typeof(UtilityTests),
|
||||
BindingFlags.Instance | BindingFlags.Public,
|
||||
nameof(GetMethodTest),
|
||||
new [] { typeof(Utility) },
|
||||
Array.Empty<object>()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMethodInvalidParamsTest()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => Utility.GetMethod(typeof(UtilityTests),
|
||||
BindingFlags.Instance | BindingFlags.Public,
|
||||
nameof(GetMethodTest),
|
||||
Array.Empty<Type>(),
|
||||
new object[] { this }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMethodTest2()
|
||||
{
|
||||
MethodInfo method = Utility.GetMethod(typeof(Merger),
|
||||
BindingFlags.Static | BindingFlags.Public,
|
||||
nameof(Merger.MergeLists),
|
||||
new [] { typeof(string) },
|
||||
new object[] { "string", "string2", null });
|
||||
Assert.Equal(nameof(Merger.MergeLists), method.Name);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,86 +1,126 @@
|
||||
using System;
|
||||
using Kyoo.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public class ProviderComposite : IMetadataProvider
|
||||
/// <summary>
|
||||
/// A metadata provider composite that merge results from all available providers.
|
||||
/// </summary>
|
||||
public class ProviderComposite : IProviderComposite
|
||||
{
|
||||
private readonly IEnumerable<IMetadataProvider> _providers;
|
||||
/// <summary>
|
||||
/// The list of metadata providers
|
||||
/// </summary>
|
||||
private readonly ICollection<IMetadataProvider> _providers;
|
||||
|
||||
/// <summary>
|
||||
/// The list of selected providers. If no provider has been selected, this is null.
|
||||
/// </summary>
|
||||
private ICollection<Provider> _selectedProviders;
|
||||
|
||||
/// <summary>
|
||||
/// The logger used to print errors.
|
||||
/// </summary>
|
||||
private readonly ILogger<ProviderComposite> _logger;
|
||||
|
||||
public ProviderComposite(IEnumerable<IMetadataProvider> providers)
|
||||
/// <summary>
|
||||
/// Since this is a composite and not a real provider, no metadata is available.
|
||||
/// It is not meant to be stored or selected. This class will handle merge based on what is required.
|
||||
/// </summary>
|
||||
public Provider Provider => null;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="ProviderComposite"/> with a list of available providers.
|
||||
/// </summary>
|
||||
/// <param name="providers">The list of providers to merge.</param>
|
||||
/// <param name="logger">The logger used to print errors.</param>
|
||||
public ProviderComposite(IEnumerable<IMetadataProvider> providers, ILogger<ProviderComposite> logger)
|
||||
{
|
||||
_providers = providers;
|
||||
_providers = providers.ToArray();
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void UseProviders(IEnumerable<Provider> providers)
|
||||
{
|
||||
_selectedProviders = providers.ToArray();
|
||||
}
|
||||
|
||||
public Provider Provider { get; }
|
||||
public Task<T> Get<T>(T item) where T : class, IResource
|
||||
/// <summary>
|
||||
/// Return the list of providers that should be used for queries.
|
||||
/// </summary>
|
||||
/// <returns>The list of providers to use, respecting the <see cref="UseProviders"/>.</returns>
|
||||
private IEnumerable<IMetadataProvider> _GetProviders()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return _selectedProviders?
|
||||
.Select(x => _providers.FirstOrDefault(y => y.Provider.Slug == x.Slug))
|
||||
.Where(x => x != null)
|
||||
?? _providers;
|
||||
}
|
||||
|
||||
public Task<ICollection<T>> Search<T>(string query) where T : class, IResource
|
||||
/// <inheritdoc />
|
||||
public async Task<T> Get<T>(T item)
|
||||
where T : class, IResource
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
T ret = null;
|
||||
|
||||
foreach (IMetadataProvider provider in _GetProviders())
|
||||
{
|
||||
try
|
||||
{
|
||||
ret = Merger.Merge(ret, await provider.Get(ret ?? item));
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
// Silenced
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "The provider {Provider} could not get a {Type}",
|
||||
provider.Provider.Name, typeof(T).Name);
|
||||
}
|
||||
}
|
||||
|
||||
return Merger.Merge(ret, item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ICollection<T>> Search<T>(string query)
|
||||
where T : class, IResource
|
||||
{
|
||||
List<T> ret = new();
|
||||
|
||||
foreach (IMetadataProvider provider in _GetProviders())
|
||||
{
|
||||
try
|
||||
{
|
||||
ret.AddRange(await provider.Search<T>(query));
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
// Silenced
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "The provider {Provider} could not search for {Type}",
|
||||
provider.Provider.Name, typeof(T).Name);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Task<ICollection<PeopleRole>> GetPeople(Show show)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
// private async Task<T> GetMetadata<T>(Func<IMetadataProvider, Task<T>> providerCall, Library library, string what)
|
||||
// where T : new()
|
||||
// {
|
||||
// T ret = new();
|
||||
//
|
||||
// IEnumerable<IMetadataProvider> providers = library?.Providers
|
||||
// .Select(x => _providers.FirstOrDefault(y => y.Provider.Slug == x.Slug))
|
||||
// .Where(x => x != null)
|
||||
// ?? _providers;
|
||||
//
|
||||
// foreach (IMetadataProvider provider in providers)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// ret = Merger.Merge(ret, await providerCall(provider));
|
||||
// } catch (Exception ex)
|
||||
// {
|
||||
// await Console.Error.WriteLineAsync(
|
||||
// $"The provider {provider.Provider.Name} could not work for {what}. Exception: {ex.Message}");
|
||||
// }
|
||||
// }
|
||||
// return ret;
|
||||
// }
|
||||
//
|
||||
// private async Task<List<T>> GetMetadata<T>(
|
||||
// Func<IMetadataProvider, Task<ICollection<T>>> providerCall,
|
||||
// Library library,
|
||||
// string what)
|
||||
// {
|
||||
// List<T> ret = new();
|
||||
//
|
||||
// IEnumerable<IMetadataProvider> providers = library?.Providers
|
||||
// .Select(x => _providers.FirstOrDefault(y => y.Provider.Slug == x.Slug))
|
||||
// .Where(x => x != null)
|
||||
// ?? _providers;
|
||||
//
|
||||
// foreach (IMetadataProvider provider in providers)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// ret.AddRange(await providerCall(provider) ?? new List<T>());
|
||||
// } catch (Exception ex)
|
||||
// {
|
||||
// await Console.Error.WriteLineAsync(
|
||||
// $"The provider {provider.Provider.Name} coudln't work for {what}. Exception: {ex.Message}");
|
||||
// }
|
||||
// }
|
||||
// return ret;
|
||||
// }
|
||||
//
|
||||
|
||||
// public async Task<Collection> GetCollectionFromName(string name, Library library)
|
||||
// {
|
||||
// Collection collection = await GetMetadata(
|
||||
|
Loading…
x
Reference in New Issue
Block a user