Skip to content

Commit 0f59127

Browse files
kylejuliandevtoddbaertaskpt
authored
feat: Add Dependency Injection extensions for Adding Flagd Provider (#410)
Signed-off-by: Kyle Julian <[email protected]> Co-authored-by: Todd Baert <[email protected]> Co-authored-by: André Silva <[email protected]>
1 parent f7dfe49 commit 0f59127

File tree

9 files changed

+648
-4
lines changed

9 files changed

+648
-4
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using System;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Microsoft.Extensions.Logging;
4+
using Microsoft.Extensions.Logging.Abstractions;
5+
using Microsoft.Extensions.Options;
6+
using OpenFeature.Contrib.Providers.Flagd;
7+
using OpenFeature.Contrib.Providers.Flagd.DependencyInjection;
8+
9+
namespace OpenFeature.DependencyInjection.Providers.Flagd;
10+
11+
/// <summary>
12+
/// Extension methods for configuring the <see cref="OpenFeatureBuilder"/>.
13+
/// </summary>
14+
public static class FeatureBuilderExtensions
15+
{
16+
/// <summary>
17+
/// Adds the <see cref="FlagdProvider"/> to the <see cref="OpenFeatureBuilder"/> with default <see cref="FlagdProviderOptions"/> configuration.
18+
/// </summary>
19+
/// <param name="builder">The <see cref="OpenFeatureBuilder"/> instance to configure.</param>
20+
/// <returns>The <see cref="OpenFeatureBuilder"/> instance for chaining.</returns>
21+
public static OpenFeatureBuilder AddFlagdProvider(this OpenFeatureBuilder builder)
22+
{
23+
builder.Services.AddOptions<FlagdProviderOptions>(FlagdProviderOptions.DefaultName);
24+
return builder.AddProvider(sp => CreateProvider(sp, null));
25+
}
26+
27+
/// <summary>
28+
/// Adds the <see cref="FlagdProvider"/> to the <see cref="OpenFeatureBuilder"/> with <see cref="FlagdProviderOptions"/> configuration.
29+
/// </summary>
30+
/// <param name="builder">The <see cref="OpenFeatureBuilder"/> instance to configure.</param>
31+
/// <param name="options">Options to configure <see cref="FlagdProvider"/>.</param>
32+
/// <returns>The <see cref="OpenFeatureBuilder"/> instance for chaining.</returns>
33+
public static OpenFeatureBuilder AddFlagdProvider(this OpenFeatureBuilder builder, Action<FlagdProviderOptions> options)
34+
{
35+
builder.Services.Configure(FlagdProviderOptions.DefaultName, options);
36+
return builder.AddProvider(sp => CreateProvider(sp, null));
37+
}
38+
39+
/// <summary>
40+
/// Adds the <see cref="FlagdProvider"/> to the <see cref="OpenFeatureBuilder"/> with a specific domain and default <see cref="FlagdProviderOptions"/> configuration.
41+
/// </summary>
42+
/// <param name="builder">The <see cref="OpenFeatureBuilder"/> instance to configure.</param>
43+
/// <param name="domain">The unique domain of the provider.</param>
44+
/// <returns>The <see cref="OpenFeatureBuilder"/> instance for chaining.</returns>
45+
public static OpenFeatureBuilder AddFlagdProvider(this OpenFeatureBuilder builder, string domain)
46+
{
47+
builder.Services.AddOptions<FlagdProviderOptions>(domain);
48+
return builder.AddProvider(domain, CreateProvider);
49+
}
50+
51+
/// <summary>
52+
/// Adds the <see cref="FlagdProvider"/> to the <see cref="OpenFeatureBuilder"/> with a specific domain and <see cref="FlagdProviderOptions"/> configuration.
53+
/// </summary>
54+
/// <param name="builder">The <see cref="OpenFeatureBuilder"/> instance to configure.</param>
55+
/// <param name="domain">The unique domain of the provider.</param>
56+
/// <param name="options">Options to configure <see cref="FlagdProvider"/>.</param>
57+
/// <returns>The <see cref="OpenFeatureBuilder"/> instance for chaining.</returns>
58+
public static OpenFeatureBuilder AddFlagdProvider(this OpenFeatureBuilder builder, string domain, Action<FlagdProviderOptions> options)
59+
{
60+
builder.Services.Configure(domain, options);
61+
return builder.AddProvider(domain, CreateProvider);
62+
}
63+
64+
private static FlagdProvider CreateProvider(IServiceProvider provider, string domain)
65+
{
66+
var optionsMonitor = provider.GetRequiredService<IOptionsMonitor<FlagdProviderOptions>>();
67+
var logger = provider.GetService<ILogger<FlagdProvider>>();
68+
logger ??= NullLogger<FlagdProvider>.Instance;
69+
70+
var options = string.IsNullOrEmpty(domain)
71+
? optionsMonitor.Get(FlagdProviderOptions.DefaultName)
72+
: optionsMonitor.Get(domain);
73+
74+
var config = options.ToFlagdConfig();
75+
config.Logger = logger;
76+
77+
return new FlagdProvider(config);
78+
}
79+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using OpenFeature.Contrib.Providers.Flagd;
2+
3+
namespace OpenFeature.DependencyInjection.Providers.Flagd;
4+
5+
/// <summary>
6+
/// Configuration options for the Flagd provider.
7+
/// </summary>
8+
public record FlagdProviderOptions
9+
{
10+
/// <summary>
11+
/// Default name for the Flagd provider.
12+
/// </summary>
13+
public const string DefaultName = "FlagdProvider";
14+
15+
/// <summary>
16+
/// The host for the provider to connect to. Defaults to "localhost".
17+
/// </summary>
18+
public string Host { get; set; } = "localhost";
19+
20+
/// <summary>
21+
/// The Port property of the config. Defaults to 8013.
22+
/// </summary>
23+
public int Port { get; set; } = 8013;
24+
25+
/// <summary>
26+
/// Use TLS for communication between the provider and the host. Defaults to false.
27+
/// </summary>
28+
public bool UseTls { get; set; } = false;
29+
30+
/// <summary>
31+
/// Enable/disable the local cache for static flag values. Defaults to false.
32+
/// </summary>
33+
public bool CacheEnabled { get; set; } = false;
34+
35+
/// <summary>
36+
/// The maximum size of the cache. Defaults to 10.
37+
/// </summary>
38+
public int MaxCacheSize { get; set; } = 10;
39+
40+
/// <summary>
41+
/// Path to the certificate file. Defaults to empty string.
42+
/// </summary>
43+
public string CertificatePath { get; set; } = string.Empty;
44+
45+
/// <summary>
46+
/// Path to the socket. Defaults to empty string.
47+
/// </summary>
48+
public string SocketPath { get; set; } = string.Empty;
49+
50+
/// <summary>
51+
/// Maximum number of times the connection to the event stream should be re-attempted. Defaults to 3.
52+
/// </summary>
53+
public int MaxEventStreamRetries { get; set; } = 3;
54+
55+
/// <summary>
56+
/// Which type of resolver to use. Defaults to <see cref="ResolverType.RPC"/>.
57+
/// </summary>
58+
public ResolverType ResolverType { get; set; } = ResolverType.RPC;
59+
60+
/// <summary>
61+
/// Source selector for the in-process provider. Defaults to empty string.
62+
/// </summary>
63+
public string SourceSelector { get; set; } = string.Empty;
64+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System;
2+
using OpenFeature.DependencyInjection.Providers.Flagd;
3+
4+
namespace OpenFeature.Contrib.Providers.Flagd.DependencyInjection;
5+
6+
internal static class FlagdProviderOptionsExtensions
7+
{
8+
public static FlagdConfig ToFlagdConfig(this FlagdProviderOptions options)
9+
{
10+
if (options == null)
11+
{
12+
throw new ArgumentNullException(nameof(options), "FlagdProviderOptions cannot be null.");
13+
}
14+
15+
var config = FlagdConfig.Builder()
16+
.WithHost(options.Host)
17+
.WithPort(options.Port)
18+
.WithTls(options.UseTls)
19+
.WithCache(options.CacheEnabled)
20+
.WithMaxCacheSize(options.MaxCacheSize)
21+
.WithCertificatePath(options.CertificatePath)
22+
.WithSocketPath(options.SocketPath)
23+
.WithMaxEventStreamRetries(options.MaxEventStreamRetries)
24+
.WithResolverType(options.ResolverType)
25+
.WithSourceSelector(options.SourceSelector)
26+
.Build();
27+
28+
return config;
29+
}
30+
}

src/OpenFeature.Contrib.Providers.Flagd/OpenFeature.Contrib.Providers.Flagd.csproj

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<PackageId>OpenFeature.Contrib.Providers.Flagd</PackageId>
@@ -36,8 +36,11 @@
3636
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3737
<PrivateAssets>all</PrivateAssets>
3838
</PackageReference>
39-
<PackageReference Include="Microsoft.CSharp" Version="4.7.0"
40-
Condition="'$(TargetFramework)' == 'netstandard2.0'" />
41-
<PackageReference Include="OpenFeature" Version="[2.0,3.0)" />
39+
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" Condition="'$(TargetFramework)' == 'netstandard2.0'" />
40+
<PackageReference Include="OpenFeature" Version="$(OpenFeatureVersion)" />
41+
<PackageReference Include="OpenFeature.DependencyInjection" Version="$(OpenFeatureVersion)" />
4242
</ItemGroup>
43+
<PropertyGroup>
44+
<OpenFeatureVersion>[2.2,2.99999]</OpenFeatureVersion>
45+
</PropertyGroup>
4346
</Project>

src/OpenFeature.Contrib.Providers.Flagd/README.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,23 @@ The flagd Flag provider allows you to connect to your flagd instance.
1313
We will first install the **OpenFeature SDK** and the **flagd provider**.
1414

1515
### .NET Cli
16+
1617
```shell
1718
dotnet add package OpenFeature.Contrib.Providers.Flagd
1819
```
20+
1921
### Package Manager
2022

2123
```shell
2224
NuGet\Install-Package OpenFeature.Contrib.Providers.Flagd
2325
```
26+
2427
### Package Reference
2528

2629
```xml
2730
<PackageReference Include="OpenFeature.Contrib.Providers.Flagd" />
2831
```
32+
2933
### Packet cli
3034

3135
```shell
@@ -76,6 +80,80 @@ namespace OpenFeatureTestApp
7680
}
7781
```
7882

83+
## Using the flagd Provider with the OpenFeature SDK and Dependency Injection
84+
85+
You can also use the flagd Provider with the OpenFeature SDK and Dependency Injection. The following example shows how to do this using Microsoft.Extensions.DependencyInjection:
86+
87+
Before you start, make sure you have the `OpenFeature.Hosting` NuGet package installed:
88+
89+
```shell
90+
dotnet add package OpenFeature.Hosting
91+
```
92+
93+
Or with Package Manager:
94+
95+
```shell
96+
NuGet\Install-Package OpenFeature.Hosting
97+
```
98+
99+
Now you can set up Dependency Injection with OpenFeature and the flagd Provider in your `Program.cs` file. When not specifying any configuration options, the flagd Provider will use the default values for the variables as described below.
100+
101+
```csharp
102+
using OpenFeature;
103+
using OpenFeature.DependencyInjection.Providers.Flagd;
104+
105+
namespace OpenFeatureTestApp
106+
{
107+
class Hello {
108+
static void Main(string[] args) {
109+
var builder = WebApplication.CreateBuilder(args);
110+
111+
builder.Services.AddOpenFeature(config =>
112+
{
113+
config.AddHostedFeatureLifecycle()
114+
.AddFlagdProvider();
115+
});
116+
117+
var app = builder.Build();
118+
119+
// ... ommitted for brevity
120+
}
121+
}
122+
}
123+
```
124+
125+
You can override the default configuration options by specifying properties on the `FlagdProviderOptions` on the `AddFlagdProvider` method.
126+
127+
```csharp
128+
using OpenFeature;
129+
using OpenFeature.DependencyInjection.Providers.Flagd;
130+
131+
namespace OpenFeatureTestApp
132+
{
133+
class Hello {
134+
static void Main(string[] args) {
135+
var builder = WebApplication.CreateBuilder(args);
136+
137+
builder.Services.AddOpenFeature(config =>
138+
{
139+
config.AddHostedFeatureLifecycle()
140+
.AddFlagdProvider(o =>
141+
{
142+
o.Host = builder.Configuration["FlagdProviderOptions:Host"];
143+
o.Port = int.Parse(builder.Configuration["FlagdProviderOptions:Port"] ?? "8013");
144+
145+
// other configurations can be set here
146+
});
147+
});
148+
149+
var app = builder.Build();
150+
151+
// ... ommitted for brevity
152+
}
153+
}
154+
}
155+
```
156+
79157
### Configuring the FlagdProvider
80158

81159
The URI of the flagd server to which the `flagd Provider` connects to can either be passed directly to the constructor, or be configured using the following environment variables:

0 commit comments

Comments
 (0)