Allowing untyped sections in the config

This commit is contained in:
Zoe Roux 2021-05-20 01:14:48 +02:00
parent 5f7604a563
commit 52e7093c26
7 changed files with 80 additions and 12 deletions

View File

@ -132,7 +132,7 @@ namespace Kyoo.Authentication
}
string permStr = $"{permission.ToLower()}.{kind.ToString()!.ToLower()}";
string overallStr = $"{_group.ToString()}.{kind.ToString()!.ToLower()}";
string overallStr = $"{_group.ToString().ToLower()}.{kind.ToString()!.ToLower()}";
AuthenticateResult res = await context.HttpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);
if (res.Succeeded)
{

View File

@ -87,5 +87,11 @@ namespace Kyoo.Models
{
return CreateReference(path, typeof(T));
}
public static ConfigurationReference CreateUntyped(string path)
{
return new(path, null);
}
}
}

View File

@ -83,6 +83,20 @@ namespace Kyoo
return services;
}
/// <summary>
/// Add an editable configuration to the editable configuration list.
/// WARNING: this method allow you to add an unmanaged type. This type won't be editable. This can be used
/// for external libraries or variable arguments.
/// </summary>
/// <param name="services">The service collection to edit</param>
/// <param name="path">The root path of the editable configuration. It should not be a nested type.</param>
/// <returns>The given service collection is returned.</returns>
public static IServiceCollection AddUntypedConfiguration(this IServiceCollection services, string path)
{
services.AddSingleton(ConfigurationReference.CreateUntyped(path));
return services;
}
/// <summary>
/// Get the public URL of kyoo using the given configuration instance.
/// </summary>

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.IO;
using System.Linq;
@ -35,13 +36,30 @@ namespace Kyoo.Controllers
_references = references.ToDictionary(x => x.Path, x => x.Type, StringComparer.OrdinalIgnoreCase);
}
private Type GetType(string path)
{
path = path.Replace("__", ":");
// TODO handle lists and dictionaries.
if (_references.TryGetValue(path, out Type type))
{
if (type != null)
return type;
throw new ArgumentException($"The configuration at {path} is not editable or readable.");
}
string parent = path.Contains(':') ? path[..path.IndexOf(':')] : null;
if (parent != null && _references.TryGetValue(parent, out type) && type == null)
throw new ArgumentException($"The configuration at {path} is not editable or readable.");
throw new ItemNotFoundException($"No configuration exists for the name: {path}");
}
/// <inheritdoc />
public object GetValue(string path)
{
path = path.Replace("__", ":");
// TODO handle lists and dictionaries.
if (!_references.TryGetValue(path, out Type type))
throw new ItemNotFoundException($"No configuration exists for the name: {path}");
Type type = GetType(path);
object ret = _configuration.GetValue(type, path);
if (ret != null)
return ret;
@ -55,8 +73,7 @@ namespace Kyoo.Controllers
{
path = path.Replace("__", ":");
// TODO handle lists and dictionaries.
if (!_references.TryGetValue(path, out Type type))
throw new ItemNotFoundException($"No configuration exists for the name: {path}");
Type type = GetType(path);
if (typeof(T).IsAssignableFrom(type))
throw new InvalidCastException($"The type {typeof(T).Name} is not valid for " +
$"a resource of type {type.Name}.");
@ -67,8 +84,7 @@ namespace Kyoo.Controllers
public async Task EditValue(string path, object value)
{
path = path.Replace("__", ":");
if (!_references.TryGetValue(path, out Type type))
throw new ItemNotFoundException($"No configuration exists for the name: {path}");
Type type = GetType(path);
value = JObject.FromObject(value).ToObject(type);
if (value == null)
throw new ArgumentException("Invalid value format.");
@ -87,18 +103,48 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="config">The configuration to transform</param>
/// <returns>A strongly typed representation of the configuration.</returns>
[SuppressMessage("ReSharper", "RedundantJumpStatement")]
private ExpandoObject ToObject(IConfiguration config)
{
ExpandoObject obj = new();
foreach (IConfigurationSection section in config.GetChildren())
{
if (!_references.TryGetValue(section.Path, out Type type))
try
{
Type type = GetType(section.Path);
obj.TryAdd(section.Key, section.Get(type));
}
catch (ArgumentException)
{
obj.TryAdd(section.Key, ToUntyped(section));
}
catch
{
continue;
obj.TryAdd(section.Key, section.Get(type));
}
}
return obj;
}
/// <summary>
/// Transform the configuration section in nested expando objects.
/// </summary>
/// <param name="config">The section to convert</param>
/// <returns>The converted section</returns>
private static object ToUntyped(IConfigurationSection config)
{
ExpandoObject obj = new();
foreach (IConfigurationSection section in config.GetChildren())
{
obj.TryAdd(section.Key, ToUntyped(section));
}
if (!obj.Any())
return config.Value;
return obj;
}
}
}

View File

@ -99,6 +99,8 @@ namespace Kyoo
services.AddConfiguration<TaskOptions>(TaskOptions.Path);
services.Configure<MediaOptions>(_configuration.GetSection(MediaOptions.Path));
services.AddConfiguration<MediaOptions>(MediaOptions.Path);
services.AddUntypedConfiguration("database");
services.AddUntypedConfiguration("logging");
services.AddControllers()
.AddNewtonsoftJson(x =>

View File

@ -109,7 +109,7 @@ namespace Kyoo
.UseKestrel(options => { options.AddServerHeader = false; })
.UseIIS()
.UseIISIntegration()
.UseUrls(configuration.GetValue<string>("basics:urls"))
.UseUrls(configuration.GetValue<string>("basics:url"))
.UseStartup<Startup>();
}
}

View File

@ -71,9 +71,9 @@ namespace Kyoo.Api
{
return NotFound();
}
catch (ArgumentException)
catch (ArgumentException ex)
{
return BadRequest();
return BadRequest(ex.Message);
}
}
}