Skip to content

Commit

Permalink
feat: Feature Provider Enhancements- #321 (#324)
Browse files Browse the repository at this point in the history
This update focuses on enhancing the feature provider integration and
testing framework, incorporating improvements to flexibility, usability,
and testing capabilities.
This update addresses [GitHub Issue
#321](#321) in the
OpenFeature .NET SDK repository.

---

### Key Enhancements

1. **Dependency Injection (DI) Enhancements:**
   - Improved lifecycle management for better resource handling.
- Streamlined registration for feature providers, reducing configuration
complexity.
- Introduced the `AddProvider` extension method to simplify and adapt
feature provider integration during service setup.

2. **Simplified Codebase:**
- Removed `FeatureProviderFactory` logic, eliminating unnecessary
complexity and improving usability.

3. **Improved InMemoryProvider:**
- Enhanced the registration process for the `InMemoryProvider`, enabling
smoother and more intuitive usage.

4. **Testing Improvements:**
- Established a dedicated integration test project for comprehensive
validation.
- Improved overall test coverage, ensuring the reliability and
robustness of the framework.

---------

Signed-off-by: Artyom Tonoyan <arttonoyan@gmail.com>
Co-authored-by: chrfwow <christian.lutnik@dynatrace.com>
  • Loading branch information
arttonoyan and chrfwow authored Dec 6, 2024
1 parent bf9de4e commit 70f847b
Show file tree
Hide file tree
Showing 18 changed files with 616 additions and 232 deletions.
3 changes: 2 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageVersion Include="System.Collections.Immutable" Version="1.7.1" />
<PackageVersion Include="System.Threading.Channels" Version="6.0.0" />
Expand All @@ -29,7 +30,7 @@
<PackageVersion Include="SpecFlow.xUnit" Version="3.9.74" />
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="8.0.11" />
</ItemGroup>

<ItemGroup Condition="'$(OS)' == 'Unix'">
Expand Down
7 changes: 7 additions & 0 deletions OpenFeature.sln
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenFeature.DependencyInjec
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenFeature.Hosting", "src\OpenFeature.Hosting\OpenFeature.Hosting.csproj", "{C99DA02A-3981-45A6-B3F8-4A1A48653DEE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenFeature.IntegrationTests", "test\OpenFeature.IntegrationTests\OpenFeature.IntegrationTests.csproj", "{68463B47-36B4-8DB5-5D02-662C169E85B0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -119,6 +121,10 @@ Global
{C99DA02A-3981-45A6-B3F8-4A1A48653DEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C99DA02A-3981-45A6-B3F8-4A1A48653DEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C99DA02A-3981-45A6-B3F8-4A1A48653DEE}.Release|Any CPU.Build.0 = Release|Any CPU
{68463B47-36B4-8DB5-5D02-662C169E85B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{68463B47-36B4-8DB5-5D02-662C169E85B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{68463B47-36B4-8DB5-5D02-662C169E85B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{68463B47-36B4-8DB5-5D02-662C169E85B0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -137,6 +143,7 @@ Global
{C5415057-2700-48B5-940A-7A10969FA639} = {C97E9975-E10A-4817-AE2C-4DD69C3C02D4}
{EB35F9F6-8A79-410E-A293-9387BC4AC9A7} = {65FBA159-23E0-4CF9-881B-F78DBFF198E9}
{C99DA02A-3981-45A6-B3F8-4A1A48653DEE} = {C97E9975-E10A-4817-AE2C-4DD69C3C02D4}
{68463B47-36B4-8DB5-5D02-662C169E85B0} = {65FBA159-23E0-4CF9-881B-F78DBFF198E9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {41F01B78-FB06-404F-8AD0-6ED6973F948F}
Expand Down
60 changes: 31 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,41 +338,43 @@ builder.Services.AddOpenFeature(featureBuilder => {
});
});
```
#### Creating a New Provider
To integrate a custom provider, such as InMemoryProvider, you’ll need to create a factory that builds and configures the provider. This section demonstrates how to set up InMemoryProvider as a new provider with custom configuration options.

**Configuring InMemoryProvider as a New Provider**
<br />Begin by creating a custom factory class, `InMemoryProviderFactory`, that implements `IFeatureProviderFactory`. This factory will initialize your provider with any necessary configurations.
```csharp
public class InMemoryProviderFactory : IFeatureProviderFactory
{
internal IDictionary<string, Flag>? Flags { get; set; }

public FeatureProvider Create() => new InMemoryProvider(Flags);
}
```
**Adding an Extension Method to OpenFeatureBuilder**
<br />To streamline the configuration process, add an extension method, `AddInMemoryProvider`, to `OpenFeatureBuilder`. This allows you to set up the provider with either a domain-scoped or a default configuration.
### Registering a Custom Provider
You can register a custom provider, such as `InMemoryProvider`, with OpenFeature using the `AddProvider` method. This approach allows you to dynamically resolve services or configurations during registration.

```csharp
public static partial class FeatureBuilderExtensions
{
public static OpenFeatureBuilder AddInMemoryProvider(this OpenFeatureBuilder builder, Action<IDictionary<string, Flag>>? configure = null)
=> builder.AddProvider<InMemoryProviderFactory>(factory => ConfigureFlags(factory, configure));
services.AddOpenFeature()
.AddProvider(provider =>
{
// Resolve services or configurations as needed
var configuration = provider.GetRequiredService<IConfiguration>();
var flags = new Dictionary<string, Flag>
{
{ "feature-key", new Flag<bool>(configuration.GetValue<bool>("FeatureFlags:Key")) }
};

// Register a custom provider, such as InMemoryProvider
return new InMemoryProvider(flags);
});
```

public static OpenFeatureBuilder AddInMemoryProvider(this OpenFeatureBuilder builder, string domain, Action<IDictionary<string, Flag>>? configure = null)
=> builder.AddProvider<InMemoryProviderFactory>(domain, factory => ConfigureFlags(factory, configure));
#### Adding a Domain-Scoped Provider

private static void ConfigureFlags(InMemoryProviderFactory factory, Action<IDictionary<string, Flag>>? configure)
{
if (configure == null)
return;
You can also register a domain-scoped custom provider, enabling configurations specific to each domain:

var flag = new Dictionary<string, Flag>();
configure.Invoke(flag);
factory.Flags = flag;
}
}
```csharp
services.AddOpenFeature()
.AddProvider("my-domain", (provider, domain) =>
{
// Resolve services or configurations as needed for the domain
var flags = new Dictionary<string, Flag>
{
{ $"{domain}-feature-key", new Flag<bool>(true) }
};

// Register a domain-scoped custom provider such as InMemoryProvider
return new InMemoryProvider(flags);
});
```

<!-- x-hide-in-docs-start -->
Expand Down
23 changes: 0 additions & 23 deletions src/OpenFeature.DependencyInjection/IFeatureProviderFactory.cs

This file was deleted.

Loading

0 comments on commit 70f847b

Please sign in to comment.