Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions DevCycle.SDK.Server.Common/API/DevCycleBaseClient.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<Dictionary<string, Feature>> AllFeatures(DevCycleUser user);
public abstract Task<Dictionary<string, ReadOnlyVariable<object>>> AllVariables(DevCycleUser user);
public abstract Task<Variable<T>> Variable<T>(DevCycleUser user, string key, T defaultValue);
Expand Down
5 changes: 5 additions & 0 deletions DevCycle.SDK.Server.Common/API/DevCycleProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,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<ResolutionDetails<bool>> ResolveBooleanValueAsync(string flagKey, bool defaultValue, EvaluationContext context = null,
CancellationToken cancellationToken = new CancellationToken())
{
Expand Down
10 changes: 10 additions & 0 deletions DevCycle.SDK.Server.Local.MSTests/DevCycleTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Comment thread
jonathannorris marked this conversation as resolved.
}
}
}
29 changes: 26 additions & 3 deletions DevCycle.SDK.Server.Local/Api/DevCycleLocalClient.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -73,8 +75,9 @@
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;

Expand All @@ -84,7 +87,7 @@
ILoggerFactory loggerFactory,
EnvironmentConfigManager configManager,
ILocalBucketing localBucketing,
DevCycleRestClientOptions restClientOptions = null

Check warning on line 90 in DevCycle.SDK.Server.Local/Api/DevCycleLocalClient.cs

View workflow job for this annotation

GitHub Actions / run-example

Cannot convert null literal to non-nullable reference type.

Check warning on line 90 in DevCycle.SDK.Server.Local/Api/DevCycleLocalClient.cs

View workflow job for this annotation

GitHub Actions / run-example

Cannot convert null literal to non-nullable reference type.

Check warning on line 90 in DevCycle.SDK.Server.Local/Api/DevCycleLocalClient.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.
)
{
ValidateSDKKey(sdkKey);
Expand All @@ -103,11 +106,14 @@
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(); });
// Dispatched via Task.Run so the initial config fetch never continues on the
// constructing thread's SynchronizationContext. Callers that block on
// InitializeAsync from a UI or legacy ASP.NET thread would otherwise deadlock.
Comment thread
jonathannorris marked this conversation as resolved.
initializeTask = Task.Run(() => this.configManager.InitializeConfigAsync());
OpenFeatureProvider = new DevCycleProvider(this, logger);
}

Expand Down Expand Up @@ -259,6 +265,23 @@
return OpenFeatureProvider;
}

public override async Task InitializeAsync(CancellationToken cancellationToken = default)
{
if (cancellationToken.CanBeCanceled)
{
var tcs = new TaskCompletionSource<bool>(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<Dictionary<string, Feature>> AllFeatures(DevCycleUser user)
{
if (!configManager.Initialized)
Expand Down
Loading