feat: bump OpenFeature to 2.13.0 and implement provider InitializeAsync#203
feat: bump OpenFeature to 2.13.0 and implement provider InitializeAsync#203jonathannorris wants to merge 7 commits into
Conversation
Implements InitializeAsync on DevCycleProvider so SetProviderAsync properly waits for DevCycleLocalClient to finish its initial config fetch before resolving. Previously, early flag evaluations after SetProviderAsync returned defaults instead of targeted values. - DevCycleBaseClient: add virtual InitializeAsync(CancellationToken) returning Task.CompletedTask (Cloud is a no-op) - DevCycleLocalClient: capture init task; override InitializeAsync to await it with WhenAny+TaskCompletionSource cancellation (netstandard2.0 compatible) - DevCycleProvider: override OpenFeature InitializeAsync, delegating to Client.InitializeAsync - Bump OpenFeature 2.2.0 -> 2.13.0 - Bump Microsoft.Extensions.Logging.* 8.x -> 10.0.0 and System.Text.Json 8/9.x -> 10.0.0 (required by OpenFeature 2.13.0) - Update test/benchmark target frameworks net8.0 -> net10.0
There was a problem hiding this comment.
Pull request overview
This PR updates the SDK’s OpenFeature integration so Api.Instance.SetProviderAsync(...) can await the DevCycle Local client’s initial configuration download, preventing early evaluations from returning defaults. It also bumps OpenFeature to 2.13.0 and aligns dependent packages/framework targets to satisfy the newer dependency requirements.
Changes:
- Implement OpenFeature provider
InitializeAsyncand add aDevCycleBaseClient.InitializeAsync(...)hook;DevCycleLocalClientnow awaits the initial config download task. - Upgrade
OpenFeatureto2.13.0and bump related dependencies (Microsoft.Extensions.Logging.*,System.Text.Json) to10.0.0. - Move benchmark/test projects to
net10.0and add a regression test ensuringSetProviderAsyncwaits for initialization.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| DevCycle.SDK.Server.Local/DevCycle.SDK.Server.Local.csproj | Dependency bumps; adds InternalsVisibleTo entry. |
| DevCycle.SDK.Server.Local/Api/DevCycleLocalClient.cs | Tracks and awaits initial config download via InitializeAsync. |
| DevCycle.SDK.Server.Local.MSTests/DevCycleTest.cs | Adds regression test for OpenFeature initialization waiting behavior. |
| DevCycle.SDK.Server.Local.MSTests/DevCycle.SDK.Server.Local.MSTests.csproj | Targets net10.0 and bumps System.Text.Json to 10.0.0. |
| DevCycle.SDK.Server.Local.Example/DevCycle.SDK.Server.Local.Example.csproj | Bumps System.Text.Json to 10.0.0. |
| DevCycle.SDK.Server.Local.Benchmark/DevCycle.SDK.Server.Local.Benchmark.csproj | Targets net10.0 and bumps System.Text.Json to 10.0.0. |
| DevCycle.SDK.Server.Common/DevCycle.SDK.Server.Common.csproj | Bumps OpenFeature to 2.13.0 and updates logging/json dependencies to 10.0.0. |
| DevCycle.SDK.Server.Common/API/DevCycleProvider.cs | Implements InitializeAsync to delegate to client initialization. |
| DevCycle.SDK.Server.Common/API/DevCycleBaseClient.cs | Adds virtual InitializeAsync(...) no-op by default. |
| DevCycle.SDK.Server.Cloud/DevCycle.SDK.Server.Cloud.csproj | Bumps System.Text.Json to 10.0.0. |
| DevCycle.SDK.Server.Cloud.MSTests/DevCycle.SDK.Server.Cloud.MSTests.csproj | Targets net10.0 and bumps System.Text.Json to 10.0.0. |
| DevCycle.SDK.Server.Cloud.Example/DevCycle.SDK.Server.Cloud.Example.csproj | Bumps System.Text.Json to 10.0.0. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
OpenFeature 2.13.0 requires Microsoft.Extensions.Logging.Abstractions >= 10.0.0, which conflicts with the test harness Docker image (dotnet/sdk:8.0). OpenFeature 2.9.0 has the same InitializeAsync API but only requires MEL >= 8.0.0, staying compatible with .NET 8. Reverts the over-aggressive dependency bumps to MEL 10.0.0, System.Text.Json 10.0.0, net10.0 TFMs — none of those were needed for the init implementation.
…pins MEL.Abstractions 10.0.0 and System.Text.Json 10.0.0 both ship net8.0 TFMs, so they're fully compatible with .NET 8 consumers. The previous attempt wrongly switched test project TFMs to net10.0 (causing the test harness dotnet/sdk:8.0 Docker image to fail). This keeps all TFMs at net8.0 and just pins the package versions that OpenFeature 2.13.0 requires.
- Remove accidental InternalsVisibleTo for OFMultiProviderRepro - Store InitializeConfigAsync task directly instead of wrapping in Task.Run
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…vior Uses a gated HttpMessageHandler backed by a TaskCompletionSource to hold the config response. Verifies SetProviderAsync is pending (not completed) while initialization is blocked, then releases the gate and confirms flag evaluation returns a real value — not the default.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 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); |
Summary
InitializeAsynconDevCycleProvidersoApi.Instance.SetProviderAsync(...)properly waits forDevCycleLocalClientto finish downloading its initial config before resolving. Without this, early flag evaluations afterSetProviderAsyncreturn defaults instead of targeted values.Microsoft.Extensions.Logging.*8.x → 10.0.0 andSystem.Text.Json8/9.x → 10.0.0 (required by OpenFeature 2.13.0; both packages shipnet8.0TFMs so .NET 8 consumers are unaffected)Motivation
Other DevCycle SDKs already bridge the client initialization signal into the OpenFeature provider's
initializemethod (Node awaitsonClientInitialized(), Java/Python poll with a 2s timeout). The .NET provider was missing this —SetProviderAsyncresolved immediately even when the local client hadn't yet fetched its config from the CDN.Implementation
DevCycleBaseClientgets a newvirtual Task InitializeAsync(CancellationToken)that returnsTask.CompletedTaskby default (Cloud is a no-op).DevCycleLocalClientoverrides it to await the init task that was previously fired-and-forgotten in the constructor.DevCycleProvider.InitializeAsyncdelegates toClient.InitializeAsync, wiring the OpenFeature provider lifecycle into the DevCycle client's readiness signal.The cancellation path uses
WhenAny+TaskCompletionSourceto stay compatible withnetstandard2.0(whereTask.WaitAsync(CancellationToken)isn't available).Notes
Requires DevCycleHQ/test-harness#600 to land first — the dotnet proxy needs explicit
MEL.AbstractionsandSystem.Text.Json10.0.0 pins to avoid a NuGetNU1605conflict in thedotnet/sdk:8.0Docker image.