From 35b51c012db8d794eebbea8257305742e73d05ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Fri, 28 Feb 2025 08:16:14 +0000 Subject: [PATCH 1/8] refactor: Improve event processing by using Task.Run and update method signatures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- src/OpenFeature/EventExecutor.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/OpenFeature/EventExecutor.cs b/src/OpenFeature/EventExecutor.cs index ad53a949..be0a8006 100644 --- a/src/OpenFeature/EventExecutor.cs +++ b/src/OpenFeature/EventExecutor.cs @@ -10,8 +10,6 @@ namespace OpenFeature { - internal delegate Task ShutdownDelegate(CancellationToken cancellationToken); - internal sealed partial class EventExecutor : IAsyncDisposable { private readonly object _lockObj = new object(); @@ -28,8 +26,7 @@ internal sealed partial class EventExecutor : IAsyncDisposable public EventExecutor() { this._logger = NullLogger.Instance; - var eventProcessing = new Thread(this.ProcessEventAsync); - eventProcessing.Start(); + Task.Run(this.ProcessEventAsync); } public ValueTask DisposeAsync() => new(this.ShutdownAsync()); @@ -234,7 +231,7 @@ private async void ProcessFeatureProviderEventsAsync(object? providerRef) switch (item) { case ProviderEventPayload eventPayload: - this.UpdateProviderStatus(typedProviderRef, eventPayload); + UpdateProviderStatus(typedProviderRef, eventPayload); await this.EventChannel.Writer.WriteAsync(new Event { Provider = typedProviderRef, EventPayload = eventPayload }).ConfigureAwait(false); break; } @@ -242,12 +239,14 @@ private async void ProcessFeatureProviderEventsAsync(object? providerRef) } // Method to process events - private async void ProcessEventAsync() + private async Task ProcessEventAsync() { while (await this.EventChannel.Reader.WaitToReadAsync().ConfigureAwait(false)) { if (!this.EventChannel.Reader.TryRead(out var item)) + { continue; + } switch (item) { @@ -309,7 +308,7 @@ private async void ProcessEventAsync() } // map events to provider status as per spec: https://openfeature.dev/specification/sections/events/#requirement-535 - private void UpdateProviderStatus(FeatureProvider provider, ProviderEventPayload eventPayload) + private static void UpdateProviderStatus(FeatureProvider provider, ProviderEventPayload eventPayload) { switch (eventPayload.Type) { From cf142e6fbfc10fa5b686cd88603bef0b93a7c5bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Fri, 28 Feb 2025 08:21:12 +0000 Subject: [PATCH 2/8] refactor: Simplify message assignment using switch expression in event processing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- src/OpenFeature/EventExecutor.cs | 42 +++++++++++++++----------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/OpenFeature/EventExecutor.cs b/src/OpenFeature/EventExecutor.cs index be0a8006..6a550429 100644 --- a/src/OpenFeature/EventExecutor.cs +++ b/src/OpenFeature/EventExecutor.cs @@ -183,35 +183,31 @@ private void EmitOnRegistration(FeatureProvider? provider, ProviderEventTypes ev } var status = provider.Status; - var message = ""; - if (status == ProviderStatus.Ready && eventType == ProviderEventTypes.ProviderReady) + var message = status switch { - message = "Provider is ready"; - } - else if (status == ProviderStatus.Error && eventType == ProviderEventTypes.ProviderError) - { - message = "Provider is in error state"; - } - else if (status == ProviderStatus.Stale && eventType == ProviderEventTypes.ProviderStale) + ProviderStatus.Ready when eventType == ProviderEventTypes.ProviderReady => "Provider is ready", + ProviderStatus.Error when eventType == ProviderEventTypes.ProviderError => "Provider is in error state", + ProviderStatus.Stale when eventType == ProviderEventTypes.ProviderStale => "Provider is in stale state", + _ => string.Empty + }; + + if (string.IsNullOrWhiteSpace(message)) { - message = "Provider is in stale state"; + return; } - if (message != "") + try { - try - { - handler.Invoke(new ProviderEventPayload - { - ProviderName = provider.GetMetadata()?.Name, - Type = eventType, - Message = message - }); - } - catch (Exception exc) + handler.Invoke(new ProviderEventPayload { - this.ErrorRunningHandler(exc); - } + ProviderName = provider.GetMetadata()?.Name, + Type = eventType, + Message = message + }); + } + catch (Exception exc) + { + this.ErrorRunningHandler(exc); } } From 10a29ca64e7fe7263788938fc7073500a8fa18cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Fri, 28 Feb 2025 08:33:35 +0000 Subject: [PATCH 3/8] refactor: Streamline event processing by extracting handler logic into separate methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- src/OpenFeature/EventExecutor.cs | 107 +++++++++++++++++-------------- 1 file changed, 59 insertions(+), 48 deletions(-) diff --git a/src/OpenFeature/EventExecutor.cs b/src/OpenFeature/EventExecutor.cs index 6a550429..f1344a58 100644 --- a/src/OpenFeature/EventExecutor.cs +++ b/src/OpenFeature/EventExecutor.cs @@ -244,65 +244,76 @@ private async Task ProcessEventAsync() continue; } - switch (item) + if (item is not Event e) { - case Event e: - lock (this._lockObj) - { - if (e.EventPayload?.Type != null && this._apiHandlers.TryGetValue(e.EventPayload.Type, out var eventHandlers)) - { - foreach (var eventHandler in eventHandlers) - { - this.InvokeEventHandler(eventHandler, e); - } - } + continue; + } - // look for client handlers and call invoke method there - foreach (var keyAndValue in this._namedProviderReferences) - { - if (keyAndValue.Value == e.Provider && keyAndValue.Key != null) - { - if (this._clientHandlers.TryGetValue(keyAndValue.Key, out var clientRegistry)) - { - if (e.EventPayload?.Type != null && clientRegistry.TryGetValue(e.EventPayload.Type, out var clientEventHandlers)) - { - foreach (var eventHandler in clientEventHandlers) - { - this.InvokeEventHandler(eventHandler, e); - } - } - } - } - } + lock (this._lockObj) + { + this.ProcessApiHandlers(e); + this.ProcessClientHandlers(e); + this.ProcessDefaultProviderHandlers(e); + } + } + } - if (e.Provider != this._defaultProvider) - { - break; - } - // handling the default provider - invoke event handlers for clients which are not bound - // to a particular feature provider - foreach (var keyAndValues in this._clientHandlers) + private void ProcessApiHandlers(Event e) + { + if (e.EventPayload?.Type != null && this._apiHandlers.TryGetValue(e.EventPayload.Type, out var eventHandlers)) + { + foreach (var eventHandler in eventHandlers) + { + this.InvokeEventHandler(eventHandler, e); + } + } + } + + private void ProcessClientHandlers(Event e) + { + foreach (var keyAndValue in this._namedProviderReferences) + { + if (keyAndValue.Value == e.Provider && keyAndValue.Key != null) + { + if (this._clientHandlers.TryGetValue(keyAndValue.Key, out var clientRegistry)) + { + if (e.EventPayload?.Type != null && clientRegistry.TryGetValue(e.EventPayload.Type, out var clientEventHandlers)) + { + foreach (var eventHandler in clientEventHandlers) { - if (this._namedProviderReferences.TryGetValue(keyAndValues.Key, out _)) - { - // if there is an association for the client to a specific feature provider, then continue - continue; - } - if (e.EventPayload?.Type != null && keyAndValues.Value.TryGetValue(e.EventPayload.Type, out var clientEventHandlers)) - { - foreach (var eventHandler in clientEventHandlers) - { - this.InvokeEventHandler(eventHandler, e); - } - } + this.InvokeEventHandler(eventHandler, e); } } - break; + } + } + } + } + + private void ProcessDefaultProviderHandlers(Event e) + { + if (e.Provider != this._defaultProvider) + { + return; + } + + foreach (var keyAndValues in this._clientHandlers) + { + if (this._namedProviderReferences.ContainsKey(keyAndValues.Key)) + { + continue; } + if (e.EventPayload?.Type != null && keyAndValues.Value.TryGetValue(e.EventPayload.Type, out var clientEventHandlers)) + { + foreach (var eventHandler in clientEventHandlers) + { + this.InvokeEventHandler(eventHandler, e); + } + } } } + // map events to provider status as per spec: https://openfeature.dev/specification/sections/events/#requirement-535 private static void UpdateProviderStatus(FeatureProvider provider, ProviderEventPayload eventPayload) { From 02eb0caf0d1ad04f9f125f16ba7542ef8c0168cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Fri, 28 Feb 2025 08:36:01 +0000 Subject: [PATCH 4/8] refactor: Replace Thread with Task.Run for asynchronous event processing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- src/OpenFeature/EventExecutor.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/OpenFeature/EventExecutor.cs b/src/OpenFeature/EventExecutor.cs index f1344a58..e9eee127 100644 --- a/src/OpenFeature/EventExecutor.cs +++ b/src/OpenFeature/EventExecutor.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -143,8 +142,7 @@ private void StartListeningAndShutdownOld(FeatureProvider newProvider, FeaturePr if (!this.IsProviderActive(newProvider)) { this._activeSubscriptions.Add(newProvider); - var featureProviderEventProcessing = new Thread(this.ProcessFeatureProviderEventsAsync); - featureProviderEventProcessing.Start(newProvider); + Task.Run(() => this.ProcessFeatureProviderEventsAsync(newProvider)); } if (oldProvider != null && !this.IsProviderBound(oldProvider)) @@ -211,7 +209,7 @@ private void EmitOnRegistration(FeatureProvider? provider, ProviderEventTypes ev } } - private async void ProcessFeatureProviderEventsAsync(object? providerRef) + private async Task ProcessFeatureProviderEventsAsync(object? providerRef) { var typedProviderRef = (FeatureProvider?)providerRef; if (typedProviderRef?.GetEventChannel() is not { Reader: { } reader }) From 865fe24984e2be61db33c8eac2add910ed67657b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Fri, 28 Feb 2025 08:41:02 +0000 Subject: [PATCH 5/8] refactor: Replace List and Dictionary initializations with target-typed new expressions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- src/OpenFeature/EventExecutor.cs | 36 ++++++++++++++------------------ 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/OpenFeature/EventExecutor.cs b/src/OpenFeature/EventExecutor.cs index e9eee127..59fd116d 100644 --- a/src/OpenFeature/EventExecutor.cs +++ b/src/OpenFeature/EventExecutor.cs @@ -11,14 +11,14 @@ namespace OpenFeature { internal sealed partial class EventExecutor : IAsyncDisposable { - private readonly object _lockObj = new object(); + private readonly object _lockObj = new(); public readonly Channel EventChannel = Channel.CreateBounded(1); private FeatureProvider? _defaultProvider; - private readonly Dictionary _namedProviderReferences = new Dictionary(); - private readonly List _activeSubscriptions = new List(); + private readonly Dictionary _namedProviderReferences = []; + private readonly List _activeSubscriptions = []; - private readonly Dictionary> _apiHandlers = new Dictionary>(); - private readonly Dictionary>> _clientHandlers = new Dictionary>>(); + private readonly Dictionary> _apiHandlers = []; + private readonly Dictionary>> _clientHandlers = []; private ILogger _logger; @@ -38,7 +38,7 @@ internal void AddApiLevelHandler(ProviderEventTypes eventType, EventHandlerDeleg { if (!this._apiHandlers.TryGetValue(eventType, out var eventHandlers)) { - eventHandlers = new List(); + eventHandlers = []; this._apiHandlers[eventType] = eventHandlers; } @@ -66,13 +66,13 @@ internal void AddClientHandler(string client, ProviderEventTypes eventType, Even // check if there is already a list of handlers for the given client and event type if (!this._clientHandlers.TryGetValue(client, out var registry)) { - registry = new Dictionary>(); + registry = []; this._clientHandlers[client] = registry; } if (!this._clientHandlers[client].TryGetValue(eventType, out var eventHandlers)) { - eventHandlers = new List(); + eventHandlers = []; this._clientHandlers[client][eventType] = eventHandlers; } @@ -123,16 +123,15 @@ internal void RegisterClientFeatureProvider(string client, FeatureProvider? prov } lock (this._lockObj) { - var newProvider = provider; FeatureProvider? oldProvider = null; if (this._namedProviderReferences.TryGetValue(client, out var foundOldProvider)) { oldProvider = foundOldProvider; } - this._namedProviderReferences[client] = newProvider; + this._namedProviderReferences[client] = provider; - this.StartListeningAndShutdownOld(newProvider, oldProvider); + this.StartListeningAndShutdownOld(provider, oldProvider); } } @@ -271,17 +270,14 @@ private void ProcessClientHandlers(Event e) { foreach (var keyAndValue in this._namedProviderReferences) { - if (keyAndValue.Value == e.Provider && keyAndValue.Key != null) + if (keyAndValue.Value == e.Provider + && this._clientHandlers.TryGetValue(keyAndValue.Key, out var clientRegistry) + && e.EventPayload?.Type != null + && clientRegistry.TryGetValue(e.EventPayload.Type, out var clientEventHandlers)) { - if (this._clientHandlers.TryGetValue(keyAndValue.Key, out var clientRegistry)) + foreach (var eventHandler in clientEventHandlers) { - if (e.EventPayload?.Type != null && clientRegistry.TryGetValue(e.EventPayload.Type, out var clientEventHandlers)) - { - foreach (var eventHandler in clientEventHandlers) - { - this.InvokeEventHandler(eventHandler, e); - } - } + this.InvokeEventHandler(eventHandler, e); } } } From 393ba50eca9a78271faffde9c18358d2641fdc45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Fri, 28 Feb 2025 09:23:01 +0000 Subject: [PATCH 6/8] refactor: Update EventChannel to be nullable and handle potential null cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- src/OpenFeature/FeatureProvider.cs | 4 ++-- src/OpenFeature/Providers/Memory/InMemoryProvider.cs | 6 +++++- .../Providers/Memory/InMemoryProviderTests.cs | 2 +- test/OpenFeature.Tests/TestImplementations.cs | 7 +------ 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/OpenFeature/FeatureProvider.cs b/src/OpenFeature/FeatureProvider.cs index de3f2797..8e547b15 100644 --- a/src/OpenFeature/FeatureProvider.cs +++ b/src/OpenFeature/FeatureProvider.cs @@ -33,7 +33,7 @@ public abstract class FeatureProvider /// /// The event channel of the provider. /// - protected readonly Channel EventChannel = Channel.CreateBounded(1); + protected readonly Channel? EventChannel = Channel.CreateBounded(1); /// /// Metadata describing the provider. @@ -140,7 +140,7 @@ public virtual Task ShutdownAsync(CancellationToken cancellationToken = default) /// Returns the event channel of the provider. /// /// The event channel of the provider - public virtual Channel GetEventChannel() => this.EventChannel; + public virtual Channel? GetEventChannel() => this.EventChannel; /// /// Track a user action or application state, usually representing a business objective or outcome. The implementation of this method is optional. diff --git a/src/OpenFeature/Providers/Memory/InMemoryProvider.cs b/src/OpenFeature/Providers/Memory/InMemoryProvider.cs index 3283ea22..c9cb59f3 100644 --- a/src/OpenFeature/Providers/Memory/InMemoryProvider.cs +++ b/src/OpenFeature/Providers/Memory/InMemoryProvider.cs @@ -65,7 +65,11 @@ public async Task UpdateFlagsAsync(IDictionary? flags = null) FlagsChanged = changed, // emit all Message = "flags changed", }; - await this.EventChannel.Writer.WriteAsync(@event).ConfigureAwait(false); + + if (this.EventChannel != null) + { + await this.EventChannel.Writer.WriteAsync(@event).ConfigureAwait(false); + } } /// diff --git a/test/OpenFeature.Tests/Providers/Memory/InMemoryProviderTests.cs b/test/OpenFeature.Tests/Providers/Memory/InMemoryProviderTests.cs index c83ce0ce..81b710d4 100644 --- a/test/OpenFeature.Tests/Providers/Memory/InMemoryProviderTests.cs +++ b/test/OpenFeature.Tests/Providers/Memory/InMemoryProviderTests.cs @@ -227,7 +227,7 @@ await provider.UpdateFlagsAsync(new Dictionary(){ ) }}); - var res = await provider.GetEventChannel().Reader.ReadAsync() as ProviderEventPayload; + var res = await provider.GetEventChannel()!.Reader.ReadAsync() as ProviderEventPayload; Assert.Equal(ProviderEventTypes.ProviderConfigurationChanged, res?.Type); await Assert.ThrowsAsync(() => provider.ResolveBooleanValueAsync("old-flag", false, EvaluationContext.Empty)); diff --git a/test/OpenFeature.Tests/TestImplementations.cs b/test/OpenFeature.Tests/TestImplementations.cs index 724278e8..c470c9a9 100644 --- a/test/OpenFeature.Tests/TestImplementations.cs +++ b/test/OpenFeature.Tests/TestImplementations.cs @@ -150,12 +150,7 @@ public override void Track(string trackingEventName, EvaluationContext? evaluati internal ValueTask SendEventAsync(ProviderEventTypes eventType, CancellationToken cancellationToken = default) { - return this.EventChannel.Writer.WriteAsync(new ProviderEventPayload { Type = eventType, ProviderName = this.GetMetadata().Name, }, cancellationToken); - } - - internal ValueTask SendEventAsync(ProviderEventPayload payload, CancellationToken cancellationToken = default) - { - return this.EventChannel.Writer.WriteAsync(payload, cancellationToken); + return this.EventChannel!.Writer.WriteAsync(new ProviderEventPayload { Type = eventType, ProviderName = this.GetMetadata().Name, }, cancellationToken); } } } From 93c1f22973da1bde3cc0c353ea54049d0fc800be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Fri, 28 Feb 2025 09:26:08 +0000 Subject: [PATCH 7/8] refactor: Update ProcessFeatureProviderEventsAsync to use FeatureProvider directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- src/OpenFeature/EventExecutor.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/OpenFeature/EventExecutor.cs b/src/OpenFeature/EventExecutor.cs index 59fd116d..b54bd9c0 100644 --- a/src/OpenFeature/EventExecutor.cs +++ b/src/OpenFeature/EventExecutor.cs @@ -208,10 +208,9 @@ private void EmitOnRegistration(FeatureProvider? provider, ProviderEventTypes ev } } - private async Task ProcessFeatureProviderEventsAsync(object? providerRef) + private async Task ProcessFeatureProviderEventsAsync(FeatureProvider provider) { - var typedProviderRef = (FeatureProvider?)providerRef; - if (typedProviderRef?.GetEventChannel() is not { Reader: { } reader }) + if (provider.GetEventChannel() is not { Reader: { } reader }) { return; } @@ -224,8 +223,8 @@ private async Task ProcessFeatureProviderEventsAsync(object? providerRef) switch (item) { case ProviderEventPayload eventPayload: - UpdateProviderStatus(typedProviderRef, eventPayload); - await this.EventChannel.Writer.WriteAsync(new Event { Provider = typedProviderRef, EventPayload = eventPayload }).ConfigureAwait(false); + UpdateProviderStatus(provider, eventPayload); + await this.EventChannel.Writer.WriteAsync(new Event { Provider = provider, EventPayload = eventPayload }).ConfigureAwait(false); break; } } From e2c1f3ad7a27d06d7f98eefe9b8743ca055eb5f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Wed, 12 Mar 2025 10:51:44 +0000 Subject: [PATCH 8/8] refactor: Remove nullable type from EventChannel and simplify related code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- src/OpenFeature/FeatureProvider.cs | 4 ++-- src/OpenFeature/Providers/Memory/InMemoryProvider.cs | 5 +---- .../Providers/Memory/InMemoryProviderTests.cs | 2 +- test/OpenFeature.Tests/TestImplementations.cs | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/OpenFeature/FeatureProvider.cs b/src/OpenFeature/FeatureProvider.cs index 8e547b15..de3f2797 100644 --- a/src/OpenFeature/FeatureProvider.cs +++ b/src/OpenFeature/FeatureProvider.cs @@ -33,7 +33,7 @@ public abstract class FeatureProvider /// /// The event channel of the provider. /// - protected readonly Channel? EventChannel = Channel.CreateBounded(1); + protected readonly Channel EventChannel = Channel.CreateBounded(1); /// /// Metadata describing the provider. @@ -140,7 +140,7 @@ public virtual Task ShutdownAsync(CancellationToken cancellationToken = default) /// Returns the event channel of the provider. /// /// The event channel of the provider - public virtual Channel? GetEventChannel() => this.EventChannel; + public virtual Channel GetEventChannel() => this.EventChannel; /// /// Track a user action or application state, usually representing a business objective or outcome. The implementation of this method is optional. diff --git a/src/OpenFeature/Providers/Memory/InMemoryProvider.cs b/src/OpenFeature/Providers/Memory/InMemoryProvider.cs index c9cb59f3..4a06dc85 100644 --- a/src/OpenFeature/Providers/Memory/InMemoryProvider.cs +++ b/src/OpenFeature/Providers/Memory/InMemoryProvider.cs @@ -66,10 +66,7 @@ public async Task UpdateFlagsAsync(IDictionary? flags = null) Message = "flags changed", }; - if (this.EventChannel != null) - { - await this.EventChannel.Writer.WriteAsync(@event).ConfigureAwait(false); - } + await this.EventChannel.Writer.WriteAsync(@event).ConfigureAwait(false); } /// diff --git a/test/OpenFeature.Tests/Providers/Memory/InMemoryProviderTests.cs b/test/OpenFeature.Tests/Providers/Memory/InMemoryProviderTests.cs index 81b710d4..c83ce0ce 100644 --- a/test/OpenFeature.Tests/Providers/Memory/InMemoryProviderTests.cs +++ b/test/OpenFeature.Tests/Providers/Memory/InMemoryProviderTests.cs @@ -227,7 +227,7 @@ await provider.UpdateFlagsAsync(new Dictionary(){ ) }}); - var res = await provider.GetEventChannel()!.Reader.ReadAsync() as ProviderEventPayload; + var res = await provider.GetEventChannel().Reader.ReadAsync() as ProviderEventPayload; Assert.Equal(ProviderEventTypes.ProviderConfigurationChanged, res?.Type); await Assert.ThrowsAsync(() => provider.ResolveBooleanValueAsync("old-flag", false, EvaluationContext.Empty)); diff --git a/test/OpenFeature.Tests/TestImplementations.cs b/test/OpenFeature.Tests/TestImplementations.cs index c470c9a9..df738efe 100644 --- a/test/OpenFeature.Tests/TestImplementations.cs +++ b/test/OpenFeature.Tests/TestImplementations.cs @@ -150,7 +150,7 @@ public override void Track(string trackingEventName, EvaluationContext? evaluati internal ValueTask SendEventAsync(ProviderEventTypes eventType, CancellationToken cancellationToken = default) { - return this.EventChannel!.Writer.WriteAsync(new ProviderEventPayload { Type = eventType, ProviderName = this.GetMetadata().Name, }, cancellationToken); + return this.EventChannel.Writer.WriteAsync(new ProviderEventPayload { Type = eventType, ProviderName = this.GetMetadata().Name, }, cancellationToken); } } }