Skip to content

Commit 9d8ab03

Browse files
askptCopilot
andauthored
feat: Add events to the multi provider (#568)
* feat: Implement event handling infrastructure in MultiProvider Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Implement event processing for MultiProvider Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Enhance event handling in MultiProvider with improved status management Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Add event emission for ProviderConfigurationChanged in MultiProvider Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Implement graceful shutdown for event processing in MultiProvider Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Add error handling and event emission for evaluation failures in MultiProvider Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Update test name for EvaluateAsync to reflect error handling for unsupported run modes Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Add unit tests for event emission in MultiProvider Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Enhance MultiProvider event tests with initialization and error state handling Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Refactor event emission test for error state in MultiProvider Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Refactor MultiProvider event tests for improved readability and error handling Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Refactor MultiProvider event tests for improved clarity and consistency Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Improve event handling logic in MultiProvider for better performance and clarity Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Add event handling documentation for MultiProvider Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Dispose event processing cancellation token in MultiProvider shutdown Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Implement thread-safe access to provider status in MultiProvider Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * Replace Dictionary with ConcurrentDictionary for event listening tasks in MultiProvider Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * feat: Safeguard against ObjectDisposedException during event processing cancellation in MultiProvider Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * Update src/OpenFeature.Providers.MultiProvider/MultiProvider.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * fix: prevent duplicate event listening tasks for registered providers Signed-off-by: GitHub <noreply@github.com> * feat: enhance MultiProvider to include logging and prevent duplicate event listeners Signed-off-by: GitHub <noreply@github.com> * refactor: remove unused config folder and reorganize test project entries Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * fix: prevent duplicate event listening by checking existing tasks before adding Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * fix: clear event listening tasks during provider shutdown Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> * fix: streamline event listening task addition by using TryAdd method Signed-off-by: GitHub <noreply@github.com> * fix: remove setting of ProviderStatus to Fatal during shutdown Signed-off-by: GitHub <noreply@github.com> --------- Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> Signed-off-by: GitHub <noreply@github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 67d5312 commit 9d8ab03

File tree

7 files changed

+811
-29
lines changed

7 files changed

+811
-29
lines changed

OpenFeature.slnx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@
1414
<File Path="renovate.json" />
1515
<File Path="version.txt" />
1616
</Folder>
17-
<Folder Name="/.root/.config/">
18-
<File Path=".config/dotnet-tools.json" />
19-
</Folder>
2017
<Folder Name="/.root/.github/" />
2118
<Folder Name="/.root/.github/ISSUE_TEMPLATE/">
2219
<File Path=".github/ISSUE_TEMPLATE/bug.yaml" />
@@ -59,14 +56,14 @@
5956
<File Path="src/Directory.Build.targets" />
6057
</Folder>
6158
<Folder Name="/test/">
59+
<Project Path="test/OpenFeature.AotCompatibility/OpenFeature.AotCompatibility.csproj" />
6260
<Project Path="test/OpenFeature.Benchmarks/OpenFeature.Benchmarks.csproj" />
6361
<Project Path="test/OpenFeature.DependencyInjection.Tests/OpenFeature.DependencyInjection.Tests.csproj" />
6462
<Project Path="test/OpenFeature.E2ETests/OpenFeature.E2ETests.csproj" />
6563
<Project Path="test/OpenFeature.Hosting.Tests/OpenFeature.Hosting.Tests.csproj" />
6664
<Project Path="test/OpenFeature.IntegrationTests/OpenFeature.IntegrationTests.csproj" />
67-
<Project Path="test/OpenFeature.Tests/OpenFeature.Tests.csproj" />
68-
<Project Path="test/OpenFeature.AotCompatibility/OpenFeature.AotCompatibility.csproj" />
6965
<Project Path="test/OpenFeature.Providers.MultiProvider.Tests/OpenFeature.Providers.MultiProvider.Tests.csproj" />
66+
<Project Path="test/OpenFeature.Tests/OpenFeature.Tests.csproj" />
7067
<File Path="test/Directory.Build.props" />
7168
</Folder>
72-
</Solution>
69+
</Solution>

src/OpenFeature.Providers.MultiProvider/MultiProvider.cs

Lines changed: 319 additions & 19 deletions
Large diffs are not rendered by default.

src/OpenFeature.Providers.MultiProvider/README.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,69 @@ await multiProvider.ShutdownAsync();
181181
await multiProvider.DisposeAsync();
182182
```
183183

184+
## Events
185+
186+
The MultiProvider supports OpenFeature events and provides specification-compliant event handling. It follows the [OpenFeature Multi-Provider specification](https://openfeature.dev/specification/appendix-a#status-and-event-handling) for event handling behavior.
187+
188+
### Event Handling Example
189+
190+
```csharp
191+
using OpenFeature;
192+
using OpenFeature.Providers.MultiProvider;
193+
194+
// Create the MultiProvider with multiple providers
195+
var providerEntries = new[]
196+
{
197+
new ProviderEntry(new ProviderA(), "provider-a"),
198+
new ProviderEntry(new ProviderB(), "provider-b")
199+
};
200+
var multiProvider = new MultiProvider(providerEntries);
201+
202+
// Subscribe to MultiProvider events
203+
Api.Instance.AddHandler(ProviderEventTypes.ProviderReady, (eventDetails) =>
204+
{
205+
Console.WriteLine($"MultiProvider is ready: {eventDetails?.ProviderName}");
206+
});
207+
208+
Api.Instance.AddHandler(ProviderEventTypes.ProviderStale, (eventDetails) =>
209+
{
210+
Console.WriteLine($"MultiProvider became stale: {eventDetails?.Message}");
211+
});
212+
213+
Api.Instance.AddHandler(ProviderEventTypes.ProviderConfigurationChanged, (eventDetails) =>
214+
{
215+
Console.WriteLine($"Configuration changed - Flags: {string.Join(", ", eventDetails?.FlagsChanged ?? [])}");
216+
});
217+
218+
Api.Instance.AddHandler(ProviderEventTypes.ProviderError, (eventDetails) =>
219+
{
220+
Console.WriteLine($"MultiProvider error: {eventDetails?.Message}");
221+
});
222+
223+
// Set the provider - this will initialize all underlying providers
224+
// and emit PROVIDER_READY when all are successfully initialized
225+
await Api.Instance.SetProviderAsync(multiProvider);
226+
227+
// Later, if an underlying provider becomes stale and changes MultiProvider status:
228+
// Only then will a PROVIDER_STALE event be emitted from MultiProvider
229+
```
230+
231+
### Event Lifecycle
232+
233+
1. **During Initialization**:
234+
235+
- MultiProvider emits `PROVIDER_READY` when all underlying providers initialize successfully
236+
- MultiProvider emits `PROVIDER_ERROR` if any providers fail to initialize (causing aggregate status to become ERROR/FATAL)
237+
238+
2. **Runtime Status Changes**:
239+
240+
- Status-changing events from underlying providers are captured internally
241+
- MultiProvider only emits events when its aggregate status changes due to these internal events
242+
- Example: If MultiProvider is READY and one provider becomes STALE, MultiProvider emits `PROVIDER_STALE`
243+
244+
3. **Configuration Changes**:
245+
- `PROVIDER_CONFIGURATION_CHANGED` events from underlying providers are always re-emitted
246+
184247
## Requirements
185248

186249
- .NET 8+

src/OpenFeature/OpenFeature.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<InternalsVisibleTo Include="OpenFeature.Tests" />
2121
<InternalsVisibleTo Include="OpenFeature.E2ETests" />
2222
<InternalsVisibleTo Include="OpenFeature.DependencyInjection" />
23+
<InternalsVisibleTo Include="OpenFeature.Providers.MultiProvider" />
2324
<InternalsVisibleTo Include="OpenFeature.Providers.MultiProvider.Tests" />
2425
<None Include="../../README.md" Pack="true" PackagePath="/" />
2526
</ItemGroup>

0 commit comments

Comments
 (0)