Skip to content

Commit 3991282

Browse files
larsfjermeerhardt
andauthored
Add IServiceProvider to Nats Client Extensions (#6929)
* Add IServiceProvider to Nats Client Extensions * Update src/Components/Aspire.NATS.Net/AspireNatsClientExtensions.cs Co-authored-by: Eric Erhardt <[email protected]> --------- Co-authored-by: Eric Erhardt <[email protected]>
1 parent 386f0c0 commit 3991282

File tree

3 files changed

+168
-11
lines changed

3 files changed

+168
-11
lines changed

src/Components/Aspire.NATS.Net/AspireNatsClientExtensions.cs

+71-10
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,26 @@ public static class AspireNatsClientExtensions
2121
private const string DefaultConfigSectionName = "Aspire:NATS:Net";
2222
private const string ActivityNameSource = "NATS.Net";
2323

24+
/// <inheritdoc cref="AddNatsClient(IHostApplicationBuilder, string, Action{NatsClientSettings}?, Func{IServiceProvider,NatsOpts,NatsOpts}?)"/>
25+
public static void AddNatsClient(this IHostApplicationBuilder builder, string connectionName)
26+
=> AddNatsClientInternal(builder, connectionName: connectionName, serviceKey: null, configureSettings: null, configureOptions: null);
27+
28+
/// <inheritdoc cref="AddNatsClient(IHostApplicationBuilder, string, Action{NatsClientSettings}?, Func{IServiceProvider,NatsOpts,NatsOpts}?)"/>
29+
public static void AddNatsClient(this IHostApplicationBuilder builder, string connectionName, Action<NatsClientSettings>? configureSettings)
30+
=> AddNatsClientInternal(builder, connectionName: connectionName, serviceKey: null, configureSettings: configureSettings, configureOptions: null);
31+
32+
/// <inheritdoc cref="AddNatsClient(IHostApplicationBuilder, string, Action{NatsClientSettings}?, Func{IServiceProvider,NatsOpts,NatsOpts}?)"/>
33+
public static void AddNatsClient(this IHostApplicationBuilder builder, string connectionName, Func<NatsOpts, NatsOpts>? configureOptions)
34+
=> AddNatsClientInternal(builder, connectionName: connectionName, serviceKey: null, configureSettings: null, configureOptions: Wrap(configureOptions));
35+
36+
/// <inheritdoc cref="AddNatsClient(IHostApplicationBuilder, string, Action{NatsClientSettings}?, Func{IServiceProvider,NatsOpts,NatsOpts}?)"/>
37+
public static void AddNatsClient(this IHostApplicationBuilder builder, string connectionName, Func<IServiceProvider, NatsOpts, NatsOpts>? configureOptions)
38+
=> AddNatsClientInternal(builder, connectionName: connectionName, serviceKey: null, configureSettings: null, configureOptions: configureOptions);
39+
40+
/// <inheritdoc cref="AddNatsClient(IHostApplicationBuilder, string, Action{NatsClientSettings}?, Func{IServiceProvider,NatsOpts,NatsOpts}?)"/>
41+
public static void AddNatsClient(this IHostApplicationBuilder builder, string connectionName, Action<NatsClientSettings>? configureSettings, Func<NatsOpts, NatsOpts>? configureOptions)
42+
=> AddNatsClientInternal(builder, connectionName: connectionName, serviceKey: null, configureSettings: configureSettings, configureOptions: Wrap(configureOptions));
43+
2444
/// <summary>
2545
/// Registers <see cref="INatsConnection"/> service for connecting NATS server with NATS client.
2646
/// Configures health check and logging for the NATS client.
@@ -31,12 +51,44 @@ public static class AspireNatsClientExtensions
3151
/// <param name="configureOptions">An optional delegate that can be used for customizing NATS options that aren't exposed as standard configuration.</param>
3252
/// <exception cref="ArgumentNullException">Thrown if mandatory <paramref name="builder"/> is null.</exception>
3353
/// <exception cref="InvalidOperationException">Thrown when mandatory <see cref="NatsClientSettings.ConnectionString"/> is not provided.</exception>
34-
public static void AddNatsClient(this IHostApplicationBuilder builder, string connectionName, Action<NatsClientSettings>? configureSettings = null, Func<NatsOpts, NatsOpts>? configureOptions = null)
54+
public static void AddNatsClient(this IHostApplicationBuilder builder, string connectionName, Action<NatsClientSettings>? configureSettings, Func<IServiceProvider, NatsOpts, NatsOpts>? configureOptions)
3555
{
36-
ArgumentNullException.ThrowIfNull(builder);
37-
ArgumentException.ThrowIfNullOrEmpty(connectionName);
56+
AddNatsClientInternal(builder, connectionName: connectionName, serviceKey: null, configureSettings: configureSettings, configureOptions: configureOptions);
57+
}
58+
59+
/// <inheritdoc cref="AddKeyedNatsClient(IHostApplicationBuilder, string, Action{NatsClientSettings}?, Func{IServiceProvider,NatsOpts,NatsOpts}?)"/>
60+
public static void AddKeyedNatsClient(this IHostApplicationBuilder builder, string name)
61+
{
62+
ArgumentException.ThrowIfNullOrEmpty(name);
63+
AddNatsClientInternal(builder, connectionName: name, serviceKey: name, configureSettings: null, configureOptions: null);
64+
}
3865

39-
AddNatsClient(builder, connectionName: connectionName, serviceKey: null, configureSettings: configureSettings, configureOptions: configureOptions);
66+
/// <inheritdoc cref="AddKeyedNatsClient(IHostApplicationBuilder, string, Action{NatsClientSettings}?, Func{IServiceProvider,NatsOpts,NatsOpts}?)"/>
67+
public static void AddKeyedNatsClient(this IHostApplicationBuilder builder, string name, Action<NatsClientSettings>? configureSettings)
68+
{
69+
ArgumentException.ThrowIfNullOrEmpty(name);
70+
AddNatsClientInternal(builder, connectionName: name, serviceKey: name, configureSettings: configureSettings, configureOptions: null);
71+
}
72+
73+
/// <inheritdoc cref="AddKeyedNatsClient(IHostApplicationBuilder, string, Action{NatsClientSettings}?, Func{IServiceProvider,NatsOpts,NatsOpts}?)"/>
74+
public static void AddKeyedNatsClient(this IHostApplicationBuilder builder, string name, Func<NatsOpts, NatsOpts>? configureOptions)
75+
{
76+
ArgumentException.ThrowIfNullOrEmpty(name);
77+
AddNatsClientInternal(builder, connectionName: name, serviceKey: name, configureSettings: null, configureOptions: Wrap(configureOptions));
78+
}
79+
80+
/// <inheritdoc cref="AddKeyedNatsClient(IHostApplicationBuilder, string, Action{NatsClientSettings}?, Func{IServiceProvider,NatsOpts,NatsOpts}?)"/>
81+
public static void AddKeyedNatsClient(this IHostApplicationBuilder builder, string name, Func<IServiceProvider, NatsOpts, NatsOpts>? configureOptions)
82+
{
83+
ArgumentException.ThrowIfNullOrEmpty(name);
84+
AddNatsClientInternal(builder, connectionName: name, serviceKey: name, configureSettings: null, configureOptions: configureOptions);
85+
}
86+
87+
/// <inheritdoc cref="AddKeyedNatsClient(IHostApplicationBuilder, string, Action{NatsClientSettings}?, Func{IServiceProvider,NatsOpts,NatsOpts}?)"/>
88+
public static void AddKeyedNatsClient(this IHostApplicationBuilder builder, string name, Action<NatsClientSettings>? configureSettings, Func<NatsOpts, NatsOpts>? configureOptions)
89+
{
90+
ArgumentException.ThrowIfNullOrEmpty(name);
91+
AddNatsClientInternal(builder, connectionName: name, serviceKey: name, configureSettings: configureSettings, configureOptions: Wrap(configureOptions));
4092
}
4193

4294
/// <summary>
@@ -50,17 +102,16 @@ public static void AddNatsClient(this IHostApplicationBuilder builder, string co
50102
/// <exception cref="ArgumentNullException">Thrown when <paramref name="builder"/> or <paramref name="name"/> is null.</exception>
51103
/// <exception cref="ArgumentException">Thrown if mandatory <paramref name="name"/> is empty.</exception>
52104
/// <exception cref="InvalidOperationException">Thrown when mandatory <see cref="NatsClientSettings.ConnectionString"/> is not provided.</exception>
53-
public static void AddKeyedNatsClient(this IHostApplicationBuilder builder, string name, Action<NatsClientSettings>? configureSettings = null, Func<NatsOpts, NatsOpts>? configureOptions = null)
105+
public static void AddKeyedNatsClient(this IHostApplicationBuilder builder, string name, Action<NatsClientSettings>? configureSettings, Func<IServiceProvider, NatsOpts, NatsOpts>? configureOptions)
54106
{
55-
ArgumentNullException.ThrowIfNull(builder);
56107
ArgumentException.ThrowIfNullOrEmpty(name);
57-
58-
AddNatsClient(builder, connectionName: name, serviceKey: name, configureSettings: configureSettings, configureOptions: configureOptions);
108+
AddNatsClientInternal(builder, connectionName: name, serviceKey: name, configureSettings: configureSettings, configureOptions: configureOptions);
59109
}
60110

61-
private static void AddNatsClient(this IHostApplicationBuilder builder, string connectionName, object? serviceKey, Action<NatsClientSettings>? configureSettings, Func<NatsOpts, NatsOpts>? configureOptions)
111+
private static void AddNatsClientInternal(this IHostApplicationBuilder builder, string connectionName, object? serviceKey, Action<NatsClientSettings>? configureSettings, Func<IServiceProvider, NatsOpts, NatsOpts>? configureOptions)
62112
{
63113
ArgumentNullException.ThrowIfNull(builder);
114+
ArgumentException.ThrowIfNullOrEmpty(connectionName);
64115

65116
NatsClientSettings settings = new();
66117
var configSection = builder.Configuration.GetSection(DefaultConfigSectionName);
@@ -84,7 +135,7 @@ NatsConnection Factory(IServiceProvider provider)
84135

85136
if (configureOptions != null)
86137
{
87-
options = configureOptions(options);
138+
options = configureOptions(provider, options);
88139
}
89140

90141
if (settings.ConnectionString == null)
@@ -145,4 +196,14 @@ public static void AddNatsJetStream(this IHostApplicationBuilder builder)
145196
return new NatsJSContextFactory().CreateContext(provider.GetRequiredService<INatsConnection>());
146197
});
147198
}
199+
200+
private static Func<IServiceProvider, NatsOpts, NatsOpts>? Wrap(Func<NatsOpts, NatsOpts>? func)
201+
{
202+
if (func is null)
203+
{
204+
return null;
205+
}
206+
207+
return (_, options) => func(options);
208+
}
148209
}
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,15 @@
11
#nullable enable
2-
2+
*REMOVED*static Microsoft.Extensions.Hosting.AspireNatsClientExtensions.AddKeyedNatsClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! name, System.Action<Aspire.NATS.Net.NatsClientSettings!>? configureSettings = null, System.Func<NATS.Client.Core.NatsOpts!, NATS.Client.Core.NatsOpts!>? configureOptions = null) -> void
3+
static Microsoft.Extensions.Hosting.AspireNatsClientExtensions.AddKeyedNatsClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! name) -> void
4+
static Microsoft.Extensions.Hosting.AspireNatsClientExtensions.AddKeyedNatsClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! name, System.Action<Aspire.NATS.Net.NatsClientSettings!>? configureSettings) -> void
5+
static Microsoft.Extensions.Hosting.AspireNatsClientExtensions.AddKeyedNatsClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! name, System.Func<NATS.Client.Core.NatsOpts!, NATS.Client.Core.NatsOpts!>? configureOptions) -> void
6+
static Microsoft.Extensions.Hosting.AspireNatsClientExtensions.AddKeyedNatsClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! name, System.Func<System.IServiceProvider!, NATS.Client.Core.NatsOpts!, NATS.Client.Core.NatsOpts!>? configureOptions) -> void
7+
static Microsoft.Extensions.Hosting.AspireNatsClientExtensions.AddKeyedNatsClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! name, System.Action<Aspire.NATS.Net.NatsClientSettings!>? configureSettings, System.Func<NATS.Client.Core.NatsOpts!, NATS.Client.Core.NatsOpts!>? configureOptions) -> void
8+
static Microsoft.Extensions.Hosting.AspireNatsClientExtensions.AddKeyedNatsClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! name, System.Action<Aspire.NATS.Net.NatsClientSettings!>? configureSettings, System.Func<System.IServiceProvider!, NATS.Client.Core.NatsOpts!, NATS.Client.Core.NatsOpts!>? configureOptions) -> void
9+
*REMOVED*static Microsoft.Extensions.Hosting.AspireNatsClientExtensions.AddNatsClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! connectionName, System.Action<Aspire.NATS.Net.NatsClientSettings!>? configureSettings = null, System.Func<NATS.Client.Core.NatsOpts!, NATS.Client.Core.NatsOpts!>? configureOptions = null) -> void
10+
static Microsoft.Extensions.Hosting.AspireNatsClientExtensions.AddNatsClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! connectionName) -> void
11+
static Microsoft.Extensions.Hosting.AspireNatsClientExtensions.AddNatsClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! connectionName, System.Action<Aspire.NATS.Net.NatsClientSettings!>? configureSettings) -> void
12+
static Microsoft.Extensions.Hosting.AspireNatsClientExtensions.AddNatsClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! connectionName, System.Func<NATS.Client.Core.NatsOpts!, NATS.Client.Core.NatsOpts!>? configureOptions) -> void
13+
static Microsoft.Extensions.Hosting.AspireNatsClientExtensions.AddNatsClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! connectionName, System.Func<System.IServiceProvider!, NATS.Client.Core.NatsOpts!, NATS.Client.Core.NatsOpts!>? configureOptions) -> void
14+
static Microsoft.Extensions.Hosting.AspireNatsClientExtensions.AddNatsClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! connectionName, System.Action<Aspire.NATS.Net.NatsClientSettings!>? configureSettings, System.Func<NATS.Client.Core.NatsOpts!, NATS.Client.Core.NatsOpts!>? configureOptions) -> void
15+
static Microsoft.Extensions.Hosting.AspireNatsClientExtensions.AddNatsClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! connectionName, System.Action<Aspire.NATS.Net.NatsClientSettings!>? configureSettings, System.Func<System.IServiceProvider!, NATS.Client.Core.NatsOpts!, NATS.Client.Core.NatsOpts!>? configureOptions) -> void

tests/Aspire.NATS.Net.Tests/NatsClientPublicApiTests.cs

+83
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using Microsoft.Extensions.Configuration;
5+
using Microsoft.Extensions.DependencyInjection;
46
using Microsoft.Extensions.Hosting;
7+
using NATS.Client.Core;
58
using Xunit;
69

710
namespace Aspire.NATS.Net.Tests;
@@ -96,4 +99,84 @@ public void AddNatsJetStreamShouldThrowWhenBuilderIsNull()
9699
var exception = Assert.Throws<ArgumentNullException>(action);
97100
Assert.Equal(nameof(builder), exception.ParamName);
98101
}
102+
103+
[Theory]
104+
[InlineData(true, true, false, false)]
105+
[InlineData(true, true, true, false)]
106+
[InlineData(true, true, false, true)]
107+
[InlineData(true, false, false, false)]
108+
[InlineData(true, false, true, false)]
109+
[InlineData(true, false, false, true)]
110+
[InlineData(false, true, false, false)]
111+
[InlineData(false, true, true, false)]
112+
[InlineData(false, true, false, true)]
113+
[InlineData(false, false, false, false)]
114+
[InlineData(false, false, true, false)]
115+
[InlineData(false, false, false, true)]
116+
public void AddNatsClientConfigured(bool useKeyed, bool useConfigureSettings, bool useConfigureOptions, bool useConfigureOptionsWithServiceProvider)
117+
{
118+
var builder = Host.CreateEmptyApplicationBuilder(null);
119+
builder.Configuration.AddInMemoryCollection([
120+
new KeyValuePair<string, string?>("ConnectionStrings:Nats", "nats")
121+
]);
122+
var name = "Nats";
123+
bool configureSettingsIsCalled = false, configureOptionsIsCalled = false;
124+
125+
Action action = (useKeyed, useConfigureSettings, useConfigureOptions, useConfigureOptionsWithServiceProvider) switch
126+
{
127+
// Single Client
128+
(false, false, false, false) => () => builder.AddNatsClient(name),
129+
(false, true, false, false) => () => builder.AddNatsClient(name, configureSettings: ConfigureSettings),
130+
(false, false, true, false) => () => builder.AddNatsClient(name, configureOptions: ConfigureOptions),
131+
(false, false, false, true) => () => builder.AddNatsClient(name, configureOptions: ConfigureOptionsWithServiceProvider),
132+
(false, true, true, false) => () => builder.AddNatsClient(name, configureSettings: ConfigureSettings, configureOptions: ConfigureOptions),
133+
(false, true, false, true) => () => builder.AddNatsClient(name, configureSettings: ConfigureSettings, configureOptions: ConfigureOptionsWithServiceProvider),
134+
135+
// Keyed Client
136+
(true, false, false, false) => () => builder.AddKeyedNatsClient(name),
137+
(true, true, false, false) => () => builder.AddKeyedNatsClient(name, configureSettings: ConfigureSettings),
138+
(true, false, true, false) => () => builder.AddKeyedNatsClient(name, configureOptions: ConfigureOptions),
139+
(true, false, false, true) => () => builder.AddKeyedNatsClient(name, configureOptions: ConfigureOptionsWithServiceProvider),
140+
(true, true, true, false) => () => builder.AddKeyedNatsClient(name, configureSettings: ConfigureSettings, configureOptions: ConfigureOptions),
141+
(true, true, false, true) => () => builder.AddKeyedNatsClient(name, configureSettings: ConfigureSettings, configureOptions: ConfigureOptionsWithServiceProvider),
142+
143+
_ => throw new InvalidOperationException()
144+
};
145+
146+
action();
147+
148+
using var host = builder.Build();
149+
150+
_ = useKeyed
151+
? host.Services.GetRequiredKeyedService<INatsConnection>(name)
152+
: host.Services.GetRequiredService<INatsConnection>();
153+
154+
if (useConfigureSettings)
155+
{
156+
Assert.True(configureSettingsIsCalled);
157+
}
158+
159+
if (useConfigureOptions || useConfigureOptionsWithServiceProvider)
160+
{
161+
Assert.True(configureOptionsIsCalled);
162+
}
163+
164+
void ConfigureSettings(NatsClientSettings _)
165+
{
166+
configureSettingsIsCalled = true;
167+
}
168+
169+
NatsOpts ConfigureOptions(NatsOpts _)
170+
{
171+
configureOptionsIsCalled = true;
172+
return NatsOpts.Default;
173+
}
174+
175+
NatsOpts ConfigureOptionsWithServiceProvider(IServiceProvider provider, NatsOpts _)
176+
{
177+
var __ = provider.GetRequiredService<IConfiguration>();
178+
configureOptionsIsCalled = true;
179+
return NatsOpts.Default;
180+
}
181+
}
99182
}

0 commit comments

Comments
 (0)