Skip to content

Commit

Permalink
Merge pull request #806 from sitkoru/module-options-multiple-sources
Browse files Browse the repository at this point in the history
Multiple sources for module options
  • Loading branch information
SonicGD authored Apr 10, 2023
2 parents 3b77b20 + 43430d0 commit f89b653
Show file tree
Hide file tree
Showing 16 changed files with 71 additions and 21 deletions.
34 changes: 20 additions & 14 deletions src/Sitko.Core.App/ApplicationModuleRegistration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal sealed class ApplicationModuleRegistration<TModule, TModuleOptions> : A
private readonly TModule instance;

private readonly Dictionary<Guid, TModuleOptions> optionsCache = new();
private readonly string optionsKey;
private readonly string[] optionKeys;
private readonly Type? validatorType;

public ApplicationModuleRegistration(
Expand All @@ -27,7 +27,7 @@ public ApplicationModuleRegistration(
{
this.instance = instance;
this.configureOptions = configureOptions;
this.optionsKey = optionsKey ?? instance.OptionsKey;
this.optionKeys = !string.IsNullOrEmpty(optionsKey) ? new[] { optionsKey } : instance.OptionKeys;
var optionsInstance = Activator.CreateInstance<TModuleOptions>();
if (optionsInstance is IModuleOptionsWithValidation moduleOptionsWithValidation)
{
Expand All @@ -45,19 +45,23 @@ public ApplicationModuleRegistration(
public override IApplicationModule GetInstance() => instance;

public override (string? optionsKey, object options) GetOptions(IApplicationContext applicationContext) =>
(optionsKey, CreateOptions(applicationContext));
(optionKeys.Last(), CreateOptions(applicationContext));

public override ApplicationModuleRegistration ConfigureOptions(IApplicationContext context,
IServiceCollection services)
{
var builder = services.AddOptions<TModuleOptions>()
.Bind(context.Configuration.GetSection(optionsKey))
.PostConfigure(
options =>
{
options.Configure(context);
configureOptions?.Invoke(context, options);
});
var builder = services.AddOptions<TModuleOptions>();
foreach (var optionsKey in optionKeys)
{
builder = builder.Bind(context.Configuration.GetSection(optionsKey));
}

builder = builder.PostConfigure(
options =>
{
options.Configure(context);
configureOptions?.Invoke(context, options);
});

if (validatorType is not null)
{
Expand Down Expand Up @@ -135,7 +139,11 @@ private TModuleOptions CreateOptions(IApplicationContext applicationContext, boo
else
{
options = Activator.CreateInstance<TModuleOptions>();
applicationContext.Configuration.Bind(optionsKey, options);
foreach (var optionsKey in optionKeys)
{
applicationContext.Configuration.Bind(optionsKey, options);
}

options.Configure(applicationContext);
configureOptions?.Invoke(applicationContext, options);
optionsCache[applicationContext.Id] = options;
Expand Down Expand Up @@ -228,5 +236,3 @@ public abstract (bool isSuccess, IEnumerable<Type> missingModules) CheckRequired

public abstract bool IsEnabled(IApplicationContext context);
}


2 changes: 1 addition & 1 deletion src/Sitko.Core.App/BaseApplicationModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public virtual Task<bool> OnAfterRunAsync(Application application, IApplicationC
string[] args) => Task.FromResult(true);

public abstract string OptionsKey { get; }
public virtual string[] OptionKeys => new[] { OptionsKey };
public virtual bool AllowMultiple => false;

public TModuleOptions GetOptions(IServiceProvider serviceProvider) =>
Expand All @@ -64,4 +65,3 @@ public virtual void Configure(IApplicationContext applicationContext)
{
}
}

2 changes: 1 addition & 1 deletion src/Sitko.Core.App/IApplicationModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Sitko.Core.App;

public interface IApplicationModule<in TModuleOptions> : IApplicationModule where TModuleOptions : class, new()
{
string OptionsKey { get; }
public string[] OptionKeys { get; }
bool AllowMultiple { get; }

void ConfigureServices(IApplicationContext applicationContext, IServiceCollection services,
Expand Down
2 changes: 1 addition & 1 deletion src/Sitko.Core.Db.InMemory/InMemoryDatabaseModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class
where TDbContext : DbContext
{
public override string OptionsKey => $"Db:InMemory:{typeof(TDbContext).Name}";
public override string[] OptionKeys => new[] { "Db:InMemory:Default", OptionsKey };

public override void ConfigureServices(IApplicationContext applicationContext, IServiceCollection services,
InMemoryDatabaseModuleOptions<TDbContext> startupOptions)
Expand Down Expand Up @@ -48,4 +49,3 @@ private void ConfigureInMemory(DbContextOptionsBuilder options,
applicationContext);
}
}

3 changes: 2 additions & 1 deletion src/Sitko.Core.Db.Postgres/PostgresDatabaseModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public class
{
public override string OptionsKey => $"Db:Postgres:{typeof(TDbContext).Name}";

public override string[] OptionKeys => new[] { "Db:Postgres:Default", OptionsKey };

public override async Task InitAsync(IApplicationContext applicationContext, IServiceProvider serviceProvider)
{
await base.InitAsync(applicationContext, serviceProvider);
Expand Down Expand Up @@ -103,4 +105,3 @@ private void ConfigureNpgsql(DbContextOptionsBuilder options,
applicationContext);
}
}

5 changes: 3 additions & 2 deletions src/Sitko.Core.Grpc.Client.Consul/ConsulGrpcClientModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ public class ConsulGrpcClientModule<TClient> : GrpcClientModule<TClient, ConsulG
ConsulGrpcClientModuleOptions<TClient>>
where TClient : ClientBase<TClient>
{
public override string OptionsKey => "Grpc:Client:Consul";
public override string OptionsKey => $"Grpc:Client:Consul:{typeof(TClient).Name}";

public override string[] OptionKeys => new[] { "Grpc:Client:Consul:Default", OptionsKey };

public override IEnumerable<Type> GetRequiredModules(IApplicationContext applicationContext,
ConsulGrpcClientModuleOptions<TClient> options) =>
Expand All @@ -19,4 +21,3 @@ public class ConsulGrpcClientModuleOptions<TClient> : GrpcClientModuleOptions<TC
where TClient : ClientBase<TClient>
{
}

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public class ExternalGrpcClientModule<TClient> : GrpcClientModule<TClient,
{
public override string OptionsKey => $"Grpc:Client:External:{typeof(TClient).Name}";

public override string[] OptionKeys => new[] { "Grpc:Client:External:Default", OptionsKey };

protected override void RegisterResolver(IServiceCollection services,
ExternalGrpcClientModuleOptions<TClient> config) =>
services.AddSingleton<IGrpcServiceAddressResolver<TClient>>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ public class FileSystemStorageMetadataModule<TStorageOptions> : BaseStorageMetad
where TStorageOptions : StorageOptions, IFileSystemStorageOptions
{
public override string OptionsKey => $"Storage:Metadata:FileSystem:{typeof(TStorageOptions).Name}";
public override string[] OptionKeys => new[] { "Storage:Metadata:FileSystem:Default", OptionsKey };
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ public class
where TStorageOptions : StorageOptions, IFileSystemStorageOptions, new()
{
public override string OptionsKey => $"Storage:FileSystem:{typeof(TStorageOptions).Name}";
public override string[] OptionKeys => new[] { "Storage:FileSystem:Default", OptionsKey };
}
1 change: 1 addition & 0 deletions src/Sitko.Core.Storage.ImgProxy/ImgProxyStorageModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class
where TStorageOptions : StorageOptions
{
public override string OptionsKey => $"Storage:ImgProxy:{typeof(TStorageOptions).Name}";
public override string[] OptionKeys => new[] { "Storage:ImgProxy:Default", OptionsKey };

public override void ConfigureServices(IApplicationContext applicationContext, IServiceCollection services,
BaseApplicationModuleOptions startupOptions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class
where TStorageOptions : StorageOptions
{
public override string OptionsKey => $"Storage:Metadata:Postgres:{typeof(TStorageOptions).Name}";
public override string[] OptionKeys => new[] { "Storage:Metadata:Postgres:Default", OptionsKey };

public override void ConfigureServices(IApplicationContext applicationContext, IServiceCollection services,
PostgresStorageMetadataModuleOptions<TStorageOptions> startupOptions)
Expand Down
1 change: 1 addition & 0 deletions src/Sitko.Core.Storage.Remote/RemoteStorageModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class
where TStorageOptions : StorageOptions, IRemoteStorageOptions, new()
{
public override string OptionsKey => $"Storage:Remote:{typeof(TStorageOptions).Name}";
public override string[] OptionKeys => new[] { "Storage:Remote:Default", OptionsKey };

public override void ConfigureServices(IApplicationContext applicationContext, IServiceCollection services,
TStorageOptions startupOptions)
Expand Down
1 change: 1 addition & 0 deletions src/Sitko.Core.Storage.S3/S3StorageMetadataModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ public class S3StorageMetadataModule<TStorageOptions> : BaseStorageMetadataModul
where TStorageOptions : S3StorageOptions, new()
{
public override string OptionsKey => $"Storage:Metadata:S3:{typeof(TStorageOptions).Name}";
public override string[] OptionKeys => new[] { "Storage:Metadata:S3:Default", OptionsKey };
}

1 change: 1 addition & 0 deletions src/Sitko.Core.Storage.S3/S3StorageModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class S3StorageModule<TS3StorageOptions> : StorageModule<S3Storage<TS3Sto
where TS3StorageOptions : S3StorageOptions, new()
{
public override string OptionsKey => $"Storage:S3:{typeof(TS3StorageOptions).Name}";
public override string[] OptionKeys => new[] { "Storage:S3:Default", OptionsKey };

public override void ConfigureServices(IApplicationContext applicationContext, IServiceCollection services,
TS3StorageOptions startupOptions)
Expand Down
2 changes: 2 additions & 0 deletions src/Sitko.Core.Storage/Cache/BaseStorageCacheModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class
FileStorageCacheOptions> where TStorageOptions : StorageOptions
{
public override string OptionsKey => $"Storage:Cache:FileSystem:{typeof(TStorageOptions).Name}";
public override string[] OptionKeys => new[] { "Storage:Cache:FileSystem:Default", OptionsKey };
}

public class
Expand All @@ -31,5 +32,6 @@ public class
InMemoryStorageCacheOptions> where TStorageOptions : StorageOptions
{
public override string OptionsKey => $"Storage:Cache:InMemory:{typeof(TStorageOptions).Name}";
public override string[] OptionKeys => new[] { "Storage:Cache:InMemory:Default", OptionsKey };
}

33 changes: 32 additions & 1 deletion tests/Sitko.Core.App.Tests/ConfigurationTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Sitko.Core.Xunit;
using Xunit;
using Xunit.Abstractions;
Expand All @@ -21,6 +23,23 @@ public async Task NestedModules()
var options = app.GetModulesOptions();
options.Should().HaveCount(3);
}

[Theory]
[InlineData("Baz__Foo", "123", "Baz__Bar", "456")] // all from base options
[InlineData("Baz__Inner__Foo", "123", "Baz__Bar", "456")] // first from module, second from base
[InlineData("Baz__Foo", "123", "Baz__Inner__Bar", "456")] // first from base, second from module
[InlineData("Baz__Inner__Foo", "123", "Baz__Inner__Bar", "456")] // all from module options
public async Task BaseOptions(string fooKey, string fooValue, string barKey, string barValue)
{
Environment.SetEnvironmentVariable(fooKey, fooValue);
Environment.SetEnvironmentVariable(barKey, barValue);
var app = new TestApplication(Array.Empty<string>());
app.AddModule<TestModuleBaz, TestModuleBazOptions>();
var sp = await app.GetServiceProviderAsync();
var options = sp.GetRequiredService<IOptions<TestModuleBazOptions>>();
options.Value.Foo.Should().Be(fooValue);
options.Value.Bar.Should().Be(barValue);
}
}

public class TestModuleFoo : BaseApplicationModule<TestModuleFooOptions>
Expand All @@ -33,6 +52,19 @@ public class TestModuleFooBar : BaseApplicationModule<TestModuleFooBarOptions>
public override string OptionsKey => "Foo:Bar";
}

public class TestModuleBaz : BaseApplicationModule<TestModuleBazOptions>
{
public override string OptionsKey => "Baz:Inner";

public override string[] OptionKeys => new[] { "Baz", OptionsKey };
}

public class TestModuleBazOptions : BaseModuleOptions
{
public string Foo { get; set; } = "";
public string Bar { get; set; } = "";
}

public class TestModuleFooBarOptions : BaseModuleOptions
{
public string Bar { get; set; } = "";
Expand All @@ -42,4 +74,3 @@ public class TestModuleFooOptions : BaseModuleOptions
{
public string Foo { get; set; } = "";
}

0 comments on commit f89b653

Please sign in to comment.