Skip to content

Commit a3a05ef

Browse files
authored
Merge pull request #68 from ChangemakerStudios/feature/fix-hybrid-config-basic-auth-67
Fix hybrid configuration for basic authentication credentials
2 parents 5a409f0 + c357e54 commit a3a05ef

File tree

3 files changed

+306
-48
lines changed

3 files changed

+306
-48
lines changed

README.md

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -102,21 +102,24 @@ public void ConfigureServices(IServiceCollection services)
102102
{
103103
.....
104104
// Configure with an action
105-
services.AddGotenbergSharpClient(options =>
106-
{
107-
options.ServiceUrl = new Uri("http://localhost:3000");
108-
options.TimeOut = TimeSpan.FromMinutes(5);
109-
options.BasicAuthUsername = "username";
110-
options.BasicAuthPassword = "password";
111-
// Configure retry policy
112-
options.RetryPolicy = new RetryPolicyOptions
105+
services.AddOptions<GotenbergSharpClientOptions>()
106+
.Configure(options =>
113107
{
114-
Enabled = true,
115-
RetryCount = 4,
116-
BackoffPower = 1.5,
117-
LoggingEnabled = true
118-
};
119-
});
108+
options.ServiceUrl = new Uri("http://localhost:3000");
109+
options.TimeOut = TimeSpan.FromMinutes(5);
110+
options.BasicAuthUsername = "username";
111+
options.BasicAuthPassword = "password";
112+
// Configure retry policy
113+
options.RetryPolicy = new RetryOptions
114+
{
115+
Enabled = true,
116+
RetryCount = 4,
117+
BackoffPower = 1.5,
118+
LoggingEnabled = true
119+
};
120+
});
121+
122+
services.AddGotenbergSharpClient();
120123
.....
121124
}
122125
```
@@ -125,18 +128,19 @@ public void ConfigureServices(IServiceCollection services)
125128
```csharp
126129
public void ConfigureServices(IServiceCollection services)
127130
{
128-
.....
131+
.....
129132
services.AddOptions<GotenbergSharpClientOptions>()
130-
.Bind(Configuration.GetSection(nameof(GotenbergSharpClient)));
133+
.Bind(Configuration.GetSection(nameof(GotenbergSharpClient)))
134+
.PostConfigure(options =>
135+
{
136+
// Override or add settings programmatically (runs after binding)
137+
options.TimeOut = TimeSpan.FromMinutes(10); // Override timeout
138+
options.BasicAuthUsername = Environment.GetEnvironmentVariable("GOTENBERG_USER");
139+
options.BasicAuthPassword = Environment.GetEnvironmentVariable("GOTENBERG_PASS");
140+
});
131141

132-
// Override or add settings programmatically
133-
services.AddGotenbergSharpClient(options =>
134-
{
135-
options.TimeOut = TimeSpan.FromMinutes(10); // Override timeout
136-
options.BasicAuthUsername = Environment.GetEnvironmentVariable("GOTENBERG_USER");
137-
options.BasicAuthPassword = Environment.GetEnvironmentVariable("GOTENBERG_PASS");
138-
});
139-
.....
142+
services.AddGotenbergSharpClient();
143+
.....
140144
}
141145
```
142146

src/Gotenberg.Sharp.Api.Client/Extensions/TypedClientServiceCollectionExtensions.cs

Lines changed: 138 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,31 +30,78 @@ public static class TypedClientServiceCollectionExtensions
3030
{
3131
/// <summary>
3232
/// Registers GotenbergSharpClient with dependency injection using configured options.
33-
/// Configure options via appsettings.json or by calling services.Configure&lt;GotenbergSharpClientOptions&gt;().
3433
/// </summary>
3534
/// <param name="services">The service collection.</param>
36-
/// <param name="configureClientOptions">Optional function to configure client options after they are retrieved.</param>
3735
/// <returns>An IHttpClientBuilder for further configuration.</returns>
3836
/// <exception cref="ArgumentNullException">Thrown when services is null.</exception>
3937
/// <remarks>
40-
/// This method configures the HttpClient with automatic compression, retry policies, and basic authentication if
41-
/// credentials are provided.
42-
/// Options should be configured in the "GotenbergSharpClient" section of appsettings.json or programmatically.
38+
/// <para>
39+
/// This method registers the GotenbergSharpClient with automatic compression, retry policies,
40+
/// and basic authentication if credentials are provided in the options.
41+
/// </para>
42+
/// <para>
43+
/// Options must be registered before calling this method using
44+
/// standard .NET options configuration methods.
45+
/// The client retrieves options from the DI container using <c>IOptions&lt;TOptions&gt;</c>.
46+
/// </para>
47+
/// <para>
48+
/// Example usage:
49+
/// <code>
50+
/// services.AddOptions&lt;GotenbergSharpClientOptions&gt;()
51+
/// .Bind(configuration.GetSection("GotenbergSharpClient"));
52+
/// services.AddGotenbergSharpClient();
53+
/// </code>
54+
/// </para>
4355
/// </remarks>
4456
public static IHttpClientBuilder AddGotenbergSharpClient(
45-
this IServiceCollection services,
46-
Action<GotenbergSharpClientOptions>? configureClientOptions = null)
57+
this IServiceCollection services)
4758
{
4859
if (services == null)
4960
{
5061
throw new ArgumentNullException(nameof(services));
5162
}
5263

53-
return services.AddGotenbergSharpClient((sp, client) =>
64+
return services.AddGotenbergSharpClient<GotenbergSharpClientOptions>();
65+
}
66+
67+
/// <summary>
68+
/// Registers GotenbergSharpClient with dependency injection using configured options.
69+
/// </summary>
70+
/// <typeparam name="TOptions">The options type, must inherit from GotenbergSharpClientOptions.</typeparam>
71+
/// <param name="services">The service collection.</param>
72+
/// <returns>An IHttpClientBuilder for further configuration.</returns>
73+
/// <exception cref="ArgumentNullException">Thrown when services is null.</exception>
74+
/// <remarks>
75+
/// <para>
76+
/// This method registers the GotenbergSharpClient with automatic compression, retry policies,
77+
/// and basic authentication if credentials are provided in the options.
78+
/// </para>
79+
/// <para>
80+
/// Options must be registered before calling this method using
81+
/// standard .NET options configuration methods.
82+
/// The client retrieves options from the DI container using <c>IOptions&lt;TOptions&gt;</c>.
83+
/// </para>
84+
/// <para>
85+
/// Example usage:
86+
/// <code>
87+
/// services.AddOptions&lt;GotenbergSharpClientOptions&gt;()
88+
/// .Bind(configuration.GetSection("GotenbergSharpClient"));
89+
/// services.AddGotenbergSharpClient();
90+
/// </code>
91+
/// </para>
92+
/// </remarks>
93+
public static IHttpClientBuilder AddGotenbergSharpClient<TOptions>(
94+
this IServiceCollection services)
95+
where TOptions : GotenbergSharpClientOptions, new()
96+
{
97+
if (services == null)
5498
{
55-
var ops = GetOptions(sp) ?? new GotenbergSharpClientOptions();
99+
throw new ArgumentNullException(nameof(services));
100+
}
56101

57-
configureClientOptions?.Invoke(ops);
102+
return services.AddGotenbergSharpClient<TOptions>((sp, client) =>
103+
{
104+
var ops = sp.GetRequiredService<IOptions<TOptions>>().Value;
58105

59106
client.Timeout = ops.TimeOut;
60107
client.BaseAddress = ops.ServiceUrl;
@@ -65,18 +112,92 @@ public static IHttpClientBuilder AddGotenbergSharpClient(
65112
/// Registers GotenbergSharpClient with dependency injection using a custom HttpClient configuration.
66113
/// </summary>
67114
/// <param name="services">The service collection.</param>
68-
/// <param name="configureClient">Action to configure the HttpClient instance.</param>
69-
/// <param name="configureClientOptions">Optional function to configure client options after they are retrieved.</param>
115+
/// <param name="configureClient">
116+
/// Action to configure the HttpClient instance. The action receives the service provider and HttpClient
117+
/// for custom configuration.
118+
/// </param>
70119
/// <returns>An IHttpClientBuilder for further configuration.</returns>
71120
/// <exception cref="ArgumentNullException">Thrown when configureClient is null.</exception>
72121
/// <remarks>
73-
/// This overload allows full control over HttpClient configuration. The client is configured with
74-
/// automatic compression, timeout handling, and exponential backoff retry policies.
122+
/// <para>
123+
/// This overload allows full control over HttpClient configuration while still using the options
124+
/// for basic authentication and retry policies. The client is configured with automatic compression,
125+
/// timeout handling, and exponential backoff retry policies based on the registered options.
126+
/// </para>
127+
/// <para>
128+
/// Options must be registered before calling this method using
129+
/// standard .NET options configuration methods.
130+
/// </para>
131+
/// <para>
132+
/// Example usage:
133+
/// <code>
134+
/// services.AddOptions&lt;GotenbergSharpClientOptions&gt;()
135+
/// .Bind(configuration.GetSection("GotenbergSharpClient"))
136+
/// .PostConfigure(options =>
137+
/// {
138+
/// options.BasicAuthUsername = "user";
139+
/// options.BasicAuthPassword = "pass";
140+
/// });
141+
///
142+
/// services.AddGotenbergSharpClient((sp, client) =>
143+
/// {
144+
/// // Custom HttpClient configuration
145+
/// client.DefaultRequestHeaders.Add("X-Custom-Header", "value");
146+
/// });
147+
/// </code>
148+
/// </para>
75149
/// </remarks>
76150
public static IHttpClientBuilder AddGotenbergSharpClient(
77151
this IServiceCollection services,
78-
Action<IServiceProvider, HttpClient> configureClient,
79-
Action<GotenbergSharpClientOptions>? configureClientOptions = null)
152+
Action<IServiceProvider, HttpClient> configureClient)
153+
{
154+
return services.AddGotenbergSharpClient<GotenbergSharpClientOptions>(configureClient);
155+
}
156+
157+
/// <summary>
158+
/// Registers GotenbergSharpClient with dependency injection using a custom HttpClient configuration.
159+
/// </summary>
160+
/// <typeparam name="TOptions">The options type, must inherit from GotenbergSharpClientOptions.</typeparam>
161+
/// <param name="services">The service collection.</param>
162+
/// <param name="configureClient">
163+
/// Action to configure the HttpClient instance. The action receives the service provider and HttpClient
164+
/// for custom configuration.
165+
/// </param>
166+
/// <returns>An IHttpClientBuilder for further configuration.</returns>
167+
/// <exception cref="ArgumentNullException">Thrown when configureClient is null.</exception>
168+
/// <remarks>
169+
/// <para>
170+
/// This overload allows full control over HttpClient configuration while still using the options
171+
/// for basic authentication and retry policies. The client is configured with automatic compression,
172+
/// timeout handling, and exponential backoff retry policies based on the registered options.
173+
/// </para>
174+
/// <para>
175+
/// Options must be registered before calling this method using
176+
/// standard .NET options configuration methods.
177+
/// </para>
178+
/// <para>
179+
/// Example usage:
180+
/// <code>
181+
/// services.AddOptions&lt;GotenbergSharpClientOptions&gt;()
182+
/// .Bind(configuration.GetSection("GotenbergSharpClient"))
183+
/// .PostConfigure(options =>
184+
/// {
185+
/// options.BasicAuthUsername = "user";
186+
/// options.BasicAuthPassword = "pass";
187+
/// });
188+
///
189+
/// services.AddGotenbergSharpClient((sp, client) =>
190+
/// {
191+
/// // Custom HttpClient configuration
192+
/// client.DefaultRequestHeaders.Add("X-Custom-Header", "value");
193+
/// });
194+
/// </code>
195+
/// </para>
196+
/// </remarks>
197+
public static IHttpClientBuilder AddGotenbergSharpClient<TOptions>(
198+
this IServiceCollection services,
199+
Action<IServiceProvider, HttpClient> configureClient)
200+
where TOptions : GotenbergSharpClientOptions, new()
80201
{
81202
if (configureClient == null)
82203
{
@@ -94,9 +215,7 @@ public static IHttpClientBuilder AddGotenbergSharpClient(
94215
}))
95216
.AddHttpMessageHandler(sp =>
96217
{
97-
var ops = GetOptions(sp) ?? new GotenbergSharpClientOptions();
98-
99-
configureClientOptions?.Invoke(ops);
218+
var ops = sp.GetRequiredService<IOptions<TOptions>>().Value;
100219

101220
var hasUsername = !string.IsNullOrWhiteSpace(ops.BasicAuthUsername);
102221
var hasPassword = !string.IsNullOrWhiteSpace(ops.BasicAuthPassword);
@@ -122,9 +241,4 @@ public static IHttpClientBuilder AddGotenbergSharpClient(
122241

123242
return builder;
124243
}
125-
126-
private static GotenbergSharpClientOptions? GetOptions(IServiceProvider sp)
127-
{
128-
return sp.GetService<IOptions<GotenbergSharpClientOptions>>()?.Value;
129-
}
130244
}

0 commit comments

Comments
 (0)