Skip to content

Commit 034b4db

Browse files
committed
Improve OpenStackNet.Configuration
* Make it immutable, so only changes made through Configure() persist. This prevents someone (or us) from grabbing the config, tweaking it or passing it to a FlurlClient and having that accidentally alter the global config. * Consolidate all configuration into the OpenStackNetConfigurationOptions. Handle json.net, flurl and the sdk. * Deprecate Configure(), it doesn't need to be called anymore. * Deprecate Configure(flurlSettings, sdkSettings), we will respect it for now but it's gone in 2.0.
1 parent 2e8b116 commit 034b4db

File tree

4 files changed

+152
-88
lines changed

4 files changed

+152
-88
lines changed

src/corelib/Extensions/TypeExtensions.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ public static string GetAssemblyFileVersion(this Type type)
3131
}
3232
}
3333

34+
/// <summary />
35+
public static T Clone<T>(this T src)
36+
where T : new()
37+
{
38+
var dest = new T();
39+
src.CopyProperties(dest);
40+
return dest;
41+
}
42+
3443
/// <summary />
3544
public static void CopyProperties<T>(this T src, T dest)
3645
{

src/corelib/OpenStackNet.cs

Lines changed: 137 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public static class OpenStackNet
1919
{
2020
/// <summary>
2121
/// Global configuration which affects OpenStack.NET's behavior.
22-
/// <para>Modify using <see cref="Configure"/>.</para>
22+
/// <para>Modify using <see cref="Configure(Action{OpenStackNetConfigurationOptions})"/>.</para>
2323
/// </summary>
2424
public static OpenStackNetConfigurationOptions Configuration
2525
{
@@ -31,100 +31,74 @@ public static OpenStackNetConfigurationOptions Configuration
3131
}
3232
}
3333

34-
private static readonly OpenStackNetConfigurationOptions _config = new OpenStackNetConfigurationOptions();
34+
private static OpenStackNetConfigurationOptions _config;
3535
private static readonly object ConfigureLock = new object();
3636
private static bool _isConfigured;
3737

3838
/// <summary>
39-
/// Provides thread-safe accesss to OpenStack.NET's global configuration options.
40-
/// <para>
41-
/// Can only be called once at application start-up, before instantiating any OpenStack.NET objects.
42-
/// </para>
39+
/// <para>DEPRECATED. This no longer needs to be explicityly called, unless you require customizations. In that case, use <see cref="Configure(Action{OpenStackNetConfigurationOptions})"/> .</para>
40+
/// Initializes the SDK using the default configuration.
41+
/// </summary>
42+
[Obsolete("This will be removed in v2.0. Use Configure(Action{OpenStackNetConfigurationOptions}) instead if you need to customize anything.")]
43+
public static void Configure()
44+
{
45+
Configure(null);
46+
}
47+
48+
/// <summary>
49+
/// <para>Provides thread-safe accesss to OpenStack.NET's global configuration options.</para>
50+
/// <para>Can only be called once at application start-up, before instantiating any OpenStack.NET objects.</para>
51+
/// </summary>
52+
/// <param name="configure">Additional configuration of OpenStack.NET's global settings.</param>
53+
public static void Configure(Action<OpenStackNetConfigurationOptions> configure)
54+
{
55+
Configure(null, configure);
56+
}
57+
58+
/// <summary>
59+
/// <para>DEPRECATED, use <see cref="Configure(Action{OpenStackNetConfigurationOptions})"/> instead.</para>
60+
/// <para>Provides thread-safe accesss to OpenStack.NET's global configuration options.</para>
61+
/// <para>Can only be called once at application start-up, before instantiating any OpenStack.NET objects.</para>
4362
/// </summary>
4463
/// <param name="configureFlurl">Addtional configuration of the OpenStack.NET Flurl client settings <seealso cref="Flurl.Http.FlurlHttp.Configure" />.</param>
4564
/// <param name="configure">Additional configuration of OpenStack.NET's global settings.</param>
46-
public static void Configure(Action<FlurlHttpSettings> configureFlurl = null, Action<OpenStackNetConfigurationOptions> configure = null)
65+
[Obsolete("This will be removed in v2.0. Use Configure(Action{OpenStackNetConfigurationOptions}) instead.")]
66+
public static void Configure(Action<FlurlHttpSettings> configureFlurl, Action<OpenStackNetConfigurationOptions> configure)
4767
{
4868
lock (ConfigureLock)
4969
{
50-
// Check if a user is attempting to apply custom configuration after the default config has been applied
51-
if (_isConfigured && (configureFlurl != null || configure != null))
70+
if (_isConfigured)
5271
{
53-
Trace.TraceError("Ignoring additional call to OpenStackNet.Configure. It can only be called once at application start-up, before instantiating any OpenStack.NET objects.");
72+
// Check if a user is attempting to apply custom configuration after the default config has been applied
73+
if(configureFlurl != null || configure != null)
74+
Trace.TraceError("Ignoring additional call to OpenStackNet.Configure. It can only be called once at application start-up, before instantiating any OpenStack.NET objects.");
75+
5476
return;
5577
}
5678

57-
configure?.Invoke(_config);
58-
ConfigureFlurl(configureFlurl);
59-
79+
// TODO: Use the line below once we hit 2.0 and configureFlurl is deprecated
80+
// _config = new OpenStackNetConfigurationOptions(configure);
81+
_config = new OpenStackNetConfigurationOptions(options =>
82+
{
83+
configureFlurl?.Invoke(options.FlurlHttpSettings);
84+
configure?.Invoke(options);
85+
});
6086
_isConfigured = true;
6187
}
6288
}
6389

6490
/// <summary>
65-
/// Resets all configuration (OpenStack.NET, Flurl and Json.NET) so that <see cref="Configure"/> can be called again.
91+
/// Resets all configuration (OpenStack.NET, Flurl and Json.NET) so that <see cref="Configure(Action{OpenStackNetConfigurationOptions})"/> can be called again.
6692
/// </summary>
6793
public static void ResetDefaults()
6894
{
6995
lock (ConfigureLock)
7096
{
71-
_config.ResetDefaults();
97+
_config = null;
7298
_isConfigured = false;
7399
}
74100
}
75101

76-
private static void ConfigureFlurl(Action<FlurlHttpSettings> configureFlurl = null)
77-
{
78-
var flurlSettings = _config.FlurlHttpSettings;
79-
80-
// Apply the application's default settings
81-
configureFlurl?.Invoke(flurlSettings);
82-
83-
flurlSettings.JsonSerializer = new NewtonsoftJsonSerializer(new JsonSerializerSettings
84-
{
85-
DefaultValueHandling = DefaultValueHandling.Ignore,
86-
MissingMemberHandling = MissingMemberHandling.Ignore,
87-
NullValueHandling = NullValueHandling.Ignore,
88-
ContractResolver = new OpenStackContractResolver()
89-
});
90-
91-
//
92-
// Apply our default settings
93-
//
94-
if (flurlSettings.HttpClientFactory is DefaultHttpClientFactory)
95-
flurlSettings.HttpClientFactory = new AuthenticatedHttpClientFactory();
96-
97-
// Apply our event handling without clobbering any application level handlers
98-
var applicationBeforeCall = flurlSettings.BeforeCall;
99-
flurlSettings.BeforeCall = call =>
100-
{
101-
SetUserAgentHeader(call);
102-
applicationBeforeCall?.Invoke(call);
103-
};
104-
105-
var applicationAfterCall = flurlSettings.AfterCall;
106-
flurlSettings.AfterCall = call =>
107-
{
108-
Tracing.TraceHttpCall(call);
109-
applicationAfterCall?.Invoke(call);
110-
};
111-
112-
var applicationOnError = flurlSettings.OnError;
113-
flurlSettings.OnError = call =>
114-
{
115-
Tracing.TraceFailedHttpCall(call);
116-
applicationOnError?.Invoke(call);
117-
};
118-
}
119-
120-
private static void SetUserAgentHeader(HttpCall call)
121-
{
122-
foreach (var userAgent in Configuration.UserAgents)
123-
{
124-
call.Request.Headers.UserAgent.Add(userAgent);
125-
}
126-
}
127-
128102
/// <summary>
129103
/// Provides global point for programmatically configuraing tracing
130104
/// </summary>
@@ -169,37 +143,119 @@ private static string SerializeHttpCall(HttpCall httpCall)
169143
}
170144

171145
/// <summary>
172-
/// A set of properties that affect OpenStack.NET's behavior.
173-
/// <para>Generally set via the static <see cref="OpenStack.OpenStackNet.Configure"/> method.</para>
146+
/// A readonly set of properties that affect OpenStack.NET's behavior.
147+
/// <para>To configure, pass a custom action via the static <see cref="OpenStackNet.Configure(Action{OpenStackNetConfigurationOptions})"/> method.</para>
174148
/// </summary>
175149
public class OpenStackNetConfigurationOptions
176150
{
151+
private readonly bool _isInitialized;
152+
private readonly FlurlHttpSettings _flurlHttpSettings;
153+
private readonly JsonSerializerSettings _jsonSerializerSettings;
154+
private readonly List<ProductInfoHeaderValue> _userAgents;
155+
177156
/// <summary/>
178-
protected internal OpenStackNetConfigurationOptions()
157+
protected internal OpenStackNetConfigurationOptions(Action<OpenStackNetConfigurationOptions> configure = null)
179158
{
180-
ResetDefaults();
159+
_flurlHttpSettings = new FlurlHttpSettings();
160+
_jsonSerializerSettings = new JsonSerializerSettings();
161+
_userAgents = new List<ProductInfoHeaderValue>
162+
{
163+
new ProductInfoHeaderValue("openstack.net", GetType().GetAssemblyFileVersion())
164+
};
165+
166+
configure?.Invoke(this);
167+
ApplyDefaults();
168+
_isInitialized = true;
181169
}
182170

183171
/// <summary>
184172
/// Custom Flurl.Http configuration settings which are specific to requests made by this SDK.
185173
/// </summary>
186-
public FlurlHttpSettings FlurlHttpSettings { get; private set; }
174+
public FlurlHttpSettings FlurlHttpSettings
175+
{
176+
get
177+
{
178+
if(_isInitialized)
179+
return _flurlHttpSettings.Clone();
180+
181+
return _flurlHttpSettings;
182+
}
183+
}
184+
185+
/// <summary>
186+
/// Custom Json.NET configuration settings which are specific to requests made by this SDK.
187+
/// </summary>
188+
public JsonSerializerSettings JsonSerializerSettings
189+
{
190+
get
191+
{
192+
if (_isInitialized)
193+
return _jsonSerializerSettings.Clone();
194+
195+
return _jsonSerializerSettings;
196+
}
197+
}
187198

188199
/// <summary>
189-
/// Additional application specific user agents which should be set in the UserAgent header on all requests.
200+
/// Additional application specific user agents which should be set in the UserAgent header on all requests made by this SDK.
190201
/// </summary>
191-
public List<ProductInfoHeaderValue> UserAgents { get; private set; }
202+
public IList<ProductInfoHeaderValue> UserAgents
203+
{
204+
get
205+
{
206+
if(_isInitialized)
207+
return _userAgents.AsReadOnly();
192208

193-
/// <summary>
194-
/// Clear all custom global options and set default values.
195-
/// </summary>
196-
public virtual void ResetDefaults()
209+
return _userAgents;
210+
}
211+
}
212+
213+
private void ApplyDefaults()
197214
{
198-
FlurlHttpSettings = new FlurlHttpSettings();
199-
UserAgents = new List<ProductInfoHeaderValue>
215+
//
216+
// Apply our default settings on top of user customizations, hopefully without clobbering anything
217+
//
218+
_jsonSerializerSettings.DefaultValueHandling = DefaultValueHandling.Ignore;
219+
_jsonSerializerSettings.MissingMemberHandling = MissingMemberHandling.Ignore;
220+
_jsonSerializerSettings.NullValueHandling = NullValueHandling.Ignore;
221+
if(!(_jsonSerializerSettings.ContractResolver is OpenStackContractResolver))
222+
_jsonSerializerSettings.ContractResolver = new OpenStackContractResolver();
223+
224+
_flurlHttpSettings.JsonSerializer = new NewtonsoftJsonSerializer(_jsonSerializerSettings);
225+
226+
// When in test mode (set via HttpTest), this will be a custom class (TestHttpClientFactory)
227+
if (_flurlHttpSettings.HttpClientFactory?.GetType() == typeof(DefaultHttpClientFactory))
228+
_flurlHttpSettings.HttpClientFactory = new AuthenticatedHttpClientFactory();
229+
230+
// Apply our event handling and optionally include any custom application handlers
231+
var applicationBeforeCall = _flurlHttpSettings.BeforeCall;
232+
_flurlHttpSettings.BeforeCall = call =>
200233
{
201-
new ProductInfoHeaderValue("openstack.net", GetType().GetAssemblyFileVersion())
234+
SetUserAgentHeader(call);
235+
applicationBeforeCall?.Invoke(call);
236+
};
237+
238+
var applicationAfterCall = _flurlHttpSettings.AfterCall;
239+
_flurlHttpSettings.AfterCall = call =>
240+
{
241+
OpenStackNet.Tracing.TraceHttpCall(call);
242+
applicationAfterCall?.Invoke(call);
243+
};
244+
245+
var applicationOnError = _flurlHttpSettings.OnError;
246+
_flurlHttpSettings.OnError = call =>
247+
{
248+
OpenStackNet.Tracing.TraceFailedHttpCall(call);
249+
applicationOnError?.Invoke(call);
202250
};
203251
}
252+
253+
private void SetUserAgentHeader(HttpCall call)
254+
{
255+
foreach (ProductInfoHeaderValue userAgent in UserAgents)
256+
{
257+
call.Request.Headers.UserAgent.Add(userAgent);
258+
}
259+
}
204260
}
205261
}

src/corelib/Testing/HttpTest.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,19 @@ public class HttpTest : Flurl.Http.Testing.HttpTest, IDisposable
2121
/// <summary>
2222
/// Initializes a new instance of the <see cref="HttpTest"/> class.
2323
/// </summary>
24-
/// <param name="configureFlurl">Addtional configuration of the OpenStack.NET Flurl client settings <seealso cref="Flurl.Http.FlurlHttp.Configure" />.</param>
2524
/// <param name="configure">Additional configuration of OpenStack.NET's global settings.</param>
26-
public HttpTest(Action<FlurlHttpSettings> configureFlurl = null, Action<OpenStackNetConfigurationOptions> configure = null)
25+
public HttpTest(Action<OpenStackNetConfigurationOptions> configure = null)
2726
{
28-
Action<FlurlHttpSettings> setFlurlTestMode = opts =>
27+
Action<FlurlHttpSettings> setTestMode = settings =>
2928
{
30-
configureFlurl?.Invoke(opts);
31-
opts.HttpClientFactory = new TestHttpClientFactory(this);
32-
opts.AfterCall = call =>
29+
settings.HttpClientFactory = new TestHttpClientFactory(this);
30+
settings.AfterCall = call =>
3331
{
3432
CallLog.Add(call);
3533
};
3634
};
3735
OpenStackNet.ResetDefaults();
38-
OpenStackNet.Configure(setFlurlTestMode, configure);
36+
OpenStackNet.Configure(setTestMode, configure);
3937
}
4038

4139
/// <inheritdoc />

src/testing/unit/OpenStackNetTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using OpenStack.Testing;
1010
using Xunit;
1111
using Xunit.Abstractions;
12+
#pragma warning disable 618 // We can remove this once OpenStackNet.Configure() is made internal and obsolete is removed
1213

1314
namespace OpenStack
1415
{

0 commit comments

Comments
 (0)