-
Couldn't load subscription status.
- Fork 22
feat: Extract minimal DI integration to OpenFeature.DependencyInjection.Abstractions #596
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: Extract minimal DI integration to OpenFeature.DependencyInjection.Abstractions #596
Conversation
Introduced a new project, `OpenFeature.DependencyInjection.Abstractions`, to the solution. This project supports dependency injection abstractions and targets multiple frameworks (`netstandard2.0`, `net8.0`, `net9.0`, `net462`) for broad compatibility. Configured the project with the `Microsoft.NET.Sdk` SDK and set the root namespace to `OpenFeature.DependencyInjection.Abstractions`. Added dependencies on `Microsoft.Extensions.DependencyInjection.Abstractions` and `Microsoft.Extensions.Options` to enable DI and options configuration.
Refactored the OpenFeature framework to introduce `OpenFeatureProviderBuilder`, enhancing support for dependency injection and provider management. - Changed namespaces to align with DI abstractions. - Made `FeatureCodes` public for broader accessibility. - Added `InternalsVisibleTo` for testing and project references. - Introduced `OpenFeatureProviderBuilder` for managing providers and policies. - Added extension methods for provider and policy registration. - Refactored `OpenFeatureBuilder` to inherit from `OpenFeatureProviderBuilder`. - Consolidated shared functionality in `OpenFeatureProviderOptions`. - Updated `FeatureBuilderExtensions` and `InMemoryProviderOptions` to use the new abstraction. - Updated tests to reflect new method signatures and hierarchy. - Removed redundant methods and properties, improving code organization. These changes improve maintainability, extensibility, and alignment with modern DI patterns.
Simplified the `AddProvider` API by removing the `TFeatureProvider` generic type parameter and directly using the `FeatureProvider` type. Updated the `TOptions` parameter to ensure it derives from `OpenFeatureProviderOptions`. Added a `<ProjectReference>` to `OpenFeature.csproj` in `OpenFeature.DependencyInjection.Abstractions.csproj`. Updated `OpenFeatureOptions` to `OpenFeatureProviderOptions` in the default name selector policy. Refactored `AddInMemoryProvider` methods to align with the new API. Updated tests in `OpenFeatureBuilderExtensionsTests` to reflect the changes and validate the updated functionality. Performed general code cleanup, including XML documentation updates, to improve clarity and maintain consistency across the codebase.
Refactored `FeatureLifecycleManager` to modularize provider, hook, and handler initialization with new methods: `InitializeProvidersAsync`, `InitializeHooks`, and `InitializeHandlers`. Updated `EnsureInitializedAsync` to use these methods for improved readability and maintainability. Revised `AddInMemoryProvider` in `FeatureBuilderExtensions` to use a generic `FeatureProvider` abstraction. Adjusted `CreateProvider` methods accordingly. Improved code clarity in `FeatureFlagIntegrationTest` by renaming variables for consistency and removing redundant assignments.
Updated FeatureLifecycleManagerTests to replace OpenFeatureOptions with OpenFeatureProviderOptions for configuring feature providers. Added support for hooks and keyed singletons to enhance modularity. Introduced additional feature flag retrieval in FeatureFlagIntegrationTest. Added dependency on OpenFeature.DependencyInjection.Abstractions.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #596 +/- ##
==========================================
- Coverage 89.76% 89.68% -0.09%
==========================================
Files 77 80 +3
Lines 3166 3179 +13
Branches 364 369 +5
==========================================
+ Hits 2842 2851 +9
Misses 253 253
- Partials 71 75 +4 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
OpenFeature.slnx
Outdated
| <Project Path="samples/AspNetCore/Samples.AspNetCore.csproj" /> | ||
| </Folder> | ||
| <Folder Name="/src/"> | ||
| <Project Path="src/OpenFeature.DependencyInjection.Abstractions/OpenFeature.DependencyInjection.Abstractions.csproj" Id="607a23fa-2c2f-4d2e-9079-adae18326512" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: I'm not sure the ID property is needed here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed: 3d680f3
src/OpenFeature.DependencyInjection.Abstractions/OpenFeatureProviderOptions.cs
Show resolved
Hide resolved
| /// </code> | ||
| /// </example> | ||
| internal static class FeatureCodes | ||
| public static class FeatureCodes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: Do we need these FeatureCodes anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kylejuliandev Answer: Yes - within the scope of this PR, FeatureCodes are still required; the current changes reference them.
Follow-up proposal: It’s a good time to graduate them from Experimental. Not in this PR - I suggest a separate task.
@askpt @beeme1mr what do you think about me opening a follow-up issue to:
- remove the
Experimentalattribute, - update docs/usages,
- add a changelog entry and brief migration note?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think with @kylejuliandev efforts to deprecate the OpenFeature.DependencyInjection library and this would be great candidate for 2.10. Feel free to create a new issue and add it to 2.10 milestone 👍
|
|
||
| <PropertyGroup> | ||
| <TargetFrameworks>netstandard2.0;net8.0;net9.0;net462</TargetFrameworks> | ||
| <RootNamespace>OpenFeature.DependencyInjection.Abstractions</RootNamespace> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Should we have these abstractions in their own namespace? Would it be more accessible to have them in say just the OpenFeature namespace?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kylejuliandev Great question - thanks for calling it out.
I’d keep these in their own namespace for now. OpenFeature.DependencyInjection.Abstractions targets provider authors (integrating new providers), not typical app consumers. Keeping it separate avoids cluttering the root OpenFeature namespace and lets us evolve DI-specific types independently.
Of course, I’m open to discussion about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer the @arttonoyan. This is a very specific Abstraction layer.
I am wondering if we should keep in a OpenFeature.Abstractions instead. This would be a "library" of just OpenFeature related abstractions like "hooks", "provider"... I am happy to keep OpenFeature.DependencyInjection.Abstractions if the rest of the team agrees.
src/OpenFeature.DependencyInjection.Abstractions/OpenFeatureProviderBuilder.cs
Show resolved
Hide resolved
| } | ||
| } | ||
|
|
||
| /// <inheritdoc /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Not sure we need the inheritdoc section here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// <inheritdoc /> |
| var services = new ServiceCollection(); | ||
| var provider = new NoOpFeatureProvider(); | ||
| services.AddOptions<OpenFeatureOptions>().Configure(options => | ||
| services.AddOptions<OpenFeatureProviderOptions>().Configure(options => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: it should be fine for this to remain OpenFeatureOptions right? OpenFeatureOptions extends OpenFeatureProviderOptions
| options.DefaultNameSelector = provider => | ||
| { | ||
| var options = provider.GetRequiredService<IOptions<OpenFeatureOptions>>().Value; | ||
| var options = provider.GetRequiredService<IOptions<OpenFeatureProviderOptions>>().Value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: it should be fine for this to remain OpenFeatureOptions right? OpenFeatureOptions extends OpenFeatureProviderOptions
| } | ||
| } | ||
|
|
||
| private readonly HashSet<string> _hookNames = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thought: I wonder if we can simplify the Hook registration. If instead of relying on HookNames we just inject Hooks like services.AddTransient<Hook>(myHook). And when we retrieve these hooks out to register them in the provider we just read it back out like var hooks = _serviceProvider.GetServices<Hook>();
Would eliminate the need to track hook names in this options file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems a great suggestion. My concern is if we register two different instance Hooks of the same type. What should happen? Should we allow this behaviour?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went ahead over the weekend and gave it a stab in #609. Seems simple enough. I think if we register two hooks of the same type, then we should still handle them both. Maybe let's take this conversation over there. Happy to close this here as it's not really relevant to the changes
…hub.com/arttonoyan/dotnet-sdk into feature/di-abstractions-builder-api-587
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! I only added a couple of minor comments.
Please have a chat with @kylejuliandev to try understand the impact on his deprecations efforts. I remember he mentioned a couple of fixes he was working on for the DI and I don't want that effort to get lost during this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file misses the README file necessary for the package documentation.
src/OpenFeature.DependencyInjection.Abstractions/OpenFeatureProviderBuilderExtensions.cs
Show resolved
Hide resolved
| InitializeHandlers(); | ||
| } | ||
|
|
||
| /// <inheritdoc /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is private method, so this is not needed.
| /// <inheritdoc /> |
| } | ||
| } | ||
|
|
||
| /// <inheritdoc /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// <inheritdoc /> |
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| private async Task InitializeProvidersAsync(CancellationToken cancellationToken) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cancellationToken seems to not be used here.
| using OpenFeature.DependencyInjection.Abstractions; | ||
| using OpenFeature.Providers.Memory; | ||
|
|
||
| namespace OpenFeature.Hosting.Providers.Memory; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should move to OpenFeature.Providers.Memory
| } | ||
| } | ||
|
|
||
| private readonly HashSet<string> _hookNames = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems a great suggestion. My concern is if we register two different instance Hooks of the same type. What should happen? Should we allow this behaviour?
This PR
Extract the minimal, provider-agnostic DI surface into a new package:
OpenFeature.DependencyInjection.Abstractions.This isolates the contracts and lightweight wiring needed to integrate any OpenFeature provider without pulling in a concrete implementation.
Related Issues
Fixes #587
Notes
OpenFeatureOptionsinto a new base options type:OpenFeatureProviderOptions.OpenFeatureOptionsnow inherits fromOpenFeatureProviderOptions.OpenFeatureOptionsnow targetsOpenFeatureProviderOptions.Before (internal)
After (internal)
Impact
OpenFeatureOptions) require updates to targetOpenFeatureProviderOptions.How to test
This is a regression-only refactor.
Expectations