Skip to content
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

feat: add support for eventing #166

Merged
merged 31 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c81376d
added event executor with support for api level events
bacherfl Dec 18, 2023
70031cb
wire up api level event handling
bacherfl Dec 20, 2023
073a313
client level events
bacherfl Dec 22, 2023
7fc79d4
client level events: wiring up and added first unit test
bacherfl Dec 22, 2023
8a88fed
client level events: wiring up and added first unit test
bacherfl Dec 22, 2023
5e9eb59
added locks, added unit tests
bacherfl Jan 2, 2024
0f5a4a8
added xmldoc comments
bacherfl Jan 2, 2024
8f40909
fixed more tests
bacherfl Jan 2, 2024
9ac736a
fixed formatting
bacherfl Jan 2, 2024
2640c22
fixed formatting
bacherfl Jan 2, 2024
d999ea3
fix warnings, clean up debugging logs
bacherfl Jan 2, 2024
6c9e26a
added sleep to give time for event to be received
bacherfl Jan 3, 2024
2541880
additional tests
bacherfl Jan 3, 2024
dc27b0c
use WriteAsync to write to channel
bacherfl Jan 3, 2024
6836250
await WriteAsync
bacherfl Jan 3, 2024
e4aa68e
additional tests
bacherfl Jan 3, 2024
2f05f0b
added event handling section in readme
bacherfl Jan 3, 2024
4c28144
added event handling section in readme
bacherfl Jan 3, 2024
2e1b27f
updated feature table
bacherfl Jan 4, 2024
1b69d84
incorporated feedback from PR review
bacherfl Jan 5, 2024
fca794d
fix formatting
bacherfl Jan 5, 2024
2615ac2
fixed null pointer exception
bacherfl Jan 8, 2024
0f419cf
increase timout for unit tests
bacherfl Jan 8, 2024
c30345a
fix unit tests, add additional test cases
bacherfl Jan 8, 2024
ef289d3
set shutdown delegate to noop in unit tests
bacherfl Jan 9, 2024
317c2db
fix formatting
bacherfl Jan 9, 2024
4f1cec8
use Debug class to log exceptions
bacherfl Jan 9, 2024
672fa32
adapted to pr review
bacherfl Jan 10, 2024
d5270d4
fixed formatting
bacherfl Jan 10, 2024
8ef38a8
move channels package reference to Common.props
bacherfl Jan 12, 2024
2494599
give more time to receive event
bacherfl Jan 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion README.md
beeme1mr marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public async Task Example()
| ✅ | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. |
| ✅ | [Logging](#logging) | Integrate with popular logging packages. |
| ✅ | [Named clients](#named-clients) | Utilize multiple providers in a single application. |
| | [Eventing](#eventing) | React to state changes in the provider or flag management system. |
| | [Eventing](#eventing) | React to state changes in the provider or flag management system. |
| ✅ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. |
| ✅ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. |

Expand Down Expand Up @@ -167,6 +167,43 @@ client.AddHooks(new ExampleClientHook());
var value = await client.GetBooleanValue("boolFlag", false, context, new FlagEvaluationOptions(new ExampleInvocationHook()));
```

### Eventing

Events allow you to react to state changes in the provider or underlying flag management system, such as flag definition changes,
provider readiness, or error conditions.
Initialization events (`PROVIDER_READY` on success, `PROVIDER_ERROR` on failure) are dispatched for every provider.
Some providers support additional events, such as `PROVIDER_CONFIGURATION_CHANGED`.

Please refer to the documentation of the provider you're using to see what events are supported.

Example usage of an Event handler:

```csharp
public static void EventHandler(ProviderEventPayload eventDetails)
{
Console.WriteLine(eventDetails.Type);
}
```

```csharp
EventHandlerDelegate callback = EventHandler;
// add an implementation of the EventHandlerDelegate for the PROVIDER_READY event
Api.Instance.AddHandler(ProviderEventTypes.ProviderReady, callback);
```

It is also possible to register an event handler for a specific client, as in the following example:

```csharp
EventHandlerDelegate callback = EventHandler;

var myClient = Api.Instance.GetClient("my-client");

var provider = new ExampleProvider();
await Api.Instance.SetProvider(myClient.GetMetadata().Name, provider);

myClient.AddHandler(ProviderEventTypes.ProviderReady, callback);
```

### Logging

The .NET SDK uses Microsoft.Extensions.Logging. See the [manual](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging?tabs=command-line) for complete documentation.
Expand Down
19 changes: 18 additions & 1 deletion src/OpenFeature/Api.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using OpenFeature.Constant;
using OpenFeature.Model;

namespace OpenFeature
Expand All @@ -13,7 +14,7 @@ namespace OpenFeature
/// In the absence of a provider the evaluation API uses the "No-op provider", which simply returns the supplied default flag value.
/// </summary>
/// <seealso href="https://github.com/open-feature/spec/blob/v0.5.2/specification/sections/01-flag-evaluation.md#1-flag-evaluation-api"/>
public sealed class Api
public sealed class Api : IEventBus
{
private EvaluationContext _evaluationContext = EvaluationContext.Empty;
private readonly ProviderRepository _repository = new ProviderRepository();
Expand All @@ -22,6 +23,8 @@ public sealed class Api
/// The reader/writer locks are not disposed because the singleton instance should never be disposed.
private readonly ReaderWriterLockSlim _evaluationContextLock = new ReaderWriterLockSlim();

internal readonly EventExecutor EventExecutor = new EventExecutor();


/// <summary>
/// Singleton instance of Api
Expand All @@ -42,6 +45,7 @@ private Api() { }
/// <param name="featureProvider">Implementation of <see cref="FeatureProvider"/></param>
public async Task SetProvider(FeatureProvider featureProvider)
{
this.EventExecutor.RegisterDefaultFeatureProvider(featureProvider);
await this._repository.SetProvider(featureProvider, this.GetContext()).ConfigureAwait(false);
}

Expand All @@ -54,6 +58,7 @@ public async Task SetProvider(FeatureProvider featureProvider)
/// <param name="featureProvider">Implementation of <see cref="FeatureProvider"/></param>
public async Task SetProvider(string clientName, FeatureProvider featureProvider)
{
this.EventExecutor.RegisterClientFeatureProvider(clientName, featureProvider);
await this._repository.SetProvider(clientName, featureProvider, this.GetContext()).ConfigureAwait(false);
}

Expand Down Expand Up @@ -202,5 +207,17 @@ public async Task Shutdown()
{
await this._repository.Shutdown().ConfigureAwait(false);
}

/// <inheritdoc />
public void AddHandler(ProviderEventTypes type, EventHandlerDelegate handler)
{
this.EventExecutor.AddApiLevelHandler(type, handler);
}

/// <inheritdoc />
public void RemoveHandler(ProviderEventTypes type, EventHandlerDelegate handler)
{
this.EventExecutor.RemoveApiLevelHandler(type, handler);
}
}
}
25 changes: 25 additions & 0 deletions src/OpenFeature/Constant/EventType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace OpenFeature.Constant
{
/// <summary>
/// The ProviderEventTypes enum represents the available event types of a provider.
/// </summary>
public enum ProviderEventTypes
{
/// <summary>
/// ProviderReady should be emitted by a provider upon completing its initialisation.
/// </summary>
ProviderReady,
/// <summary>
/// ProviderError should be emitted by a provider upon encountering an error.
/// </summary>
ProviderError,
/// <summary>
/// ProviderConfigurationChanged should be emitted by a provider when a flag configuration has been changed.
/// </summary>
ProviderConfigurationChanged,
/// <summary>
/// ProviderStale should be emitted by a provider when it goes into the stale state.
/// </summary>
ProviderStale
}
}
Loading