Fixing task arguments

This commit is contained in:
Zoe Roux 2021-05-03 23:33:48 +02:00
parent 7613efb4b4
commit a18f393926
7 changed files with 76 additions and 38 deletions

View File

@ -105,6 +105,7 @@ namespace Kyoo.Authentication
.AddInMemoryIdentityResources(IdentityContext.GetIdentityResources())
.AddInMemoryApiScopes(IdentityContext.GetScopes())
.AddInMemoryApiResources(IdentityContext.GetApis())
.AddInMemoryClients(IdentityContext.GetClients())
// .AddProfileService<AccountController>()
.AddSigninKeys(certificateOptions);

View File

@ -7,6 +7,7 @@
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<GenerateDependencyFile>false</GenerateDependencyFile>
<GenerateRuntimeConfigurationFiles>false</GenerateRuntimeConfigurationFiles>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<Company>SDG</Company>
<Authors>Zoe Roux</Authors>

View File

@ -145,6 +145,7 @@ namespace Kyoo.Controllers
ICollection<Type> needed = conditional.Condition.Needed
.Where(y => !available.Contains(y))
.ToList();
// TODO handle circular dependencies, actually it might stack overflow.
needed = needed.Where(x => !conditionals
.Where(y => y.Type == x)
.Any(y => IsAvailable(y)))

View File

@ -24,7 +24,7 @@ namespace Kyoo.Controllers
/// </summary>
private readonly IUnityContainer _container;
/// <summary>
/// The configuration instance used to get schedule informations
/// The configuration instance used to get schedule information
/// </summary>
private readonly IConfiguration _configuration;
/// <summary>
@ -37,7 +37,7 @@ namespace Kyoo.Controllers
/// </summary>
private List<(ITask task, DateTime scheduledDate)> _tasks;
/// <summary>
/// The queue of tasks that should be runned as soon as possible.
/// The queue of tasks that should be run as soon as possible.
/// </summary>
private readonly Queue<(ITask, Dictionary<string, object>)> _queuedTasks = new();
/// <summary>
@ -108,27 +108,11 @@ namespace Kyoo.Controllers
_runningTask = task;
try
{
ICollection<TaskParameter> all = task.GetParameters();
TaskParameters args = new(arguments
.Select(x => (value: x, arg: all
.FirstOrDefault(y => string.Equals(y.Name, x.Key, StringComparison.OrdinalIgnoreCase))))
.Select(x =>
{
if (x.arg == null)
throw new ArgumentException($"Invalid argument name: {x.value.Key}");
return x.arg.CreateValue(x.value.Value);
}));
_logger.LogInformation("Task starting: {Task}", task.Name);
InjectServices(task);
await task.Run(args, _taskToken.Token);
_logger.LogInformation("Task finished: {Task}", task.Name);
await RunTask(task, arguments);
}
catch (Exception e)
{
_logger.LogError("An unhandled exception occured while running the task {Task}.\n" +
"Inner exception: {Exception}\n\n", task.Name, e.Message);
_logger.LogError(e, "An unhandled exception occured while running the task {Task}", task.Name);
}
}
else
@ -139,14 +123,51 @@ namespace Kyoo.Controllers
}
}
/// <summary>
/// Parse parameters, inject a task and run it.
/// </summary>
/// <param name="task">The task to run</param>
/// <param name="arguments">The arguments to pass to the function</param>
/// <exception cref="ArgumentException">There was an invalid argument or a required argument was not found.</exception>
private async Task RunTask(ITask task, Dictionary<string, object> arguments)
{
_logger.LogInformation("Task starting: {Task}", task.Name);
ICollection<TaskParameter> all = task.GetParameters();
ICollection<string> invalids = arguments.Keys
.Where(x => all.Any(y => x != y.Name))
.ToArray();
if (invalids.Any())
{
string invalidsStr = string.Join(", ", invalids);
throw new ArgumentException($"{invalidsStr} are invalid arguments for the task {task.Name}");
}
TaskParameters args = new(all
.Select(x =>
{
object value = arguments
.FirstOrDefault(y => string.Equals(y.Key, x.Name, StringComparison.OrdinalIgnoreCase))
.Value;
if (value == null && x.IsRequired)
throw new ArgumentException($"The argument {x.Name} is required to run {task.Name}" +
" but it was not specified.");
return x.CreateValue(value ?? x.DefaultValue);
}));
InjectServices(task);
await task.Run(args, _taskToken.Token);
_logger.LogInformation("Task finished: {Task}", task.Name);
}
/// <summary>
/// Inject services into the <see cref="InjectedAttribute"/> marked properties of the given object.
/// </summary>
/// <param name="obj">The object to inject</param>
/// <typeparam name="T">The type of the object.</typeparam>
private void InjectServices<T>(T obj)
private void InjectServices(ITask obj)
{
IEnumerable<PropertyInfo> properties = typeof(T).GetProperties()
IEnumerable<PropertyInfo> properties = obj.GetType().GetProperties()
.Where(x => x.GetCustomAttribute<InjectedAttribute>() != null)
.Where(x => x.CanWrite);
@ -180,7 +201,7 @@ namespace Kyoo.Controllers
.Where(x => x.RunOnStartup && x.Priority != int.MaxValue)
.OrderByDescending(x => x.Priority);
foreach (ITask task in startupTasks)
_queuedTasks.Enqueue((task, null));
_queuedTasks.Enqueue((task, new Dictionary<string, object>()));
}
/// <inheritdoc />

View File

@ -9,6 +9,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Unity;
using Unity.Microsoft.DependencyInjection;
namespace Kyoo
{
@ -99,7 +100,8 @@ namespace Kyoo
if (context.HostingEnvironment.IsDevelopment())
StaticWebAssetsLoader.UseStaticWebAssets(context.HostingEnvironment, context.Configuration);
})
.UseUnityProvider(container)
// .UseUnityProvider(container)
.UseUnityServiceProvider(container)
.ConfigureServices(x => x.AddRouting())
.UseKestrel(options => { options.AddServerHeader = false; })
.UseIIS()

View File

@ -3,11 +3,13 @@ using System.IO;
using Kyoo.Controllers;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Unity;
using Unity.Lifetime;
@ -19,11 +21,13 @@ namespace Kyoo
public class Startup
{
private readonly IConfiguration _configuration;
private readonly ILoggerFactory _loggerFactory;
public Startup(IConfiguration configuration)
public Startup(IConfiguration configuration, ILoggerFactory loggerFactory, IServiceProvider provider)
{
_configuration = configuration;
_loggerFactory = loggerFactory;
}
public void ConfigureServices(IServiceCollection services)
@ -60,11 +64,20 @@ namespace Kyoo
// });
// services.AddAuthentication()
// container.Resolve<IConfiguration>();
services.AddSingleton<ITaskManager, TaskManager>();
services.AddHostedService(x => x.GetService<ITaskManager>() as TaskManager);
}
public void ConfigureContainer(UnityContainer container) { }
public void ConfigureContainer(IUnityContainer container)
{
container.RegisterType<IPluginManager, PluginManager>(new SingletonLifetimeManager());
container.RegisterInstance(_configuration);
PluginManager pluginManager = new(container, _configuration, _loggerFactory.CreateLogger<PluginManager>());
pluginManager.ReloadPlugins();
}
public void Configure(IUnityContainer container, IApplicationBuilder app, IWebHostEnvironment env)
{
@ -103,18 +116,15 @@ namespace Kyoo
});
app.UseResponseCompression();
// app.UseSpa(spa =>
// {
// spa.Options.SourcePath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Kyoo.WebApp");
//
// if (env.IsDevelopment())
// spa.UseAngularCliServer("start");
// });
//
container.RegisterType<IPluginManager, PluginManager>(new SingletonLifetimeManager());
// container.Resolve<IConfiguration>();
app.UseSpa(spa =>
{
spa.Options.SourcePath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Kyoo.WebApp");
if (env.IsDevelopment())
spa.UseAngularCliServer("start");
});
IPluginManager pluginManager = container.Resolve<IPluginManager>();
pluginManager.ReloadPlugins();
foreach (IPlugin plugin in pluginManager.GetAllPlugins())
plugin.ConfigureAspNet(app);

View File

@ -18,6 +18,8 @@
"logging": {
"logLevel": {
"default": "Trace",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Kyoo": "Trace"
}
},