Skip to content

Commit

Permalink
feat: Add service extensions for options using reflection
Browse files Browse the repository at this point in the history
  • Loading branch information
s2quake committed Aug 12, 2024
1 parent 99a6d9c commit f4f69c7
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 23 deletions.
24 changes: 2 additions & 22 deletions sdk/node/Libplanet.Node.Extensions/LibplanetServicesExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,15 @@ namespace Libplanet.Node.Extensions;

public static class LibplanetServicesExtensions
{
public static ILibplanetNodeBuilder AddLibplanetNode(
this IServiceCollection services)
{
return new LibplanetNodeBuilder(services);
}

public static ILibplanetNodeBuilder AddLibplanetNode(
this IServiceCollection services,
IConfiguration configuration)
{
SynchronizationContext.SetSynchronizationContext(SynchronizationContext.Current ?? new());
services.AddSingleton(SynchronizationContext.Current!);
services.AddOptions<StoreOptions>()
.Bind(configuration.GetSection(StoreOptions.Position))
.ValidateDataAnnotations();
services.Configure<SoloProposeOption>(configuration.GetSection(SoloProposeOption.Position));
services.AddOptions<GenesisOptions>()
.Bind(configuration.GetSection(GenesisOptions.Position))
.ValidateDataAnnotations();
services.AddOptions<StoreOptions>(SeedOptions.BlocksyncSeed)
.Bind(configuration.GetSection(SeedOptions.BlocksyncSeed))
.ValidateDataAnnotations();
services.AddOptions<StoreOptions>(SeedOptions.ConsensusSeed)
.Bind(configuration.GetSection(SeedOptions.ConsensusSeed))
.ValidateDataAnnotations();
services.AddOptions<NodeOptions>()
.Bind(configuration.GetSection(NodeOptions.Position))
.ValidateDataAnnotations();
services.AddOptionsFromDomain(configuration);

return AddLibplanetNode(services);
return new LibplanetNodeBuilder(services, configuration);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
using Libplanet.Node.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace Libplanet.Node.Extensions.NodeBuilder;

public class LibplanetNodeBuilder : ILibplanetNodeBuilder
{
internal LibplanetNodeBuilder(IServiceCollection services)
private readonly IConfiguration _configuration;

internal LibplanetNodeBuilder(IServiceCollection services, IConfiguration configuration)
{
Services = services;
_configuration = configuration;
Services.AddSingletonsFromDomain();
}

Expand All @@ -22,6 +26,7 @@ public ILibplanetNodeBuilder WithSolo()
public ILibplanetNodeBuilder WithNode()
{
Services.AddSingletonsFromDomain(scope: "Node");
Services.AddOptionsFromDomain(_configuration, scope: "Node");
return this;
}

Expand All @@ -31,6 +36,7 @@ public ILibplanetNodeBuilder WithValidate() =>
public ILibplanetNodeBuilder WithSeed()
{
Services.AddSingletonsFromDomain(scope: "Seed");
Services.AddOptionsFromDomain(_configuration, scope: "Seed");
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Libplanet.Node.DependencyInjection;

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class OptionsAttribute(string name) : Attribute
{
public string Name { get; } = name;

public string Scope { get; set; } = string.Empty;
}
28 changes: 28 additions & 0 deletions sdk/node/Libplanet.Node/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Libplanet.Node.DependencyInjection;
using Libplanet.Node.Options;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

Expand Down Expand Up @@ -31,6 +32,33 @@ static IEnumerable<SingletonAttribute> GetAttributes(Type type, string scope)
return @this;
}

public static IServiceCollection AddOptionsFromDomain(
this IServiceCollection @this, IConfiguration configuration)
=> @this.AddOptionsFromDomain(configuration, scope: string.Empty);

public static IServiceCollection AddOptionsFromDomain(
this IServiceCollection @this, IConfiguration configuration, string scope)
{
var types = ServiceUtility.GetTypes(typeof(OptionsAttribute), inherit: true);
foreach (var type in types)
{
var optionsNames = GetAttributes(type, scope).Select(item => item.Name);
foreach (var optionsName in optionsNames)
{
@this.AddOptions<StoreOptions>()
.Bind(configuration.GetSection(optionsName))
.ValidateDataAnnotations();
}

static IEnumerable<OptionsAttribute> GetAttributes(Type type, string scope)
=> Attribute.GetCustomAttributes(type, typeof(OptionsAttribute))
.OfType<OptionsAttribute>()
.Where(item => item.Scope == scope);
}

return @this;
}

public static IServiceCollection AddOptionsConfigurator<TO, TC>(this IServiceCollection @this)
where TO : OptionsBase<TO>
where TC : OptionsConfiguratorBase<TO>
Expand Down
1 change: 1 addition & 0 deletions sdk/node/Libplanet.Node/Libplanet.Node.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions sdk/node/Libplanet.Node/Options/GenesisOptions.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using Libplanet.Crypto;
using Libplanet.Net;
using Libplanet.Node.DataAnnotations;
using Libplanet.Node.DependencyInjection;

namespace Libplanet.Node.Options;

[Options(Position)]
public sealed class GenesisOptions : OptionsBase<GenesisOptions>
{
public const string Position = "Genesis";
Expand Down
2 changes: 2 additions & 0 deletions sdk/node/Libplanet.Node/Options/NodeOptions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Libplanet.Node.DataAnnotations;
using Libplanet.Node.DependencyInjection;

namespace Libplanet.Node.Options;

[Options(Position, Scope = "Node")]
public sealed class NodeOptions : OptionsBase<NodeOptions>
{
public const string Position = "Node";
Expand Down
3 changes: 3 additions & 0 deletions sdk/node/Libplanet.Node/Options/SeedOptions.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using System.ComponentModel.DataAnnotations;
using Libplanet.Node.DataAnnotations;
using Libplanet.Node.DependencyInjection;

namespace Libplanet.Node.Options;

[Options(BlocksyncSeed, Scope = "Seed")]
[Options(ConsensusSeed, Scope = "Seed")]
public sealed class SeedOptions : OptionsBase<SeedOptions>
{
public const string BlocksyncSeed = nameof(BlocksyncSeed);
Expand Down
3 changes: 3 additions & 0 deletions sdk/node/Libplanet.Node/Options/StoreOptions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using Libplanet.Node.DependencyInjection;

namespace Libplanet.Node.Options;

[Options(Position)]
public sealed class StoreOptions : OptionsBase<StoreOptions>
{
public const string Position = "Store";
Expand Down

0 comments on commit f4f69c7

Please sign in to comment.