Skip to content

Commit c415a25

Browse files
authored
AddConfig using reflection (#42)
This adds a new method to the configuration utils, that allows you to dynamically register config objects from public members of an arbitrary type. It also modifies the old AddConfig method to use this new one, return the config object (to make configuration easier), and accept subtypes of VersionedConfig instead of BaseConfig.
1 parent 95fe820 commit c415a25

File tree

3 files changed

+98
-7
lines changed

3 files changed

+98
-7
lines changed

Cognite.Config/Configuration.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
using Microsoft.Extensions.DependencyInjection;
12
using System;
23
using System.Collections.Generic;
34
using System.IO;
5+
using System.Linq;
6+
using System.Reflection;
47
using System.Text.RegularExpressions;
58
using YamlDotNet.Core;
69
using YamlDotNet.Core.Events;
@@ -170,6 +173,34 @@ private static int GetVersion(Dictionary<object, object> versionedConfig)
170173
}
171174
throw new ConfigurationException("The yaml configuration file should contain a 'version' tag");
172175
}
176+
/// <summary>
177+
/// Attempt to add the list of config-object types to the <see cref="ServiceCollection"/>, it looks at
178+
/// the public properties of <typeparamref name="T"/>.
179+
/// Applies to subtypes of the types given in <paramref name="types"/>. Having multiple candidates
180+
/// for a type can be unpredictable.
181+
/// </summary>
182+
/// <typeparam name="T">Configuration object type</typeparam>
183+
/// <param name="services">Services to add to</param>
184+
/// <param name="config">Configuration object to add from</param>
185+
/// <param name="types">List of types that should be added</param>
186+
public static void AddConfig<T>(this IServiceCollection services, T config, params Type[] types)
187+
{
188+
if (!types.Any()) return;
189+
foreach (var prop in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public))
190+
{
191+
if (!prop.CanRead) continue;
192+
foreach (var type in types)
193+
{
194+
if (type.IsAssignableFrom(prop.PropertyType))
195+
{
196+
var value = prop.GetValue(config);
197+
if (value is null) break;
198+
services.AddSingleton(type, value);
199+
break;
200+
}
201+
}
202+
}
203+
}
173204
}
174205

175206
internal class TemplatedValueDeserializer : INodeDeserializer

ExtractorUtils.Test/ConfigurationTest.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Cognite.Extractor.Logging;
77
using Cognite.Extractor.Metrics;
88
using Cognite.Extractor.Utils;
9+
using Cognite.Extractor.StateStorage;
910

1011
namespace ExtractorUtils.Test
1112
{
@@ -215,5 +216,64 @@ public static void InjectConfiguration() {
215216
File.Delete(path2);
216217
File.Delete(path3);
217218
}
219+
220+
public class ExtendedCogniteConfig : CogniteConfig
221+
{
222+
public int DataSetId { get; set; }
223+
}
224+
225+
public class CustomConfig
226+
{
227+
public string SomeValue { get; set; }
228+
}
229+
230+
public class ExtendedConfig : VersionedConfig
231+
{
232+
public LoggerConfig Logger { get; set; }
233+
234+
public ExtendedCogniteConfig Cognite { get; set; }
235+
236+
public StateStoreConfig StateStore { get; set; }
237+
238+
public CustomConfig CustomConfig { get; set; }
239+
public override void GenerateDefaults()
240+
{
241+
Logger = new LoggerConfig();
242+
}
243+
}
244+
245+
[Fact]
246+
public static void TestExtendedConfiguration()
247+
{
248+
string path = "test-custom-config.yml";
249+
string[] lines = { "version: 2",
250+
"logger:",
251+
" console:",
252+
" level: verbose",
253+
"cognite:",
254+
" data-set-id: 123",
255+
"custom-config:",
256+
" some-value: value" };
257+
var services = new ServiceCollection();
258+
259+
File.WriteAllLines(path, lines);
260+
261+
var config = services.AddConfig<ExtendedConfig>(path, 2);
262+
services.AddConfig(config, typeof(CustomConfig));
263+
using var provider = services.BuildServiceProvider();
264+
265+
var cogniteConfig = provider.GetRequiredService<CogniteConfig>();
266+
var loggerConfig = provider.GetRequiredService<LoggerConfig>();
267+
var customConfig = provider.GetRequiredService<CustomConfig>();
268+
269+
Assert.NotNull(cogniteConfig);
270+
Assert.NotNull(loggerConfig);
271+
Assert.NotNull(customConfig);
272+
Assert.Equal(123, (cogniteConfig as ExtendedCogniteConfig).DataSetId);
273+
Assert.Equal("value", customConfig.SomeValue);
274+
275+
File.Delete(path);
276+
277+
}
218278
}
219279
}

ExtractorUtils/Configuration/Configuration.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Cognite.Extractor.Configuration;
33
using Cognite.Extractor.Logging;
44
using Cognite.Extractor.Metrics;
5+
using Cognite.Extractor.StateStorage;
56

67
namespace Cognite.Extractor.Utils
78
{
@@ -21,19 +22,18 @@ public static class ConfigurationExtensions
2122
/// <param name="services">The service collection</param>
2223
/// <param name="path">Path to the file</param>
2324
/// <param name="acceptedConfigVersions">Accepted versions</param>
24-
/// <typeparam name="T">A type that inherits from <see cref="BaseConfig"/></typeparam>
25+
/// <typeparam name="T">A type that inherits from <see cref="VersionedConfig"/></typeparam>
2526
/// <exception cref="ConfigurationException">Thrown when the version is not valid,
2627
/// the yaml file is not found or in case of yaml parsing error</exception>
27-
public static void AddConfig<T>(this IServiceCollection services,
28+
/// <returns>An instance of the configuration object</returns>
29+
public static T AddConfig<T>(this IServiceCollection services,
2830
string path,
29-
params int[] acceptedConfigVersions) where T : BaseConfig
31+
params int[] acceptedConfigVersions) where T : VersionedConfig
3032
{
3133
var config = ConfigurationUtils.TryReadConfigFromFile<T>(path, acceptedConfigVersions);
3234
services.AddSingleton<T>(config);
33-
services.AddSingleton(config.Cognite);
34-
services.AddSingleton(config.Logger);
35-
services.AddSingleton(config.Metrics);
36-
services.AddSingleton(config.StateStore);
35+
services.AddConfig<T>(config, typeof(CogniteConfig), typeof(LoggerConfig), typeof(MetricsConfig), typeof(StateStoreConfig));
36+
return config;
3737
}
3838
}
3939

0 commit comments

Comments
 (0)