diff --git a/DevCycle.SDK.Server.Cloud.Example/DevCycle.SDK.Server.Cloud.Example.csproj b/DevCycle.SDK.Server.Cloud.Example/DevCycle.SDK.Server.Cloud.Example.csproj index 45a6c36b..b2a13e08 100644 --- a/DevCycle.SDK.Server.Cloud.Example/DevCycle.SDK.Server.Cloud.Example.csproj +++ b/DevCycle.SDK.Server.Cloud.Example/DevCycle.SDK.Server.Cloud.Example.csproj @@ -18,7 +18,7 @@ - + diff --git a/DevCycle.SDK.Server.Cloud.MSTests/DevCycle.SDK.Server.Cloud.MSTests.csproj b/DevCycle.SDK.Server.Cloud.MSTests/DevCycle.SDK.Server.Cloud.MSTests.csproj index e263c47e..5d3c03a0 100644 --- a/DevCycle.SDK.Server.Cloud.MSTests/DevCycle.SDK.Server.Cloud.MSTests.csproj +++ b/DevCycle.SDK.Server.Cloud.MSTests/DevCycle.SDK.Server.Cloud.MSTests.csproj @@ -18,7 +18,7 @@ - + diff --git a/DevCycle.SDK.Server.Cloud/DevCycle.SDK.Server.Cloud.csproj b/DevCycle.SDK.Server.Cloud/DevCycle.SDK.Server.Cloud.csproj index 6d167d1c..4d2b517b 100644 --- a/DevCycle.SDK.Server.Cloud/DevCycle.SDK.Server.Cloud.csproj +++ b/DevCycle.SDK.Server.Cloud/DevCycle.SDK.Server.Cloud.csproj @@ -43,7 +43,7 @@ - + diff --git a/DevCycle.SDK.Server.Common/API/DevCycleBaseClient.cs b/DevCycle.SDK.Server.Common/API/DevCycleBaseClient.cs index 8af1d7be..31cba2a6 100644 --- a/DevCycle.SDK.Server.Common/API/DevCycleBaseClient.cs +++ b/DevCycle.SDK.Server.Common/API/DevCycleBaseClient.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Net; +using System.Threading; using System.Threading.Tasks; using DevCycle.SDK.Server.Common.Exception; using DevCycle.SDK.Server.Common.Model; @@ -22,6 +23,7 @@ public abstract class DevCycleBaseClient : IDevCycleClient public abstract string Platform(); public abstract IDevCycleApiClient GetApiClient(); public abstract DevCycleProvider GetOpenFeatureProvider(); + public virtual Task InitializeAsync(CancellationToken cancellationToken = default) => Task.CompletedTask; public abstract Task> AllFeatures(DevCycleUser user); public abstract Task>> AllVariables(DevCycleUser user); public abstract Task> Variable(DevCycleUser user, string key, T defaultValue); diff --git a/DevCycle.SDK.Server.Common/API/DevCycleProvider.cs b/DevCycle.SDK.Server.Common/API/DevCycleProvider.cs index 7c70a6a0..3304b90d 100644 --- a/DevCycle.SDK.Server.Common/API/DevCycleProvider.cs +++ b/DevCycle.SDK.Server.Common/API/DevCycleProvider.cs @@ -24,6 +24,11 @@ public override Metadata GetMetadata() return new Metadata(Client.SdkPlatform); } + public override Task InitializeAsync(EvaluationContext context, CancellationToken cancellationToken = default) + { + return Client.InitializeAsync(cancellationToken); + } + public override async Task> ResolveBooleanValueAsync(string flagKey, bool defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = new CancellationToken()) { diff --git a/DevCycle.SDK.Server.Common/DevCycle.SDK.Server.Common.csproj b/DevCycle.SDK.Server.Common/DevCycle.SDK.Server.Common.csproj index 4a688d03..02ec9d9d 100644 --- a/DevCycle.SDK.Server.Common/DevCycle.SDK.Server.Common.csproj +++ b/DevCycle.SDK.Server.Common/DevCycle.SDK.Server.Common.csproj @@ -15,12 +15,12 @@ - + - + - + diff --git a/DevCycle.SDK.Server.Local.Benchmark/DevCycle.SDK.Server.Local.Benchmark.csproj b/DevCycle.SDK.Server.Local.Benchmark/DevCycle.SDK.Server.Local.Benchmark.csproj index ec797201..f08a697c 100644 --- a/DevCycle.SDK.Server.Local.Benchmark/DevCycle.SDK.Server.Local.Benchmark.csproj +++ b/DevCycle.SDK.Server.Local.Benchmark/DevCycle.SDK.Server.Local.Benchmark.csproj @@ -14,7 +14,7 @@ - + diff --git a/DevCycle.SDK.Server.Local.Example/DevCycle.SDK.Server.Local.Example.csproj b/DevCycle.SDK.Server.Local.Example/DevCycle.SDK.Server.Local.Example.csproj index 41dfd88c..21c83fce 100644 --- a/DevCycle.SDK.Server.Local.Example/DevCycle.SDK.Server.Local.Example.csproj +++ b/DevCycle.SDK.Server.Local.Example/DevCycle.SDK.Server.Local.Example.csproj @@ -18,7 +18,7 @@ - + diff --git a/DevCycle.SDK.Server.Local.MSTests/DevCycle.SDK.Server.Local.MSTests.csproj b/DevCycle.SDK.Server.Local.MSTests/DevCycle.SDK.Server.Local.MSTests.csproj index 8495f0ec..6a352e27 100644 --- a/DevCycle.SDK.Server.Local.MSTests/DevCycle.SDK.Server.Local.MSTests.csproj +++ b/DevCycle.SDK.Server.Local.MSTests/DevCycle.SDK.Server.Local.MSTests.csproj @@ -19,7 +19,7 @@ - + diff --git a/DevCycle.SDK.Server.Local.MSTests/DevCycleTest.cs b/DevCycle.SDK.Server.Local.MSTests/DevCycleTest.cs index 7d4ba834..a276a473 100644 --- a/DevCycle.SDK.Server.Local.MSTests/DevCycleTest.cs +++ b/DevCycle.SDK.Server.Local.MSTests/DevCycleTest.cs @@ -624,5 +624,15 @@ public async Task EvalHooks_MultipleHooksInOptions() Assert.AreEqual(1, hook2.FinallyCallCount); Assert.IsNotNull(result); } + + [TestMethod] + public async Task TestOpenFeatureProviderWaitsForClientInit() + { + using var dvcClient = DevCycleTestClient.getTestClient(); + await OpenFeature.Api.Instance.SetProviderAsync(dvcClient.GetOpenFeatureProvider()); + var ctx = EvaluationContext.Builder().Set("user_id", "j_test").Build(); + var result = await OpenFeature.Api.Instance.GetClient().GetBooleanValueAsync("test", false, ctx); + Assert.IsTrue(result); + } } } diff --git a/DevCycle.SDK.Server.Local/Api/DevCycleLocalClient.cs b/DevCycle.SDK.Server.Local/Api/DevCycleLocalClient.cs index 01cf24c0..60c8bb29 100644 --- a/DevCycle.SDK.Server.Local/Api/DevCycleLocalClient.cs +++ b/DevCycle.SDK.Server.Local/Api/DevCycleLocalClient.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using System.Timers; +using SystemTimer = System.Timers.Timer; using DevCycle.SDK.Server.Common.API; using DevCycle.SDK.Server.Common.Model; using DevCycle.SDK.Server.Common.Model.Local; @@ -73,8 +75,9 @@ public class DevCycleLocalClient : DevCycleBaseClient private readonly EventQueue eventQueue; private readonly ILocalBucketing localBucketing; private readonly ILogger logger; - private readonly Timer timer; + private readonly SystemTimer timer; private bool closing; + private readonly Task initializeTask; private DevCycleProvider OpenFeatureProvider { get; } private readonly EvalHooksRunner evalHooksRunner; @@ -103,11 +106,11 @@ internal DevCycleLocalClient( logger.LogWarning("The config CDN slug is being overriden, please ensure to update the config to v2 according to the config CDN updates documentation."); } - timer = new Timer(dvcLocalOptions.EventFlushIntervalMs); + timer = new SystemTimer(dvcLocalOptions.EventFlushIntervalMs); timer.Elapsed += OnTimedEvent; timer.AutoReset = true; timer.Enabled = true; - Task.Run(async delegate { await this.configManager.InitializeConfigAsync(); }); + initializeTask = this.configManager.InitializeConfigAsync(); OpenFeatureProvider = new DevCycleProvider(this); } @@ -259,6 +262,23 @@ public override DevCycleProvider GetOpenFeatureProvider() return OpenFeatureProvider; } + public override async Task InitializeAsync(CancellationToken cancellationToken = default) + { + if (cancellationToken.CanBeCanceled) + { + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + using (cancellationToken.Register(() => tcs.TrySetResult(true))) + { + var completed = await Task.WhenAny(initializeTask, tcs.Task).ConfigureAwait(false); + if (completed != initializeTask) + { + cancellationToken.ThrowIfCancellationRequested(); + } + } + } + await initializeTask.ConfigureAwait(false); + } + public override Task> AllFeatures(DevCycleUser user) { if (!configManager.Initialized) diff --git a/DevCycle.SDK.Server.Local/DevCycle.SDK.Server.Local.csproj b/DevCycle.SDK.Server.Local/DevCycle.SDK.Server.Local.csproj index 120e8e10..2d10b015 100644 --- a/DevCycle.SDK.Server.Local/DevCycle.SDK.Server.Local.csproj +++ b/DevCycle.SDK.Server.Local/DevCycle.SDK.Server.Local.csproj @@ -50,13 +50,13 @@ all - - + + - - + +